bnx: Add TSO support
authorSepherosa Ziehau <sephe@dragonflybsd.org>
Sun, 29 Jul 2012 05:29:49 +0000 (13:29 +0800)
committerSepherosa Ziehau <sephe@dragonflybsd.org>
Mon, 30 Jul 2012 06:26:16 +0000 (14:26 +0800)
Obtained-from: FreeBSD if_bge.c

For TSO header information, ether_tso_pullup() is used instead of
making our own wheel.

BNX_TSO_DEBUG is added to help testing various TCP parameters tuning

sys/conf/options
sys/dev/netif/bge/if_bgereg.h
sys/dev/netif/bnx/Makefile
sys/dev/netif/bnx/if_bnx.c
sys/dev/netif/bnx/if_bnxvar.h

index a74fd9c..5c0a22e 100644 (file)
@@ -617,6 +617,9 @@ AH_SUPPORT_AR5416   opt_ah.h
 # bce driver
 BCE_DEBUG              opt_bce.h
 
+# bnx driver
+BNX_TSO_DEBUG          opt_bnx.h
+
 # ed driver
 ED_NO_MIIBUS           opt_ed.h
 
index f8ce12b..175aca1 100644 (file)
 #define BGE_RDMAMODE_FIFO_SIZE_128     0x00020000
 #define BGE_RDMAMODE_FIFO_LONG_BURST   0x00030000
 #define BGE_RDMAMODE_MULT_DMA_RD_DIS   0x01000000
+#define BGE_RDMAMODE_TSO4_ENABLE       0x08000000
 #define BGE_RDMAMODE_H2BNC_VLAN_DET    0x20000000
 
 /* Read DMA status register */
@@ -2068,11 +2069,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
 };
index 35f2d6f..090e1e4 100644 (file)
@@ -1,11 +1,16 @@
 KMOD=  if_bnx
 SRCS=  if_bnx.c
 SRCS+= miibus_if.h device_if.h bus_if.h pci_if.h
-SRCS+= opt_polling.h
+SRCS+= opt_polling.h opt_bnx.h
 
 .ifndef BUILDING_WITH_KERNEL
+
 opt_polling.h:
        echo '#define DEVICE_POLLING 1' > ${.OBJDIR}/${.TARGET}
+
+opt_bnx.h:
+       touch ${.OBJDIR}/${.TARGET}
+
 .endif
 
 .include <bsd.kmod.mk>
index 18e3b5a..806c80c 100644 (file)
@@ -33,7 +33,7 @@
  * $FreeBSD: src/sys/dev/bge/if_bge.c,v 1.3.2.39 2005/07/03 03:41:18 silby Exp $
  */
 
-
+#include "opt_bnx.h"
 #include "opt_polling.h"
 
 #include <sys/param.h>
@@ -50,6 +50,9 @@
 #include <sys/sockio.h>
 #include <sys/sysctl.h>
 
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+
 #include <net/bpf.h>
 #include <net/ethernet.h>
 #include <net/if.h>
@@ -180,6 +183,8 @@ static void bnx_dma_block_free(bus_dma_tag_t, bus_dmamap_t, void *);
 static struct mbuf *
                bnx_defrag_shortdma(struct mbuf *);
 static int     bnx_encap(struct bnx_softc *, struct mbuf **, uint32_t *);
+static int     bnx_setup_tso(struct bnx_softc *, struct mbuf **,
+                   uint16_t *, uint16_t *);
 
 static void    bnx_reset(struct bnx_softc *);
 static int     bnx_chipinit(struct bnx_softc *);
@@ -1629,6 +1634,8 @@ bnx_blockinit(struct bnx_softc *sc)
                 */
                val &= ~BGE_RDMAMODE_MULT_DMA_RD_DIS;
        }
+       if (sc->bnx_flags & BNX_FLAG_TSO)
+               val |= BGE_RDMAMODE_TSO4_ENABLE;
        val |= BGE_RDMAMODE_FIFO_LONG_BURST;
        CSR_WRITE_4(sc, BGE_RDMA_MODE, val);
        DELAY(40);
@@ -1652,7 +1659,12 @@ bnx_blockinit(struct bnx_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->bnx_flags & BNX_FLAG_TSO) {
+               CSR_WRITE_4(sc, BGE_SDI_MODE, BGE_SDIMODE_ENABLE |
+                   BGE_SDIMODE_HW_LSO_PRE_DMA);
+       } 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);
@@ -1743,6 +1755,10 @@ bnx_attach(device_t dev)
        driver_intr_t *intr_func;
        uintptr_t mii_priv = 0;
        u_int intr_flags;
+#ifdef BNX_TSO_DEBUG
+       char desc[32];
+       int i;
+#endif
 
        sc = device_get_softc(dev);
        sc->bnx_dev = dev;
@@ -1841,6 +1857,11 @@ bnx_attach(device_t dev)
        }
        sc->bnx_flags |= BNX_FLAG_SHORTDMA;
 
