emx: Add TSO support
authorSepherosa Ziehau <sephe@dragonflybsd.org>
Wed, 1 Aug 2012 08:51:02 +0000 (16:51 +0800)
committerSepherosa Ziehau <sephe@dragonflybsd.org>
Wed, 1 Aug 2012 08:51:02 +0000 (16:51 +0800)
sys/dev/netif/emx/if_emx.c
sys/dev/netif/emx/if_emx.h

index da1c491..6e4d0e0 100644 (file)
@@ -217,6 +217,9 @@ static int  emx_newbuf(struct emx_softc *, struct emx_rxdata *, int, int);
 static int     emx_encap(struct emx_softc *, struct mbuf **);
 static int     emx_txcsum(struct emx_softc *, struct mbuf *,
                    uint32_t *, uint32_t *);
 static int     emx_encap(struct emx_softc *, struct mbuf **);
 static int     emx_txcsum(struct emx_softc *, struct mbuf *,
                    uint32_t *, uint32_t *);
+static int     emx_tso_pullup(struct emx_softc *, struct mbuf **);
+static int     emx_tso_setup(struct emx_softc *, struct mbuf *,
+                   uint32_t *, uint32_t *);
 
 static int     emx_is_valid_eaddr(const uint8_t *);
 static int     emx_reset(struct emx_softc *);
 
 static int     emx_is_valid_eaddr(const uint8_t *);
 static int     emx_reset(struct emx_softc *);
@@ -440,6 +443,17 @@ emx_attach(device_t dev)
        if (e1000_set_mac_type(&sc->hw))
                return ENXIO;
 
        if (e1000_set_mac_type(&sc->hw))
                return ENXIO;
 
+       /*
+        * Pullup extra 4bytes into the first data segment, see:
+        * 82571/82572 specification update errata #7
+        *
+        * NOTE:
+        * 4bytes instead of 2bytes, which are mentioned in the errata,
+        * are pulled; mainly to keep rest of the data properly aligned.
+        */
+       if (sc->hw.mac.type == e1000_82571 || sc->hw.mac.type == e1000_82572)
+               sc->flags |= EMX_FLAG_TSO_PULLEX;
+
        /* Enable bus mastering */
        pci_enable_busmaster(dev);
 
        /* Enable bus mastering */
        pci_enable_busmaster(dev);
 
@@ -993,14 +1007,28 @@ emx_ioctl(struct ifnet *ifp, u_long command, caddr_t data, struct ucred *cr)
        case SIOCSIFCAP:
                reinit = 0;
                mask = ifr->ifr_reqcap ^ ifp->if_capenable;
        case SIOCSIFCAP:
                reinit = 0;
                mask = ifr->ifr_reqcap ^ ifp->if_capenable;
-               if (mask & IFCAP_HWCSUM) {
-                       ifp->if_capenable ^= (mask & IFCAP_HWCSUM);
+               if (mask & IFCAP_RXCSUM) {
+                       ifp->if_capenable ^= IFCAP_RXCSUM;
                        reinit = 1;
                }
                if (mask & IFCAP_VLAN_HWTAGGING) {
                        ifp->if_capenable ^= IFCAP_VLAN_HWTAGGING;
                        reinit = 1;
                }
                        reinit = 1;
                }
                if (mask & IFCAP_VLAN_HWTAGGING) {
                        ifp->if_capenable ^= IFCAP_VLAN_HWTAGGING;
                        reinit = 1;
                }
