Author: yongari
Date: Sun Nov 22 21:16:30 2009
New Revision: 199671
URL: http://svn.freebsd.org/changeset/base/199671

Log:
  Implement TSO for BCM5755 or newer controllers. Some controllers
  seem to require a special firmware to use TSO. But the firmware is
  not available to FreeBSD and Linux claims that the TSO performed by
  the firmware is slower than hardware based TSO. Moreover the
  firmware based TSO has one known bug which can't handle TSO if
  ethernet header + IP/TCP header is greater than 80 bytes. The
  workaround for the TSO bug exist but it seems it's too expensive
  than not using TSO at all. Some hardwares also have the TSO bug so
  limit the TSO to the controllers that are not affected TSO issues
  (e.g. 5755 or higher).
  While I'm here set VLAN tag bit to all descriptors that belengs to
  a frame instead of the first descriptor of a frame. The datasheet
  is not clear how to handle VLAN tag bit but it worked either way in
  my testing. This makes it simplify TSO configuration a little bit.
  
  Big thanks to davidch@ who sent me detailed TSO information.
  Without this I was not able to implement it.
  
  Tested by:    current

Modified:
  head/sys/dev/bge/if_bge.c
  head/sys/dev/bge/if_bgereg.h

Modified: head/sys/dev/bge/if_bge.c
==============================================================================
--- head/sys/dev/bge/if_bge.c   Sun Nov 22 20:50:27 2009        (r199670)
+++ head/sys/dev/bge/if_bge.c   Sun Nov 22 21:16:30 2009        (r199671)
@@ -96,6 +96,7 @@ __FBSDID("$FreeBSD$");
 #include <netinet/in_systm.h>
 #include <netinet/in.h>
 #include <netinet/ip.h>
+#include <netinet/tcp.h>
 
 #include <machine/bus.h>
 #include <machine/resource.h>
@@ -1811,6 +1812,8 @@ bge_blockinit(struct bge_softc *sc)
                    BGE_RDMAMODE_MBUF_SBD_CRPT_ATTN;
        if (sc->bge_flags & BGE_FLAG_PCIE)
                val |= BGE_RDMAMODE_FIFO_LONG_BURST;
+       if (sc->bge_flags & BGE_FLAG_TSO)
+               val |= BGE_RDMAMODE_TSO4_ENABLE;
        CSR_WRITE_4(sc, BGE_RDMA_MODE, val);
        DELAY(40);
 
@@ -1837,7 +1840,10 @@ bge_blockinit(struct bge_softc *sc)
        CSR_WRITE_4(sc, BGE_SDC_MODE, val);
 
        /* Turn on send data initiator state machine */
-       CSR_WRITE_4(sc, BGE_SDI_MODE, BGE_SDIMODE_ENABLE);
+       if (sc->bge_flags & BGE_FLAG_TSO)
+               CSR_WRITE_4(sc, BGE_SDI_MODE, BGE_SDIMODE_ENABLE | 0x08);
+       else
+               CSR_WRITE_4(sc, BGE_SDI_MODE, BGE_SDIMODE_ENABLE);
 
        /* Turn on send BD initiator state machine */
        CSR_WRITE_4(sc, BGE_SBDI_MODE, BGE_SBDIMODE_ENABLE);
@@ -2105,6 +2111,7 @@ bge_dma_alloc(device_t dev)
        struct bge_dmamap_arg ctx;
        struct bge_softc *sc;
        bus_addr_t lowaddr;
+       bus_size_t txsegsz, txmaxsegsz;
        int i, error;
 
        sc = device_get_softc(dev);
@@ -2131,10 +2138,17 @@ bge_dma_alloc(device_t dev)
        /*
         * Create tag for Tx mbufs.
         */
