diff options
-rw-r--r-- | sys/dev/bge/if_bge.c | 171 | ||||
-rw-r--r-- | sys/dev/bge/if_bgereg.h | 10 |
2 files changed, 149 insertions, 32 deletions
diff --git a/sys/dev/bge/if_bge.c b/sys/dev/bge/if_bge.c index 7adde30..d14ff88 100644 --- a/sys/dev/bge/if_bge.c +++ b/sys/dev/bge/if_bge.c @@ -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 mbuf **m_head, uint32_t *txidx) 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 mbuf **m_head, uint32_t *txidx) 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 mbuf **m_head, uint32_t *txidx) /* 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 command, caddr_t data) 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; diff --git a/sys/dev/bge/if_bgereg.h b/sys/dev/bge/if_bgereg.h index 2fb849f..cefe516 100644 --- a/sys/dev/bge/if_bgereg.h +++ b/sys/dev/bge/if_bgereg.h @@ -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 |