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);