From owner-svn-src-all@FreeBSD.ORG Sun Nov 22 21:16:31 2009 Return-Path: Delivered-To: svn-src-all@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 3596C1065693; Sun, 22 Nov 2009 21:16:31 +0000 (UTC) (envelope-from yongari@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id 243528FC14; Sun, 22 Nov 2009 21:16:31 +0000 (UTC) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.3/8.14.3) with ESMTP id nAMLGVM7029690; Sun, 22 Nov 2009 21:16:31 GMT (envelope-from yongari@svn.freebsd.org) Received: (from yongari@localhost) by svn.freebsd.org (8.14.3/8.14.3/Submit) id nAMLGUdC029687; Sun, 22 Nov 2009 21:16:30 GMT (envelope-from yongari@svn.freebsd.org) Message-Id: <200911222116.nAMLGUdC029687@svn.freebsd.org> From: Pyun YongHyeon Date: Sun, 22 Nov 2009 21:16:30 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Subject: svn commit: r199671 - head/sys/dev/bge X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 22 Nov 2009 21:16:31 -0000 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 #include #include +#include #include #include @@ -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