+               if (mask & IFCAP_TXCSUM) {
+                       ifp->if_capenable ^= IFCAP_TXCSUM;
+                       if (ifp->if_capenable & IFCAP_TXCSUM)
+                               ifp->if_hwassist |= EMX_CSUM_FEATURES;
+                       else
+                               ifp->if_hwassist &= ~EMX_CSUM_FEATURES;
+               }
+               if (mask & IFCAP_TSO) {
+                       ifp->if_capenable ^= IFCAP_TSO;
+                       if (ifp->if_capenable & IFCAP_TSO)
+                               ifp->if_hwassist |= CSUM_TSO;
+                       else
+                               ifp->if_hwassist &= ~CSUM_TSO;
+               }
                if (mask & IFCAP_RSS)
                        ifp->if_capenable ^= IFCAP_RSS;
                if (reinit && (ifp->if_flags & IFF_RUNNING))
                if (mask & IFCAP_RSS)
                        ifp->if_capenable ^= IFCAP_RSS;
                if (reinit && (ifp->if_flags & IFF_RUNNING))
@@ -1142,12 +1170,6 @@ emx_init(void *xsc)
                E1000_WRITE_REG(&sc->hw, E1000_CTRL, ctrl);
        }
 
                E1000_WRITE_REG(&sc->hw, E1000_CTRL, ctrl);
        }
 
-       /* Set hardware offload abilities */
-       if (ifp->if_capenable & IFCAP_TXCSUM)
-               ifp->if_hwassist = EMX_CSUM_FEATURES;
-       else
-               ifp->if_hwassist = 0;
-
        /* Configure for OS presence */
        emx_get_mgmt(sc);
 
        /* Configure for OS presence */
        emx_get_mgmt(sc);
 
@@ -1416,6 +1438,13 @@ emx_encap(struct emx_softc *sc, struct mbuf **m_headp)
        uint32_t txd_upper, txd_lower, cmd = 0;
        int maxsegs, nsegs, i, j, first, last = 0, error;
 
        uint32_t txd_upper, txd_lower, cmd = 0;
        int maxsegs, nsegs, i, j, first, last = 0, error;
 
+       if (m_head->m_pkthdr.csum_flags & CSUM_TSO) {
+               error = emx_tso_pullup(sc, m_headp);
+               if (error)
+                       return error;
+               m_head = *m_headp;
+       }
+
        txd_upper = txd_lower = 0;
 
        /*
        txd_upper = txd_lower = 0;
 
        /*
@@ -1450,7 +1479,11 @@ emx_encap(struct emx_softc *sc, struct mbuf **m_headp)
        m_head = *m_headp;
        sc->tx_nsegs += nsegs;
 
        m_head = *m_headp;
        sc->tx_nsegs += nsegs;
 
-       if (m_head->m_pkthdr.csum_flags & EMX_CSUM_FEATURES) {
+       if (m_head->m_pkthdr.csum_flags & CSUM_TSO) {
+               /* TSO will consume one TX desc */
+               sc->tx_nsegs += emx_tso_setup(sc, m_head,
+                   &txd_upper, &txd_lower);
+       } else if (m_head->m_pkthdr.csum_flags & EMX_CSUM_FEATURES) {
                /* TX csum offloading will consume one TX desc */
                sc->tx_nsegs += emx_txcsum(sc, m_head, &txd_upper, &txd_lower);
        }
                /* TX csum offloading will consume one TX desc */
                sc->tx_nsegs += emx_txcsum(sc, m_head, &txd_upper, &txd_lower);
        }
@@ -1739,8 +1772,11 @@ emx_stop(struct emx_softc *sc)
                emx_free_rx_ring(sc, &sc->rx_data[i]);
 
        sc->csum_flags = 0;
                emx_free_rx_ring(sc, &sc->rx_data[i]);
 
        sc->csum_flags = 0;
-       sc->csum_ehlen = 0;
+       sc->csum_lhlen = 0;
        sc->csum_iphlen = 0;
        sc->csum_iphlen = 0;
+       sc->csum_thlen = 0;
+       sc->csum_mss = 0;
+       sc->csum_pktlen = 0;
 
        sc->tx_dd_head = 0;
        sc->tx_dd_tail = 0;
 
        sc->tx_dd_head = 0;
        sc->tx_dd_tail = 0;
