Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 24 May 2009 19:50:16 GMT
From:      Marius Strobl <marius@FreeBSD.org>
To:        Perforce Change Reviews <perforce@freebsd.org>
Subject:   PERFORCE change 162668 for review
Message-ID:  <200905241950.n4OJoG9X091681@repoman.freebsd.org>

next in thread | raw e-mail | index | archive | help
http://perforce.freebsd.org/chv.cgi?CH=162668

Change 162668 by marius@flak on 2009/05/24 19:49:48

	- Improve the alignment checking of the completion and descriptor
	  rings, remove padding as they are sized in a way so that this
	  isn't required.
	- Implement TCP/UDP checksum offloading.
	- Implement support for jumbo frames.
	- Some performance improvements and minor bug fixes.
	- Turn on RX descriptor cleanup callout as this is still an issue
	  with these chips and remove CAS_RINT_TIMEOUT as option.
	- Add workarounds for some silicon bugs.
	- Fix module loading.
	- Fix some CAS_DEBUG compilation.
	- Don't assign stack garbage as Ethernet address to single-port
	  cards in !OFW machines.
	
	This driver should be feature complete and close to being
	committable to HEAD now.

Affected files ...

.. //depot/projects/usiii/dev/cas/if_cas.c#3 edit
.. //depot/projects/usiii/dev/cas/if_casreg.h#4 edit
.. //depot/projects/usiii/dev/cas/if_casvar.h#3 edit

Differences ...

==== //depot/projects/usiii/dev/cas/if_cas.c#3 (text+ko) ====

@@ -41,10 +41,6 @@
 #define	CAS_DEBUG
 #endif
 
-#if 0	/* XXX: In case of emergency, re-enable this. */
-#define	CAS_RINT_TIMEOUT
-#endif
-
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/bus.h>
@@ -96,22 +92,34 @@
 
 #include "miibus_if.h"
 
-CTASSERT(powerof2(CAS_NRXCOMP) && CAS_NRXCOMP >= 128 && CAS_NRXCOMP <= 32768);
-CTASSERT(powerof2(CAS_NRXDESC) && CAS_NRXDESC >= 32 && CAS_NRXDESC <= 8192);
-CTASSERT(powerof2(CAS_NRXDESC2) && CAS_NRXDESC2 >= 32 && CAS_NRXDESC2 <= 8192);
-CTASSERT(powerof2(CAS_NTXDESC) && CAS_NTXDESC >= 32 && CAS_NTXDESC <= 8192);
+#define RINGASSERT(n , min, max)					\
+	CTASSERT(powerof2(n) && (n) >= (min) && (n) <= (max))
+
+RINGASSERT(CAS_NRXCOMP, 128, 32768);
+RINGASSERT(CAS_NRXDESC, 32, 8192);
+RINGASSERT(CAS_NRXDESC2, 32, 8192);
+RINGASSERT(CAS_NTXDESC, 32, 8192);
+
+#undef RINGASSERT
+
+#define	CCDASSERT(m, a)							\
+	CTASSERT((offsetof(struct cas_control_data, m) & ((a) - 1)) == 0)
+
+CCDASSERT(ccd_rxcomps, CAS_RX_COMP_ALIGN);
+CCDASSERT(ccd_rxdescs, CAS_RX_DESC_ALIGN);
+CCDASSERT(ccd_rxdescs2, CAS_RX_DESC_ALIGN);
+
+#undef CCDASSERT
 
-#define	TRIES	10000
+#define	CAS_TRIES	10000
 
 /*
-XXXCAS
- * The hardware supports basic TCP/UDP checksum offloading.  However,
- * the hardware doesn't compensate the checksum for UDP datagram which
- * can yield to 0x0.  As a safe guard, UDP checksum offload is disabled
- * by default.  It can be reactivated by setting special link option
- * link0 with ifconfig(8).
+ * According to documentation, the hardware has support for basic TCP
+ * checksum offloading only, in practice this can be also used for UDP
+ * however (i.e. the problem of previous Sun NICs that a checksum of 0x0
+ * was not converted to 0xffff no longer exists).
  */
-#define	CAS_CSUM_FEATURES	(CSUM_TCP)
+#define	CAS_CSUM_FEATURES	(CSUM_TCP | CSUM_UDP)
 
 static inline void cas_add_rxdesc(struct cas_softc *sc, u_int idx);
 static int	cas_attach(struct cas_softc *sc);
@@ -143,10 +151,9 @@
 static void	cas_resume(struct cas_softc *sc);
 static u_int	cas_descsize(u_int sz);
 static void	cas_rint(struct cas_softc *sc);
-#ifdef CAS_RINT_TIMEOUT
 static void	cas_rint_timeout(void *arg);
-#endif
-static inline void cas_rxcksum(struct mbuf *m, uint64_t word4);
+static inline void cas_rxcksum(struct mbuf *m, uint16_t cksum);
+static inline void cas_rxcompinit(struct cas_rx_comp *rxcomp);
 static u_int	cas_rxcompsize(u_int sz);
 static void	cas_rxdma_callback(void *xsc, bus_dma_segment_t *segs,
 		    int nsegs, int error);
@@ -157,10 +164,12 @@
 static void	cas_suspend(struct cas_softc *sc);
 static void	cas_tick(void *arg);
 static void	cas_tint(struct cas_softc *sc);
+static inline void cas_txkick(struct cas_softc *sc);
 static int	cas_watchdog(struct cas_softc *sc);
 
 static devclass_t cas_devclass;
-DRIVER_MODULE(miibus, cas, miibus_driver, miibus_devclass, 0, 0);
+
+MODULE_DEPEND(cas, ether, 1, 1, 1);
 MODULE_DEPEND(cas, miibus, 1, 1, 1);
 
 #ifdef CAS_DEBUG
