svn commit: r201868 - stable/8/sys/dev/vge

Pyun YongHyeon yongari at FreeBSD.org
Sat Jan 9 00:17:44 UTC 2010


Author: yongari
Date: Sat Jan  9 00:17:43 2010
New Revision: 201868
URL: http://svn.freebsd.org/changeset/base/201868

Log:
  MFC r200638:
    Implement interrupt moderation scheme supported by VT61xx
    controllers. TX/RX interrupt mitigation is controlled by
    VGE_TXSUPPTHR and VGE_RXSUPPTHR register. These registers suppress
    generation of interrupts until the programmed frames counter equals
    to the registers. VT61xx also supports interrupt hold off timer
    register. If this interrupt hold off timer is active all interrupts
    would be disabled until the timer reaches to 0. The timer value is
    reloaded whenever VGE_ISR register written. The timer resolution is
    about 20us.
  
    Previously vge(4) used single shot timer to reduce Tx completion
    interrupts. This required VGE_CRS1 register access in Tx
    start/completion handler to rearm new timeout value and it did not
    show satisfactory result(more than 50k interrupts under load). Rx
    interrupts was not moderated at all such that vge(4) used to
    generate too many interrupts which in turn made polling(4) better
    approach under high network load.
  
    This change activates all interrupt moderation mechanism and
    initial values were tuned to generate interrupt less than 8k per
    second. That number of interrupts wouldn't add additional packet
    latencies compared to polling(4). These interrupt parameters could
    be changed with sysctl.
    dev.vge.%d.int_holdoff
    dev.vge.%d.rx_coal_pkt
    dev.vge.%d.tx_coal_pkt
    Interface has be brought down and up again before change take
    effect.
  
    With interrupt moderation there is no more need to loop in
    interrupt handler. This loop always added one more register access.
    While I'm here remove dead code which tried to implement subset of
    interrupt moderation.

Modified:
  stable/8/sys/dev/vge/if_vge.c
  stable/8/sys/dev/vge/if_vgereg.h
  stable/8/sys/dev/vge/if_vgevar.h
Directory Properties:
  stable/8/sys/   (props changed)
  stable/8/sys/amd64/include/xen/   (props changed)
  stable/8/sys/cddl/contrib/opensolaris/   (props changed)
  stable/8/sys/contrib/dev/acpica/   (props changed)
  stable/8/sys/contrib/pf/   (props changed)
  stable/8/sys/dev/xen/xenpci/   (props changed)

Modified: stable/8/sys/dev/vge/if_vge.c
==============================================================================
--- stable/8/sys/dev/vge/if_vge.c	Sat Jan  9 00:14:50 2010	(r201867)
+++ stable/8/sys/dev/vge/if_vge.c	Sat Jan  9 00:17:43 2010	(r201868)
@@ -175,6 +175,7 @@ static int	vge_ifmedia_upd(struct ifnet 
 static void	vge_init(void *);
 static void	vge_init_locked(struct vge_softc *);
 static void	vge_intr(void *);
+static void	vge_intr_holdoff(struct vge_softc *);
 static int	vge_ioctl(struct ifnet *, u_long, caddr_t);
 static void	vge_link_statchg(void *);
 static int	vge_miibus_readreg(device_t, int, int);
@@ -1631,15 +1632,6 @@ vge_txeof(struct vge_softc *sc)
 	sc->vge_cdata.vge_tx_considx = cons;
 	if (sc->vge_cdata.vge_tx_cnt == 0)
 		sc->vge_timer = 0;
-	else {
-		/*
-		 * If not all descriptors have been released reaped yet,
-		 * reload the timer so that we will eventually get another
-		 * interrupt that will cause us to re-enter this routine.
-		 * This is done in case the transmitter has gone idle.
-		 */
-		CSR_WRITE_1(sc, VGE_CRS1, VGE_CR1_TIMER0_ENABLE);
-	}
 }
 
 static void
@@ -1746,30 +1738,21 @@ vge_intr(void *arg)
 
 	/* Disable interrupts */
 	CSR_WRITE_1(sc, VGE_CRC3, VGE_CR3_INT_GMSK);
