Module Name: src
Committed By: riz
Date: Mon Sep 3 19:09:41 UTC 2012
Modified Files:
src/sys/dev/pci [netbsd-6]: if_wm.c if_wmreg.h
Log Message:
Pull up following revision(s) (requested by bouyer in ticket #534):
sys/dev/pci/if_wmreg.h: revision 1.48
sys/dev/pci/if_wm.c: revision 1.232
sys/dev/pci/if_wm.c: revision 1.233
sys/dev/pci/if_wm.c: revision 1.234
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.
Enable VLAN hardware tagging on all chips that have the new queue mechanism.
Tested with 82575{EB,GB}, 82576, 82580, I350 and ICH9.
Shut up gcc about some uninitialized variables.
To generate a diff of this commit:
cvs rdiff -u -r1.227.2.2 -r1.227.2.3 src/sys/dev/pci/if_wm.c
cvs rdiff -u -r1.46.2.1 -r1.46.2.2 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.227.2.2 src/sys/dev/pci/if_wm.c:1.227.2.3
--- src/sys/dev/pci/if_wm.c:1.227.2.2 Thu Aug 9 08:00:55 2012
+++ src/sys/dev/pci/if_wm.c Mon Sep 3 19:09:41 2012
@@ -1,4 +1,4 @@
-/* $NetBSD: if_wm.c,v 1.227.2.2 2012/08/09 08:00:55 martin Exp $ */
+/* $NetBSD: if_wm.c,v 1.227.2.3 2012/09/03 19:09:41 riz 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.227.2.2 2012/08/09 08:00:55 martin Exp $");
+__KERNEL_RCSID(0, "$NetBSD: if_wm.c,v 1.227.2.3 2012/09/03 19:09:41 riz 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_flags & WM_F_NEWQUEUE) != 0)
+ 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;
@@ -1926,9 +1934,7 @@ wm_attach(device_t parent, device_t self
/*
* If we're a i82543 or greater, we can support VLANs.
*/
- if (sc->sc_type == WM_T_82575 || sc->sc_type == WM_T_82576)
- sc->sc_ethercom.ec_capabilities |= ETHERCAP_VLAN_MTU;
- else if (sc->sc_type >= WM_T_82543)
+ if (sc->sc_type >= WM_T_82543)
sc->sc_ethercom.ec_capabilities |=
ETHERCAP_VLAN_MTU | ETHERCAP_VLAN_HWTAGGING;
@@ -2761,6 +2767,483 @@ 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.
+ */
+ *cmdlenp = 0;
+ *fieldsp = 0;
+
+ 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;
+ }
+
+ 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;
+
+ 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. */
+ uint32_t cmdlen, fields, dcmdlen;
+ 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;
+ cmdlen = 0;
+ fields = 0;
+ }
+
+ /* 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 +3260,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 +3381,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 +3444,7 @@ wm_intr(void *arg)
if (handled) {
/* Try to get more packets going. */
- wm_start(ifp);
+ ifp->if_start(ifp);
}
return handled;
@@ -5438,7 +5942,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.46.2.1 src/sys/dev/pci/if_wmreg.h:1.46.2.2
--- src/sys/dev/pci/if_wmreg.h:1.46.2.1 Thu Jun 28 16:06:36 2012
+++ src/sys/dev/pci/if_wmreg.h Mon Sep 3 19:09:41 2012
@@ -1,4 +1,4 @@
-/* $NetBSD: if_wmreg.h,v 1.46.2.1 2012/06/28 16:06:36 riz Exp $ */
+/* $NetBSD: if_wmreg.h,v 1.46.2.2 2012/09/03 19:09:41 riz 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