@@ -180,8 +189,6 @@
 	ifp = sc->sc_ifp = if_alloc(IFT_ETHER);
 	if (ifp == NULL)
 		return (ENOSPC);
-	if ((sc->sc_flags & CAS_NO_CSUM) == 0)
-		sc->sc_csum_features = CAS_CSUM_FEATURES;
 	ifp->if_softc = sc;
 	if_initname(ifp, device_get_name(sc->sc_dev),
 	    device_get_unit(sc->sc_dev));
@@ -194,9 +201,7 @@
 	IFQ_SET_READY(&ifp->if_snd);
 
 	callout_init_mtx(&sc->sc_tick_ch, &sc->sc_mtx, 0);
-#ifdef CAS_RINT_TIMEOUT
 	callout_init_mtx(&sc->sc_rx_ch, &sc->sc_mtx, 0);
-#endif
 
 	/* Make sure the chip is stopped. */
 	cas_reset(sc);
@@ -277,8 +282,8 @@
 	}
 
 	/*
-	 * Allocate the receive buffer, create and load the DMA maps
-	 * for it.
+	 * Allocate the receive buffers, create and load the DMA maps
+	 * for them.
 	 */
 	for (i = 0; i < CAS_NRXDESC; i++) {
 		if ((error = bus_dmamem_alloc(sc->sc_rdmatag,
@@ -376,11 +381,12 @@
 	 * Tell the upper layer(s) we support long frames/checksum offloads.
 	 */
 	ifp->if_data.ifi_hdrlen = sizeof(struct ether_vlan_header);
-	ifp->if_capabilities |= IFCAP_VLAN_MTU | IFCAP_HWCSUM;
-	ifp->if_hwassist |= sc->sc_csum_features;
-#if 0
-XXXCAS	ifp->if_capenable |= IFCAP_VLAN_MTU | IFCAP_HWCSUM;
-#endif
+	ifp->if_capabilities = IFCAP_VLAN_MTU;
+	if ((sc->sc_flags & CAS_NO_CSUM) == 0) {
+		ifp->if_capabilities |= IFCAP_HWCSUM;
+		ifp->if_hwassist = CAS_CSUM_FEATURES;
+	}
+	ifp->if_capenable = ifp->if_capabilities;
 
 	return (0);
 
@@ -431,9 +437,7 @@
 	cas_stop(ifp);
 	CAS_UNLOCK(sc);
 	callout_drain(&sc->sc_tick_ch);
-#ifdef CAS_RINT_TIMEOUT
 	callout_drain(&sc->sc_rx_ch);
-#endif
 	ether_ifdetach(ifp);
 	if_free(ifp);
 	device_delete_child(sc->sc_dev, sc->sc_miibus);
@@ -493,7 +497,7 @@
 }
 
 static inline void
-cas_rxcksum(struct mbuf *m, uint64_t word4)
+cas_rxcksum(struct mbuf *m, uint16_t cksum)
 {
 	struct ether_header *eh;
 	struct ip *ip;
@@ -501,7 +505,6 @@
 	uint16_t *opts;
 	int32_t hlen, len, pktlen;
 	uint32_t temp32;
-	uint16_t cksum;
 
 	pktlen = m->m_pkthdr.len;
 	if (pktlen < sizeof(struct ether_header) + sizeof(struct ip))
@@ -540,8 +543,7 @@
 		return;
 	}
 
-	cksum = ~(CAS_GET(word4, CAS_RC4_TCP_CSUM));
-	/* checksum fixup for IP options */
+	cksum = ~cksum;
 	len = hlen - sizeof(struct ip);
 	if (len > 0) {
 		opts = (uint16_t *)(ip + 1);
@@ -631,7 +633,7 @@
 	int i;
 	uint32_t reg;
 
-	for (i = TRIES; i--; DELAY(100)) {
+	for (i = CAS_TRIES; i--; DELAY(100)) {
 		reg = CAS_READ_4(sc, r);
 		if ((reg & clr) == 0 && (reg & set) == set)
 			return (1);
@@ -675,13 +677,13 @@
 #endif
 
 	callout_stop(&sc->sc_tick_ch);
-#ifdef CAS_RINT_TIMEOUT
 	callout_stop(&sc->sc_rx_ch);
-#endif
+
+	/* Disable all interrupts in order to avoid spurious ones. */
+	CAS_WRITE_4(sc, CAS_INTMASK, 0xffffffff);
 
-	/* XXX should we reset these instead? */
-	cas_disable_tx(sc);
-	cas_disable_rx(sc);
+	cas_reset_tx(sc);
+	cas_reset_rx(sc);
 
 	/*
 	 * Release any queued transmit buffers.
@@ -784,11 +786,24 @@
 	return (cas_bitwait(sc, CAS_MAC_TX_CONF, CAS_MAC_TX_CONF_EN, 0));
 }
 
+static inline void
+cas_rxcompinit(struct cas_rx_comp *rxcomp)
+{
+
+	rxcomp->crc_word1 = 0;
+	rxcomp->crc_word2 = 0;
+	rxcomp->crc_word3 =
+	    htole64(CAS_SET(ETHER_HDR_LEN + sizeof(struct ip), CAS_RC3_CSO));
+	rxcomp->crc_word4 = htole64(CAS_RC4_ZERO);
+}
+
 static void
 cas_meminit(struct cas_softc *sc)
 {
 	int i;
 
+	CAS_LOCK_ASSERT(sc, MA_OWNED);
+
 	/*
 	 * Initialize the transmit descriptor ring.
 	 */
@@ -803,12 +818,8 @@
 	/*
 	 * Initialize the receive completion ring.
 	 */
-	for (i = 0; i < CAS_NRXCOMP; i++) {
-		sc->sc_rxcomps[i].crc_word1 = 0;
-		sc->sc_rxcomps[i].crc_word2 = 0;
-		sc->sc_rxcomps[i].crc_word3 = 0;
-		sc->sc_rxcomps[i].crc_word4 = htole64(CAS_RC4_ZERO);
-	}
+	for (i = 0; i < CAS_NRXCOMP; i++)
+		cas_rxcompinit(&sc->sc_rxcomps[i]);
 	sc->sc_rxcptr = 0;
 
 	/*
@@ -934,29 +945,21 @@
 	cas_setladrf(sc);
 
 	/* step 6 & 7.  Program Ring Base Addresses. */
-	KASSERT((CAS_CDTXDADDR(sc, 0) & (CAS_TX_DESC_ALIGN - 1)) == 0,
-	    ("TX descriptors incorrectly aligned"));
 	CAS_WRITE_4(sc, CAS_TX_DESC3_BASE_HI,
 	    (((uint64_t)CAS_CDTXDADDR(sc, 0)) >> 32));
 	CAS_WRITE_4(sc, CAS_TX_DESC3_BASE_LO,
 	    CAS_CDTXDADDR(sc, 0) & 0xffffffff);
 
-	KASSERT((CAS_CDRXCADDR(sc, 0) & (CAS_RX_COMP_ALIGN - 1)) == 0,
-	    ("RX completions incorrectly aligned"));
 	CAS_WRITE_4(sc, CAS_RX_COMP_BASE_HI,
 	    (((uint64_t)CAS_CDRXCADDR(sc, 0)) >> 32));
 	CAS_WRITE_4(sc, CAS_RX_COMP_BASE_LO,
 	    CAS_CDRXCADDR(sc, 0) & 0xffffffff);
 
-	KASSERT((CAS_CDRXDADDR(sc, 0) & (CAS_RX_DESC_ALIGN - 1)) == 0,
-	    ("RX descriptors incorrectly aligned"));
 	CAS_WRITE_4(sc, CAS_RX_DESC_BASE_HI,
 	    (((uint64_t)CAS_CDRXDADDR(sc, 0)) >> 32));
 	CAS_WRITE_4(sc, CAS_RX_DESC_BASE_LO,
 	    CAS_CDRXDADDR(sc, 0) & 0xffffffff);
 
-	KASSERT((CAS_CDRXD2ADDR(sc, 0) & (CAS_RX_DESC_ALIGN - 1)) == 0,
-	    ("RX descriptors 2 incorrectly aligned"));
 	if ((sc->sc_flags & CAS_REG_PLUS) != 0) {
 		CAS_WRITE_4(sc, CAS_RX_DESC2_BASE_HI,
 		    (((uint64_t)CAS_CDRXD2ADDR(sc, 0)) >> 32));
@@ -977,12 +980,15 @@
 	CAS_WRITE_4(sc, CAS_CAW, CAS_CAW_RR_DIS);
 
 	/*
-	 * Enable infinite bursts for revisions without PCI issues.
-XXXCAS
-	 * Doing so greatly improves RX performance.
+	 * Enable infinite bursts for revisions without PCI issues if
+	 * applicable.  Doing so greatly improves the TX performance on
+	 * !__sparc64__.
 	 */
 	CAS_WRITE_4(sc, CAS_INF_BURST,
-	    (sc->sc_flags & CAS_TABORT) == 0 ? CAS_INF_BURST_EN : 0);
+#if !defined(__sparc64__)
+	    (sc->sc_flags & CAS_TABORT) == 0 ? CAS_INF_BURST_EN :
+#endif
+	    0);
 
 	/* Set up interrupts. */
 	CAS_WRITE_4(sc, CAS_INTMASK,
@@ -1037,12 +1043,6 @@
 		v |= cas_descsize(CAS_NRXDESC2) << CAS_RX_CONF_DESC2_SHFT;
 	CAS_WRITE_4(sc, CAS_RX_CONF,
 	    v | (ETHER_ALIGN << CAS_RX_CONF_SOFF_SHFT));
-#if 0
-XXXCAS
-	/* RX TCP/UDP checksum offset */
-	v |= ((ETHER_HDR_LEN + sizeof(struct ip)) <<
-	    CAS_RX_CONFIG_CXM_START_SHFT);
-#endif
 
 	/* Set the PAUSE thresholds.  We use the maximum OFF threshold. */
 	CAS_WRITE_4(sc, CAS_RX_PTHRS,
@@ -1133,6 +1133,8 @@
 	uint64_t cflags;
 	int error, nexttx, nsegs, offset, seg;
 
+	CAS_LOCK_ASSERT(sc, MA_OWNED);
+
 	/* Get a work queue entry. */
 	if ((txs = STAILQ_FIRST(&sc->sc_txfreeq)) == NULL) {
 		/* Ran out of descriptors. */
@@ -1140,7 +1142,7 @@
 	}
 
 	cflags = 0;
-	if (((*m_head)->m_pkthdr.csum_flags & sc->sc_csum_features) != 0) {
+	if (((*m_head)->m_pkthdr.csum_flags & CAS_CSUM_FEATURES) != 0) {
 		if (M_WRITABLE(*m_head) == 0) {
 			m = m_dup(*m_head, M_DONTWAIT);
 			m_freem(*m_head);
@@ -1269,6 +1271,8 @@
 	int i;
 	const u_char *laddr = IF_LLADDR(sc->sc_ifp);
 
+	CAS_LOCK_ASSERT(sc, MA_OWNED);
+
 	/* These registers are not cleared on reset. */
 	if ((sc->sc_flags & CAS_INITED) == 0) {
 		/* magic values */
@@ -1280,7 +1284,7 @@
 		CAS_WRITE_4(sc, CAS_MAC_MIN_FRAME, ETHER_MIN_LEN);
 		/* max frame length and max burst size */
 		CAS_WRITE_4(sc, CAS_MAC_MAX_BF,
-		    ((ETHER_MAX_LEN + ETHER_VLAN_ENCAP_LEN) <<
+		    ((ETHER_MAX_LEN_JUMBO + ETHER_VLAN_ENCAP_LEN) <<
 		    CAS_MAC_MAX_BF_FRM_SHFT) |
 		    (0x2000 << CAS_MAC_MAX_BF_BST_SHFT));
 
@@ -1354,12 +1358,32 @@
 	CAS_UNLOCK(sc);
 }
 
+static inline void
+cas_txkick(struct cas_softc *sc)
+{
+
+	/*
+	 * Update the TX kick register.  This register has to point to the
+	 * descriptor after the last valid one and for optimum performance
+	 * should be incremented in multiples of 4 (the DMA engine fetches/
+	 * updates descriptors in batches of 4).
+	 */
+#ifdef CAS_DEBUG
+	CTR3(KTR_CAS, "%s: %s: kicking TX %d",
+	    device_get_name(sc->sc_dev), __func__, sc->sc_txnext);
+#endif
+	CAS_CDSYNC(sc, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
+	CAS_WRITE_4(sc, CAS_TX_KICK3, sc->sc_txnext);
+}
+
 static void
 cas_start_locked(struct ifnet *ifp)
 {
 	struct cas_softc *sc = ifp->if_softc;
 	struct mbuf *m;
-	int ntx;
+	int kicked, ntx;
+
+	CAS_LOCK_ASSERT(sc, MA_OWNED);
 
 	if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) !=
 	    IFF_DRV_RUNNING || (sc->sc_flags & CAS_LINK) == 0)
@@ -1371,6 +1395,7 @@
 	    sc->sc_txnext);
 #endif
 	ntx = 0;
+	kicked = 0;
 	for (; !IFQ_DRV_IS_EMPTY(&ifp->if_snd) && sc->sc_txfree > 1;) {
 		IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
 		if (m == NULL)
@@ -1382,19 +1407,18 @@
 			IFQ_DRV_PREPEND(&ifp->if_snd, m);
 			break;
 		}
+		if ((sc->sc_txnext % 4) == 0) {
+			cas_txkick(sc);
+			kicked = 1;
+		} else
+			kicked = 0;
 		ntx++;
-		/* Kick the transmitter. */
-#ifdef CAS_DEBUG
-		CTR3(KTR_CAS, "%s: %s: kicking TX %d",
-		    device_get_name(sc->sc_dev), __func__, sc->sc_txnext);
-#endif
-		CAS_CDSYNC(sc, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
-		CAS_WRITE_4(sc, CAS_TX_KICK3, sc->sc_txnext);
-
 		BPF_MTAP(ifp, m);
 	}
 
 	if (ntx > 0) {
+		if (kicked == 0)
+			cas_txkick(sc);
 #ifdef CAS_DEBUG
 		CTR2(KTR_CAS, "%s: packets enqueued, OWN on %d",
 		    device_get_name(sc->sc_dev), sc->sc_txnext);
@@ -1420,6 +1444,8 @@
 #ifdef CAS_DEBUG
 	int i;
 
+	CAS_LOCK_ASSERT(sc, MA_OWNED);
+
 	CTR2(KTR_CAS, "%s: %s", device_get_name(sc->sc_dev), __func__);
 #endif
 
@@ -1430,7 +1456,6 @@
 	progress = 0;
 	CAS_CDSYNC(sc, BUS_DMASYNC_POSTREAD);
 	while ((txs = STAILQ_FIRST(&sc->sc_txdirtyq)) != NULL) {
-
 #ifdef CAS_DEBUG
 		if ((ifp->if_flags & IFF_DEBUG) != 0) {
 			printf("    txsoft %p transmit chain:\n", txs);
@@ -1511,8 +1536,8 @@
 		 * and restart.
 		 */
 		ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
-		sc->sc_wdog_timer = STAILQ_EMPTY(&sc->sc_txdirtyq) ? 0 : 5;
-
+		if (STAILQ_EMPTY(&sc->sc_txdirtyq))
+			sc->sc_wdog_timer = 0;
 		cas_start_locked(ifp);
 	}
 
@@ -1522,34 +1547,44 @@
 #endif
 }
 
-#ifdef CAS_RINT_TIMEOUT
 static void
 cas_rint_timeout(void *arg)
 {
 	struct cas_softc *sc = arg;
 
 	CAS_LOCK_ASSERT(sc, MA_OWNED);
+
 	cas_rint(sc);
 }
-#endif
 
 static void
 cas_rint(struct cas_softc *sc)
 {
-	struct cas_rxdsoft *rxds;
+	struct cas_rxdsoft *rxds, *rxds2;
 	struct ifnet *ifp = sc->sc_ifp;
-	struct mbuf *m;
+	struct mbuf *m, *m2;
 	uint64_t word1, word2, word3, word4;
 	uint32_t rxhead;
-	u_int i, idx, len;
+	u_int idx, idx2, len, off, skip;
+
+	CAS_LOCK_ASSERT(sc, MA_OWNED);
 
-#ifdef CAS_RINT_TIMEOUT
 	callout_stop(&sc->sc_rx_ch);
-#endif
+
 #ifdef CAS_DEBUG
 	CTR2(KTR_CAS, "%s: %s", device_get_name(sc->sc_dev), __func__);
 #endif
 
+#define	PRINTWORD(n, delimiter)						\
+	printf("word ## n: 0x%016llx%c", (long long)word ## n, delimiter)
+
+#define	SKIPASSERT(n)							\
+	KASSERT(sc->sc_rxcomps[sc->sc_rxcptr].crc_word ## n == 0,	\
+	    ("%s: word ## n not 0", __func__))
+
+#define	WORDTOH(n)							\
+	word ## n = le64toh(sc->sc_rxcomps[sc->sc_rxcptr].crc_word ## n)
+
 	/*
 	 * Read the completion head register once.  This limits
 	 * how long the following loop can execute.
@@ -1559,27 +1594,37 @@
 	CTR4(KTR_CAS, "%s: sc->sc_rxcptr %d, sc->sc_rxdptr %d, head %d",
 	    __func__, sc->rxcptr, sc->sc_rxdptr, rxhead);
 #endif
+	skip = 0;
 	CAS_CDSYNC(sc, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
-	for (i = sc->sc_rxcptr; i != rxhead; i = CAS_NEXTRXCOMP(i)) {
-		word1 = le64toh(sc->sc_rxcomps[i].crc_word1);
-		word2 = le64toh(sc->sc_rxcomps[i].crc_word2);
-		word3 = le64toh(sc->sc_rxcomps[i].crc_word3);
-		word4 = le64toh(sc->sc_rxcomps[i].crc_word4);
+	for (; sc->sc_rxcptr != rxhead;
+	    sc->sc_rxcptr = CAS_NEXTRXCOMP(sc->sc_rxcptr)) {
+		if (skip != 0) {
+			SKIPASSERT(1);
+			SKIPASSERT(2);
+			SKIPASSERT(3);
+
+			--skip;
+			goto skip;
+		}
+
+		WORDTOH(1);
+		WORDTOH(2);
+		WORDTOH(3);
+		WORDTOH(4);
 
 #ifdef CAS_DEBUG
 		if ((ifp->if_flags & IFF_DEBUG) != 0) {
-			printf("    completion %d: ", i);
-			printf("word1: 0x%016llx\t", (long long)word1);
-			printf("word2: 0x%016llx\t", (long long)word2);
-			printf("word3: 0x%016llx\t", (long long)word3);
-			printf("word4: 0x%016llx\n", (long long)word4);
+			printf("    completion %d: ", sc->sc_rxcptr);
+			PRINTWORD(1, '\t');
+			PRINTWORD(2, '\t');
+			PRINTWORD(3, '\t');
+			PRINTWORD(4, '\n');
 		}
 #endif
 
 		if (__predict_false(
 		    (word1 & CAS_RC1_TYPE_MASK) == CAS_RC1_TYPE_HW ||
 		    (word4 & CAS_RC4_ZERO) != 0)) {
-#ifdef CAS_RINT_TIMEOUT
 			/*
 			 * The descriptor is still marked as owned, although
 			 * it is supposed to have completed.  This has been
@@ -1590,9 +1635,6 @@
 			 */
 			callout_reset(&sc->sc_rx_ch, CAS_RXOWN_TICKS,
 			    cas_rint_timeout, sc);
-#endif
-			device_printf(sc->sc_dev,
-			    "completion still owned\n");
 			break;
 		}
 
@@ -1604,13 +1646,25 @@
 			continue;
 		}
 
-		len = CAS_GET(word2, CAS_RC2_HDR_SIZE);
-		if (len != 0) {
+		KASSERT(CAS_GET(word1, CAS_RC1_DATA_SIZE) == 0 ||
+		    CAS_GET(word2, CAS_RC2_HDR_SIZE) == 0,
+		    ("%s: data and header present", __func__));
+		KASSERT((word1 & CAS_RC1_SPLIT_PKT) == 0 ||
+		    CAS_GET(word2, CAS_RC2_HDR_SIZE) == 0,
+		    ("%s: split and header present", __func__));
+		KASSERT(CAS_GET(word1, CAS_RC1_DATA_SIZE) == 0 ||
+		    (word1 & CAS_RC1_RELEASE_HDR) == 0,
+		    ("%s: data present but header release", __func__));
+		KASSERT(CAS_GET(word2, CAS_RC2_HDR_SIZE) == 0 ||
+		    (word1 & CAS_RC1_RELEASE_DATA) == 0,
+		    ("%s: header present but data release", __func__));
+
+		if ((len = CAS_GET(word2, CAS_RC2_HDR_SIZE)) != 0) {
 			idx = CAS_GET(word2, CAS_RC2_HDR_INDEX);
+			off = CAS_GET(word2, CAS_RC2_HDR_OFF);
 #ifdef CAS_DEBUG
 			CTR4(KTR_CAS, "%s: hdr at idx %d, off %d, len %d",
-			    __func__, idx, (u_int)CAS_GET(word2,
-			    CAS_RC2_HDR_OFF), len);
+			    __func__, idx, off, len);
 #endif
 			rxds = &sc->sc_rxdsoft[idx];
 			MGETHDR(m, M_DONTWAIT, MT_DATA);
@@ -1619,8 +1673,7 @@
 			if (m != NULL) {
 				refcount_acquire(&rxds->rxds_refcount);
 				MEXTADD(m, (caddr_t)rxds->rxds_buf +
-				    CAS_GET(word2, CAS_RC2_HDR_OFF) * 256 +
-				    ETHER_ALIGN, len, cas_free,
+				    off * 256 + ETHER_ALIGN, len, cas_free,
 #if __FreeBSD_version < 800016
 				    rxds,
 #else
@@ -1649,16 +1702,12 @@
 			if ((word1 & CAS_RC1_RELEASE_HDR) != 0 &&
 			    refcount_release(&rxds->rxds_refcount) != 0)
 				cas_add_rxdesc(sc, idx);
-		}
-else if ((word1 & CAS_RC1_RELEASE_HDR) != 0)
-device_printf(sc->sc_dev, "unexpected header release\n");
-
-		len = CAS_GET(word1, CAS_RC1_DATA_SIZE);
-		if (len != 0) {
+		} else if ((len = CAS_GET(word1, CAS_RC1_DATA_SIZE)) != 0) {
 			idx = CAS_GET(word1, CAS_RC1_DATA_INDEX);
+			off = CAS_GET(word1, CAS_RC1_DATA_OFF);
 #ifdef CAS_DEBUG
 			CTR4(KTR_CAS, "%s: data at idx %d, off %d, len %d",
-			    idx, (u_int)CAS_GET(word1, CAS_RC2_HDR_OFF), len);
+			    __func__, idx, off, len);
 #endif
 			rxds = &sc->sc_rxdsoft[idx];
 			MGETHDR(m, M_DONTWAIT, MT_DATA);
@@ -1666,9 +1715,10 @@
 			    BUS_DMASYNC_POSTREAD);
 			if (m != NULL) {
 				refcount_acquire(&rxds->rxds_refcount);
-				MEXTADD(m, (caddr_t)rxds->rxds_buf +
-				    CAS_GET(word1, CAS_RC1_DATA_OFF) +
-				    ETHER_ALIGN, len, cas_free,
+				off += ETHER_ALIGN;
+				m->m_len = min(CAS_PAGE_SIZE - off, len);
+				MEXTADD(m, (caddr_t)rxds->rxds_buf + off,
+				    m->m_len, cas_free,
 #if __FreeBSD_version < 800016
 				    rxds,
 #else
@@ -1680,9 +1730,50 @@
 					m = NULL;
 				}
 			}
+			idx2 = 0;
+			rxds2 = NULL;
+			if ((word1 & CAS_RC1_SPLIT_PKT) != 0) {
+				KASSERT((word1 & CAS_RC1_RELEASE_NEXT) != 0,
+				    ("%s: split but no release next",
+				    __func__));
+
+				idx2 = CAS_GET(word2, CAS_RC2_NEXT_INDEX);
+#ifdef CAS_DEBUG
+				CTR2(KTR_CAS, "%s: split at idx %d",
+				    __func__, idx2);
+#endif
+				rxds2 = &sc->sc_rxdsoft[idx2];
+				MGET(m2, M_DONTWAIT, MT_DATA);
+				bus_dmamap_sync(sc->sc_rdmatag,
+				    rxds2->rxds_dmamap,
+				    BUS_DMASYNC_POSTREAD);
+				if (m2 != NULL) {
+					refcount_acquire(
+					    &rxds2->rxds_refcount);
+					m2->m_len = len - m->m_len;
+					MEXTADD(m2, (caddr_t)rxds2->rxds_buf,
+					    m2->m_len, cas_free,
+#if __FreeBSD_version < 800016
+					    rxds2,
+#else
+					    sc, (void *)(uintptr_t)idx2,
+#endif
+					    0, EXT_NET_DRV);
+					if ((m2->m_flags & M_EXT) == 0) {
+						m_freem(m2);
+						m2 = NULL;
+					}
+				}
+				if (m2 != NULL)
+					m->m_next = m2;
+				else {
+					m_freem(m);
+					m = NULL;
+				}
+			}
 			if (m != NULL) {
 				m->m_pkthdr.rcvif = ifp;
-				m->m_pkthdr.len = m->m_len = len;
+				m->m_pkthdr.len = len;
 				ifp->if_ipackets++;
 				if ((ifp->if_capenable & IFCAP_RXCSUM) != 0)
 					cas_rxcksum(m, CAS_GET(word4,
@@ -1697,27 +1788,23 @@
 			if ((word1 & CAS_RC1_RELEASE_DATA) != 0 &&
 			    refcount_release(&rxds->rxds_refcount) != 0)
 				cas_add_rxdesc(sc, idx);
+			if ((word1 & CAS_RC1_SPLIT_PKT) != 0 &&
+			    refcount_release(&rxds2->rxds_refcount) != 0)
+				cas_add_rxdesc(sc, idx2);
 		}
-else if ((word1 & CAS_RC1_RELEASE_DATA) != 0)
-device_printf(sc->sc_dev, "unexpected data release\n");
 
-if ((word1 & CAS_RC1_SPLIT_PKT) != 0)
-device_printf(sc->sc_dev, "unexpected split packet\n");
+		skip = CAS_GET(word1, CAS_RC1_SKIP);
 
-		i += CAS_GET(word1, CAS_RC1_SKIP);
-	}
-
-	for (; sc->sc_rxcptr != i;
-	    sc->sc_rxcptr = CAS_NEXTRXCOMP(sc->sc_rxcptr)) {
-		sc->sc_rxcomps[sc->sc_rxcptr].crc_word1 = 0;
-		sc->sc_rxcomps[sc->sc_rxcptr].crc_word2 = 0;
-		sc->sc_rxcomps[sc->sc_rxcptr].crc_word3 = 0;
-		sc->sc_rxcomps[sc->sc_rxcptr].crc_word4 =
-		    htole64(CAS_RC4_ZERO);
+ skip:
+		cas_rxcompinit(&sc->sc_rxcomps[sc->sc_rxcptr]);
 	}
 	CAS_CDSYNC(sc, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
 	CAS_WRITE_4(sc, CAS_RX_COMP_TAIL, sc->sc_rxcptr);
 
+#undef PRINTWORD
+#undef SKIPASSERT
+#undef WORDTOH
+
 #ifdef CAS_DEBUG
 	CTR4(KTR_CAS, "%s: done sc->sc_rxcptr %d, sc->sc_rxdptr %d, head %d",
 	    __func__, sc->rxcptr, sc->sc_rxdptr,
@@ -1753,15 +1840,17 @@
 cas_add_rxdesc(struct cas_softc *sc, u_int idx)
 {
 
+	CAS_LOCK_ASSERT(sc, MA_OWNED);
+
 	CAS_UPDATE_RXDESC(sc, sc->sc_rxdptr, idx);
+	sc->sc_rxdptr = CAS_NEXTRXDESC(sc->sc_rxdptr);
 
 	/*
 	 * Update the RX kick register.  This register has to point to the
 	 * descriptor after the last valid one (before the current batch)
-	 * and must be incremented in multiples of 4 (because the DMA
-	 * engine fetches/updates descriptors in batches of 4).
+	 * and for optimum performance should be incremented in multiples
+	 * of 4 (the DMA engine fetches/updates descriptors in batches of 4).
 	 */
-	sc->sc_rxdptr = CAS_NEXTRXDESC(sc->sc_rxdptr);
 	if ((sc->sc_rxdptr % 4) == 0) {
 		CAS_CDSYNC(sc, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
 		CAS_WRITE_4(sc, CAS_RX_KICK,
@@ -1811,24 +1900,26 @@
 	/*
 	 * PCS interrupts must be cleared, otherwise no traffic is passed!
 	 */
-	if ((status & CAS_INTR_PCS) != 0) {
+	if ((status & CAS_INTR_PCS_INT) != 0) {
 		status2 =
-		    CAS_READ_4(sc, CAS_MII_INTERRUP_STATUS) |
-		    CAS_READ_4(sc, CAS_MII_INTERRUP_STATUS);
-		if ((status2 & CAS_MII_INTERRUP_LINK) != 0)
+		    CAS_READ_4(sc, CAS_PCS_INTR_STATUS) |
+		    CAS_READ_4(sc, CAS_PCS_INTR_STATUS);
+		if ((status2 & CAS_PCS_INTR_LINK) != 0)
 			device_printf(sc->sc_dev,
 			    "%s: PCS link status changed\n", __func__);
 	}
 	if ((status & CAS_MAC_CTRL_STATUS) != 0) {
 		status2 = CAS_READ_4(sc, CAS_MAC_CTRL_STATUS);
-		if ((status2 & CAS_MAC_PAUSED) != 0)
+		if ((status2 & CAS_MAC_CTRL_PAUSE) != 0)
 			device_printf(sc->sc_dev,
 			    "%s: PAUSE received (PAUSE time %d slots)\n",
-			    __func__, CAS_MAC_PAUSE_TIME(status2));
-		if ((status2 & CAS_MAC_PAUSE) != 0)
+			    __func__,
+			    (status2 & CAS_MAC_CTRL_STATUS_PT_MASK) >>
+			    CAS_MAC_CTRL_STATUS_PT_SHFT);
+		if ((status2 & CAS_MAC_CTRL_PAUSE) != 0)
 			device_printf(sc->sc_dev,
 			    "%s: transited to PAUSE state\n", __func__);
-		if ((status2 & CAS_MAC_RESUME) != 0)
+		if ((status2 & CAS_MAC_CTRL_NON_PAUSE) != 0)
 			device_printf(sc->sc_dev,
 			    "%s: transited to non-PAUSE state\n", __func__);
 	}
@@ -2078,13 +2169,17 @@
 cas_mii_statchg(device_t dev)
 {
 	struct cas_softc *sc;
+	struct ifnet *ifp;
 	int gigabit;
 	uint32_t rxcfg, txcfg, v;
 
 	sc = device_get_softc(dev);
+	ifp = sc->sc_ifp;
 
+	CAS_LOCK_ASSERT(sc, MA_OWNED);
+
 #ifdef CAS_DEBUG
-	if ((sc->sc_ifp->if_flags & IFF_DEBUG) != 0)
+	if ((ifp->if_flags & IFF_DEBUG) != 0)
 		device_printf(sc->sc_dev, "%s: status change: PHY = %d\n",
 		    __func__, sc->sc_phyad);
 #endif
@@ -2148,6 +2243,30 @@
 #endif
 	CAS_WRITE_4(sc, CAS_MAC_CTRL_CONF, v);
 
+	/*
+	 * All supported chips have a bug causing incorrect checksum
+	 * to be calculated when letting them strip the FCS in half-
+	 * duplex mode.  In theory we could disable FCS stripping and
+	 * manually adjust the checksum accordingly.  It seems to make
+	 * more sense to optimze for the common case and just disable
+	 * hardware checksumming in half-duplex mode though.
+	 */
+	if ((IFM_OPTIONS(sc->sc_mii->mii_media_active) & IFM_FDX) == 0) {
+		ifp->if_capenable &= ~IFCAP_HWCSUM;
+		ifp->if_hwassist = 0;
+	} else if ((sc->sc_flags & CAS_NO_CSUM) == 0) {
+		ifp->if_capenable = ifp->if_capabilities;
+		ifp->if_hwassist = CAS_CSUM_FEATURES;
+	}
+
+	if (sc->sc_variant == CAS_SATURN) {
+		if ((IFM_OPTIONS(sc->sc_mii->mii_media_active) & IFM_FDX) == 0)
+			/* silicon bug workaround */
+			CAS_WRITE_4(sc, CAS_MAC_PREAMBLE_LEN, 0x41);
+		else
+			CAS_WRITE_4(sc, CAS_MAC_PREAMBLE_LEN, 0x7);
+	}
+
 	if ((IFM_OPTIONS(sc->sc_mii->mii_media_active) & IFM_FDX) == 0 &&
 	    gigabit != 0)
 		CAS_WRITE_4(sc, CAS_MAC_SLOT_TIME,
@@ -2229,13 +2348,21 @@
 				cas_init_locked(sc);
 		} else if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0)
 			cas_stop(ifp);
-		if ((ifp->if_flags & IFF_LINK0) != 0)
-			sc->sc_csum_features |= CSUM_UDP;
+		sc->sc_ifflags = ifp->if_flags;
+		CAS_UNLOCK(sc);
+		break;
+	case SIOCSIFCAP:
+		CAS_LOCK(sc);
+		if ((sc->sc_flags & CAS_NO_CSUM) != 0) {
+			error = EINVAL;
+			CAS_UNLOCK(sc);
+			break;
+		}
+		ifp->if_capenable = ifr->ifr_reqcap;
+		if ((ifp->if_capenable & IFCAP_TXCSUM) != 0)
+			ifp->if_hwassist = CAS_CSUM_FEATURES;
 		else
-			sc->sc_csum_features &= ~CSUM_UDP;
-		if ((ifp->if_capenable & IFCAP_TXCSUM) != 0)
-			ifp->if_hwassist = sc->sc_csum_features;
-		sc->sc_ifflags = ifp->if_flags;
+			ifp->if_hwassist = 0;
 		CAS_UNLOCK(sc);
 		break;
 	case SIOCADDMULTI:
@@ -2244,19 +2371,17 @@
 		cas_setladrf(sc);
 		CAS_UNLOCK(sc);
 		break;
+	case SIOCSIFMTU:
+		if ((ifr->ifr_mtu < ETHERMIN) ||
+		    (ifr->ifr_mtu > ETHERMTU_JUMBO))
+			error = EINVAL;
+		else
+			ifp->if_mtu = ifr->ifr_mtu;
+		break;
 	case SIOCGIFMEDIA:
 	case SIOCSIFMEDIA:
 		error = ifmedia_ioctl(ifp, ifr, &sc->sc_mii->mii_media, cmd);
 		break;
-	case SIOCSIFCAP:
-		CAS_LOCK(sc);
-		ifp->if_capenable = ifr->ifr_reqcap;
-		if ((ifp->if_capenable & IFCAP_TXCSUM) != 0)
-			ifp->if_hwassist = sc->sc_csum_features;
-		else
-			ifp->if_hwassist = 0;
-		CAS_UNLOCK(sc);
-		break;
 	default:
 		error = ether_ioctl(ifp, cmd, data);
 		break;
@@ -2366,7 +2491,7 @@
 	DEVMETHOD(miibus_writereg,	cas_mii_writereg),
 	DEVMETHOD(miibus_statchg,	cas_mii_statchg),
 
-	{ 0, 0 }
+	KOBJMETHOD_END
 };
 
 static driver_t cas_pci_driver = {
@@ -2376,8 +2501,8 @@
 };
 
 DRIVER_MODULE(cas, pci, cas_pci_driver, cas_devclass, 0, 0);
+DRIVER_MODULE(miibus, cas, miibus_driver, miibus_devclass, 0, 0);
 MODULE_DEPEND(cas, pci, 1, 1, 1);
-MODULE_DEPEND(cas, ether, 1, 1, 1);
 
 static const struct cas_pci_dev {
 	uint32_t	cpd_devid;
@@ -2583,7 +2708,7 @@
 		goto fail;
 	}
 	i = 0;
-	if (found >= pci_get_slot(dev))
+	if (found > 1 && pci_get_slot(dev) < sizeof(enaddr) / sizeof(*enaddr))
 		i = pci_get_slot(dev);
 	memcpy(sc->sc_enaddr, enaddr[i], ETHER_ADDR_LEN);
 #endif

==== //depot/projects/usiii/dev/cas/if_casreg.h#4 (text+ko) ====

@@ -999,5 +999,6 @@
 #define	CAS_RC4_LEN_MMATCH	0x8000000000000000ULL	/* length field mism. */
 
 #define	CAS_GET(reg, bits)	(((reg) & (bits ## _MASK)) >> (bits ## _SHFT))
+#define	CAS_SET(val, bits)	(((val) << (bits ## _SHFT)) & (bits ## _MASK))
 
 #endif

==== //depot/projects/usiii/dev/cas/if_casvar.h#3 (text+ko) ====

@@ -87,14 +87,8 @@
  */
 struct cas_control_data {
 	struct cas_desc ccd_txdescs[CAS_NTXDESC];	/* TX descriptors */
-	char ccd_pad0[(sizeof(struct cas_desc) * CAS_NTXDESC) %
-	    CAS_RX_COMP_ALIGN];
 	struct cas_rx_comp ccd_rxcomps[CAS_NRXCOMP];	/* RX completions */
-	char ccd_pad1[(sizeof(struct cas_rx_comp) * CAS_NRXCOMP) %
-	    CAS_RX_DESC_ALIGN];
 	struct cas_desc ccd_rxdescs[CAS_NRXDESC];	/* RX descriptors */
-	char ccd_pad2[(sizeof(struct cas_desc) * CAS_NRXDESC) %
-	    CAS_RX_DESC_ALIGN];
 	struct cas_desc ccd_rxdescs2[CAS_NRXDESC2];	/* RX descriptors 2 */
 };
 
@@ -202,7 +196,6 @@
 	u_int		sc_rxdptr;	/* next ready RX descriptor */
 
 	int		sc_ifflags;
-	u_long		sc_csum_features;
 };
 
 #define	CAS_BARRIER(sc, offs, len, flags)				\
@@ -228,7 +221,7 @@
 #define	CAS_CDSYNC(sc, ops)						\
 	bus_dmamap_sync((sc)->sc_cdmatag, (sc)->sc_cddmamap, (ops));
 
-#define	__CAS_UPDATE_RXDESC(rxd, rxds, s)					\
+#define	__CAS_UPDATE_RXDESC(rxd, rxds, s)				\
 do {									\
 									\
 	refcount_init(&(rxds)->rxds_refcount, 1);			\
@@ -246,11 +239,12 @@
 #if __FreeBSD_version < 800016
 #define	CAS_INIT_RXDESC(sc, d, s)					\
 do {									\
-	struct cas_rxdsoft *__rxds = &(sc)->sc_rxdsoft[(s)];		\
+	struct cas_rxdsoft *__rxds;					\
 									\
+	__rxds = &(sc)->sc_rxdsoft[(s)];				\
 	__rxds->rxds_sc = (sc);						\
 	__rxds->rxds_idx = (s);						\
-	__CAS_UPDATE_RXDESC(&(sc)->sc_rxdescs[(d)], __rxds, (s));		\
+	__CAS_UPDATE_RXDESC(&(sc)->sc_rxdescs[(d)], __rxds, (s));	\
 } while (0)
 #else
 #define	CAS_INIT_RXDESC(sc, d, s)	CAS_UPDATE_RXDESC(sc, d, s)



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200905241950.n4OJoG9X091681>