Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/netbsd-6]: src/sys/dev/pci Pull up following revision(s) (requested by b...



details:   https://anonhg.NetBSD.org/src/rev/3c9cd6f9bb3c
branches:  netbsd-6
changeset: 774578:3c9cd6f9bb3c
user:      riz <riz%NetBSD.org@localhost>
date:      Mon Sep 03 19:09:41 2012 +0000

description:
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.

diffstat:

 sys/dev/pci/if_wm.c    |  532 +++++++++++++++++++++++++++++++++++++++++++++++-
 sys/dev/pci/if_wmreg.h |   59 +++++-
 2 files changed, 576 insertions(+), 15 deletions(-)

diffs (truncated from 703 to 300 lines):

diff -r 4e23583a617a -r 3c9cd6f9bb3c sys/dev/pci/if_wm.c
--- a/sys/dev/pci/if_wm.c       Mon Sep 03 19:03:39 2012 +0000
+++ b/sys/dev/pci/if_wm.c       Mon Sep 03 19:09:41 2012 +0000
@@ -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 @@
         * 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 @@
 };
 
 #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 @@
        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 @@
 } 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 @@
        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 @@
        /*
         * 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_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)



Home | Main Index | Thread Index | Old Index