+       sc->bnx_flags |= BNX_FLAG_TSO;
+       if (sc->bnx_asicrev == BGE_ASICREV_BCM5719 &&
+           sc->bnx_chipid == BGE_CHIPID_BCM5719_A0)
+               sc->bnx_flags &= ~BNX_FLAG_TSO;
+
        if (sc->bnx_asicrev == BGE_ASICREV_BCM5717 ||
            BNX_IS_57765_FAMILY(sc)) {
                /*
@@ -1963,6 +1984,10 @@ bnx_attach(device_t dev)
 
        ifp->if_capabilities |= IFCAP_HWCSUM;
        ifp->if_hwassist = BNX_CSUM_FEATURES;
+       if (sc->bnx_flags & BNX_FLAG_TSO) {
+               ifp->if_capabilities |= IFCAP_TSO;
+               ifp->if_hwassist |= CSUM_TSO;
+       }
        ifp->if_capenable = ifp->if_capabilities;
 
        /*
@@ -2141,6 +2166,15 @@ bnx_attach(device_t dev)
            sc, 0, bnx_sysctl_tx_coal_bds_int, "I",
            "Transmit max coalesced BD count during interrupt.");
 
+#ifdef BNX_TSO_DEBUG
+       for (i = 0; i < BNX_TSO_NSTATS; ++i) {
+               ksnprintf(desc, sizeof(desc), "tso%d", i + 1);
+               SYSCTL_ADD_ULONG(&sc->bnx_sysctl_ctx,
+                   SYSCTL_CHILDREN(sc->bnx_sysctl_tree), OID_AUTO,
+                   desc, CTLFLAG_RW, &sc->bnx_tsosegs[i], "");
+       }
+#endif
+
        /*
         * Call MI attach routine.
         */
@@ -2771,13 +2805,31 @@ static int
 bnx_encap(struct bnx_softc *sc, struct mbuf **m_head0, uint32_t *txidx)
 {
        struct bge_tx_bd *d = NULL;
-       uint16_t csum_flags = 0;
+       uint16_t csum_flags = 0, vlan_tag = 0, mss = 0;
        bus_dma_segment_t segs[BNX_NSEG_NEW];
        bus_dmamap_t map;
        int error, maxsegs, nsegs, idx, i;
        struct mbuf *m_head = *m_head0, *m_new;
 
-       if (m_head->m_pkthdr.csum_flags) {
+       if (m_head->m_pkthdr.csum_flags & CSUM_TSO) {
+#ifdef BNX_TSO_DEBUG
+               int tso_nsegs;
+#endif
+
+               error = bnx_setup_tso(sc, m_head0, &mss, &csum_flags);
+               if (error)
+                       return error;
+               m_head = *m_head0;
+
+#ifdef BNX_TSO_DEBUG
+               tso_nsegs = (m_head->m_pkthdr.len / m_head->m_pkthdr.segsz) - 1;
+               if (tso_nsegs > (BNX_TSO_NSTATS - 1))
+                       tso_nsegs = BNX_TSO_NSTATS - 1;
+               else if (tso_nsegs < 0)
+                       tso_nsegs = 0;
+               sc->bnx_tsosegs[tso_nsegs]++;
+#endif
+       } else if (m_head->m_pkthdr.csum_flags & BNX_CSUM_FEATURES) {
                if (m_head->m_pkthdr.csum_flags & CSUM_IP)
                        csum_flags |= BGE_TXBDFLAG_IP_CSUM;
                if (m_head->m_pkthdr.csum_flags & (CSUM_TCP | CSUM_UDP))
@@ -2787,6 +2839,10 @@ bnx_encap(struct bnx_softc *sc, struct mbuf **m_head0, uint32_t *txidx)
                else if (m_head->m_flags & M_FRAG)
                        csum_flags |= BGE_TXBDFLAG_IP_FRAG;
        }
+       if (m_head->m_flags & M_VLANTAG) {
+               csum_flags |= BGE_TXBDFLAG_VLAN_TAG;
+               vlan_tag = m_head->m_pkthdr.ether_vlantag;
+       }
 
        idx = *txidx;
        map = sc->bnx_cdata.bnx_tx_dmamap[idx];
@@ -2822,7 +2878,8 @@ bnx_encap(struct bnx_softc *sc, struct mbuf **m_head0, uint32_t *txidx)
                }
                *m_head0 = m_head = m_new;
        }
-       if (sc->bnx_force_defrag && m_head->m_next != NULL) {
+       if ((m_head->m_pkthdr.csum_flags & CSUM_TSO) == 0 &&
+           sc->bnx_force_defrag && m_head->m_next != NULL) {
                /*
                 * Forcefully defragment mbuf chain to overcome hardware
                 * limitation which only support a single outstanding
@@ -2849,6 +2906,8 @@ bnx_encap(struct bnx_softc *sc, struct mbuf **m_head0, uint32_t *txidx)
                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;
@@ -2857,15 +2916,6 @@ bnx_encap(struct bnx_softc *sc, struct mbuf **m_head0, uint32_t *txidx)
        /* Mark the last segment as end of packet... */
        d->bge_flags |= BGE_TXBDFLAG_END;
 
-       /* Set vlan tag to the first segment of the packet. */
-       d = &sc->bnx_ldata.bnx_tx_ring[*txidx];
-       if (m_head->m_flags & M_VLANTAG) {
-               d->bge_flags |= BGE_TXBDFLAG_VLAN_TAG;
-               d->bge_vlan_tag = m_head->m_pkthdr.ether_vlantag;
-       } else {
-               d->bge_vlan_tag = 0;
-       }
-
        /*
         * Insure that the map for this transmission is placed at
         * the array index of the last descriptor in this chain.
@@ -3252,10 +3302,17 @@ bnx_ioctl(struct ifnet *ifp, u_long command, caddr_t data, struct ucred *cr)
                mask = ifr->ifr_reqcap ^ ifp->if_capenable;
                if (mask & IFCAP_HWCSUM) {
                        ifp->if_capenable ^= (mask & IFCAP_HWCSUM);
-                       if (IFCAP_HWCSUM & ifp->if_capenable)
-                               ifp->if_hwassist = BNX_CSUM_FEATURES;
+                       if (ifp->if_capenable & IFCAP_TXCSUM)
+                               ifp->if_hwassist |= BNX_CSUM_FEATURES;
                        else
-                               ifp->if_hwassist = 0;
+                               ifp->if_hwassist &= ~BNX_CSUM_FEATURES;
+               }
+               if (mask & IFCAP_TSO) {
+                       ifp->if_capenable ^= (mask & IFCAP_TSO);
+                       if (ifp->if_capenable & IFCAP_TSO)
+                               ifp->if_hwassist |= CSUM_TSO;
+                       else
+                               ifp->if_hwassist &= ~CSUM_TSO;
                }
                break;
        default:
@@ -3467,6 +3524,7 @@ static int
 bnx_dma_alloc(struct bnx_softc *sc)
 {
        struct ifnet *ifp = &sc->arpcom.ac_if;
+       bus_size_t txmaxsz;
        int i, error;
 
        /*
@@ -3533,10 +3591,14 @@ bnx_dma_alloc(struct bnx_softc *sc)
        /*
         * Create DMA tag and maps for TX mbufs.
         */
+       if (sc->bnx_flags & BNX_FLAG_TSO)
+               txmaxsz = IP_MAXPACKET + sizeof(struct ether_vlan_header);
+       else
+               txmaxsz = BNX_JUMBO_FRAMELEN;
        error = bus_dma_tag_create(sc->bnx_cdata.bnx_parent_tag, 1, 0,
                                   BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR,
                                   NULL, NULL,
-                                  BNX_JUMBO_FRAMELEN, BNX_NSEG_NEW, MCLBYTES,
+                                  txmaxsz, BNX_NSEG_NEW, PAGE_SIZE,
                                   BUS_DMA_ALLOCNOW | BUS_DMA_WAITOK |
                                   BUS_DMA_ONEBPAGE,
                                   &sc->bnx_cdata.bnx_tx_mtag);
@@ -4209,3 +4271,35 @@ bnx_dma_swap_options(struct bnx_softc *sc)
        }
        return dma_options;
 }
+
+static int
+bnx_setup_tso(struct bnx_softc *sc, struct mbuf **mp,
+    uint16_t *mss0, uint16_t *flags0)
+{
+       struct mbuf *m;
+       struct ip *ip;
+       struct tcphdr *th;
+       int thoff, iphlen, hoff, hlen;
+       boolean_t ret;
+       uint16_t flags, mss;
+
+       ret = ether_tso_pullup(mp, &hoff, &ip, &iphlen, &th, &thoff);
+       if (!ret)
+               return ENOBUFS;
+
+       m = *mp;
+       mss = m->m_pkthdr.segsz;
+       flags = BGE_TXBDFLAG_CPU_PRE_DMA | BGE_TXBDFLAG_CPU_POST_DMA;
+
+       ip->ip_len = htons(mss + iphlen + thoff);
+       th->th_sum = 0;
+
+       hlen = (iphlen + thoff) >> 2;
+       mss |= ((hlen & 0x3) << 14);
+       flags |= ((hlen & 0xf8) << 7) | ((hlen & 0x4) << 2);
+
+       *mss0 = mss;
+       *flags0 = flags;
+
+       return 0;
+}
index b2c242d..2624e2b 100644 (file)
@@ -210,6 +210,7 @@ struct bnx_softc {
 #define BNX_FLAG_57765_PLUS    0x00000040
 #define BNX_FLAG_57765_FAMILY  0x00000080
 #define BNX_FLAG_STATUSTAG_BUG 0x00000100
+#define BNX_FLAG_TSO           0x00000200
 #define BNX_FLAG_NO_EEPROM     0x10000000
 #define BNX_FLAG_SHORTDMA      0x40000000
 
@@ -263,9 +264,12 @@ struct bnx_softc {
 
        void                    (*bnx_link_upd)(struct bnx_softc *, uint32_t);
        uint32_t                bnx_link_chg;
+
+#define BNX_TSO_NSTATS         45
+       u_long                  bnx_tsosegs[BNX_TSO_NSTATS];
 };
 
-#define BNX_NSEG_NEW           32
+#define BNX_NSEG_NEW           40
 #define BNX_NSEG_SPARE         5
 #define BNX_NSEG_RSVD          16