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-head mailing list