+       if (sc->bge_flags & BGE_FLAG_TSO) {
+               txsegsz = BGE_TSOSEG_SZ;
+               txmaxsegsz = 65535 + sizeof(struct ether_vlan_header);
+       } else {
+               txsegsz = MCLBYTES;
+               txmaxsegsz = MCLBYTES * BGE_NSEG_NEW;
+       }
        error = bus_dma_tag_create(sc->bge_cdata.bge_parent_tag, 1,
-           0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL,
-           NULL, MCLBYTES * BGE_NSEG_NEW, BGE_NSEG_NEW, MCLBYTES,
-           BUS_DMA_ALLOCNOW, NULL, NULL, &sc->bge_cdata.bge_tx_mtag);
+           0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL,
+           txmaxsegsz, BGE_NSEG_NEW, txsegsz, 0, NULL, NULL,
+           &sc->bge_cdata.bge_tx_mtag);
 
        if (error) {
                device_printf(sc->bge_dev, "could not allocate TX dma tag\n");
@@ -2146,7 +2160,7 @@ bge_dma_alloc(device_t dev)
         */
        error = bus_dma_tag_create(sc->bge_cdata.bge_parent_tag, 1, 0,
            BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, 1,
-           MCLBYTES, BUS_DMA_ALLOCNOW, NULL, NULL, &sc->bge_cdata.bge_rx_mtag);
+           MCLBYTES, 0, NULL, NULL, &sc->bge_cdata.bge_rx_mtag);
 
        if (error) {
                device_printf(sc->bge_dev, "could not allocate RX dma tag\n");
@@ -2592,6 +2606,21 @@ bge_attach(device_t dev)
            misccfg == BGE_MISCCFG_BOARD_ID_5788M)
                sc->bge_flags |= BGE_FLAG_5788;
 
+       /*
+        * Some controllers seem to require a special firmware to use
+        * TSO. But the firmware is not available to FreeBSD and Linux
+        * claims that the TSO performed by the firmware is slower than
+        * hardware based TSO. Moreover the firmware based TSO has one
+        * known bug which can't handle TSO if ethernet header + IP/TCP
+        * header is greater than 80 bytes. The workaround for the TSO
+        * bug exist but it seems it's too expensive than not using
+        * TSO at all. Some hardwares also have the TSO bug so limit
+        * the TSO to the controllers that are not affected TSO issues
+        * (e.g. 5755 or higher).
+        */
+       if (BGE_IS_5755_PLUS(sc))
+               sc->bge_flags |= BGE_FLAG_TSO;
+
        /*
         * Check if this is a PCI-X or PCI Express device.
         */
@@ -2738,6 +2767,10 @@ bge_attach(device_t dev)
        ifp->if_hwassist = BGE_CSUM_FEATURES;
        ifp->if_capabilities = IFCAP_HWCSUM | IFCAP_VLAN_HWTAGGING |
            IFCAP_VLAN_MTU;
+       if ((sc->bge_flags & BGE_FLAG_TSO) != 0) {
+               ifp->if_hwassist |= CSUM_TSO;
+               ifp->if_capabilities |= IFCAP_TSO4;
+       }
 #ifdef IFCAP_VLAN_HWCSUM
        ifp->if_capabilities |= IFCAP_VLAN_HWCSUM;
 #endif
@@ -3752,6 +3785,72 @@ bge_cksum_pad(struct mbuf *m)
        return (0);
 }
 
