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