@@ -1840,11 +1876,12 @@ emx_setup_ifp(struct emx_softc *sc)
 
        ifp->if_capabilities = IFCAP_HWCSUM |
                               IFCAP_VLAN_HWTAGGING |
 
        ifp->if_capabilities = IFCAP_HWCSUM |
                               IFCAP_VLAN_HWTAGGING |
-                              IFCAP_VLAN_MTU;
+                              IFCAP_VLAN_MTU |
+                              IFCAP_TSO;
        if (sc->rx_ring_cnt > 1)
                ifp->if_capabilities |= IFCAP_RSS;
        ifp->if_capenable = ifp->if_capabilities;
        if (sc->rx_ring_cnt > 1)
                ifp->if_capabilities |= IFCAP_RSS;
        ifp->if_capenable = ifp->if_capabilities;
-       ifp->if_hwassist = EMX_CSUM_FEATURES;
+       ifp->if_hwassist = EMX_CSUM_FEATURES | CSUM_TSO;
 
        /*
         * Tell the upper layer(s) we support long frames.
 
        /*
         * Tell the upper layer(s) we support long frames.
@@ -2147,7 +2184,6 @@ emx_txcsum(struct emx_softc *sc, struct mbuf *mp,
           uint32_t *txd_upper, uint32_t *txd_lower)
 {
        struct e1000_context_desc *TXD;
           uint32_t *txd_upper, uint32_t *txd_lower)
 {
        struct e1000_context_desc *TXD;
-       struct emx_txbuf *tx_buffer;
        int curr_txd, ehdrlen, csum_flags;
        uint32_t cmd, hdr_len, ip_hlen;
 
        int curr_txd, ehdrlen, csum_flags;
        uint32_t cmd, hdr_len, ip_hlen;
 
@@ -2155,7 +2191,7 @@ emx_txcsum(struct emx_softc *sc, struct mbuf *mp,
        ip_hlen = mp->m_pkthdr.csum_iphlen;
        ehdrlen = mp->m_pkthdr.csum_lhlen;
 
        ip_hlen = mp->m_pkthdr.csum_iphlen;
        ehdrlen = mp->m_pkthdr.csum_lhlen;
 
-       if (sc->csum_ehlen == ehdrlen && sc->csum_iphlen == ip_hlen &&
+       if (sc->csum_lhlen == ehdrlen && sc->csum_iphlen == ip_hlen &&
            sc->csum_flags == csum_flags) {
                /*
                 * Same csum offload context as the previous packets;
            sc->csum_flags == csum_flags) {
                /*
                 * Same csum offload context as the previous packets;
@@ -2171,7 +2207,6 @@ emx_txcsum(struct emx_softc *sc, struct mbuf *mp,
         */
 
        curr_txd = sc->next_avail_tx_desc;
         */
 
        curr_txd = sc->next_avail_tx_desc;
-       tx_buffer = &sc->tx_buf[curr_txd];
        TXD = (struct e1000_context_desc *)&sc->tx_desc_base[curr_txd];
 
        cmd = 0;
        TXD = (struct e1000_context_desc *)&sc->tx_desc_base[curr_txd];
 
        cmd = 0;
@@ -2222,7 +2257,7 @@ emx_txcsum(struct emx_softc *sc, struct mbuf *mp,
                     E1000_TXD_DTYP_D;          /* Data descr */
 
        /* Save the information for this csum offloading context */
                     E1000_TXD_DTYP_D;          /* Data descr */
 
        /* Save the information for this csum offloading context */
-       sc->csum_ehlen = ehdrlen;
+       sc->csum_lhlen = ehdrlen;
        sc->csum_iphlen = ip_hlen;
        sc->csum_flags = csum_flags;
        sc->csum_txd_upper = *txd_upper;
        sc->csum_iphlen = ip_hlen;
        sc->csum_flags = csum_flags;
        sc->csum_txd_upper = *txd_upper;
@@ -3720,3 +3755,130 @@ emx_disable_aspm(struct emx_softc *sc)
        link_ctrl &= ~disable;
        pci_write_config(dev, reg, link_ctrl, 2);
 }
        link_ctrl &= ~disable;
        pci_write_config(dev, reg, link_ctrl, 2);
 }
