svn commit: r194904 - head/sys/dev/cas
Marius Strobl
marius at FreeBSD.org
Wed Jun 24 20:56:08 UTC 2009
Author: marius
Date: Wed Jun 24 20:56:06 2009
New Revision: 194904
URL: http://svn.freebsd.org/changeset/base/194904
Log:
- Change this driver to do taskqueue(9) based TX and interrupt
handling in order to reduce interrupt overhead which results in
better performance.
- Call ether_ifdetach(9) before stopping the controller and the
callouts detach in order to prevent active BPF listeners to clear
promiscuous mode which may lead to the tick callout being restarted
which will trigger a panic once it's actually gone.
- Add explicit IFF_DRV_RUNNING checking in order to prevent extra
link up/down events when using dhclient(8).
- Use the correct macro for deciding whether 2/3 of the available TX
descriptors are used.
- Wrap the RX fault printing in #ifdef CAS_DEBUG in order to not
unnecessarily frighten users and as debugging was the actual
intention. Real errors caused by these faults still will be
accumulated as input errors. It might be a good idea to later on
add driver specific counters for the faults though.
Submitted by: yongari (original patch)
Modified:
head/sys/dev/cas/if_cas.c
head/sys/dev/cas/if_casvar.h
Modified: head/sys/dev/cas/if_cas.c
==============================================================================
--- head/sys/dev/cas/if_cas.c Wed Jun 24 20:52:59 2009 (r194903)
+++ head/sys/dev/cas/if_cas.c Wed Jun 24 20:56:06 2009 (r194904)
@@ -57,7 +57,7 @@ __FBSDID("$FreeBSD$");
#include <sys/rman.h>
#include <sys/socket.h>
#include <sys/sockio.h>
-#include <sys/rman.h>
+#include <sys/taskqueue.h>
#include <net/bpf.h>
#include <net/ethernet.h>
@@ -135,7 +135,8 @@ static void cas_free(void *arg1, void* a
static void cas_init(void *xsc);
static void cas_init_locked(struct cas_softc *sc);
static void cas_init_regs(struct cas_softc *sc);
-static void cas_intr(void *v);
+static int cas_intr(void *v);
+static void cas_intr_task(void *arg, int pending __unused);
static int cas_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data);
static int cas_load_txmbuf(struct cas_softc *sc, struct mbuf **m_head);
static int cas_mediachange(struct ifnet *ifp);
@@ -159,13 +160,13 @@ static void cas_rxdma_callback(void *xsc
int nsegs, int error);
static void cas_setladrf(struct cas_softc *sc);
static void cas_start(struct ifnet *ifp);
-static void cas_start_locked(struct ifnet *ifp);
static void cas_stop(struct ifnet *ifp);
static void cas_suspend(struct cas_softc *sc);
static void cas_tick(void *arg);
static void cas_tint(struct cas_softc *sc);
+static void cas_tx_task(void *arg, int pending __unused);
static inline void cas_txkick(struct cas_softc *sc);
-static int cas_watchdog(struct cas_softc *sc);
+static void cas_watchdog(struct cas_softc *sc);
static devclass_t cas_devclass;
@@ -201,7 +202,19 @@ cas_attach(struct cas_softc *sc)
IFQ_SET_READY(&ifp->if_snd);
callout_init_mtx(&sc->sc_tick_ch, &sc->sc_mtx, 0);
- callout_init_mtx(&sc->sc_rx_ch, &sc->sc_mtx, 0);
+ callout_init(&sc->sc_rx_ch, 1);
+ /* Create local taskq. */
+ TASK_INIT(&sc->sc_intr_task, 0, cas_intr_task, sc);
+ TASK_INIT(&sc->sc_tx_task, 1, cas_tx_task, ifp);
+ sc->sc_tq = taskqueue_create_fast("cas_taskq", M_WAITOK,
+ taskqueue_thread_enqueue, &sc->sc_tq);
+ if (sc->sc_tq == NULL) {
+ device_printf(sc->sc_dev, "could not create taskqueue\n");
+ error = ENXIO;
+ goto fail_ifnet;
+ }
+ taskqueue_start_threads(&sc->sc_tq, 1, PI_NET, "%s taskq",
+ device_get_nameunit(sc->sc_dev));
/* Make sure the chip is stopped. */
cas_reset(sc);
@@ -211,7 +224,7 @@ cas_attach(struct cas_softc *sc)
BUS_SPACE_MAXSIZE, 0, BUS_SPACE_MAXSIZE, 0, NULL, NULL,
&sc->sc_pdmatag);
if (error != 0)
- goto fail_ifnet;
+ goto fail_taskq;
error = bus_dma_tag_create(sc->sc_pdmatag, 1, 0,
BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL,
@@ -422,6 +435,8 @@ cas_attach(struct cas_softc *sc)
bus_dma_tag_destroy(sc->sc_rdmatag);
fail_ptag:
bus_dma_tag_destroy(sc->sc_pdmatag);
+ fail_taskq:
+ taskqueue_free(sc->sc_tq);
fail_ifnet:
if_free(ifp);
return (error);
@@ -433,13 +448,16 @@ cas_detach(struct cas_softc *sc)
struct ifnet *ifp = sc->sc_ifp;
int i;
+ ether_ifdetach(ifp);
CAS_LOCK(sc);
cas_stop(ifp);
CAS_UNLOCK(sc);
callout_drain(&sc->sc_tick_ch);
callout_drain(&sc->sc_rx_ch);
- ether_ifdetach(ifp);
+ taskqueue_drain(sc->sc_tq, &sc->sc_intr_task);
+ taskqueue_drain(sc->sc_tq, &sc->sc_tx_task);
if_free(ifp);
+ taskqueue_free(sc->sc_tq);
device_delete_child(sc->sc_dev, sc->sc_miibus);
for (i = 0; i < CAS_NRXDESC; i++)
@@ -586,12 +604,11 @@ static void
cas_tick(void *arg)
{
struct cas_softc *sc = arg;
- struct ifnet *ifp;
+ struct ifnet *ifp = sc->sc_ifp;
uint32_t v;
CAS_LOCK_ASSERT(sc, MA_OWNED);
- ifp = sc->sc_ifp;
/*
* Unload collision and error counters.
*/
@@ -622,8 +639,10 @@ cas_tick(void *arg)
mii_tick(sc->sc_mii);
- if (cas_watchdog(sc) == EJUSTRETURN)
- return;
+ if (sc->sc_txfree != CAS_MAXTXFREE)
+ cas_tint(sc);
+
+ cas_watchdog(sc);
callout_reset(&sc->sc_tick_ch, hz, cas_tick, sc);
}
@@ -915,6 +934,9 @@ cas_init_locked(struct cas_softc *sc)
CAS_LOCK_ASSERT(sc, MA_OWNED);
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0)
+ return;
+
#ifdef CAS_DEBUG
CTR2(KTR_CAS, "%s: %s: calling stop", device_get_name(sc->sc_dev),
__func__);
@@ -994,7 +1016,7 @@ cas_init_locked(struct cas_softc *sc)
/* Set up interrupts. */
CAS_WRITE_4(sc, CAS_INTMASK,
- ~(CAS_INTR_TX_INT_ME | CAS_INTR_TX_ALL | CAS_INTR_TX_TAG_ERR |
+ ~(CAS_INTR_TX_INT_ME | CAS_INTR_TX_TAG_ERR |
CAS_INTR_RX_DONE | CAS_INTR_RX_BUF_NA | CAS_INTR_RX_TAG_ERR |
CAS_INTR_RX_COMP_FULL | CAS_INTR_RX_BUF_AEMPTY |
CAS_INTR_RX_COMP_AFULL | CAS_INTR_RX_LEN_MMATCH |
@@ -1003,6 +1025,8 @@ cas_init_locked(struct cas_softc *sc)
| CAS_INTR_PCS_INT | CAS_INTR_MIF
#endif
));
+ /* Don't clear top level interrupts when CAS_STATUS_ALIAS is read. */
+ CAS_WRITE_4(sc, CAS_CLEAR_ALIAS, 0);
CAS_WRITE_4(sc, CAS_MAC_RX_MASK, ~CAS_MAC_RX_OVERFLOW);
CAS_WRITE_4(sc, CAS_MAC_TX_MASK,
~(CAS_MAC_TX_UNDERRUN | CAS_MAC_TX_MAX_PKT_ERR));
@@ -1240,7 +1264,7 @@ cas_load_txmbuf(struct cas_softc *sc, st
CTR3(KTR_CAS, "%s: start of frame at segment %d, TX %d",
__func__, seg, nexttx);
#endif
- if (sc->sc_txwin += nsegs > CAS_NTXSEGS * 2 / 3) {
+ if (sc->sc_txwin += nsegs > CAS_MAXTXFREE * 2 / 3) {
sc->sc_txwin = 0;
sc->sc_txdescs[txs->txs_firstdesc].cd_flags |=
htole64(cflags | CAS_TD_START_OF_FRAME | CAS_TD_INT_ME);
@@ -1351,13 +1375,12 @@ cas_init_regs(struct cas_softc *sc)
}
static void
-cas_start(struct ifnet *ifp)
+cas_tx_task(void *arg, int pending __unused)
{
- struct cas_softc *sc = ifp->if_softc;
+ struct ifnet *ifp;
- CAS_LOCK(sc);
- cas_start_locked(ifp);
- CAS_UNLOCK(sc);
+ ifp = (struct ifnet *)arg;
+ cas_start(ifp);
}
static inline void
@@ -1379,17 +1402,22 @@ cas_txkick(struct cas_softc *sc)
}
static void
-cas_start_locked(struct ifnet *ifp)
+cas_start(struct ifnet *ifp)
{
struct cas_softc *sc = ifp->if_softc;
struct mbuf *m;
int kicked, ntx;
- CAS_LOCK_ASSERT(sc, MA_OWNED);
+ CAS_LOCK(sc);
if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) !=
- IFF_DRV_RUNNING || (sc->sc_flags & CAS_LINK) == 0)
+ IFF_DRV_RUNNING || (sc->sc_flags & CAS_LINK) == 0) {
+ CAS_UNLOCK(sc);
return;
+ }
+
+ if (sc->sc_txfree < CAS_MAXTXFREE / 4)
+ cas_tint(sc);
#ifdef CAS_DEBUG
CTR4(KTR_CAS, "%s: %s: txfree %d, txnext %d",
@@ -1434,6 +1462,8 @@ cas_start_locked(struct ifnet *ifp)
sc->sc_wdog_timer);
#endif
}
+
+ CAS_UNLOCK(sc);
}
static void
@@ -1530,17 +1560,10 @@ cas_tint(struct cas_softc *sc)
#endif
if (progress) {
- if (sc->sc_txfree == CAS_NTXDESC - 1)
- sc->sc_txwin = 0;
-
- /*
- * We freed some descriptors, so reset IFF_DRV_OACTIVE
- * and restart.
- */
+ /* We freed some descriptors, so reset IFF_DRV_OACTIVE. */
ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
if (STAILQ_EMPTY(&sc->sc_txdirtyq))
sc->sc_wdog_timer = 0;
- cas_start_locked(ifp);
}
#ifdef CAS_DEBUG
@@ -1554,7 +1577,7 @@ cas_rint_timeout(void *arg)
{
struct cas_softc *sc = arg;
- CAS_LOCK_ASSERT(sc, MA_OWNED);
+ CAS_LOCK_ASSERT(sc, MA_NOTOWNED);
cas_rint(sc);
}
@@ -1569,7 +1592,7 @@ cas_rint(struct cas_softc *sc)
uint32_t rxhead;
u_int idx, idx2, len, off, skip;
- CAS_LOCK_ASSERT(sc, MA_OWNED);
+ CAS_LOCK_ASSERT(sc, MA_NOTOWNED);
callout_stop(&sc->sc_rx_ch);
@@ -1695,9 +1718,7 @@ cas_rint(struct cas_softc *sc)
cas_rxcksum(m, CAS_GET(word4,
CAS_RC4_TCP_CSUM));
/* Pass it on. */
- CAS_UNLOCK(sc);
(*ifp->if_input)(ifp, m);
- CAS_LOCK(sc);
} else
ifp->if_ierrors++;
@@ -1781,9 +1802,7 @@ cas_rint(struct cas_softc *sc)
cas_rxcksum(m, CAS_GET(word4,
CAS_RC4_TCP_CSUM));
/* Pass it on. */
- CAS_UNLOCK(sc);
(*ifp->if_input)(ifp, m);
- CAS_LOCK(sc);
} else
ifp->if_ierrors++;
@@ -1799,6 +1818,8 @@ cas_rint(struct cas_softc *sc)
skip:
cas_rxcompinit(&sc->sc_rxcomps[sc->sc_rxcptr]);
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
+ break;
}
CAS_CDSYNC(sc, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
CAS_WRITE_4(sc, CAS_RX_COMP_TAIL, sc->sc_rxcptr);
@@ -1819,7 +1840,7 @@ cas_free(void *arg1, void *arg2)
{
struct cas_rxdsoft *rxds;
struct cas_softc *sc;
- u_int idx, locked;
+ u_int idx;
#if __FreeBSD_version < 800016
rxds = arg2;
@@ -1837,18 +1858,17 @@ cas_free(void *arg1, void *arg2)
* NB: this function can be called via m_freem(9) within
* this driver!
*/
- if ((locked = CAS_LOCK_OWNED(sc)) == 0)
- CAS_LOCK(sc);
+
cas_add_rxdesc(sc, idx);
- if (locked == 0)
- CAS_UNLOCK(sc);
}
static inline void
cas_add_rxdesc(struct cas_softc *sc, u_int idx)
{
+ u_int locked;
- CAS_LOCK_ASSERT(sc, MA_OWNED);
+ if ((locked = CAS_LOCK_OWNED(sc)) == 0)
+ CAS_LOCK(sc);
bus_dmamap_sync(sc->sc_rdmatag, sc->sc_rxdsoft[idx].rxds_dmamap,
BUS_DMASYNC_PREREAD);
@@ -1866,13 +1886,19 @@ cas_add_rxdesc(struct cas_softc *sc, u_i
CAS_WRITE_4(sc, CAS_RX_KICK,
(sc->sc_rxdptr + CAS_NRXDESC - 4) & CAS_NRXDESC_MASK);
}
+
+ if (locked == 0)
+ CAS_UNLOCK(sc);
}
static void
cas_eint(struct cas_softc *sc, u_int status)
{
+ struct ifnet *ifp = sc->sc_ifp;
+
+ CAS_LOCK_ASSERT(sc, MA_NOTOWNED);
- sc->sc_ifp->if_ierrors++;
+ ifp->if_ierrors++;
device_printf(sc->sc_dev, "%s: status 0x%x", __func__, status);
if ((status & CAS_INTR_PCI_ERROR_INT) != 0) {
@@ -1886,21 +1912,43 @@ cas_eint(struct cas_softc *sc, u_int sta
}
printf("\n");
- cas_init_locked(sc);
- cas_start_locked(sc->sc_ifp);
+ ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
+ cas_init(sc);
+ if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
+ taskqueue_enqueue(sc->sc_tq, &sc->sc_tx_task);
}
-static void
+static int
cas_intr(void *v)
{
struct cas_softc *sc = v;
+
+ if (__predict_false((CAS_READ_4(sc, CAS_STATUS_ALIAS) &
+ CAS_INTR_SUMMARY) == 0))
+ return (FILTER_STRAY);
+
+ /* Disable interrupts. */
+ CAS_WRITE_4(sc, CAS_INTMASK, 0xffffffff);
+ taskqueue_enqueue(sc->sc_tq, &sc->sc_intr_task);
+
+ return (FILTER_HANDLED);
+}
+
+static void
+cas_intr_task(void *arg, int pending __unused)
+{
+ struct cas_softc *sc = arg;
+ struct ifnet *ifp = sc->sc_ifp;
uint32_t status, status2;
- status = CAS_READ_4(sc, CAS_STATUS);
- if (__predict_false((status & CAS_INTR_SUMMARY) == 0))
+ CAS_LOCK_ASSERT(sc, MA_NOTOWNED);
+
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
return;
- CAS_LOCK(sc);
+ status = CAS_READ_4(sc, CAS_STATUS);
+ if (__predict_false((status & CAS_INTR_SUMMARY) == 0))
+ goto done;
#ifdef CAS_DEBUG
CTR4(KTR_CAS, "%s: %s: cplt %x, status %x",
@@ -1941,7 +1989,6 @@ cas_intr(void *v)
(CAS_INTR_TX_TAG_ERR | CAS_INTR_RX_TAG_ERR |
CAS_INTR_RX_LEN_MMATCH | CAS_INTR_PCI_ERROR_INT)) != 0)) {
cas_eint(sc, status);
- CAS_UNLOCK(sc);
return;
}
@@ -1968,21 +2015,48 @@ cas_intr(void *v)
(CAS_INTR_RX_DONE | CAS_INTR_RX_BUF_NA | CAS_INTR_RX_COMP_FULL |
CAS_INTR_RX_BUF_AEMPTY | CAS_INTR_RX_COMP_AFULL)) != 0) {
cas_rint(sc);
+#ifdef CAS_DEBUG
if (__predict_false((status &
(CAS_INTR_RX_BUF_NA | CAS_INTR_RX_COMP_FULL |
CAS_INTR_RX_BUF_AEMPTY | CAS_INTR_RX_COMP_AFULL)) != 0))
device_printf(sc->sc_dev,
"RX fault, status %x\n", status);
+#endif
}
if ((status &
- (CAS_INTR_TX_INT_ME | CAS_INTR_TX_ALL | CAS_INTR_TX_DONE)) != 0)
+ (CAS_INTR_TX_INT_ME | CAS_INTR_TX_ALL | CAS_INTR_TX_DONE)) != 0) {
+ CAS_LOCK(sc);
cas_tint(sc);
+ CAS_UNLOCK(sc);
+ }
- CAS_UNLOCK(sc);
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
+ return;
+ else if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
+ taskqueue_enqueue(sc->sc_tq, &sc->sc_tx_task);
+
+ status = CAS_READ_4(sc, CAS_STATUS_ALIAS);
+ if (__predict_false((status & CAS_INTR_SUMMARY) != 0)) {
+ taskqueue_enqueue(sc->sc_tq, &sc->sc_intr_task);
+ return;
+ }
+
+ done:
+ /* Re-enable interrupts. */
+ CAS_WRITE_4(sc, CAS_INTMASK,
+ ~(CAS_INTR_TX_INT_ME | CAS_INTR_TX_TAG_ERR |
+ CAS_INTR_RX_DONE | CAS_INTR_RX_BUF_NA | CAS_INTR_RX_TAG_ERR |
+ CAS_INTR_RX_COMP_FULL | CAS_INTR_RX_BUF_AEMPTY |
+ CAS_INTR_RX_COMP_AFULL | CAS_INTR_RX_LEN_MMATCH |
+ CAS_INTR_PCI_ERROR_INT
+#ifdef CAS_DEBUG
+ | CAS_INTR_PCS_INT | CAS_INTR_MIF
+#endif
+ ));
}
-static int
+static void
cas_watchdog(struct cas_softc *sc)
{
struct ifnet *ifp = sc->sc_ifp;
@@ -2003,7 +2077,7 @@ cas_watchdog(struct cas_softc *sc)
#endif
if (sc->sc_wdog_timer == 0 || --sc->sc_wdog_timer != 0)
- return (0);
+ return;
if ((sc->sc_flags & CAS_LINK) != 0)
device_printf(sc->sc_dev, "device timeout\n");
@@ -2012,9 +2086,10 @@ cas_watchdog(struct cas_softc *sc)
++ifp->if_oerrors;
/* Try to get more packets going. */
+ ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
cas_init_locked(sc);
- cas_start_locked(ifp);
- return (EJUSTRETURN);
+ if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
+ taskqueue_enqueue(sc->sc_tq, &sc->sc_tx_task);
}
static void
@@ -2378,7 +2453,8 @@ cas_ioctl(struct ifnet *ifp, u_long cmd,
case SIOCADDMULTI:
case SIOCDELMULTI:
CAS_LOCK(sc);
- cas_setladrf(sc);
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0)
+ cas_setladrf(sc);
CAS_UNLOCK(sc);
break;
case SIOCSIFMTU:
@@ -2729,7 +2805,7 @@ cas_pci_attach(device_t dev)
}
if (bus_setup_intr(dev, sc->sc_res[CAS_RES_INTR], INTR_TYPE_NET |
- INTR_MPSAFE, NULL, cas_intr, sc, &sc->sc_ih) != 0) {
+ INTR_MPSAFE, cas_intr, NULL, sc, &sc->sc_ih) != 0) {
device_printf(dev, "failed to set up interrupt\n");
cas_detach(sc);
goto fail;
Modified: head/sys/dev/cas/if_casvar.h
==============================================================================
--- head/sys/dev/cas/if_casvar.h Wed Jun 24 20:52:59 2009 (r194903)
+++ head/sys/dev/cas/if_casvar.h Wed Jun 24 20:56:06 2009 (r194904)
@@ -138,6 +138,9 @@ struct cas_softc {
u_char sc_enaddr[ETHER_ADDR_LEN];
struct callout sc_tick_ch; /* tick callout */
struct callout sc_rx_ch; /* delayed RX callout */
+ struct task sc_intr_task;
+ struct task sc_tx_task;
+ struct taskqueue *sc_tq;
u_int sc_wdog_timer; /* watchdog timer */
void *sc_ih;
More information about the svn-src-all
mailing list