Module Name: src Committed By: bouyer Date: Wed Aug 29 20:39:24 UTC 2012
Modified Files: src/sys/dev/pci: if_wm.c if_wmreg.h Log Message: Make vlan and all ip/ip6 checksum offload work for the I350. On newer devices, when using the legacy TX descriptors, vlan-related flags that were set on the last descriptor of a packet have to be set on the first one. For tso/checksum offloads, a new "advanced" descriptor format has to be used. Change wcd_txdescs to a union defining all types of descriptors (they are all 16-bytes wide). Define a new tx function wm_nq_start(), which handle newer devices. There is some code duplication with wm_start(), but adding support to the existing wm_start() would make it a if () {} else {} maze. This also allows to get rid of some workaround for older chips that are not needed here. Use wm_nq_start() instead of wm_start() for the I350 (this should probably be for all WM_F_NEWQUEUE devices, but I have no hardware but the I350 to test). Call ifp->if_start() instead of wm_start() where is matters. Tested on a I350, and a i80003 (which use the old format), both with and without vlans, with and without checksum offloads. To generate a diff of this commit: cvs rdiff -u -r1.231 -r1.232 src/sys/dev/pci/if_wm.c cvs rdiff -u -r1.47 -r1.48 src/sys/dev/pci/if_wmreg.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/dev/pci/if_wm.c diff -u src/sys/dev/pci/if_wm.c:1.231 src/sys/dev/pci/if_wm.c:1.232 --- src/sys/dev/pci/if_wm.c:1.231 Thu Aug 9 07:48:39 2012 +++ src/sys/dev/pci/if_wm.c Wed Aug 29 20:39:24 2012 @@ -1,4 +1,4 @@ -/* $NetBSD: if_wm.c,v 1.231 2012/08/09 07:48:39 msaitoh Exp $ */ +/* $NetBSD: if_wm.c,v 1.232 2012/08/29 20:39:24 bouyer Exp $ */ /* * Copyright (c) 2001, 2002, 2003, 2004 Wasabi Systems, Inc. @@ -76,7 +76,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: if_wm.c,v 1.231 2012/08/09 07:48:39 msaitoh Exp $"); +__KERNEL_RCSID(0, "$NetBSD: if_wm.c,v 1.232 2012/08/29 20:39:24 bouyer Exp $"); #include <sys/param.h> #include <sys/systm.h> @@ -194,7 +194,10 @@ struct wm_control_data_82544 { * The transmit descriptors. Put these at the end, because * we might use a smaller number of them. */ - wiseman_txdesc_t wcd_txdescs[WM_NTXDESC_82544]; + union { + wiseman_txdesc_t wcdu_txdescs[WM_NTXDESC_82544]; + nq_txdesc_t wcdu_nq_txdescs[WM_NTXDESC_82544]; + } wdc_u; }; struct wm_control_data_82542 { @@ -203,7 +206,7 @@ struct wm_control_data_82542 { }; #define WM_CDOFF(x) offsetof(struct wm_control_data_82544, x) -#define WM_CDTXOFF(x) WM_CDOFF(wcd_txdescs[(x)]) +#define WM_CDTXOFF(x) WM_CDOFF(wdc_u.wcdu_txdescs[(x)]) #define WM_CDRXOFF(x) WM_CDOFF(wcd_rxdescs[(x)]) /* @@ -294,7 +297,8 @@ struct wm_softc { int sc_cd_rseg; /* real number of control segment */ size_t sc_cd_size; /* control data size */ #define sc_cddma sc_cddmamap->dm_segs[0].ds_addr -#define sc_txdescs sc_control_data->wcd_txdescs +#define sc_txdescs sc_control_data->wdc_u.wcdu_txdescs +#define sc_nq_txdescs sc_control_data->wdc_u.wcdu_nq_txdescs #define sc_rxdescs sc_control_data->wcd_rxdescs #ifdef WM_EVENT_COUNTERS @@ -490,6 +494,7 @@ do { \ } while (/*CONSTCOND*/0) static void wm_start(struct ifnet *); +static void wm_nq_start(struct ifnet *); static void wm_watchdog(struct ifnet *); static int wm_ifflags_cb(struct ethercom *); static int wm_ioctl(struct ifnet *, u_long, void *); @@ -1877,7 +1882,10 @@ wm_attach(device_t parent, device_t self ifp->if_softc = sc; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_ioctl = wm_ioctl; - ifp->if_start = wm_start; + if (sc->sc_type == WM_T_I350) + ifp->if_start = wm_nq_start; + else + ifp->if_start = wm_start; ifp->if_watchdog = wm_watchdog; ifp->if_init = wm_init; ifp->if_stop = wm_stop; @@ -2761,6 +2769,480 @@ wm_start(struct ifnet *ifp) } /* + * wm_nq_tx_offload: + * + * Set up TCP/IP checksumming parameters for the + * specified packet, for NEWQUEUE devices + */ +static int +wm_nq_tx_offload(struct wm_softc *sc, struct wm_txsoft *txs, + uint32_t *cmdlenp, uint32_t *fieldsp, bool *do_csum) +{ + struct mbuf *m0 = txs->txs_mbuf; + struct m_tag *mtag; + uint32_t vl_len, mssidx, cmdc; + struct ether_header *eh; + int offset, iphl; + + /* + * XXX It would be nice if the mbuf pkthdr had offset + * fields for the protocol headers. + */ + + eh = mtod(m0, struct ether_header *); + switch (htons(eh->ether_type)) { + case ETHERTYPE_IP: + case ETHERTYPE_IPV6: + offset = ETHER_HDR_LEN; + break; + + case ETHERTYPE_VLAN: + offset = ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN; + break; + + default: + /* + * Don't support this protocol or encapsulation. + */ + *do_csum = false; + return 0; + } + *do_csum = true; + *cmdlenp = NQTX_DTYP_D | NQTX_CMD_DEXT | NQTX_CMD_IFCS; + cmdc = NQTX_DTYP_C | NQTX_CMD_DEXT; + + vl_len = (offset << NQTXC_VLLEN_MACLEN_SHIFT); + KASSERT((offset & ~NQTXC_VLLEN_MACLEN_MASK) == 0); + + if ((m0->m_pkthdr.csum_flags & + (M_CSUM_TSOv4|M_CSUM_UDPv4|M_CSUM_TCPv4|M_CSUM_IPv4)) != 0) { + iphl = M_CSUM_DATA_IPv4_IPHL(m0->m_pkthdr.csum_data); + } else { + iphl = M_CSUM_DATA_IPv6_HL(m0->m_pkthdr.csum_data); + } + vl_len |= (iphl << NQTXC_VLLEN_IPLEN_SHIFT); + KASSERT((iphl & ~NQTXC_VLLEN_IPLEN_MASK) == 0); + + if ((mtag = VLAN_OUTPUT_TAG(&sc->sc_ethercom, m0)) != NULL) { + vl_len |= ((VLAN_TAG_VALUE(mtag) & NQTXC_VLLEN_VLAN_MASK) + << NQTXC_VLLEN_VLAN_SHIFT); + *cmdlenp |= NQTX_CMD_VLE; + } + + *fieldsp = 0; + mssidx = 0; + + if ((m0->m_pkthdr.csum_flags & (M_CSUM_TSOv4 | M_CSUM_TSOv6)) != 0) { + int hlen = offset + iphl; + int tcp_hlen; + bool v4 = (m0->m_pkthdr.csum_flags & M_CSUM_TSOv4) != 0; + + if (__predict_false(m0->m_len < + (hlen + sizeof(struct tcphdr)))) { + /* + * TCP/IP headers are not in the first mbuf; we need + * to do this the slow and painful way. Let's just + * hope this doesn't happen very often. + */ + struct tcphdr th; + + WM_EVCNT_INCR(&sc->sc_ev_txtsopain); + + m_copydata(m0, hlen, sizeof(th), &th); + if (v4) { + struct ip ip; + + m_copydata(m0, offset, sizeof(ip), &ip); + ip.ip_len = 0; + m_copyback(m0, + offset + offsetof(struct ip, ip_len), + sizeof(ip.ip_len), &ip.ip_len); + th.th_sum = in_cksum_phdr(ip.ip_src.s_addr, + ip.ip_dst.s_addr, htons(IPPROTO_TCP)); + } else { + struct ip6_hdr ip6; + + m_copydata(m0, offset, sizeof(ip6), &ip6); + ip6.ip6_plen = 0; + m_copyback(m0, + offset + offsetof(struct ip6_hdr, ip6_plen), + sizeof(ip6.ip6_plen), &ip6.ip6_plen); + th.th_sum = in6_cksum_phdr(&ip6.ip6_src, + &ip6.ip6_dst, 0, htonl(IPPROTO_TCP)); + } + m_copyback(m0, hlen + offsetof(struct tcphdr, th_sum), + sizeof(th.th_sum), &th.th_sum); + + tcp_hlen = th.th_off << 2; + } else { + /* + * TCP/IP headers are in the first mbuf; we can do + * this the easy way. + */ + struct tcphdr *th; + + if (v4) { + struct ip *ip = + (void *)(mtod(m0, char *) + offset); + th = (void *)(mtod(m0, char *) + hlen); + + ip->ip_len = 0; + th->th_sum = in_cksum_phdr(ip->ip_src.s_addr, + ip->ip_dst.s_addr, htons(IPPROTO_TCP)); + } else { + struct ip6_hdr *ip6 = + (void *)(mtod(m0, char *) + offset); + th = (void *)(mtod(m0, char *) + hlen); + + ip6->ip6_plen = 0; + th->th_sum = in6_cksum_phdr(&ip6->ip6_src, + &ip6->ip6_dst, 0, htonl(IPPROTO_TCP)); + } + tcp_hlen = th->th_off << 2; + } + hlen += tcp_hlen; + *cmdlenp |= NQTX_CMD_TSE; + + if (v4) { + WM_EVCNT_INCR(&sc->sc_ev_txtso); + *fieldsp |= NQTXD_FIELDS_IXSM | NQTXD_FIELDS_TUXSM; + } else { + WM_EVCNT_INCR(&sc->sc_ev_txtso6); + *fieldsp |= NQTXD_FIELDS_TUXSM; + } + *fieldsp |= ((m0->m_pkthdr.len - hlen) << NQTXD_FIELDS_PAYLEN_SHIFT); + KASSERT(((m0->m_pkthdr.len - hlen) & ~NQTXD_FIELDS_PAYLEN_MASK) == 0); + mssidx |= (m0->m_pkthdr.segsz << NQTXC_MSSIDX_MSS_SHIFT); + KASSERT((m0->m_pkthdr.segsz & ~NQTXC_MSSIDX_MSS_MASK) == 0); + mssidx |= (tcp_hlen << NQTXC_MSSIDX_L4LEN_SHIFT); + KASSERT((tcp_hlen & ~NQTXC_MSSIDX_L4LEN_MASK) == 0); + } else { + *fieldsp |= (m0->m_pkthdr.len << NQTXD_FIELDS_PAYLEN_SHIFT); + KASSERT((m0->m_pkthdr.len & ~NQTXD_FIELDS_PAYLEN_MASK) == 0); + } + + if (m0->m_pkthdr.csum_flags & M_CSUM_IPv4) { + *fieldsp |= NQTXD_FIELDS_IXSM; + cmdc |= NQTXC_CMD_IP4; + } + + if (m0->m_pkthdr.csum_flags & + (M_CSUM_UDPv4 | M_CSUM_TCPv4 | M_CSUM_TSOv4)) { + WM_EVCNT_INCR(&sc->sc_ev_txtusum); + if (m0->m_pkthdr.csum_flags & (M_CSUM_TCPv4 | M_CSUM_TSOv4)) { + cmdc |= NQTXC_CMD_TCP; + } else { + cmdc |= NQTXC_CMD_UDP; + } + cmdc |= NQTXC_CMD_IP4; + *fieldsp |= NQTXD_FIELDS_TUXSM; + } + if (m0->m_pkthdr.csum_flags & + (M_CSUM_UDPv6 | M_CSUM_TCPv6 | M_CSUM_TSOv6)) { + WM_EVCNT_INCR(&sc->sc_ev_txtusum6); + if (m0->m_pkthdr.csum_flags & (M_CSUM_TCPv6 | M_CSUM_TSOv6)) { + cmdc |= NQTXC_CMD_TCP; + } else { + cmdc |= NQTXC_CMD_UDP; + } + cmdc |= NQTXC_CMD_IP6; + *fieldsp |= NQTXD_FIELDS_TUXSM; + } + + /* Fill in the context descriptor. */ + sc->sc_nq_txdescs[sc->sc_txnext].nqrx_ctx.nqtxc_vl_len = + htole32(vl_len); + sc->sc_nq_txdescs[sc->sc_txnext].nqrx_ctx.nqtxc_sn = 0; + sc->sc_nq_txdescs[sc->sc_txnext].nqrx_ctx.nqtxc_cmd = + htole32(cmdc); + sc->sc_nq_txdescs[sc->sc_txnext].nqrx_ctx.nqtxc_mssidx = + htole32(mssidx); + WM_CDTXSYNC(sc, sc->sc_txnext, 1, BUS_DMASYNC_PREWRITE); + DPRINTF(WM_DEBUG_TX, + ("%s: TX: context desc %d 0x%08x%08x\n", device_xname(sc->sc_dev), + sc->sc_txnext, 0, vl_len)); + DPRINTF(WM_DEBUG_TX, ("\t0x%08x%08x\n", mssidx, cmdc)); + sc->sc_txnext = WM_NEXTTX(sc, sc->sc_txnext); + txs->txs_ndesc++; + return 0; +} + +/* + * wm_nq_start: [ifnet interface function] + * + * Start packet transmission on the interface for NEWQUEUE devices + */ +static void +wm_nq_start(struct ifnet *ifp) +{ + struct wm_softc *sc = ifp->if_softc; + struct mbuf *m0; + struct m_tag *mtag; + struct wm_txsoft *txs; + bus_dmamap_t dmamap; + int error, nexttx, lasttx = -1, seg, segs_needed; + bool do_csum, sent; + uint32_t cmdlen, fields, dcmdlen; + + if ((ifp->if_flags & (IFF_RUNNING|IFF_OACTIVE)) != IFF_RUNNING) + return; + + sent = false; + + /* + * Loop through the send queue, setting up transmit descriptors + * until we drain the queue, or use up all available transmit + * descriptors. + */ + for (;;) { + /* Grab a packet off the queue. */ + IFQ_POLL(&ifp->if_snd, m0); + if (m0 == NULL) + break; + + DPRINTF(WM_DEBUG_TX, + ("%s: TX: have packet to transmit: %p\n", + device_xname(sc->sc_dev), m0)); + + /* Get a work queue entry. */ + if (sc->sc_txsfree < WM_TXQUEUE_GC(sc)) { + wm_txintr(sc); + if (sc->sc_txsfree == 0) { + DPRINTF(WM_DEBUG_TX, + ("%s: TX: no free job descriptors\n", + device_xname(sc->sc_dev))); + WM_EVCNT_INCR(&sc->sc_ev_txsstall); + break; + } + } + + txs = &sc->sc_txsoft[sc->sc_txsnext]; + dmamap = txs->txs_dmamap; + + /* + * Load the DMA map. If this fails, the packet either + * didn't fit in the allotted number of segments, or we + * were short on resources. For the too-many-segments + * case, we simply report an error and drop the packet, + * since we can't sanely copy a jumbo packet to a single + * buffer. + */ + error = bus_dmamap_load_mbuf(sc->sc_dmat, dmamap, m0, + BUS_DMA_WRITE|BUS_DMA_NOWAIT); + if (error) { + if (error == EFBIG) { + WM_EVCNT_INCR(&sc->sc_ev_txdrop); + log(LOG_ERR, "%s: Tx packet consumes too many " + "DMA segments, dropping...\n", + device_xname(sc->sc_dev)); + IFQ_DEQUEUE(&ifp->if_snd, m0); + wm_dump_mbuf_chain(sc, m0); + m_freem(m0); + continue; + } + /* + * Short on resources, just stop for now. + */ + DPRINTF(WM_DEBUG_TX, + ("%s: TX: dmamap load failed: %d\n", + device_xname(sc->sc_dev), error)); + break; + } + + segs_needed = dmamap->dm_nsegs; + + /* + * Ensure we have enough descriptors free to describe + * the packet. Note, we always reserve one descriptor + * at the end of the ring due to the semantics of the + * TDT register, plus one more in the event we need + * to load offload context. + */ + if (segs_needed > sc->sc_txfree - 2) { + /* + * Not enough free descriptors to transmit this + * packet. We haven't committed anything yet, + * so just unload the DMA map, put the packet + * pack on the queue, and punt. Notify the upper + * layer that there are no more slots left. + */ + DPRINTF(WM_DEBUG_TX, + ("%s: TX: need %d (%d) descriptors, have %d\n", + device_xname(sc->sc_dev), dmamap->dm_nsegs, + segs_needed, sc->sc_txfree - 1)); + ifp->if_flags |= IFF_OACTIVE; + bus_dmamap_unload(sc->sc_dmat, dmamap); + WM_EVCNT_INCR(&sc->sc_ev_txdstall); + break; + } + + IFQ_DEQUEUE(&ifp->if_snd, m0); + + /* + * WE ARE NOW COMMITTED TO TRANSMITTING THE PACKET. + */ + + DPRINTF(WM_DEBUG_TX, + ("%s: TX: packet has %d (%d) DMA segments\n", + device_xname(sc->sc_dev), dmamap->dm_nsegs, segs_needed)); + + WM_EVCNT_INCR(&sc->sc_ev_txseg[dmamap->dm_nsegs - 1]); + + /* + * Store a pointer to the packet so that we can free it + * later. + * + * Initially, we consider the number of descriptors the + * packet uses the number of DMA segments. This may be + * incremented by 1 if we do checksum offload (a descriptor + * is used to set the checksum context). + */ + txs->txs_mbuf = m0; + txs->txs_firstdesc = sc->sc_txnext; + txs->txs_ndesc = segs_needed; + + /* Set up offload parameters for this packet. */ + if (m0->m_pkthdr.csum_flags & + (M_CSUM_TSOv4|M_CSUM_TSOv6| + M_CSUM_IPv4|M_CSUM_TCPv4|M_CSUM_UDPv4| + M_CSUM_TCPv6|M_CSUM_UDPv6)) { + if (wm_nq_tx_offload(sc, txs, &cmdlen, &fields, + &do_csum) != 0) { + /* Error message already displayed. */ + bus_dmamap_unload(sc->sc_dmat, dmamap); + continue; + } + } else { + do_csum = false; + } + + /* Sync the DMA map. */ + bus_dmamap_sync(sc->sc_dmat, dmamap, 0, dmamap->dm_mapsize, + BUS_DMASYNC_PREWRITE); + + /* + * Initialize the first transmit descriptor. + */ + nexttx = sc->sc_txnext; + if (!do_csum) { + /* setup a legacy descriptor */ + wm_set_dma_addr( + &sc->sc_txdescs[nexttx].wtx_addr, + dmamap->dm_segs[0].ds_addr); + sc->sc_txdescs[nexttx].wtx_cmdlen = + htole32(WTX_CMD_IFCS | dmamap->dm_segs[0].ds_len); + sc->sc_txdescs[nexttx].wtx_fields.wtxu_status = 0; + sc->sc_txdescs[nexttx].wtx_fields.wtxu_options = 0; + if ((mtag = VLAN_OUTPUT_TAG(&sc->sc_ethercom, m0)) != + NULL) { + sc->sc_txdescs[nexttx].wtx_cmdlen |= + htole32(WTX_CMD_VLE); + sc->sc_txdescs[nexttx].wtx_fields.wtxu_vlan = + htole16(VLAN_TAG_VALUE(mtag) & 0xffff); + } else { + sc->sc_txdescs[nexttx].wtx_fields.wtxu_vlan = 0; + } + dcmdlen = 0; + } else { + /* setup an advanced data descriptor */ + sc->sc_nq_txdescs[nexttx].nqtx_data.nqtxd_addr = + htole64(dmamap->dm_segs[0].ds_addr); + KASSERT((dmamap->dm_segs[0].ds_len & cmdlen) == 0); + sc->sc_nq_txdescs[nexttx].nqtx_data.nqtxd_cmdlen = + htole32(dmamap->dm_segs[0].ds_len | cmdlen ); + sc->sc_nq_txdescs[nexttx].nqtx_data.nqtxd_fields = + htole32(fields); + DPRINTF(WM_DEBUG_TX, + ("%s: TX: adv data desc %d 0x%" PRIx64 "\n", + device_xname(sc->sc_dev), nexttx, + dmamap->dm_segs[0].ds_addr)); + DPRINTF(WM_DEBUG_TX, + ("\t 0x%08x%08x\n", fields, + (uint32_t)dmamap->dm_segs[0].ds_len | cmdlen)); + dcmdlen = NQTX_DTYP_D | NQTX_CMD_DEXT; + } + + lasttx = nexttx; + nexttx = WM_NEXTTX(sc, nexttx); + /* + * fill in the next descriptors. legacy or adcanced format + * is the same here + */ + for (seg = 1; seg < dmamap->dm_nsegs; + seg++, nexttx = WM_NEXTTX(sc, nexttx)) { + sc->sc_nq_txdescs[nexttx].nqtx_data.nqtxd_addr = + htole64(dmamap->dm_segs[seg].ds_addr); + sc->sc_nq_txdescs[nexttx].nqtx_data.nqtxd_cmdlen = + htole32(dcmdlen | dmamap->dm_segs[seg].ds_len); + KASSERT((dcmdlen & dmamap->dm_segs[seg].ds_len) == 0); + sc->sc_nq_txdescs[nexttx].nqtx_data.nqtxd_fields = 0; + lasttx = nexttx; + + DPRINTF(WM_DEBUG_TX, + ("%s: TX: desc %d: %#" PRIxPADDR ", " + "len %#04zx\n", + device_xname(sc->sc_dev), nexttx, + dmamap->dm_segs[seg].ds_addr, + dmamap->dm_segs[seg].ds_len)); + } + + KASSERT(lasttx != -1); + + /* + * Set up the command byte on the last descriptor of + * the packet. If we're in the interrupt delay window, + * delay the interrupt. + */ + KASSERT((WTX_CMD_EOP | WTX_CMD_RS) == + (NQTX_CMD_EOP | NQTX_CMD_RS)); + sc->sc_txdescs[lasttx].wtx_cmdlen |= + htole32(WTX_CMD_EOP | WTX_CMD_RS); + + txs->txs_lastdesc = lasttx; + + DPRINTF(WM_DEBUG_TX, + ("%s: TX: desc %d: cmdlen 0x%08x\n", + device_xname(sc->sc_dev), + lasttx, le32toh(sc->sc_txdescs[lasttx].wtx_cmdlen))); + + /* Sync the descriptors we're using. */ + WM_CDTXSYNC(sc, sc->sc_txnext, txs->txs_ndesc, + BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE); + + /* Give the packet to the chip. */ + CSR_WRITE(sc, sc->sc_tdt_reg, nexttx); + sent = true; + + DPRINTF(WM_DEBUG_TX, + ("%s: TX: TDT -> %d\n", device_xname(sc->sc_dev), nexttx)); + + DPRINTF(WM_DEBUG_TX, + ("%s: TX: finished transmitting packet, job %d\n", + device_xname(sc->sc_dev), sc->sc_txsnext)); + + /* Advance the tx pointer. */ + sc->sc_txfree -= txs->txs_ndesc; + sc->sc_txnext = nexttx; + + sc->sc_txsfree--; + sc->sc_txsnext = WM_NEXTTXS(sc, sc->sc_txsnext); + + /* Pass the packet to any BPF listeners. */ + bpf_mtap(ifp, m0); + } + + if (sc->sc_txsfree == 0 || sc->sc_txfree <= 2) { + /* No more slots; notify upper layer. */ + ifp->if_flags |= IFF_OACTIVE; + } + + if (sent) { + /* Set a watchdog timer in case the chip flakes out. */ + ifp->if_timer = 5; + } +} + +/* * wm_watchdog: [ifnet interface function] * * Watchdog timer handler. @@ -2777,18 +3259,39 @@ wm_watchdog(struct ifnet *ifp) wm_txintr(sc); if (sc->sc_txfree != WM_NTXDESC(sc)) { +#ifdef WM_DEBUG + int i, j; + struct wm_txsoft *txs; +#endif log(LOG_ERR, "%s: device timeout (txfree %d txsfree %d txnext %d)\n", device_xname(sc->sc_dev), sc->sc_txfree, sc->sc_txsfree, sc->sc_txnext); ifp->if_oerrors++; - +#ifdef WM_DEBUG + for (i = sc->sc_txsdirty; i != sc->sc_txsnext ; + i = WM_NEXTTXS(sc, i)) { + txs = &sc->sc_txsoft[i]; + printf("txs %d tx %d -> %d\n", + i, txs->txs_firstdesc, txs->txs_lastdesc); + for (j = txs->txs_firstdesc; ; + j = WM_NEXTTX(sc, j)) { + printf("\tdesc %d: 0x%" PRIx64 "\n", j, + sc->sc_nq_txdescs[j].nqtx_data.nqtxd_addr); + printf("\t %#08x%08x\n", + sc->sc_nq_txdescs[j].nqtx_data.nqtxd_fields, + sc->sc_nq_txdescs[j].nqtx_data.nqtxd_cmdlen); + if (j == txs->txs_lastdesc) + break; + } + } +#endif /* Reset the interface. */ (void) wm_init(ifp); } /* Try to get more packets going. */ - wm_start(ifp); + ifp->if_start(ifp); } static int @@ -2877,7 +3380,7 @@ wm_ioctl(struct ifnet *ifp, u_long cmd, } /* Try to get more packets going. */ - wm_start(ifp); + ifp->if_start(ifp); splx(s); return error; @@ -2940,7 +3443,7 @@ wm_intr(void *arg) if (handled) { /* Try to get more packets going. */ - wm_start(ifp); + ifp->if_start(ifp); } return handled; @@ -5438,7 +5941,7 @@ wm_tbi_check_link(struct wm_softc *sc) DPRINTF(WM_DEBUG_LINK, ("RXCFG storm! (%d)\n", sc->sc_tbi_nrxcfg - sc->sc_tbi_lastnrxcfg)); wm_init(ifp); - wm_start(ifp); + ifp->if_start(ifp); } else if (IFM_SUBTYPE(ife->ifm_media) == IFM_AUTO) { /* If the timer expired, retry autonegotiation */ if (++sc->sc_tbi_ticks >= sc->sc_tbi_anegticks) { Index: src/sys/dev/pci/if_wmreg.h diff -u src/sys/dev/pci/if_wmreg.h:1.47 src/sys/dev/pci/if_wmreg.h:1.48 --- src/sys/dev/pci/if_wmreg.h:1.47 Fri May 25 23:37:38 2012 +++ src/sys/dev/pci/if_wmreg.h Wed Aug 29 20:39:24 2012 @@ -1,4 +1,4 @@ -/* $NetBSD: if_wmreg.h,v 1.47 2012/05/25 23:37:38 msaitoh Exp $ */ +/* $NetBSD: if_wmreg.h,v 1.48 2012/08/29 20:39:24 bouyer Exp $ */ /* * Copyright (c) 2001 Wasabi Systems, Inc. @@ -963,3 +963,60 @@ struct livengood_tcpip_ctxdesc { /* for PCI express Capability registers */ #define WM_PCI_PCIE_DCSR2_16MS 0x00000005 + +/* advanced TX descriptor for 82575 and newer */ +typedef union nq_txdesc { + struct { + uint64_t nqtxd_addr; + uint32_t nqtxd_cmdlen; + uint32_t nqtxd_fields; + } nqtx_data; + struct { + uint32_t nqtxc_vl_len; + uint32_t nqtxc_sn; + uint32_t nqtxc_cmd; + uint32_t nqtxc_mssidx; + } nqrx_ctx; +} __packed nq_txdesc_t; + + +/* Commands for nqtxd_cmdlen and nqtxc_cmd */ +#define NQTX_CMD_EOP (1U << 24) /* end of packet */ +#define NQTX_CMD_IFCS (1U << 25) /* insert FCS */ +#define NQTX_CMD_RS (1U << 27) /* report status */ +#define NQTX_CMD_DEXT (1U << 29) /* descriptor extension */ +#define NQTX_CMD_VLE (1U << 30) /* VLAN enable */ +#define NQTX_CMD_TSE (1U << 31) /* TCP segmentation enable */ + +/* Descriptor types (if DEXT is set) */ +#define NQTX_DTYP_C (2U << 20) /* context */ +#define NQTX_DTYP_D (3U << 20) /* data */ + +#define NQTXD_FIELDS_IDX_SHIFT 4 /* context index shift */ +#define NQTXD_FIELDS_IDX_MASK 0xf +#define NQTXD_FIELDS_PAYLEN_SHIFT 14 /* payload len shift */ +#define NQTXD_FIELDS_PAYLEN_MASK 0x3ffff + +#define NQTXD_FIELDS_IXSM (1U << 8) /* do IP checksum */ +#define NQTXD_FIELDS_TUXSM (1U << 9) /* do TCP/UDP checksum */ + +#define NQTXC_VLLEN_IPLEN_SHIFT 0 /* IP header len */ +#define NQTXC_VLLEN_IPLEN_MASK 0x1ff +#define NQTXC_VLLEN_MACLEN_SHIFT 9 /* MAC header len */ +#define NQTXC_VLLEN_MACLEN_MASK 0x7f +#define NQTXC_VLLEN_VLAN_SHIFT 16 /* vlan number */ +#define NQTXC_VLLEN_VLAN_MASK 0xffff + +#define NQTXC_CMD_MKRLOC_SHIFT 0 /* IP checksum offset */ +#define NQTXC_CMD_MKRLOC_MASK 0x1ff +#define NQTXC_CMD_SNAP (1U << 9) +#define NQTXC_CMD_IP4 (1U << 10) +#define NQTXC_CMD_IP6 (0U << 10) +#define NQTXC_CMD_TCP (1U << 11) +#define NQTXC_CMD_UDP (0U << 11) +#define NQTXC_MSSIDX_IDX_SHIFT 4 /* context index shift */ +#define NQTXC_MSSIDX_IDX_MASK 0xf +#define NQTXC_MSSIDX_L4LEN_SHIFT 8 /* L4 header len shift */ +#define NQTXC_MSSIDX_L4LEN_MASK 0xff +#define NQTXC_MSSIDX_MSS_SHIFT 16 /* MSS */ +#define NQTXC_MSSIDX_MSS_MASK 0xffff