+
+static int
+emx_tso_pullup(struct emx_softc *sc, struct mbuf **mp)
+{
+       int iphlen, hoff, thoff, ex = 0;
+       struct mbuf *m;
+
+       m = *mp;
+       KASSERT(M_WRITABLE(m), ("TSO mbuf not writable"));
+
+       iphlen = m->m_pkthdr.csum_iphlen;
+       thoff = m->m_pkthdr.csum_thlen;
+       hoff = m->m_pkthdr.csum_lhlen;
+
+       KASSERT(iphlen > 0, ("invalid ip hlen"));
+       KASSERT(thoff > 0, ("invalid tcp hlen"));
+       KASSERT(hoff > 0, ("invalid ether hlen"));
+
+       if (sc->flags & EMX_FLAG_TSO_PULLEX)
+               ex = 4;
+
+       if (m->m_len < hoff + iphlen + thoff + ex) {
+               m = m_pullup(m, hoff + iphlen + thoff + ex);
+               if (m == NULL) {
+                       *mp = NULL;
+                       return ENOBUFS;
+               }
+               *mp = m;
+       }
+       return 0;
+}
+
+static int
+emx_tso_setup(struct emx_softc *sc, struct mbuf *mp,
+    uint32_t *txd_upper, uint32_t *txd_lower)
+{
+       struct e1000_context_desc *TXD;
+       int hoff, iphlen, thoff, hlen;
+       int mss, pktlen, curr_txd;
+       struct ip *ip;
+
+       iphlen = mp->m_pkthdr.csum_iphlen;
+       thoff = mp->m_pkthdr.csum_thlen;
+       hoff = mp->m_pkthdr.csum_lhlen;
+       mss = mp->m_pkthdr.tso_segsz;
+       pktlen = mp->m_pkthdr.len;
+
+       ip = mtodoff(mp, struct ip *, hoff);
+       ip->ip_len = 0;
+
+       if (sc->csum_flags == CSUM_TSO &&
+           sc->csum_iphlen == iphlen &&
+           sc->csum_lhlen == hoff &&
+           sc->csum_thlen == thoff &&
+           sc->csum_mss == mss &&
+           sc->csum_pktlen == pktlen) {
+               *txd_upper = sc->csum_txd_upper;
+               *txd_lower = sc->csum_txd_lower;
+               return 0;
+       }
+       hlen = hoff + iphlen + thoff;
+
+       /*
+        * Setup a new TSO context.
+        */
+
+       curr_txd = sc->next_avail_tx_desc;
+       TXD = (struct e1000_context_desc *)&sc->tx_desc_base[curr_txd];
+
+       *txd_lower = E1000_TXD_CMD_DEXT |       /* Extended descr type */
+                    E1000_TXD_DTYP_D |         /* Data descr type */
+                    E1000_TXD_CMD_TSE;         /* Do TSE on this packet */
+
+       /* IP and/or TCP header checksum calculation and insertion. */
+       *txd_upper = (E1000_TXD_POPTS_IXSM | E1000_TXD_POPTS_TXSM) << 8;
+
+       /*
+        * Start offset for header checksum calculation.
+        * End offset for header checksum calculation.
+        * Offset of place put the checksum.
+        */
+       TXD->lower_setup.ip_fields.ipcss = hoff;
+       TXD->lower_setup.ip_fields.ipcse = htole16(hoff + iphlen - 1);
+       TXD->lower_setup.ip_fields.ipcso = hoff + offsetof(struct ip, ip_sum);
+
+       /*
+        * Start offset for payload checksum calculation.
+        * End offset for payload checksum calculation.
+        * Offset of place to put the checksum.
+        */
+       TXD->upper_setup.tcp_fields.tucss = hoff + iphlen;
+       TXD->upper_setup.tcp_fields.tucse = 0;
+       TXD->upper_setup.tcp_fields.tucso =
+           hoff + iphlen + offsetof(struct tcphdr, th_sum);
+
+       /*
+        * Payload size per packet w/o any headers.
+        * Length of all headers up to payload.
+        */
+       TXD->tcp_seg_setup.fields.mss = htole16(mss);
+       TXD->tcp_seg_setup.fields.hdr_len = hlen;
+       TXD->cmd_and_length = htole32(E1000_TXD_CMD_IFCS |
+                               E1000_TXD_CMD_DEXT |    /* Extended descr */
+                               E1000_TXD_CMD_TSE |     /* TSE context */
+                               E1000_TXD_CMD_IP |      /* Do IP csum */
+                               E1000_TXD_CMD_TCP |     /* Do TCP checksum */
+                               (pktlen - hlen));       /* Total len */
+
+       /* Save the information for this TSO context */
+       sc->csum_flags = CSUM_TSO;
+       sc->csum_lhlen = hoff;
+       sc->csum_iphlen = iphlen;
+       sc->csum_thlen = thoff;
+       sc->csum_mss = mss;
+       sc->csum_pktlen = pktlen;
+       sc->csum_txd_upper = *txd_upper;
+       sc->csum_txd_lower = *txd_lower;
+
+       if (++curr_txd == sc->num_tx_desc)
+               curr_txd = 0;
+
+       KKASSERT(sc->num_tx_desc_avail > 0);
+       sc->num_tx_desc_avail--;
+
+       sc->next_avail_tx_desc = curr_txd;
+       return 1;
+}
index 975d9fc..5ac32d9 100644 (file)
 #define EMX_TARC_SPEED_MODE            (1 << 21)
 
 #define EMX_MAX_SCATTER                        64
 #define EMX_TARC_SPEED_MODE            (1 << 21)
 
 #define EMX_MAX_SCATTER                        64