-
-	for (;;) {
-
-		status = CSR_READ_4(sc, VGE_ISR);
-		/* If the card has gone away the read returns 0xffff. */
-		if (status == 0xFFFFFFFF)
-			break;
-
-		if (status)
-			CSR_WRITE_4(sc, VGE_ISR, status);
-
-		if ((status & VGE_INTRS) == 0)
-			break;
-
+	status = CSR_READ_4(sc, VGE_ISR);
+	CSR_WRITE_4(sc, VGE_ISR, status | VGE_ISR_HOLDOFF_RELOAD);
+	/* If the card has gone away the read returns 0xffff. */
+	if (status == 0xFFFFFFFF || (status & VGE_INTRS) == 0)
+		goto done;
+	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) {
 		if (status & (VGE_ISR_RXOK|VGE_ISR_RXOK_HIPRIO))
 			vge_rxeof(sc, VGE_RX_DESC_CNT);
-
 		if (status & (VGE_ISR_RXOFLOW|VGE_ISR_RXNODESC)) {
 			vge_rxeof(sc, VGE_RX_DESC_CNT);
 			CSR_WRITE_1(sc, VGE_RXQCSRS, VGE_RXQCSR_RUN);
 			CSR_WRITE_1(sc, VGE_RXQCSRS, VGE_RXQCSR_WAK);
 		}
 
-		if (status & (VGE_ISR_TXOK0|VGE_ISR_TIMER0))
+		if (status & (VGE_ISR_TXOK0|VGE_ISR_TXOK_HIPRIO))
 			vge_txeof(sc);
 
 		if (status & (VGE_ISR_TXDMA_STALL|VGE_ISR_RXDMA_STALL)) {
@@ -1780,13 +1763,14 @@ vge_intr(void *arg)
 		if (status & VGE_ISR_LINKSTS)
 			vge_link_statchg(sc);
 	}
+done:
+	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) {
+		/* Re-enable interrupts */
+		CSR_WRITE_1(sc, VGE_CRS3, VGE_CR3_INT_GMSK);
 
-	/* Re-enable interrupts */
-	CSR_WRITE_1(sc, VGE_CRS3, VGE_CR3_INT_GMSK);
-
-	if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
-		vge_start_locked(ifp);
-
+		if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
+			vge_start_locked(ifp);
+	}
 	VGE_UNLOCK(sc);
 }
 
@@ -1985,17 +1969,6 @@ vge_start_locked(struct ifnet *ifp)
 		/* Issue a transmit command. */
 		CSR_WRITE_2(sc, VGE_TXQCSRS, VGE_TXQCSR_WAK0);
 		/*
-		 * Use the countdown timer for interrupt moderation.
-		 * 'TX done' interrupts are disabled. Instead, we reset the
-		 * countdown timer, which will begin counting until it hits
-		 * the value in the SSTIMER register, and then trigger an
-		 * interrupt. Each time we set the TIMER0_ENABLE bit, the
-		 * the timer count is reloaded. Only when the transmitter
-		 * is idle will the timer hit 0 and an interrupt fire.
-		 */
-		CSR_WRITE_1(sc, VGE_CRS1, VGE_CR1_TIMER0_ENABLE);
-
-		/*
 		 * Set a timeout in case the chip goes out to lunch.
 		 */
 		sc->vge_timer = 5;
@@ -2084,6 +2057,9 @@ vge_init_locked(struct vge_softc *sc)
 	CSR_WRITE_2(sc, VGE_RXDESCNUM, VGE_RX_DESC_CNT - 1);
 	CSR_WRITE_2(sc, VGE_RXDESC_RESIDUECNT, VGE_RX_DESC_CNT);
 
+	/* Configure interrupt moderation. */
+	vge_intr_holdoff(sc);
+
 	/* Enable and wake up the RX descriptor queue */
 	CSR_WRITE_1(sc, VGE_RXQCSRS, VGE_RXQCSR_RUN);
 	CSR_WRITE_1(sc, VGE_RXQCSRS, VGE_RXQCSR_WAK);
@@ -2110,42 +2086,6 @@ vge_init_locked(struct vge_softc *sc)
 	CSR_WRITE_1(sc, VGE_CRS0,
 	    VGE_CR0_TX_ENABLE|VGE_CR0_RX_ENABLE|VGE_CR0_START);
 