+static struct mbuf *
+bge_setup_tso(struct bge_softc *sc, struct mbuf *m, uint16_t *mss)
+{
+       struct ether_header *eh;
+       struct ip *ip;
+       struct tcphdr *tcp;
+       struct mbuf *n;
+       uint16_t hlen;
+       uint32_t ip_off, poff;
+
+       if (M_WRITABLE(m) == 0) {
+               /* Get a writable copy. */
+               n = m_dup(m, M_DONTWAIT);
+               m_freem(m);
+               if (n == NULL)
+                       return (NULL);
+               m = n;
+       }
+       ip_off = sizeof(struct ether_header);
+       m = m_pullup(m, ip_off);
+       if (m == NULL)
+               return (NULL);
+       eh = mtod(m, struct ether_header *);
+       /* Check the existence of VLAN tag. */
+       if (eh->ether_type == htons(ETHERTYPE_VLAN)) {
+               ip_off = sizeof(struct ether_vlan_header);
+               m = m_pullup(m, ip_off);
+               if (m == NULL)
+                       return (NULL);
+       }
+       m = m_pullup(m, ip_off + sizeof(struct ip));
+       if (m == NULL)
+               return (NULL);
+       ip = (struct ip *)(mtod(m, char *) + ip_off);
+       poff = ip_off + (ip->ip_hl << 2);
+       m = m_pullup(m, poff + sizeof(struct tcphdr));
+       if (m == NULL)
+               return (NULL);
+       tcp = (struct tcphdr *)(mtod(m, char *) + poff);
+       m = m_pullup(m, poff + sizeof(struct tcphdr) + tcp->th_off);
+       if (m == NULL)
+               return (NULL);
+       /*
+        * It seems controller doesn't modify IP length and TCP pseudo
+        * checksum. These checksum computed by upper stack should be 0.
+        */
+       *mss = m->m_pkthdr.tso_segsz;
+       ip->ip_sum = 0;
+       ip->ip_len = htons(*mss + (ip->ip_hl << 2) + (tcp->th_off << 2));
+       /* Clear pseudo checksum computed by TCP stack. */
+       tcp->th_sum = 0;
+       /*
+        * Broadcom controllers uses different descriptor format for
+        * TSO depending on ASIC revision. Due to TSO-capable firmware
+        * license issue and lower performance of firmware based TSO
+        * we only support hardware based TSO which is applicable for
+        * BCM5755 or newer controllers. Hardware based TSO uses 11
+        * bits to store MSS and upper 5 bits are used to store IP/TCP
+        * header length(including IP/TCP options). The header length
+        * is expressed as 32 bits unit.
+        */
+       hlen = ((ip->ip_hl << 2) + (tcp->th_off << 2)) >> 2;
+       *mss |= (hlen << 11);
+       return (m);
+}
+
 /*
  * Encapsulate an mbuf chain in the tx ring  by coupling the mbuf data
  * pointers to descriptors.
@@ -3764,11 +3863,19 @@ bge_encap(struct bge_softc *sc, struct m
        struct bge_tx_bd        *d;
        struct mbuf             *m = *m_head;
        uint32_t                idx = *txidx;
-       uint16_t                csum_flags;
+       uint16_t                csum_flags, mss, vlan_tag;
        int                     nsegs, i, error;
 
        csum_flags = 0;
-       if (m->m_pkthdr.csum_flags) {
+       mss = 0;
+       vlan_tag = 0;
+       if ((m->m_pkthdr.csum_flags & CSUM_TSO) != 0) {
+               *m_head = m = bge_setup_tso(sc, m, &mss);
+               if (*m_head == NULL)
+                       return (ENOBUFS);
+               csum_flags |= BGE_TXBDFLAG_CPU_PRE_DMA |
+                   BGE_TXBDFLAG_CPU_POST_DMA;
+       } else if ((m->m_pkthdr.csum_flags & BGE_CSUM_FEATURES) != 0) {
                if (m->m_pkthdr.csum_flags & CSUM_IP)
                        csum_flags |= BGE_TXBDFLAG_IP_CSUM;
                if (m->m_pkthdr.csum_flags & (CSUM_TCP | CSUM_UDP)) {
@@ -3815,12 +3922,29 @@ bge_encap(struct bge_softc *sc, struct m
 
        bus_dmamap_sync(sc->bge_cdata.bge_tx_mtag, map, BUS_DMASYNC_PREWRITE);
 
+#if __FreeBSD_version > 700022
+       if (m->m_flags & M_VLANTAG) {
+               csum_flags |= BGE_TXBDFLAG_VLAN_TAG;
+               vlan_tag = m->m_pkthdr.ether_vtag;
+       }
+#else
+       {
+               struct m_tag            *mtag;
+
+               if ((mtag = VLAN_OUTPUT_TAG(sc->bge_ifp, m)) != NULL) {
+                       csum_flags |= BGE_TXBDFLAG_VLAN_TAG;
+                       vlan_tag = VLAN_TAG_VALUE(mtag);
+               }
+       }
+#endif
        for (i = 0; ; i++) {
                d = &sc->bge_ldata.bge_tx_ring[idx];
                d->bge_addr.bge_addr_lo = BGE_ADDR_LO(segs[i].ds_addr);
                d->bge_addr.bge_addr_hi = BGE_ADDR_HI(segs[i].ds_addr);
                d->bge_len = segs[i].ds_len;
                d->bge_flags = csum_flags;
+               d->bge_vlan_tag = vlan_tag;
+               d->bge_mss = mss;
                if (i == nsegs - 1)
                        break;
                BGE_INC(idx, BGE_TX_RING_CNT);
@@ -3829,26 +3953,6 @@ bge_encap(struct bge_softc *sc, struct m
        /* Mark the last segment as end of packet... */
        d->bge_flags |= BGE_TXBDFLAG_END;
 
