summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoryongari <yongari@FreeBSD.org>2009-11-22 20:31:40 +0000
committeryongari <yongari@FreeBSD.org>2009-11-22 20:31:40 +0000
commit62db90a220c517dadf8f18e529ad6b728780afd2 (patch)
tree5ec5f10a33233ef522451d5da7e4af1dd7a5bb80
parent0e8a843f407768256c95a56fe1a2a5ceca7e081f (diff)
downloadFreeBSD-src-62db90a220c517dadf8f18e529ad6b728780afd2.zip
FreeBSD-src-62db90a220c517dadf8f18e529ad6b728780afd2.tar.gz
For MSI case, interrupt is not shared and we don't need to force
PCI flush to get correct status block update. Add an optimized interrupt handler that is activated for MSI case. Actual interrupt handling is done by taskqueue such that the handler does not require driver lock for Rx path. The MSI capable bge(4) controllers automatically disables further interrupt once it enters interrupt state so we don't need PIO access to disable interrupt in interrupt handler.
-rw-r--r--sys/dev/bge/if_bge.c114
-rw-r--r--sys/dev/bge/if_bgereg.h2
2 files changed, 106 insertions, 10 deletions
diff --git a/sys/dev/bge/if_bge.c b/sys/dev/bge/if_bge.c
index cd7c40f..94dd4a1 100644
--- a/sys/dev/bge/if_bge.c
+++ b/sys/dev/bge/if_bge.c
@@ -80,6 +80,7 @@ __FBSDID("$FreeBSD$");
#include <sys/module.h>
#include <sys/socket.h>
#include <sys/sysctl.h>
+#include <sys/taskqueue.h>
#include <net/if.h>
#include <net/if_arp.h>
@@ -362,7 +363,7 @@ static int bge_get_eaddr_eeprom(struct bge_softc *, uint8_t[]);
static int bge_get_eaddr(struct bge_softc *, uint8_t[]);
static void bge_txeof(struct bge_softc *, uint16_t);
-static int bge_rxeof(struct bge_softc *, uint16_t);
+static int bge_rxeof(struct bge_softc *, uint16_t, int);
static void bge_asf_driver_up (struct bge_softc *);
static void bge_tick(void *);
@@ -371,6 +372,8 @@ static void bge_stats_update_regs(struct bge_softc *);
static int bge_encap(struct bge_softc *, struct mbuf **, uint32_t *);
static void bge_intr(void *);
+static int bge_msi_intr(void *);
+static void bge_intr_task(void *, int);
static void bge_start_locked(struct ifnet *);
static void bge_start(struct ifnet *);
static int bge_ioctl(struct ifnet *, u_long, caddr_t);
@@ -2470,6 +2473,8 @@ bge_attach(device_t dev)
sc = device_get_softc(dev);
sc->bge_dev = dev;
+ TASK_INIT(&sc->bge_intr_task, 0, bge_intr_task, sc);
+
/*
* Map control/status registers.
*/
@@ -2832,8 +2837,27 @@ again:
* Hookup IRQ last.
*/
#if __FreeBSD_version > 700030
- error = bus_setup_intr(dev, sc->bge_irq, INTR_TYPE_NET | INTR_MPSAFE,
- NULL, bge_intr, sc, &sc->bge_intrhand);
+ if (BGE_IS_5755_PLUS(sc) && sc->bge_flags & BGE_FLAG_MSI) {
+ /* Take advantage of single-shot MSI. */
+ sc->bge_tq = taskqueue_create_fast("bge_taskq", M_WAITOK,
+ taskqueue_thread_enqueue, &sc->bge_tq);
+ if (sc->bge_tq == NULL) {
+ device_printf(dev, "could not create taskqueue.\n");
+ ether_ifdetach(ifp);
+ error = ENXIO;
+ goto fail;
+ }
+ taskqueue_start_threads(&sc->bge_tq, 1, PI_NET, "%s taskq",
+ device_get_nameunit(sc->bge_dev));
+ error = bus_setup_intr(dev, sc->bge_irq,
+ INTR_TYPE_NET | INTR_MPSAFE, bge_msi_intr, NULL, sc,
+ &sc->bge_intrhand);
+ if (error)
+ ether_ifdetach(ifp);
+ } else
+ error = bus_setup_intr(dev, sc->bge_irq,
+ INTR_TYPE_NET | INTR_MPSAFE, NULL, bge_intr, sc,
+ &sc->bge_intrhand);
#else
error = bus_setup_intr(dev, sc->bge_irq, INTR_TYPE_NET | INTR_MPSAFE,
bge_intr, sc, &sc->bge_intrhand);
@@ -2875,6 +2899,8 @@ bge_detach(device_t dev)
callout_drain(&sc->bge_stat_ch);
+ if (sc->bge_tq)
+ taskqueue_drain(sc->bge_tq, &sc->bge_intr_task);
ether_ifdetach(ifp);
if (sc->bge_flags & BGE_FLAG_TBI) {
@@ -2896,6 +2922,9 @@ bge_release_resources(struct bge_softc *sc)
dev = sc->bge_dev;
+ if (sc->bge_tq != NULL)
+ taskqueue_free(sc->bge_tq);
+
if (sc->bge_intrhand != NULL)
bus_teardown_intr(dev, sc->bge_irq, sc->bge_intrhand);
@@ -3135,13 +3164,12 @@ bge_reset(struct bge_softc *sc)
*/
static int
-bge_rxeof(struct bge_softc *sc, uint16_t rx_prod)
+bge_rxeof(struct bge_softc *sc, uint16_t rx_prod, int holdlck)
{
struct ifnet *ifp;
int rx_npkts = 0, stdcnt = 0, jumbocnt = 0;
uint16_t rx_cons;
- BGE_LOCK_ASSERT(sc);
rx_cons = sc->bge_rx_saved_considx;
/* Nothing to do. */
@@ -3258,9 +3286,12 @@ bge_rxeof(struct bge_softc *sc, uint16_t rx_prod)
#endif
}
- BGE_UNLOCK(sc);
- (*ifp->if_input)(ifp, m);
- BGE_LOCK(sc);
+ if (holdlck != 0) {
+ BGE_UNLOCK(sc);
+ (*ifp->if_input)(ifp, m);
+ BGE_LOCK(sc);
+ } else
+ (*ifp->if_input)(ifp, m);
rx_npkts++;
if (!(ifp->if_drv_flags & IFF_DRV_RUNNING))
@@ -3379,7 +3410,7 @@ bge_poll(struct ifnet *ifp, enum poll_cmd cmd, int count)
bge_link_upd(sc);
sc->rxcycles = count;
- rx_npkts = bge_rxeof(sc, rx_prod);
+ rx_npkts = bge_rxeof(sc, rx_prod, 1);
if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
BGE_UNLOCK(sc);
return (rx_npkts);
@@ -3393,6 +3424,69 @@ bge_poll(struct ifnet *ifp, enum poll_cmd cmd, int count)
}
#endif /* DEVICE_POLLING */
+static int
+bge_msi_intr(void *arg)
+{
+ struct bge_softc *sc;
+
+ sc = (struct bge_softc *)arg;
+ /*
+ * This interrupt is not shared and controller already
+ * disabled further interrupt.
+ */
+ taskqueue_enqueue(sc->bge_tq, &sc->bge_intr_task);
+ return (FILTER_HANDLED);
+}
+
+static void
+bge_intr_task(void *arg, int pending)
+{
+ struct bge_softc *sc;
+ struct ifnet *ifp;
+ uint32_t status;
+ uint16_t rx_prod, tx_cons;
+
+ sc = (struct bge_softc *)arg;
+ ifp = sc->bge_ifp;
+
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
+ return;
+
+ /* Get updated status block. */
+ bus_dmamap_sync(sc->bge_cdata.bge_status_tag,
+ sc->bge_cdata.bge_status_map,
+ BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
+
+ /* Save producer/consumer indexess. */
+ rx_prod = sc->bge_ldata.bge_status_block->bge_idx[0].bge_rx_prod_idx;
+ tx_cons = sc->bge_ldata.bge_status_block->bge_idx[0].bge_tx_cons_idx;
+ status = sc->bge_ldata.bge_status_block->bge_status;
+ sc->bge_ldata.bge_status_block->bge_status = 0;
+ bus_dmamap_sync(sc->bge_cdata.bge_status_tag,
+ sc->bge_cdata.bge_status_map,
+ BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
+ /* Let controller work. */
+ bge_writembx(sc, BGE_MBX_IRQ0_LO, 0);
+
+ if ((status & BGE_STATFLAG_LINKSTATE_CHANGED) != 0) {
+ BGE_LOCK(sc);
+ bge_link_upd(sc);
+ BGE_UNLOCK(sc);
+ }
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
+ /* Check RX return ring producer/consumer. */
+ bge_rxeof(sc, rx_prod, 0);
+ }
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
+ BGE_LOCK(sc);
+ /* Check TX ring producer/consumer. */
+ bge_txeof(sc, tx_cons);
+ if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
+ bge_start_locked(ifp);
+ BGE_UNLOCK(sc);
+ }
+}
+
static void
bge_intr(void *xsc)
{
@@ -3459,7 +3553,7 @@ bge_intr(void *xsc)
if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
/* Check RX return ring producer/consumer. */
- bge_rxeof(sc, rx_prod);
+ bge_rxeof(sc, rx_prod, 1);
}
if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
diff --git a/sys/dev/bge/if_bgereg.h b/sys/dev/bge/if_bgereg.h
index cc5e0b3..7fe768d 100644
--- a/sys/dev/bge/if_bgereg.h
+++ b/sys/dev/bge/if_bgereg.h
@@ -2641,6 +2641,8 @@ struct bge_softc {
#ifdef DEVICE_POLLING
int rxcycles;
#endif /* DEVICE_POLLING */
+ struct task bge_intr_task;
+ struct taskqueue *bge_tq;
};
#define BGE_LOCK_INIT(_sc, _name) \
OpenPOWER on IntegriCloud