-	/*
-	 * Configure one-shot timer for microsecond
-	 * resolution and load it for 500 usecs.
-	 */
-	CSR_SETBIT_1(sc, VGE_DIAGCTL, VGE_DIAGCTL_TIMER0_RES);
-	CSR_WRITE_2(sc, VGE_SSTIMER, 400);
-
-	/*
-	 * Configure interrupt moderation for receive. Enable
-	 * the holdoff counter and load it, and set the RX
-	 * suppression count to the number of descriptors we
-	 * want to allow before triggering an interrupt.
-	 * The holdoff timer is in units of 20 usecs.
-	 */
-
-#ifdef notyet
-	CSR_WRITE_1(sc, VGE_INTCTL1, VGE_INTCTL_TXINTSUP_DISABLE);
-	/* Select the interrupt holdoff timer page. */
-	CSR_CLRBIT_1(sc, VGE_CAMCTL, VGE_CAMCTL_PAGESEL);
-	CSR_SETBIT_1(sc, VGE_CAMCTL, VGE_PAGESEL_INTHLDOFF);
-	CSR_WRITE_1(sc, VGE_INTHOLDOFF, 10); /* ~200 usecs */
-
-	/* Enable use of the holdoff timer. */
-	CSR_WRITE_1(sc, VGE_CRS3, VGE_CR3_INT_HOLDOFF);
-	CSR_WRITE_1(sc, VGE_INTCTL1, VGE_INTCTL_SC_RELOAD);
-
-	/* Select the RX suppression threshold page. */
-	CSR_CLRBIT_1(sc, VGE_CAMCTL, VGE_CAMCTL_PAGESEL);
-	CSR_SETBIT_1(sc, VGE_CAMCTL, VGE_PAGESEL_RXSUPPTHR);
-	CSR_WRITE_1(sc, VGE_RXSUPPTHR, 64); /* interrupt after 64 packets */
-
-	/* Restore the page select bits. */
-	CSR_CLRBIT_1(sc, VGE_CAMCTL, VGE_CAMCTL_PAGESEL);
-	CSR_SETBIT_1(sc, VGE_CAMCTL, VGE_PAGESEL_MAR);
-#endif
-
 #ifdef DEVICE_POLLING
 	/*
 	 * Disable interrupts if we are polling.
@@ -2494,6 +2434,25 @@ vge_sysctl_node(struct vge_softc *sc)
 	stats = &sc->vge_stats;
 	ctx = device_get_sysctl_ctx(sc->vge_dev);
 	child = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->vge_dev));
+
+	SYSCTL_ADD_INT(ctx, child, OID_AUTO, "int_holdoff",
+	    CTLFLAG_RW, &sc->vge_int_holdoff, 0, "interrupt holdoff");
+	SYSCTL_ADD_INT(ctx, child, OID_AUTO, "rx_coal_pkt",
+	    CTLFLAG_RW, &sc->vge_rx_coal_pkt, 0, "rx coalescing packet");
+	SYSCTL_ADD_INT(ctx, child, OID_AUTO, "tx_coal_pkt",
+	    CTLFLAG_RW, &sc->vge_tx_coal_pkt, 0, "tx coalescing packet");
+
+	/* Pull in device tunables. */
+	sc->vge_int_holdoff = VGE_INT_HOLDOFF_DEFAULT;
+	resource_int_value(device_get_name(sc->vge_dev),
+	    device_get_unit(sc->vge_dev), "int_holdoff", &sc->vge_int_holdoff);
+	sc->vge_rx_coal_pkt = VGE_RX_COAL_PKT_DEFAULT;
+	resource_int_value(device_get_name(sc->vge_dev),
+	    device_get_unit(sc->vge_dev), "rx_coal_pkt", &sc->vge_rx_coal_pkt);
+	sc->vge_tx_coal_pkt = VGE_TX_COAL_PKT_DEFAULT;
+	resource_int_value(device_get_name(sc->vge_dev),
+	    device_get_unit(sc->vge_dev), "tx_coal_pkt", &sc->vge_tx_coal_pkt);
+
 	tree = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "stats", CTLFLAG_RD,
 	    NULL, "VGE statistics");
 	parent = SYSCTL_CHILDREN(tree);
@@ -2699,3 +2658,51 @@ reset_idx:
 	    mib[VGE_MIB_RX_SYMERRS] +
 	    mib[VGE_MIB_RX_LENERRS];
 }
