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>