From: Sepherosa Ziehau Date: Sun, 12 Aug 2012 10:44:28 +0000 (+0800) Subject: bge: Add TSO support for BCM5755 family chips X-Git-Tag: v3.2.0~411 X-Git-Url: http://gitweb.dragonflybsd.org/dragonfly.git/commitdiff_plain/e92f005cbd5be891b20ada83fb0d2cf332c85e3f bge: Add TSO support for BCM5755 family chips Obtained-from: FreeBSD --- diff --git a/sys/dev/netif/bge/if_bge.c b/sys/dev/netif/bge/if_bge.c index 83bf0e5..0146487 100644 --- a/sys/dev/netif/bge/if_bge.c +++ b/sys/dev/netif/bge/if_bge.c @@ -87,6 +87,9 @@ #include #include +#include +#include + #include #include #include @@ -300,6 +303,8 @@ static void bge_stats_update_regs(struct bge_softc *); static struct mbuf * bge_defrag_shortdma(struct mbuf *); static int bge_encap(struct bge_softc *, struct mbuf **, uint32_t *); +static int bge_setup_tso(struct bge_softc *, struct mbuf **, + uint16_t *, uint16_t *); #ifdef DEVICE_POLLING static void bge_poll(struct ifnet *ifp, enum poll_cmd cmd, int count); @@ -1850,6 +1855,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); @@ -1876,7 +1883,11 @@ 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 | + 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); @@ -1976,6 +1987,9 @@ bge_attach(device_t dev) callout_init(&sc->bge_stat_timer); lwkt_serialize_init(&sc->bge_jslot_serializer); + product = pci_get_device(dev); + vendor = pci_get_vendor(dev); + #ifndef BURN_BRIDGES if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) { uint32_t irq, mem; @@ -2130,11 +2144,21 @@ bge_attach(device_t dev) } } + if (BGE_IS_5755_PLUS(sc)) { + /* + * BCM5754 and BCM5787 shares the same ASIC id so + * explicit device id check is required. + * Due to unknown reason TSO does not work on BCM5755M. + */ + if (product != PCI_PRODUCT_BROADCOM_BCM5754 && + product != PCI_PRODUCT_BROADCOM_BCM5754M && + product != PCI_PRODUCT_BROADCOM_BCM5755M) + sc->bge_flags |= BGE_FLAG_TSO; + } + /* * Set various PHY quirk flags. */ - product = pci_get_device(dev); - vendor = pci_get_vendor(dev); if ((sc->bge_asicrev == BGE_ASICREV_BCM5700 || sc->bge_asicrev == BGE_ASICREV_BCM5701) && @@ -2296,6 +2320,15 @@ bge_attach(device_t dev) sc->bge_tx_coal_bds_int = BGE_TX_COAL_BDS_MIN; } + /* Set up TX spare and reserved descriptor count */ + if (sc->bge_flags & BGE_FLAG_TSO) { + sc->bge_txspare = BGE_NSEG_SPARE_TSO; + sc->bge_txrsvd = BGE_NSEG_RSVD_TSO; + } else { + sc->bge_txspare = BGE_NSEG_SPARE; + sc->bge_txrsvd = BGE_NSEG_RSVD; + } + /* Set up ifnet structure */ ifp->if_softc = sc; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; @@ -2317,7 +2350,11 @@ bge_attach(device_t dev) */ if (sc->bge_chipid != BGE_CHIPID_BCM5700_B0) { ifp->if_capabilities |= IFCAP_HWCSUM; - ifp->if_hwassist = BGE_CSUM_FEATURES; + ifp->if_hwassist |= BGE_CSUM_FEATURES; + } + if (sc->bge_flags & BGE_FLAG_TSO) { + ifp->if_capabilities |= IFCAP_TSO; + ifp->if_hwassist |= CSUM_TSO; } ifp->if_capenable = ifp->if_capabilities; @@ -2999,7 +3036,7 @@ bge_txeof(struct bge_softc *sc, uint16_t tx_cons) if (cur_tx != NULL && (BGE_TX_RING_CNT - sc->bge_txcnt) >= - (BGE_NSEG_RSVD + BGE_NSEG_SPARE)) + (sc->bge_txrsvd + sc->bge_txspare)) ifp->if_flags &= ~IFF_OACTIVE; if (sc->bge_txcnt == 0) @@ -3282,14 +3319,19 @@ bge_stats_update(struct bge_softc *sc) static int bge_encap(struct bge_softc *sc, struct mbuf **m_head0, uint32_t *txidx) { - struct bge_tx_bd *d = NULL; - uint16_t csum_flags = 0; + struct bge_tx_bd *d = NULL, *last_d; + uint16_t csum_flags = 0, mss = 0; bus_dma_segment_t segs[BGE_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) { + error = bge_setup_tso(sc, m_head0, &mss, &csum_flags); + if (error) + return ENOBUFS; + m_head = *m_head0; + } else if (m_head->m_pkthdr.csum_flags & BGE_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)) @@ -3303,8 +3345,8 @@ bge_encap(struct bge_softc *sc, struct mbuf **m_head0, uint32_t *txidx) idx = *txidx; map = sc->bge_cdata.bge_tx_dmamap[idx]; - maxsegs = (BGE_TX_RING_CNT - sc->bge_txcnt) - BGE_NSEG_RSVD; - KASSERT(maxsegs >= BGE_NSEG_SPARE, + maxsegs = (BGE_TX_RING_CNT - sc->bge_txcnt) - sc->bge_txrsvd; + KASSERT(maxsegs >= sc->bge_txspare, ("not enough segments %d", maxsegs)); if (maxsegs > BGE_NSEG_NEW) @@ -3334,7 +3376,8 @@ bge_encap(struct bge_softc *sc, struct mbuf **m_head0, uint32_t *txidx) } *m_head0 = m_head = m_new; } - if (sc->bge_force_defrag && (sc->bge_flags & BGE_FLAG_PCIE) && + if ((m_head->m_pkthdr.csum_flags & CSUM_TSO) == 0 && + sc->bge_force_defrag && (sc->bge_flags & BGE_FLAG_PCIE) && m_head->m_next != NULL) { /* * Forcefully defragment mbuf chain to overcome hardware @@ -3362,13 +3405,13 @@ bge_encap(struct bge_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_mss = mss; if (i == nsegs - 1) break; BGE_INC(idx, BGE_TX_RING_CNT); } - /* Mark the last segment as end of packet... */ - d->bge_flags |= BGE_TXBDFLAG_END; + last_d = d; /* Set vlan tag to the first segment of the packet. */ d = &sc->bge_ldata.bge_tx_ring[*txidx]; @@ -3379,6 +3422,9 @@ bge_encap(struct bge_softc *sc, struct mbuf **m_head0, uint32_t *txidx) d->bge_vlan_tag = 0; } + /* Mark the last segment as end of packet... */ + last_d->bge_flags |= BGE_TXBDFLAG_END; + /* * Insure that the map for this transmission is placed at * the array index of the last descriptor in this chain. @@ -3437,7 +3483,7 @@ bge_start(struct ifnet *ifp) if ((m_head->m_flags & M_FIRSTFRAG) && (m_head->m_pkthdr.csum_flags & CSUM_DELAY_DATA)) { if ((BGE_TX_RING_CNT - sc->bge_txcnt) < - m_head->m_pkthdr.csum_data + BGE_NSEG_RSVD) { + m_head->m_pkthdr.csum_data + sc->bge_txrsvd) { ifp->if_flags |= IFF_OACTIVE; ifq_prepend(&ifp->if_snd, m_head); break; @@ -3445,13 +3491,13 @@ bge_start(struct ifnet *ifp) } /* - * Sanity check: avoid coming within BGE_NSEG_RSVD + * Sanity check: avoid coming within bge_txrsvd * descriptors of the end of the ring. Also make - * sure there are BGE_NSEG_SPARE descriptors for + * sure there are bge_txspare descriptors for * jumbo buffers' defragmentation. */ if ((BGE_TX_RING_CNT - sc->bge_txcnt) < - (BGE_NSEG_RSVD + BGE_NSEG_SPARE)) { + (sc->bge_txrsvd + sc->bge_txspare)) { ifp->if_flags |= IFF_OACTIVE; ifq_prepend(&ifp->if_snd, m_head); break; @@ -3813,10 +3859,17 @@ bge_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 = BGE_CSUM_FEATURES; + if (ifp->if_capenable & IFCAP_TXCSUM) + ifp->if_hwassist |= BGE_CSUM_FEATURES; else - ifp->if_hwassist = 0; + ifp->if_hwassist &= ~BGE_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; } break; default: @@ -4045,6 +4098,7 @@ bge_dma_alloc(struct bge_softc *sc) struct ifnet *ifp = &sc->arpcom.ac_if; int i, error; bus_addr_t lowaddr; + bus_size_t txmaxsz; lowaddr = BUS_SPACE_MAXADDR; if (sc->bge_flags & BGE_FLAG_MAXADDR_40BIT) @@ -4114,10 +4168,14 @@ bge_dma_alloc(struct bge_softc *sc) /* * Create DMA tag and maps for TX mbufs. */ + if (sc->bge_flags & BGE_FLAG_TSO) + txmaxsz = IP_MAXPACKET + sizeof(struct ether_vlan_header); + else + txmaxsz = BGE_JUMBO_FRAMELEN; error = bus_dma_tag_create(sc->bge_cdata.bge_parent_tag, 1, 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, - BGE_JUMBO_FRAMELEN, BGE_NSEG_NEW, MCLBYTES, + txmaxsz, BGE_NSEG_NEW, PAGE_SIZE, BUS_DMA_ALLOCNOW | BUS_DMA_WAITOK | BUS_DMA_ONEBPAGE, &sc->bge_cdata.bge_tx_mtag); @@ -4806,3 +4864,50 @@ bge_enable_msi(struct bge_softc *sc) } CSR_WRITE_4(sc, BGE_MSI_MODE, msi_mode); } + +static int +bge_setup_tso(struct bge_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; + uint16_t flags, mss; + + m = *mp; + KASSERT(M_WRITABLE(m), ("TSO mbuf not writable")); + + hoff = m->m_pkthdr.csum_lhlen; + iphlen = m->m_pkthdr.csum_iphlen; + thoff = m->m_pkthdr.csum_thlen; + + KASSERT(hoff > 0, ("invalid ether header len")); + KASSERT(iphlen > 0, ("invalid ip header len")); + KASSERT(thoff > 0, ("invalid tcp header len")); + + if (__predict_false(m->m_len < hoff + iphlen + thoff)) { + m = m_pullup(m, hoff + iphlen + thoff); + if (m == NULL) { + *mp = NULL; + return ENOBUFS; + } + *mp = m; + } + ip = mtodoff(m, struct ip *, hoff); + th = mtodoff(m, struct tcphdr *, hoff + iphlen); + + mss = m->m_pkthdr.tso_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 << 11); + + *mss0 = mss; + *flags0 = flags; + + return 0; +} diff --git a/sys/dev/netif/bge/if_bgevar.h b/sys/dev/netif/bge/if_bgevar.h index 3bedd5c..ed41517 100644 --- a/sys/dev/netif/bge/if_bgevar.h +++ b/sys/dev/netif/bge/if_bgevar.h @@ -211,6 +211,7 @@ struct bge_softc { #define BGE_FLAG_TBI 0x00000001 #define BGE_FLAG_JUMBO 0x00000002 #define BGE_FLAG_ONESHOT_MSI 0x00000004 +#define BGE_FLAG_TSO 0x00000008 #define BGE_FLAG_MII_SERDES 0x00000010 #define BGE_FLAG_CPMU 0x00000020 #define BGE_FLAG_PCIX 0x00000200 @@ -256,6 +257,8 @@ struct bge_softc { int bge_mbox_reorder; int bge_if_flags; int bge_txcnt; + int bge_txspare; + int bge_txrsvd; int bge_link; int bge_link_evt; struct callout bge_stat_timer; @@ -278,9 +281,11 @@ struct bge_softc { uint32_t bge_link_chg; }; -#define BGE_NSEG_NEW 32 +#define BGE_NSEG_NEW 40 #define BGE_NSEG_SPARE 5 +#define BGE_NSEG_SPARE_TSO 33 #define BGE_NSEG_RSVD 16 +#define BGE_NSEG_RSVD_TSO 4 /* RX coalesce ticks, unit: us */ #define BGE_RX_COAL_TICKS_MIN 0