-       /* ... and put VLAN tag into first segment.  */
-       d = &sc->bge_ldata.bge_tx_ring[*txidx];
-#if __FreeBSD_version > 700022
-       if (m->m_flags & M_VLANTAG) {
-               d->bge_flags |= BGE_TXBDFLAG_VLAN_TAG;
-               d->bge_vlan_tag = m->m_pkthdr.ether_vtag;
-       } else
-               d->bge_vlan_tag = 0;
-#else
-       {
-               struct m_tag            *mtag;
-
-               if ((mtag = VLAN_OUTPUT_TAG(sc->bge_ifp, m)) != NULL) {
-                       d->bge_flags |= BGE_TXBDFLAG_VLAN_TAG;
-                       d->bge_vlan_tag = VLAN_TAG_VALUE(mtag);
-               } else
-                       d->bge_vlan_tag = 0;
-       }
-#endif
-
        /*
         * Insure that the map for this transmission
         * is placed at the array index of the last descriptor
@@ -4355,14 +4459,23 @@ bge_ioctl(struct ifnet *ifp, u_long comm
                        ifp->if_capenable ^= IFCAP_HWCSUM;
                        if (IFCAP_HWCSUM & ifp->if_capenable &&
                            IFCAP_HWCSUM & ifp->if_capabilities)
-                               ifp->if_hwassist = BGE_CSUM_FEATURES;
+                               ifp->if_hwassist |= BGE_CSUM_FEATURES;
                        else
-                               ifp->if_hwassist = 0;
+                               ifp->if_hwassist &= ~BGE_CSUM_FEATURES;
 #ifdef VLAN_CAPABILITIES
                        VLAN_CAPABILITIES(ifp);
 #endif
                }
 
+               if ((mask & IFCAP_TSO4) != 0 &&
+                   (ifp->if_capabilities & IFCAP_TSO4) != 0) {
+                       ifp->if_capenable ^= IFCAP_TSO4;
+                       if ((ifp->if_capenable & IFCAP_TSO4) != 0)
+                               ifp->if_hwassist |= CSUM_TSO;
+                       else
+                               ifp->if_hwassist &= ~CSUM_TSO;
+               }
+
                if (mask & IFCAP_VLAN_MTU) {
                        ifp->if_capenable ^= IFCAP_VLAN_MTU;
                        ifp->if_drv_flags &= ~IFF_DRV_RUNNING;

Modified: head/sys/dev/bge/if_bgereg.h
==============================================================================
--- head/sys/dev/bge/if_bgereg.h        Sun Nov 22 20:50:27 2009        
(r199670)
+++ head/sys/dev/bge/if_bgereg.h        Sun Nov 22 21:16:30 2009        
(r199671)
@@ -1402,6 +1402,8 @@
 #define        BGE_RDMAMODE_MBUF_SBD_CRPT_ATTN 0x00002000
 #define        BGE_RDMAMODE_FIFO_SIZE_128      0x00020000
 #define        BGE_RDMAMODE_FIFO_LONG_BURST    0x00030000
+#define        BGE_RDMAMODE_TSO4_ENABLE        0x08000000
+#define        BGE_RDMAMODE_TSO6_ENABLE        0x10000000
 
 /* Read DMA status register */
 #define        BGE_RDMASTAT_PCI_TGT_ABRT_ATTN  0x00000004
@@ -1949,11 +1951,11 @@ struct bge_tx_bd {
        uint16_t                bge_flags;
        uint16_t                bge_len;
        uint16_t                bge_vlan_tag;
-       uint16_t                bge_rsvd;
+       uint16_t                bge_mss;
 #else
        uint16_t                bge_len;
        uint16_t                bge_flags;
-       uint16_t                bge_rsvd;
+       uint16_t                bge_mss;
        uint16_t                bge_vlan_tag;
 #endif
 };
@@ -2482,7 +2484,8 @@ struct bge_gib {
 #define        BGE_JSLOTS      384
 
 #define        BGE_NSEG_JUMBO  4
-#define        BGE_NSEG_NEW 32
+#define        BGE_NSEG_NEW    32
+#define        BGE_TSOSEG_SZ   4096
 
 /* Maximum DMA address for controllers that have 40bit DMA address bug. */
 #if (BUS_SPACE_MAXADDR < 0xFFFFFFFFFF)
@@ -2602,6 +2605,7 @@ struct bge_softc {
 #define        BGE_FLAG_MSI            0x00000100
 #define        BGE_FLAG_PCIX           0x00000200
 #define        BGE_FLAG_PCIE           0x00000400
+#define        BGE_FLAG_TSO            0x00000800
 #define        BGE_FLAG_5700_FAMILY    0x00001000
 #define        BGE_FLAG_5705_PLUS      0x00002000
 #define        BGE_FLAG_5714_FAMILY    0x00004000
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to