+
+static void
+vge_intr_holdoff(struct vge_softc *sc)
+{
+	uint8_t intctl;
+
+	VGE_LOCK_ASSERT(sc);
+
+	/*
+	 * Set Tx interrupt supression threshold.
+	 * It's possible to use single-shot timer in VGE_CRS1 register
+	 * in Tx path such that driver can remove most of Tx completion
+	 * interrupts. However this requires additional access to
+	 * VGE_CRS1 register to reload the timer in addintion to
+	 * activating Tx kick command. Another downside is we don't know
+	 * what single-shot timer value should be used in advance so
+	 * reclaiming transmitted mbufs could be delayed a lot which in
+	 * turn slows down Tx operation.
+	 */
+	CSR_WRITE_1(sc, VGE_CAMCTL, VGE_PAGESEL_TXSUPPTHR);
+	CSR_WRITE_1(sc, VGE_TXSUPPTHR, sc->vge_tx_coal_pkt);
+
+	/* Set Rx interrupt suppresion threshold. */
+	CSR_WRITE_1(sc, VGE_CAMCTL, VGE_PAGESEL_RXSUPPTHR);
+	CSR_WRITE_1(sc, VGE_RXSUPPTHR, sc->vge_rx_coal_pkt);
+
+	intctl = CSR_READ_1(sc, VGE_INTCTL1);
+	intctl &= ~VGE_INTCTL_SC_RELOAD;
+	intctl |= VGE_INTCTL_HC_RELOAD;
+	if (sc->vge_tx_coal_pkt <= 0)
+		intctl |= VGE_INTCTL_TXINTSUP_DISABLE;
+	else
+		intctl &= ~VGE_INTCTL_TXINTSUP_DISABLE;
+	if (sc->vge_rx_coal_pkt <= 0)
+		intctl |= VGE_INTCTL_RXINTSUP_DISABLE;
+	else
+		intctl &= ~VGE_INTCTL_RXINTSUP_DISABLE;
+	CSR_WRITE_1(sc, VGE_INTCTL1, intctl);
+	CSR_WRITE_1(sc, VGE_CRC3, VGE_CR3_INT_HOLDOFF);
+	if (sc->vge_int_holdoff > 0) {
+		/* Set interrupt holdoff timer. */
+		CSR_WRITE_1(sc, VGE_CAMCTL, VGE_PAGESEL_INTHLDOFF);
+		CSR_WRITE_1(sc, VGE_INTHOLDOFF,
+		    VGE_INT_HOLDOFF_USEC(sc->vge_int_holdoff));
+		/* Enable holdoff timer. */
+		CSR_WRITE_1(sc, VGE_CRS3, VGE_CR3_INT_HOLDOFF);
+	}
+}

Modified: stable/8/sys/dev/vge/if_vgereg.h
==============================================================================
--- stable/8/sys/dev/vge/if_vgereg.h	Sat Jan  9 00:14:50 2010	(r201867)
+++ stable/8/sys/dev/vge/if_vgereg.h	Sat Jan  9 00:17:43 2010	(r201868)
@@ -300,8 +300,7 @@
 #define VGE_INTRS	(VGE_ISR_TXOK0|VGE_ISR_RXOK|VGE_ISR_STOPPED|	\
 			 VGE_ISR_RXOFLOW|VGE_ISR_PHYINT|		\
 			 VGE_ISR_LINKSTS|VGE_ISR_RXNODESC|		\
-			 VGE_ISR_RXDMA_STALL|VGE_ISR_TXDMA_STALL|	\
-			 VGE_ISR_TIMER0)
+			 VGE_ISR_RXDMA_STALL|VGE_ISR_TXDMA_STALL)
 
 /* Interrupt mask register */
 

Modified: stable/8/sys/dev/vge/if_vgevar.h
==============================================================================
--- stable/8/sys/dev/vge/if_vgevar.h	Sat Jan  9 00:14:50 2010	(r201867)
+++ stable/8/sys/dev/vge/if_vgevar.h	Sat Jan  9 00:17:43 2010	(r201868)
@@ -65,6 +65,20 @@
 #define VGE_RXBYTES(x)		(((x) & VGE_RDSTS_BUFSIZ) >> 16)
 #define VGE_MIN_FRAMELEN	60
 
+#define	VGE_INT_HOLDOFF_TICK	20
+#define	VGE_INT_HOLDOFF_USEC(x)	((x) / VGE_INT_HOLDOFF_TICK)
+#define	VGE_INT_HOLDOFF_MIN	0
+#define	VGE_INT_HOLDOFF_MAX	(255 * VGE_INT_HOLDOFF_TICK)
+#define	VGE_INT_HOLDOFF_DEFAULT	150
+
+#define	VGE_RX_COAL_PKT_MIN	1
+#define	VGE_RX_COAL_PKT_MAX	VGE_RX_DESC_CNT
+#define	VGE_RX_COAL_PKT_DEFAULT	64
+
+#define	VGE_TX_COAL_PKT_MIN	1
+#define	VGE_TX_COAL_PKT_MAX	VGE_TX_DESC_CNT
+#define	VGE_TX_COAL_PKT_DEFAULT	128
+
 struct vge_type {
 	uint16_t		vge_vid;
 	uint16_t		vge_did;
@@ -176,6 +190,9 @@ struct vge_softc {
 #define	VGE_FLAG_LINK		0x8000
 	int			vge_expcap;
 	int			vge_camidx;
+	int			vge_int_holdoff;
+	int			vge_rx_coal_pkt;
+	int			vge_tx_coal_pkt;
 	struct mtx		vge_mtx;
 	struct callout		vge_watchdog;
 	int			vge_timer;


More information about the svn-src-stable-8 mailing list