Module Name:    src
Committed By:   matt
Date:           Sun Oct  7 20:14:08 UTC 2012

Modified Files:
        src/sys/arch/arm/broadcom: bcm53xx_eth.c

Log Message:
Don't just rely on softints for handling rx traffic since that can starve
the system and eventually cause a watchdog timer to reset.  Instead use
softints for "small" number of packets and use a workqueue thread for large
numbers.  This allows receive processing to be handling at normal priorities
and won't starve out other parts of the system.


To generate a diff of this commit:
cvs rdiff -u -r1.7 -r1.8 src/sys/arch/arm/broadcom/bcm53xx_eth.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/arch/arm/broadcom/bcm53xx_eth.c
diff -u src/sys/arch/arm/broadcom/bcm53xx_eth.c:1.7 src/sys/arch/arm/broadcom/bcm53xx_eth.c:1.8
--- src/sys/arch/arm/broadcom/bcm53xx_eth.c:1.7	Sat Oct  6 01:30:46 2012
+++ src/sys/arch/arm/broadcom/bcm53xx_eth.c	Sun Oct  7 20:14:08 2012
@@ -33,7 +33,7 @@
 
 #include <sys/cdefs.h>
 
-__KERNEL_RCSID(1, "$NetBSD: bcm53xx_eth.c,v 1.7 2012/10/06 01:30:46 matt Exp $");
+__KERNEL_RCSID(1, "$NetBSD: bcm53xx_eth.c,v 1.8 2012/10/07 20:14:08 matt Exp $");
 
 #include <sys/param.h>
 #include <sys/atomic.h>