-#define EMX_TSO_SIZE                   (65535 + \
+#define EMX_TSO_SIZE                   (IP_MAXPACKET + \
                                         sizeof(struct ether_vlan_header))
                                         sizeof(struct ether_vlan_header))
-#define EMX_MAX_SEGSIZE                        4096
+#define EMX_MAX_SEGSIZE                        PAGE_SIZE
 #define EMX_MSIX_MASK                  0x01F00000 /* For 82574 use */
 
 #define EMX_CSUM_FEATURES              (CSUM_IP | CSUM_TCP | CSUM_UDP)
 #define EMX_MSIX_MASK                  0x01F00000 /* For 82574 use */
 
 #define EMX_CSUM_FEATURES              (CSUM_IP | CSUM_TCP | CSUM_UDP)
@@ -234,6 +234,7 @@ struct emx_softc {
        struct e1000_hw         hw;
        int                     flags;
 #define EMX_FLAG_SHARED_INTR   0x1
        struct e1000_hw         hw;
        int                     flags;
 #define EMX_FLAG_SHARED_INTR   0x1
+#define EMX_FLAG_TSO_PULLEX    0x2
 
        /* DragonFly operating-system-specific structures. */
        struct e1000_osdep      osdep;
 
        /* DragonFly operating-system-specific structures. */
        struct e1000_osdep      osdep;
@@ -300,8 +301,13 @@ struct emx_softc {
 
        /* Saved csum offloading context information */
        int                     csum_flags;
 
        /* Saved csum offloading context information */
        int                     csum_flags;
-       int                     csum_ehlen;
+       int                     csum_lhlen;
        int                     csum_iphlen;
        int                     csum_iphlen;
+
+       int                     csum_thlen;     /* TSO */
+       int                     csum_mss;       /* TSO */
+       int                     csum_pktlen;    /* TSO */
+
        uint32_t                csum_txd_upper;
        uint32_t                csum_txd_lower;
 
        uint32_t                csum_txd_upper;
        uint32_t                csum_txd_lower;