Index: if_gem.c =================================================================== RCS file: /home/ncvs/src/sys/dev/gem/if_gem.c,v retrieving revision 1.41 diff -u -r1.41 if_gem.c --- if_gem.c 4 May 2007 19:15:28 -0000 1.41 +++ if_gem.c 3 Jun 2007 06:23:37 -0000 @@ -66,6 +66,12 @@ #include #include +#include +#include +#include +#include +#include + #include #include @@ -75,14 +81,22 @@ #include #define TRIES 10000 +/* + * The GEM hardware support 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). + */ +#define GEM_CSUM_FEATURES (CSUM_TCP) static void gem_start(struct ifnet *); static void gem_start_locked(struct ifnet *); static void gem_stop(struct ifnet *, int); static int gem_ioctl(struct ifnet *, u_long, caddr_t); static void gem_cddma_callback(void *, bus_dma_segment_t *, int, int); -static void gem_txdma_callback(void *, bus_dma_segment_t *, int, - bus_size_t, int); +static __inline void gem_txcksum(struct gem_softc *, struct mbuf *, uint64_t *); +static __inline void gem_rxcksum(struct mbuf *, uint64_t); static void gem_tick(void *); static int gem_watchdog(struct gem_softc *); static void gem_init(void *); @@ -90,7 +104,8 @@ static void gem_init_regs(struct gem_softc *); static int gem_ringsize(int sz); static int gem_meminit(struct gem_softc *); -static int gem_load_txmbuf(struct gem_softc *, struct mbuf *); +static struct mbuf *gem_defrag(struct mbuf *, int, int); +static int gem_load_txmbuf(struct gem_softc *, struct mbuf **); static void gem_mifinit(struct gem_softc *); static int gem_bitwait(struct gem_softc *, bus_addr_t, u_int32_t, u_int32_t); @@ -156,30 +171,29 @@ error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, - MCLBYTES, GEM_NSEGS, BUS_SPACE_MAXSIZE_32BIT, 0, NULL, NULL, + BUS_SPACE_MAXSIZE_32BIT, 0, BUS_SPACE_MAXSIZE_32BIT, 0, NULL, NULL, &sc->sc_pdmatag); if (error) goto fail_ifnet; error = bus_dma_tag_create(sc->sc_pdmatag, 1, 0, - BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, MAXBSIZE, - 1, BUS_SPACE_MAXSIZE_32BIT, BUS_DMA_ALLOCNOW, NULL, NULL, - &sc->sc_rdmatag); + BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, + 1, MCLBYTES, BUS_DMA_ALLOCNOW, NULL, NULL, &sc->sc_rdmatag); if (error) goto fail_ptag; error = bus_dma_tag_create(sc->sc_pdmatag, 1, 0, - BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, - GEM_TD_BUFSIZE, GEM_NTXDESC, BUS_SPACE_MAXSIZE_32BIT, + BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, + MCLBYTES * GEM_NTXSEGS, GEM_NTXSEGS, MCLBYTES, BUS_DMA_ALLOCNOW, NULL, NULL, &sc->sc_tdmatag); if (error) goto fail_rtag; error = bus_dma_tag_create(sc->sc_pdmatag, PAGE_SIZE, 0, - BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, + BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, sizeof(struct gem_control_data), 1, - sizeof(struct gem_control_data), BUS_DMA_ALLOCNOW, - busdma_lock_mutex, &sc->sc_mtx, &sc->sc_cdmatag); + sizeof(struct gem_control_data), 0, + NULL, NULL, &sc->sc_cdmatag); if (error) goto fail_ttag; @@ -188,7 +202,9 @@ * DMA map for it. */ if ((error = bus_dmamem_alloc(sc->sc_cdmatag, - (void **)&sc->sc_control_data, 0, &sc->sc_cddmamap))) { + (void **)&sc->sc_control_data, + BUS_DMA_WAITOK | BUS_DMA_COHERENT | BUS_DMA_ZERO, + &sc->sc_cddmamap))) { device_printf(sc->sc_dev, "unable to allocate control data," " error = %d\n", error); goto fail_ctag; @@ -265,6 +281,7 @@ device_printf(sc->sc_dev, "%ukB RX FIFO, %ukB TX FIFO\n", sc->sc_rxfifosize / 1024, v / 16); + sc->sc_csum_features = GEM_CSUM_FEATURES; /* Initialize ifnet structure. */ ifp->if_softc = sc; if_initname(ifp, device_get_name(sc->sc_dev), @@ -273,7 +290,9 @@ ifp->if_start = gem_start; ifp->if_ioctl = gem_ioctl; ifp->if_init = gem_init; - ifp->if_snd.ifq_maxlen = GEM_TXQUEUELEN; + IFQ_SET_MAXLEN(&ifp->if_snd, GEM_TXQUEUELEN); + ifp->if_snd.ifq_drv_maxlen = GEM_TXQUEUELEN; + IFQ_SET_READY(&ifp->if_snd); /* * Walk along the list of attached MII devices and * establish an `MII instance' to `phy number' @@ -333,11 +352,12 @@ #endif /* - * Tell the upper layer(s) we support long frames. + * 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; - ifp->if_capenable |= IFCAP_VLAN_MTU; + ifp->if_capabilities |= IFCAP_VLAN_MTU | IFCAP_HWCSUM; + ifp->if_hwassist |= sc->sc_csum_features; + ifp->if_capenable |= IFCAP_VLAN_MTU | IFCAP_HWCSUM; return (0); @@ -441,6 +461,112 @@ GEM_UNLOCK(sc); } +static __inline void +gem_txcksum(struct gem_softc *sc, struct mbuf *m, uint64_t *cflags) +{ + struct ip *ip; + uint64_t offset, offset2; + char *p; + + offset = sizeof(struct ip) + ETHER_HDR_LEN; + for(; m && m->m_len == 0; m = m->m_next) + ; + if (m == NULL || m->m_len < ETHER_HDR_LEN) { + device_printf(sc->sc_dev, "%s: m_len < ETHER_HDR_LEN\n", + __func__); + /* checksum will be corrupted */ + goto sendit; + } + if (m->m_len < ETHER_HDR_LEN + sizeof(uint32_t)) { + if (m->m_len != ETHER_HDR_LEN) { + device_printf(sc->sc_dev, + "%s: m_len != ETHER_HDR_LEN\n", __func__); + /* checksum will be corrupted */ + goto sendit; + } + for(m = m->m_next; m && m->m_len == 0; m = m->m_next) + ; + if (m == NULL) { + /* checksum will be corrupted */ + goto sendit; + } + ip = mtod(m, struct ip *); + } else { + p = mtod(m, uint8_t *); + p += ETHER_HDR_LEN; + ip = (struct ip *)p; + } + offset = (ip->ip_hl << 2) + ETHER_HDR_LEN; + +sendit: + offset2 = m->m_pkthdr.csum_data; + *cflags = offset << GEM_TD_CXSUM_STARTSHFT; + *cflags |= ((offset + offset2) << GEM_TD_CXSUM_STUFFSHFT); + *cflags |= GEM_TD_CXSUM_ENABLE; +} + +static __inline void +gem_rxcksum(struct mbuf *m, uint64_t flags) +{ + struct ether_header *eh; + struct ip *ip; + struct udphdr *uh; + int32_t hlen, len, pktlen; + uint16_t cksum, *opts; + uint32_t temp32; + + pktlen = m->m_pkthdr.len; + if (pktlen < sizeof(struct ether_header) + sizeof(struct ip)) + return; + eh = mtod(m, struct ether_header *); + if (eh->ether_type != htons(ETHERTYPE_IP)) + return; + ip = (struct ip *)(eh + 1); + if (ip->ip_v != IPVERSION) + return; + + hlen = ip->ip_hl << 2; + pktlen -= sizeof(struct ether_header); + if (hlen < sizeof(struct ip)) + return; + if (ntohs(ip->ip_len) < hlen) + return; + if (ntohs(ip->ip_len) != pktlen) + return; + if (ip->ip_off & htons(IP_MF | IP_OFFMASK)) + return; /* can't handle fragmented packet */ + + switch (ip->ip_p) { + case IPPROTO_TCP: + if (pktlen < (hlen + sizeof(struct tcphdr))) + return; + break; + case IPPROTO_UDP: + if (pktlen < (hlen + sizeof(struct udphdr))) + return; + uh = (struct udphdr *)((uint8_t *)ip + hlen); + if (uh->uh_sum == 0) + return; /* no checksum */ + break; + default: + return; + } + + cksum = ~(flags & GEM_RD_CHECKSUM); + /* checksum fixup for IP options */ + len = hlen - sizeof(struct ip); + if (len > 0) { + opts = (uint16_t *)(ip + 1); + for (; len > 0; len -= sizeof(uint16_t), opts++) { + temp32 = cksum - *opts; + temp32 = (temp32 >> 16) + (temp32 & 65535); + cksum = temp32 & 65535; + } + } + m->m_pkthdr.csum_flags |= CSUM_DATA_VALID; + m->m_pkthdr.csum_data = cksum; +} + static void gem_cddma_callback(xsc, segs, nsegs, error) void *xsc; @@ -460,87 +586,32 @@ } static void -gem_txdma_callback(xsc, segs, nsegs, totsz, error) - void *xsc; - bus_dma_segment_t *segs; - int nsegs; - bus_size_t totsz; - int error; +gem_tick(arg) + void *arg; { - struct gem_txdma *txd = (struct gem_txdma *)xsc; - struct gem_softc *sc = txd->txd_sc; - struct gem_txsoft *txs = txd->txd_txs; - bus_size_t len = 0; - uint64_t flags = 0; - int seg, nexttx; + struct gem_softc *sc = arg; + struct ifnet *ifp; - if (error != 0) - return; + GEM_LOCK_ASSERT(sc, MA_OWNED); + + ifp = sc->sc_ifp; /* - * Ensure we have enough descriptors free to describe - * the packet. Note, we always reserve one descriptor - * at the end of the ring as a termination point, to - * prevent wrap-around. + * Unload collision counters */ - if (nsegs > sc->sc_txfree - 1) { - txs->txs_ndescs = -1; - return; - } - txs->txs_ndescs = nsegs; + ifp->if_collisions += + bus_read_4(sc->sc_res[0], GEM_MAC_NORM_COLL_CNT) + + bus_read_4(sc->sc_res[0], GEM_MAC_FIRST_COLL_CNT) + + bus_read_4(sc->sc_res[0], GEM_MAC_EXCESS_COLL_CNT) + + bus_read_4(sc->sc_res[0], GEM_MAC_LATE_COLL_CNT); - nexttx = txs->txs_firstdesc; /* - * Initialize the transmit descriptors. + * then clear the hardware counters. */ - for (seg = 0; seg < nsegs; - seg++, nexttx = GEM_NEXTTX(nexttx)) { -#ifdef GEM_DEBUG - CTR5(KTR_GEM, "txdma_cb: mapping seg %d (txd %d), len " - "%lx, addr %#lx (%#lx)", seg, nexttx, - segs[seg].ds_len, segs[seg].ds_addr, - GEM_DMA_WRITE(sc, segs[seg].ds_addr)); -#endif - - if (segs[seg].ds_len == 0) - continue; - sc->sc_txdescs[nexttx].gd_addr = - GEM_DMA_WRITE(sc, segs[seg].ds_addr); - KASSERT(segs[seg].ds_len < GEM_TD_BUFSIZE, - ("gem_txdma_callback: segment size too large!")); - flags = segs[seg].ds_len & GEM_TD_BUFSIZE; - if (len == 0) { -#ifdef GEM_DEBUG - CTR2(KTR_GEM, "txdma_cb: start of packet at seg %d, " - "tx %d", seg, nexttx); -#endif - flags |= GEM_TD_START_OF_PACKET; - if (++sc->sc_txwin > GEM_NTXSEGS * 2 / 3) { - sc->sc_txwin = 0; - flags |= GEM_TD_INTERRUPT_ME; - } - } - if (len + segs[seg].ds_len == totsz) { -#ifdef GEM_DEBUG - CTR2(KTR_GEM, "txdma_cb: end of packet at seg %d, " - "tx %d", seg, nexttx); -#endif - flags |= GEM_TD_END_OF_PACKET; - } - sc->sc_txdescs[nexttx].gd_flags = GEM_DMA_WRITE(sc, flags); - txs->txs_lastdesc = nexttx; - len += segs[seg].ds_len; - } - KASSERT((flags & GEM_TD_END_OF_PACKET) != 0, - ("gem_txdma_callback: missed end of packet!")); -} - -static void -gem_tick(arg) - void *arg; -{ - struct gem_softc *sc = arg; + bus_write_4(sc->sc_res[0], GEM_MAC_NORM_COLL_CNT, 0); + bus_write_4(sc->sc_res[0], GEM_MAC_FIRST_COLL_CNT, 0); + bus_write_4(sc->sc_res[0], GEM_MAC_EXCESS_COLL_CNT, 0); + bus_write_4(sc->sc_res[0], GEM_MAC_LATE_COLL_CNT, 0); - GEM_LOCK_ASSERT(sc, MA_OWNED); mii_tick(sc->sc_mii); if (gem_watchdog(sc) == EJUSTRETURN) @@ -573,7 +644,7 @@ { #ifdef GEM_DEBUG - CTR1(KTR_GEM, "%s: gem_reset", device_get_name(sc->sc_dev)); + CTR2(KTR_GEM, "%s: %s", device_get_name(sc->sc_dev), __func__); #endif gem_reset_rx(sc); gem_reset_tx(sc); @@ -621,7 +692,7 @@ struct gem_txsoft *txs; #ifdef GEM_DEBUG - CTR1(KTR_GEM, "%s: gem_stop", device_get_name(sc->sc_dev)); + CTR2(KTR_GEM, "%s: %s", device_get_name(sc->sc_dev), __func__); #endif callout_stop(&sc->sc_tick_ch); @@ -878,7 +949,8 @@ GEM_LOCK_ASSERT(sc, MA_OWNED); #ifdef GEM_DEBUG - CTR1(KTR_GEM, "%s: gem_init: calling stop", device_get_name(sc->sc_dev)); + CTR2(KTR_GEM, "%s: %s: calling stop", device_get_name(sc->sc_dev), + __func__); #endif /* * Initialization sequence. The numbered steps below correspond @@ -891,7 +963,8 @@ gem_stop(sc->sc_ifp, 0); gem_reset(sc); #ifdef GEM_DEBUG - CTR1(KTR_GEM, "%s: gem_init: restarting", device_get_name(sc->sc_dev)); + CTR2(KTR_GEM, "%s: %s: restarting", device_get_name(sc->sc_dev), + __func__); #endif /* Re-initialize the MIF */ @@ -943,12 +1016,14 @@ /* Encode Receive Descriptor ring size: four possible values */ v = gem_ringsize(GEM_NRXDESC /*XXX*/); + /* Rx TCP/UDP checksum offset */ + v |= ((ETHER_HDR_LEN + sizeof(struct ip)) << + GEM_RX_CONFIG_CXM_START_SHFT); /* Enable DMA */ bus_write_4(sc->sc_res[0], GEM_RX_CONFIG, v|(GEM_THRSH_1024<sc_res[0], GEM_MAC_RX_CONFIG); - v |= GEM_MAC_RX_ENABLE; + v |= GEM_MAC_RX_ENABLE | GEM_MAC_RX_STRIP_CRC; bus_write_4(sc->sc_res[0], GEM_MAC_RX_CONFIG, v); /* step 14. Issue Transmit Pending command */ @@ -980,55 +1055,210 @@ sc->sc_ifflags = ifp->if_flags; } +/* + * It's copy of ath_defrag(ath(4)). + * + * Defragment an mbuf chain, returning at most maxfrags separate + * mbufs+clusters. If this is not possible NULL is returned and + * the original mbuf chain is left in it's present (potentially + * modified) state. We use two techniques: collapsing consecutive + * mbufs and replacing consecutive mbufs by a cluster. + */ +static struct mbuf * +gem_defrag(m0, how, maxfrags) + struct mbuf *m0; + int how; + int maxfrags; +{ + struct mbuf *m, *n, *n2, **prev; + u_int curfrags; + + /* + * Calculate the current number of frags. + */ + curfrags = 0; + for (m = m0; m != NULL; m = m->m_next) + curfrags++; + /* + * First, try to collapse mbufs. Note that we always collapse + * towards the front so we don't need to deal with moving the + * pkthdr. This may be suboptimal if the first mbuf has much + * less data than the following. + */ + m = m0; +again: + for (;;) { + n = m->m_next; + if (n == NULL) + break; + if ((m->m_flags & M_RDONLY) == 0 && + n->m_len < M_TRAILINGSPACE(m)) { + bcopy(mtod(n, void *), mtod(m, char *) + m->m_len, + n->m_len); + m->m_len += n->m_len; + m->m_next = n->m_next; + m_free(n); + if (--curfrags <= maxfrags) + return (m0); + } else + m = n; + } + KASSERT(maxfrags > 1, + ("maxfrags %u, but normal collapse failed", maxfrags)); + /* + * Collapse consecutive mbufs to a cluster. + */ + prev = &m0->m_next; /* NB: not the first mbuf */ + while ((n = *prev) != NULL) { + if ((n2 = n->m_next) != NULL && + n->m_len + n2->m_len < MCLBYTES) { + m = m_getcl(how, MT_DATA, 0); + if (m == NULL) + goto bad; + bcopy(mtod(n, void *), mtod(m, void *), n->m_len); + bcopy(mtod(n2, void *), mtod(m, char *) + n->m_len, + n2->m_len); + m->m_len = n->m_len + n2->m_len; + m->m_next = n2->m_next; + *prev = m; + m_free(n); + m_free(n2); + if (--curfrags <= maxfrags) /* +1 cl -2 mbufs */ + return m0; + /* + * Still not there, try the normal collapse + * again before we allocate another cluster. + */ + goto again; + } + prev = &n->m_next; + } + /* + * No place where we can collapse to a cluster; punt. + * This can occur if, for example, you request 2 frags + * but the packet requires that both be clusters (we + * never reallocate the first mbuf to avoid moving the + * packet header). + */ +bad: + return (NULL); +} + static int -gem_load_txmbuf(sc, m0) +gem_load_txmbuf(sc, m_head) struct gem_softc *sc; - struct mbuf *m0; + struct mbuf **m_head; { - struct gem_txdma txd; struct gem_txsoft *txs; - int error; + bus_dma_segment_t txsegs[GEM_NTXSEGS]; + struct mbuf *m; + uint64_t flags, cflags; + int error, nexttx, nsegs, seg; /* Get a work queue entry. */ if ((txs = STAILQ_FIRST(&sc->sc_txfreeq)) == NULL) { /* Ran out of descriptors. */ - return (-1); + return (ENOBUFS); } - txd.txd_sc = sc; - txd.txd_txs = txs; + error = bus_dmamap_load_mbuf_sg(sc->sc_tdmatag, txs->txs_dmamap, + *m_head, txsegs, &nsegs, BUS_DMA_NOWAIT); + if (error == EFBIG) { + m = gem_defrag(*m_head, M_DONTWAIT, GEM_NTXSEGS); + if (m == NULL) { + m_freem(*m_head); + *m_head = NULL; + return (ENOBUFS); + } + *m_head = m; + error = bus_dmamap_load_mbuf_sg(sc->sc_tdmatag, txs->txs_dmamap, + *m_head, txsegs, &nsegs, BUS_DMA_NOWAIT); + if (error != 0) { + m_freem(*m_head); + *m_head = NULL; + return (error); + } + } else if (error != 0) + return (error); + if (nsegs == 0) { + m_freem(*m_head); + *m_head = NULL; + return (EIO); + } + + /* + * Ensure we have enough descriptors free to describe + * the packet. Note, we always reserve one descriptor + * at the end of the ring as a termination point, to + * prevent wrap-around. + */ + if (nsegs > sc->sc_txfree - 1) { + txs->txs_ndescs = 0; + bus_dmamap_unload(sc->sc_tdmatag, txs->txs_dmamap); + return (ENOBUFS); + } + + flags = cflags = 0; + if (((*m_head)->m_pkthdr.csum_flags & sc->sc_csum_features) != 0) + gem_txcksum(sc, *m_head, &cflags); + + txs->txs_ndescs = nsegs; txs->txs_firstdesc = sc->sc_txnext; - error = bus_dmamap_load_mbuf(sc->sc_tdmatag, txs->txs_dmamap, m0, - gem_txdma_callback, &txd, BUS_DMA_NOWAIT); - if (error != 0) - goto fail; - if (txs->txs_ndescs == -1) { - error = -1; - goto fail; + nexttx = txs->txs_firstdesc; + for (seg = 0; seg < nsegs; seg++, nexttx = GEM_NEXTTX(nexttx)) { +#ifdef GEM_DEBUG + CTR6(KTR_GEM, "%s: mapping seg %d (txd %d), len " + "%lx, addr %#lx (%#lx)", __func__, seg, nexttx, + txsegs[seg].ds_len, txsegs[seg].ds_addr, + GEM_DMA_WRITE(sc, txsegs[seg].ds_addr)); +#endif + sc->sc_txdescs[nexttx].gd_addr = + GEM_DMA_WRITE(sc, txsegs[seg].ds_addr); + KASSERT(txsegs[seg].ds_len < GEM_TD_BUFSIZE, + ("%s: segment size too large!", __func__)); + flags = txsegs[seg].ds_len & GEM_TD_BUFSIZE; + sc->sc_txdescs[nexttx].gd_flags = + GEM_DMA_WRITE(sc, flags | cflags); + txs->txs_lastdesc = nexttx; } + /* set EOP on the last descriptor */ +#ifdef GEM_DEBUG + CTR3(KTR_GEM, "%s: end of packet at seg %d, tx %d", __func__, seg, + nexttx); +#endif + sc->sc_txdescs[txs->txs_lastdesc].gd_flags |= + GEM_DMA_WRITE(sc, GEM_TD_END_OF_PACKET); + + /* Lastly set SOP on the first descriptor */ +#ifdef GEM_DEBUG + CTR3(KTR_GEM, "%s: start of packet at seg %d, tx %d", __func__, seg, + nexttx); +#endif + if (++sc->sc_txwin > GEM_NTXSEGS * 2 / 3) { + sc->sc_txwin = 0; + flags |= GEM_TD_INTERRUPT_ME; + sc->sc_txdescs[txs->txs_firstdesc].gd_flags |= + GEM_DMA_WRITE(sc, GEM_TD_INTERRUPT_ME | + GEM_TD_START_OF_PACKET); + } else + sc->sc_txdescs[txs->txs_firstdesc].gd_flags |= + GEM_DMA_WRITE(sc, GEM_TD_START_OF_PACKET); + /* Sync the DMA map. */ - bus_dmamap_sync(sc->sc_tdmatag, txs->txs_dmamap, - BUS_DMASYNC_PREWRITE); + bus_dmamap_sync(sc->sc_tdmatag, txs->txs_dmamap, BUS_DMASYNC_PREWRITE); #ifdef GEM_DEBUG - CTR3(KTR_GEM, "load_mbuf: setting firstdesc=%d, lastdesc=%d, " - "ndescs=%d", txs->txs_firstdesc, txs->txs_lastdesc, - txs->txs_ndescs); + CTR4(KTR_GEM, "%s: setting firstdesc=%d, lastdesc=%d, ndescs=%d", + __func__, txs->txs_firstdesc, txs->txs_lastdesc, txs->txs_ndescs); #endif STAILQ_REMOVE_HEAD(&sc->sc_txfreeq, txs_q); STAILQ_INSERT_TAIL(&sc->sc_txdirtyq, txs, txs_q); - txs->txs_mbuf = m0; + txs->txs_mbuf = *m_head; sc->sc_txnext = GEM_NEXTTX(txs->txs_lastdesc); sc->sc_txfree -= txs->txs_ndescs; - return (0); -fail: -#ifdef GEM_DEBUG - CTR1(KTR_GEM, "gem_load_txmbuf failed (%d)", error); -#endif - bus_dmamap_unload(sc->sc_tdmatag, txs->txs_dmamap); - return (error); + return (0); } static void @@ -1137,69 +1367,40 @@ struct ifnet *ifp; { struct gem_softc *sc = (struct gem_softc *)ifp->if_softc; - struct mbuf *m0 = NULL; - int firsttx, ntx = 0, ofree, txmfail; + struct mbuf *m; + int firsttx, ntx = 0, txmfail; if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) != IFF_DRV_RUNNING) return; - /* - * Remember the previous number of free descriptors and - * the first descriptor we'll use. - */ - ofree = sc->sc_txfree; firsttx = sc->sc_txnext; - #ifdef GEM_DEBUG - CTR3(KTR_GEM, "%s: gem_start: txfree %d, txnext %d", - device_get_name(sc->sc_dev), ofree, firsttx); + CTR4(KTR_GEM, "%s: %s: txfree %d, txnext %d", + device_get_name(sc->sc_dev), __func__, sc->sc_txfree, firsttx); #endif - - /* - * Loop through the send queue, setting up transmit descriptors - * until we drain the queue, or use up all available transmit - * descriptors. - */ - txmfail = 0; - do { - /* - * Grab a packet off the queue. - */ - IF_DEQUEUE(&ifp->if_snd, m0); - if (m0 == NULL) + for (; !IFQ_DRV_IS_EMPTY(&ifp->if_snd) && sc->sc_txfree > 1;) { + IFQ_DRV_DEQUEUE(&ifp->if_snd, m); + if (m == NULL) break; - - txmfail = gem_load_txmbuf(sc, m0); - if (txmfail > 0) { - /* Drop the mbuf and complain. */ - printf("gem_start: error %d while loading mbuf dma " - "map\n", txmfail); - continue; - } - /* Not enough descriptors. */ - if (txmfail == -1) { - if (sc->sc_txfree == GEM_MAXTXFREE) - panic("gem_start: mbuf chain too long!"); - IF_PREPEND(&ifp->if_snd, m0); + txmfail = gem_load_txmbuf(sc, &m); + if (txmfail != 0) { + if (m == NULL) + break; + ifp->if_drv_flags |= IFF_DRV_OACTIVE; + IFQ_DRV_PREPEND(&ifp->if_snd, m); break; } - ntx++; /* Kick the transmitter. */ -#ifdef GEM_DEBUG - CTR2(KTR_GEM, "%s: gem_start: kicking tx %d", - device_get_name(sc->sc_dev), sc->sc_txnext); +#ifdef GEM_DEBUG + CTR3(KTR_GEM, "%s: %s: kicking tx %d", + device_get_name(sc->sc_dev), __func__, sc->sc_txnext); #endif bus_write_4(sc->sc_res[0], GEM_TX_KICK, sc->sc_txnext); - BPF_MTAP(ifp, m0); - } while (1); - - if (txmfail == -1 || sc->sc_txfree == 0) { - /* No more slots left; notify upper layer. */ - ifp->if_drv_flags |= IFF_DRV_OACTIVE; + BPF_MTAP(ifp, m); } if (ntx > 0) { @@ -1213,8 +1414,8 @@ /* Set a watchdog timer in case the chip flakes out. */ sc->sc_wdog_timer = 5; #ifdef GEM_DEBUG - CTR2(KTR_GEM, "%s: gem_start: watchdog %d", - device_get_name(sc->sc_dev), sc->sc_wdog_timer); + CTR3(KTR_GEM, "%s: %s: watchdog %d", + device_get_name(sc->sc_dev), __func__, sc->sc_wdog_timer); #endif } } @@ -1233,27 +1434,10 @@ #ifdef GEM_DEBUG - CTR1(KTR_GEM, "%s: gem_tint", device_get_name(sc->sc_dev)); + CTR2(KTR_GEM, "%s: %s", device_get_name(sc->sc_dev), __func__); #endif /* - * Unload collision counters - */ - ifp->if_collisions += - bus_read_4(sc->sc_res[0], GEM_MAC_NORM_COLL_CNT) + - bus_read_4(sc->sc_res[0], GEM_MAC_FIRST_COLL_CNT) + - bus_read_4(sc->sc_res[0], GEM_MAC_EXCESS_COLL_CNT) + - bus_read_4(sc->sc_res[0], GEM_MAC_LATE_COLL_CNT); - - /* - * then clear the hardware counters. - */ - bus_write_4(sc->sc_res[0], GEM_MAC_NORM_COLL_CNT, 0); - bus_write_4(sc->sc_res[0], GEM_MAC_FIRST_COLL_CNT, 0); - bus_write_4(sc->sc_res[0], GEM_MAC_EXCESS_COLL_CNT, 0); - bus_write_4(sc->sc_res[0], GEM_MAC_LATE_COLL_CNT, 0); - - /* * Go through our Tx list and free mbufs for those * frames that have been transmitted. */ @@ -1285,9 +1469,9 @@ */ txlast = bus_read_4(sc->sc_res[0], GEM_TX_COMPLETION); #ifdef GEM_DEBUG - CTR3(KTR_GEM, "gem_tint: txs->txs_firstdesc = %d, " + CTR4(KTR_GEM, "%s: txs->txs_firstdesc = %d, " "txs->txs_lastdesc = %d, txlast = %d", - txs->txs_firstdesc, txs->txs_lastdesc, txlast); + __func__, txs->txs_firstdesc, txs->txs_lastdesc, txlast); #endif if (txs->txs_firstdesc <= txs->txs_lastdesc) { if ((txlast >= txs->txs_firstdesc) && @@ -1301,7 +1485,7 @@ } #ifdef GEM_DEBUG - CTR0(KTR_GEM, "gem_tint: releasing a desc"); + CTR1(KTR_GEM, "%s: releasing a desc", __func__); #endif STAILQ_REMOVE_HEAD(&sc->sc_txdirtyq, txs_q); @@ -1322,11 +1506,12 @@ } #ifdef GEM_DEBUG - CTR3(KTR_GEM, "gem_tint: GEM_TX_STATE_MACHINE %x " + CTR4(KTR_GEM, "%s: GEM_TX_STATE_MACHINE %x " "GEM_TX_DATA_PTR %llx " "GEM_TX_COMPLETION %x", - bus_read_4(sc->sc_res[0], GEM_TX_STATE_MACHINE), - ((long long) bus_read_4(sc->sc_res[0], + __func__, + bus_space_read_4(sc->sc_res[0], sc->sc_h, GEM_TX_STATE_MACHINE), + ((long long) bus_4(sc->sc_res[0], GEM_TX_DATA_PTR_HI) << 32) | bus_read_4(sc->sc_res[0], GEM_TX_DATA_PTR_LO), @@ -1339,14 +1524,16 @@ /* Freed some descriptors, so reset IFF_DRV_OACTIVE and restart. */ ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; - gem_start_locked(ifp); - sc->sc_wdog_timer = STAILQ_EMPTY(&sc->sc_txdirtyq) ? 0 : 5; + + if (ifp->if_drv_flags & IFF_DRV_RUNNING && + !IFQ_DRV_IS_EMPTY(&ifp->if_snd)) + gem_start_locked(ifp); } #ifdef GEM_DEBUG - CTR2(KTR_GEM, "%s: gem_tint: watchdog %d", - device_get_name(sc->sc_dev), sc->sc_wdog_timer); + CTR3(KTR_GEM, "%s: %s: watchdog %d", + device_get_name(sc->sc_dev), __func__, sc->sc_wdog_timer); #endif } @@ -1380,7 +1567,7 @@ callout_stop(&sc->sc_rx_ch); #endif #ifdef GEM_DEBUG - CTR1(KTR_GEM, "%s: gem_rint", device_get_name(sc->sc_dev)); + CTR2(KTR_GEM, "%s: %s", device_get_name(sc->sc_dev), __func__); #endif /* @@ -1390,8 +1577,8 @@ rxcomp = bus_read_4(sc->sc_res[0], GEM_RX_COMPLETION); #ifdef GEM_DEBUG - CTR2(KTR_GEM, "gem_rint: sc->rxptr %d, complete %d", - sc->sc_rxptr, rxcomp); + CTR3(KTR_GEM, "%s: sc->rxptr %d, complete %d", + __func__, sc->sc_rxptr, rxcomp); #endif GEM_CDSYNC(sc, BUS_DMASYNC_POSTREAD); for (i = sc->sc_rxptr; i != rxcomp; @@ -1437,8 +1624,7 @@ #endif /* - * No errors; receive the packet. Note the Gem - * includes the CRC with every packet. + * No errors; receive the packet. */ len = GEM_RD_BUFLEN(rxstat); @@ -1456,7 +1642,10 @@ m->m_data += 2; /* We're already off by two */ m->m_pkthdr.rcvif = ifp; - m->m_pkthdr.len = m->m_len = len - ETHER_CRC_LEN; + m->m_pkthdr.len = m->m_len = len; + + if ((ifp->if_capenable & IFCAP_RXCSUM) != 0) + gem_rxcksum(m, rxstat); /* Pass it on. */ GEM_UNLOCK(sc); @@ -1475,7 +1664,7 @@ } #ifdef GEM_DEBUG - CTR2(KTR_GEM, "gem_rint: done sc->rxptr %d, complete %d", + CTR3(KTR_GEM, "%s: done sc->rxptr %d, complete %d", __func__, sc->sc_rxptr, bus_read_4(sc->sc_res[0], GEM_RX_COMPLETION)); #endif } @@ -1559,8 +1748,8 @@ GEM_LOCK(sc); status = bus_read_4(sc->sc_res[0], GEM_STATUS); #ifdef GEM_DEBUG - CTR3(KTR_GEM, "%s: gem_intr: cplt %x, status %x", - device_get_name(sc->sc_dev), (status>>19), + CTR4(KTR_GEM, "%s: %s: cplt %x, status %x", + device_get_name(sc->sc_dev), __func__, (status>>19), (u_int)status); #endif @@ -1605,13 +1794,13 @@ GEM_LOCK_ASSERT(sc, MA_OWNED); #ifdef GEM_DEBUG - CTR3(KTR_GEM, "gem_watchdog: GEM_RX_CONFIG %x GEM_MAC_RX_STATUS %x " - "GEM_MAC_RX_CONFIG %x", + CTR4(KTR_GEM, "%s: GEM_RX_CONFIG %x GEM_MAC_RX_STATUS %x " + "GEM_MAC_RX_CONFIG %x", __func__, bus_read_4(sc->sc_res[0], GEM_RX_CONFIG), bus_read_4(sc->sc_res[0], GEM_MAC_RX_STATUS), bus_read_4(sc->sc_res[0], GEM_MAC_RX_CONFIG)); - CTR3(KTR_GEM, "gem_watchdog: GEM_TX_CONFIG %x GEM_MAC_TX_STATUS %x " - "GEM_MAC_TX_CONFIG %x", + CTR4(KTR_GEM, "%s: GEM_TX_CONFIG %x GEM_MAC_TX_STATUS %x " + "GEM_MAC_TX_CONFIG %x", __func__, bus_read_4(sc->sc_res[0], GEM_TX_CONFIG), bus_read_4(sc->sc_res[0], GEM_MAC_TX_STATUS), bus_read_4(sc->sc_res[0], GEM_MAC_TX_CONFIG)); @@ -1849,6 +2038,12 @@ if (ifp->if_drv_flags & IFF_DRV_RUNNING) gem_stop(ifp, 0); } + if ((ifp->if_flags & IFF_LINK0) != 0) + sc->sc_csum_features |= CSUM_UDP; + 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; GEM_UNLOCK(sc); break; @@ -1862,16 +2057,20 @@ case SIOCSIFMEDIA: error = ifmedia_ioctl(ifp, ifr, &sc->sc_mii->mii_media, cmd); break; + case SIOCSIFCAP: + GEM_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; + GEM_UNLOCK(sc); + break; default: error = ether_ioctl(ifp, cmd, data); break; } - /* Try to get things going again */ - GEM_LOCK(sc); - if (ifp->if_flags & IFF_UP) - gem_start_locked(ifp); - GEM_UNLOCK(sc); return (error); } Index: if_gemreg.h =================================================================== RCS file: /home/ncvs/src/sys/dev/gem/if_gemreg.h,v retrieving revision 1.3 diff -u -r1.3 if_gemreg.h --- if_gemreg.h 6 Jan 2005 01:42:42 -0000 1.3 +++ if_gemreg.h 3 Jun 2007 06:23:37 -0000 @@ -510,7 +510,9 @@ /* Transmit flags */ #define GEM_TD_BUFSIZE 0x0000000000007fffLL #define GEM_TD_CXSUM_START 0x00000000001f8000LL /* Cxsum start offset */ +#define GEM_TD_CXSUM_STARTSHFT 15 #define GEM_TD_CXSUM_STUFF 0x000000001fe00000LL /* Cxsum stuff offset */ +#define GEM_TD_CXSUM_STUFFSHFT 21 #define GEM_TD_CXSUM_ENABLE 0x0000000020000000LL /* Cxsum generation enable */ #define GEM_TD_END_OF_PACKET 0x0000000040000000LL #define GEM_TD_START_OF_PACKET 0x0000000080000000LL Index: if_gemvar.h =================================================================== RCS file: /home/ncvs/src/sys/dev/gem/if_gemvar.h,v retrieving revision 1.13 diff -u -r1.13 if_gemvar.h --- if_gemvar.h 4 May 2007 19:15:28 -0000 1.13 +++ if_gemvar.h 3 Jun 2007 06:23:37 -0000 @@ -104,12 +104,6 @@ STAILQ_HEAD(gem_txsq, gem_txsoft); -/* Argument structure for busdma callback */ -struct gem_txdma { - struct gem_softc *txd_sc; - struct gem_txsoft *txd_txs; -}; - /* * Software state for receive jobs. */ @@ -189,6 +183,7 @@ int sc_inited; int sc_debug; int sc_ifflags; + int sc_csum_features; struct mtx sc_mtx; };