@@ -45,6 +45,7 @@ __KERNEL_RCSID(1, "$NetBSD: bcm53xx_eth.
 #include <sys/mutex.h>
 #include <sys/socket.h>
 #include <sys/systm.h>
+#include <sys/workqueue.h>
 
 #include <net/if.h>
 #include <net/if_ether.h>
@@ -63,8 +64,9 @@ __KERNEL_RCSID(1, "$NetBSD: bcm53xx_eth.
 #define	BCMETH_MAXTXMBUFS	32
 #define	BCMETH_NTXSEGS		30
 #define	BCMETH_MAXRXMBUFS	255
-#define	BCMETH_MINRXMBUFS	32
+#define	BCMETH_MINRXMBUFS	64
 #define	BCMETH_NRXSEGS		1
+#define	BCMETH_RINGSIZE		PAGE_SIZE
 
 static int bcmeth_ccb_match(device_t, cfdata_t, void *);
 static void bcmeth_ccb_attach(device_t, device_t, void *);
@@ -132,20 +134,28 @@ struct bcmeth_softc {
 	uint32_t sc_maxfrm;
 	uint32_t sc_cmdcfg;
 	uint32_t sc_intmask;
+	uint32_t sc_rcvlazy;
 	volatile uint32_t sc_soft_flags;
 #define	SOFT_RXINTR		0x01
-#define	SOFT_RXUNDERFLOW	0x02
-#define	SOFT_TXINTR		0x04
-#define	SOFT_REINIT		0x08
+#define	SOFT_TXINTR		0x02
 
 	struct evcnt sc_ev_intr;
 	struct evcnt sc_ev_soft_intr;
+	struct evcnt sc_ev_work;;
 	struct evcnt sc_ev_tx_stall;
 
 	struct ifqueue sc_rx_bufcache;
 	struct bcmeth_mapcache *sc_rx_mapcache;     
 	struct bcmeth_mapcache *sc_tx_mapcache;
 
+	struct workqueue *sc_workq;
+	struct work sc_work;
+
+	volatile uint32_t sc_work_flags;
+#define	WORK_RXINTR		0x01
+#define	WORK_RXUNDERFLOW	0x02
+#define	WORK_REINIT		0x04
+
 	uint8_t sc_enaddr[ETHER_ADDR_LEN];
 };
 
@@ -188,6 +198,7 @@ static void bcmeth_rxq_reset(struct bcme
 
 static int bcmeth_intr(void *);
 static void bcmeth_soft_intr(void *);
+static void bcmeth_worker(struct work *, void *);
 
 static int bcmeth_mediachange(struct ifnet *);
 static void bcmeth_mediastatus(struct ifnet *, struct ifmediareq *);
@@ -296,6 +307,13 @@ bcmeth_ccb_attach(device_t parent, devic
 		return;
 	}
 
+	error = workqueue_create(&sc->sc_workq, xname, bcmeth_worker, sc,
+	    (PRI_USER + MAXPRI_USER) / 2, IPL_SOFTNET, WQ_MPSAFE|WQ_PERCPU);
+	if (error) {
+		aprint_error(": failed to create workqueue: %d\n", error);
+		return;
+	}
+
 	sc->sc_soft_ih = softint_establish(SOFTINT_MPSAFE | SOFTINT_NET,
 	    bcmeth_soft_intr, sc);
 
@@ -350,6 +368,8 @@ bcmeth_ccb_attach(device_t parent, devic
 	    NULL, xname, "intr");
 	evcnt_attach_dynamic(&sc->sc_ev_soft_intr, EVCNT_TYPE_INTR,
 	    NULL, xname, "soft intr");
+	evcnt_attach_dynamic(&sc->sc_ev_work, EVCNT_TYPE_MISC,
+	    NULL, xname, "work items");
 	evcnt_attach_dynamic(&sc->sc_ev_tx_stall, EVCNT_TYPE_MISC,
 	    NULL, xname, "tx stalls");
 }
@@ -461,8 +481,9 @@ bcmeth_ifinit(struct ifnet *ifp)
 	bcmeth_write_4(sc, GMAC_DEVCONTROL, devctl);
 
 	/* Setup lazy receive (at most 1ms). */
-	bcmeth_write_4(sc, GMAC_INTRCVLAZY, __SHIFTIN(10, INTRCVLAZY_FRAMECOUNT)
-	     | __SHIFTIN(125000000 / 1000, INTRCVLAZY_TIMEOUT));
+	sc->sc_rcvlazy =  __SHIFTIN(4, INTRCVLAZY_FRAMECOUNT)
+	     | __SHIFTIN(125000000 / 1000, INTRCVLAZY_TIMEOUT);
+	bcmeth_write_4(sc, GMAC_INTRCVLAZY, sc->sc_rcvlazy);
 
 	/* 11. Enable transmit queues in TQUEUE, and ensure that the transmit scheduling mode is correctly set in TCTRL. */
 	sc->sc_intmask |= XMTINT_0|XMTUF;
@@ -937,7 +958,7 @@ bcmeth_rxq_consume(
 			return;
 		}
 		
-		uint32_t rcvsts0 = bcmeth_read_4(sc, GMAC_RCVSTATUS0);
+		uint32_t rcvsts0 = bcmeth_read_4(sc, rxq->rxq_reg_rcvsts0);
 		uint32_t currdscr = __SHIFTOUT(rcvsts0, RCV_CURRDSCR);
 		if (consumer == rxq->rxq_first + currdscr) {
 			rxq->rxq_consumer = consumer;
@@ -1092,19 +1113,18 @@ bcmeth_rxq_attach(
 	struct bcmeth_rxqueue *rxq,
 	u_int qno)
 {
-	size_t map_size = PAGE_SIZE;
-	size_t desc_count = map_size / sizeof(rxq->rxq_first[0]);
+	size_t desc_count = BCMETH_RINGSIZE / sizeof(rxq->rxq_first[0]);
 	int error;
 	void *descs;
 
 	KASSERT(desc_count == 256 || desc_count == 512);
 
-	error = bcmeth_dmamem_alloc(sc->sc_dmat, map_size,
+	error = bcmeth_dmamem_alloc(sc->sc_dmat, BCMETH_RINGSIZE,
 	   &rxq->rxq_descmap_seg, &rxq->rxq_descmap, &descs);
 	if (error)
 		return error;
 
-	memset(descs, 0, map_size);
+	memset(descs, 0, BCMETH_RINGSIZE);
 	rxq->rxq_first = descs;
 	rxq->rxq_last = rxq->rxq_first + desc_count;
 	rxq->rxq_consumer = descs;
@@ -1143,19 +1163,18 @@ bcmeth_txq_attach(
 	struct bcmeth_txqueue *txq,
 	u_int qno)
 {
-	size_t map_size = PAGE_SIZE;
-	size_t desc_count = map_size / sizeof(txq->txq_first[0]);
+	size_t desc_count = BCMETH_RINGSIZE / sizeof(txq->txq_first[0]);
 	int error;
 	void *descs;
 
 	KASSERT(desc_count == 256 || desc_count == 512);
 
-	error = bcmeth_dmamem_alloc(sc->sc_dmat, map_size,
+	error = bcmeth_dmamem_alloc(sc->sc_dmat, BCMETH_RINGSIZE,
 	   &txq->txq_descmap_seg, &txq->txq_descmap, &descs);
 	if (error)
 		return error;
 
-	memset(descs, 0, map_size);
+	memset(descs, 0, BCMETH_RINGSIZE);
 	txq->txq_first = descs;
 	txq->txq_last = txq->txq_first + desc_count;
 	txq->txq_consumer = descs;
@@ -1504,6 +1523,7 @@ bcmeth_intr(void *arg)
 {
 	struct bcmeth_softc * const sc = arg;
 	uint32_t soft_flags = 0;
+	uint32_t work_flags = 0;
 	int rv = 0;
 
 	mutex_enter(sc->sc_hwlock);
@@ -1518,13 +1538,36 @@ bcmeth_intr(void *arg)
 			break;
 		}
 #if 0
-		aprint_normal_dev(sc->sc_dev, "%s: ievent=%#x intmask=%#x\n", 
-		    __func__, ievent, bcmeth_read_4(sc, GMAC_INTMASK));
+		aprint_normal_dev(sc->sc_dev, "%s: intstatus=%#x intmask=%#x\n", 
+		    __func__, intstatus, bcmeth_read_4(sc, GMAC_INTMASK));
 #endif
 		if (intstatus & RCVINT) {
+			struct bcmeth_rxqueue * const rxq = &sc->sc_rxq;
 			intstatus &= ~RCVINT;
 			sc->sc_intmask &= ~RCVINT;
-			soft_flags |= SOFT_RXINTR;
+
+			uint32_t rcvsts0 = bcmeth_read_4(sc, rxq->rxq_reg_rcvsts0);
+			uint32_t descs = __SHIFTOUT(rcvsts0, RCV_CURRDSCR);
+			if (descs < rxq->rxq_consumer - rxq->rxq_first) {
+				/*
+				 * We wrapped at the end so count how far
+				 * we are from the end.
+				 */
+				descs += rxq->rxq_last - rxq->rxq_consumer;
+			} else {
+				descs -= rxq->rxq_consumer - rxq->rxq_first;
+			}
+			/*
+			 * If we "timedout" we can't be hogging so use
+			 * softints.  If we exceeded then we might hogging
+			 * so let the workqueue deal with them.
+			 */
+			const uint32_t framecount = __SHIFTOUT(sc->sc_rcvlazy, INTRCVLAZY_FRAMECOUNT);
+			if (descs < framecount) {
+				soft_flags |= SOFT_RXINTR;
+			} else {
+				work_flags |= WORK_RXINTR;
+			}
 		}
 
 		if (intstatus & XMTINT_0) {
@@ -1536,7 +1579,7 @@ bcmeth_intr(void *arg)
 		if (intstatus & RCVDESCUF) {
 			intstatus &= ~RCVDESCUF;
 			sc->sc_intmask &= ~RCVDESCUF;
-			soft_flags |= SOFT_RXUNDERFLOW;
+			work_flags |= WORK_RXUNDERFLOW;
 		}
 
 		if (intstatus) {
@@ -1544,15 +1587,28 @@ bcmeth_intr(void *arg)
 			    intstatus);
 			Debugger();
 			sc->sc_intmask &= ~intstatus;
-			soft_flags |= SOFT_REINIT;
+			work_flags |= WORK_REINIT;
 			break;
 		}
 	}
 
-	if (soft_flags) {
+	if (work_flags | soft_flags) {
 		bcmeth_write_4(sc, GMAC_INTMASK, sc->sc_intmask);
-		atomic_or_uint(&sc->sc_soft_flags, soft_flags);
-		softint_schedule(sc->sc_soft_ih);
+	}
+
+	if (work_flags) {
+		if (sc->sc_work_flags == 0) {
+			workqueue_enqueue(sc->sc_workq, &sc->sc_work, NULL);
+		}
+		atomic_or_32(&sc->sc_work_flags, work_flags);
+		rv = 1;
+	}
+
+	if (soft_flags) {
+		if (sc->sc_soft_flags == 0) {
+			softint_schedule(sc->sc_soft_ih);
+		}
+		atomic_or_32(&sc->sc_soft_flags, soft_flags);
 		rv = 1;
 	}
 
@@ -1573,14 +1629,58 @@ bcmeth_soft_intr(void *arg)
 
 	sc->sc_ev_soft_intr.ev_count++;
 
-	if (soft_flags & SOFT_REINIT) {
+	if ((soft_flags & SOFT_TXINTR)
+	    || bcmeth_txq_active_p(sc, &sc->sc_txq)) {
+		/*
+		 * Let's do what we came here for.  Consume transmitted
+		 * packets off the the transmit ring.
+		 */
+		if (!bcmeth_txq_consume(sc, &sc->sc_txq)
+		    || !bcmeth_txq_enqueue(sc, &sc->sc_txq)) {
+			sc->sc_ev_tx_stall.ev_count++;
+			ifp->if_flags |= IFF_OACTIVE;
+		} else {
+			ifp->if_flags &= ~IFF_OACTIVE;
+		}
+		sc->sc_intmask |= XMTINT_0;
+	}
+
+	if (soft_flags & SOFT_RXINTR) {
+		/*
+		 * Let's consume 
+		 */
+		bcmeth_rxq_consume(sc, &sc->sc_rxq);
+		sc->sc_intmask |= RCVINT;
+	}
+
+	if (ifp->if_flags & IFF_RUNNING) {
+		bcmeth_rxq_produce(sc, &sc->sc_rxq);
+		bcmeth_write_4(sc, GMAC_INTMASK, sc->sc_intmask);
+	}
+
+	mutex_exit(sc->sc_lock);
+}
+
+void
+bcmeth_worker(struct work *wk, void *arg)
+{
+	struct bcmeth_softc * const sc = arg;
+	struct ifnet * const ifp = &sc->sc_if;
+
+	mutex_enter(sc->sc_lock);
+
+	sc->sc_ev_work.ev_count++;
+
+	uint32_t work_flags = atomic_swap_32(&sc->sc_work_flags, 0);
+	if (work_flags & WORK_REINIT) {
 		int s = splnet();
+		sc->sc_soft_flags = 0;
 		bcmeth_ifinit(ifp);
 		splx(s);
-		soft_flags = 0;
+		work_flags &= ~WORK_RXUNDERFLOW;
 	}
 
-	if (soft_flags & SOFT_RXUNDERFLOW) {
+	if (work_flags & WORK_RXUNDERFLOW) {
 		struct bcmeth_rxqueue * const rxq = &sc->sc_rxq;
 		size_t threshold = 5 * rxq->rxq_threshold / 4;
 		if (threshold >= rxq->rxq_last - rxq->rxq_first) {
@@ -1594,23 +1694,7 @@ bcmeth_soft_intr(void *arg)
 		rxq->rxq_threshold = threshold;
 	}
 
-	if ((soft_flags & SOFT_TXINTR)
-	    || bcmeth_txq_active_p(sc, &sc->sc_txq)) {
-		/*
-		 * Let's do what we came here for.  Consume transmitted
-		 * packets off the the transmit ring.
-		 */
-		if (!bcmeth_txq_consume(sc, &sc->sc_txq)
-		    || !bcmeth_txq_enqueue(sc, &sc->sc_txq)) {
-			sc->sc_ev_tx_stall.ev_count++;
-			ifp->if_flags |= IFF_OACTIVE;
-		} else {
-			ifp->if_flags &= ~IFF_OACTIVE;
-		}
-		sc->sc_intmask |= XMTINT_0;
-	}
-
-	if (soft_flags & (SOFT_RXINTR|SOFT_RXUNDERFLOW)) {
+	if (work_flags & WORK_RXINTR) {
 		/*
 		 * Let's consume 
 		 */
@@ -1621,8 +1705,6 @@ bcmeth_soft_intr(void *arg)
 	if (ifp->if_flags & IFF_RUNNING) {
 		bcmeth_rxq_produce(sc, &sc->sc_rxq);
 		bcmeth_write_4(sc, GMAC_INTMASK, sc->sc_intmask);
-	} else {
-		KASSERT((soft_flags & SOFT_RXUNDERFLOW) == 0);
 	}
 
 	mutex_exit(sc->sc_lock);

Reply via email to