diff --git a/sys/dev/sge/if_sge.c b/sys/dev/sge/if_sge.c index ee88cb6fb1d..14c2b9416d3 100644 --- a/sys/dev/sge/if_sge.c +++ b/sys/dev/sge/if_sge.c @@ -72,8 +72,13 @@ __FBSDID("$FreeBSD$"); #include #include +#include +#include +#include +#include + #include -#include +#include #include #include @@ -620,8 +625,8 @@ sge_attach(device_t dev) ifp->if_snd.ifq_drv_maxlen = SGE_TX_RING_CNT - 1; IFQ_SET_MAXLEN(&ifp->if_snd, ifp->if_snd.ifq_drv_maxlen); IFQ_SET_READY(&ifp->if_snd); - ifp->if_capabilities = IFCAP_TXCSUM | IFCAP_RXCSUM; - ifp->if_hwassist = SGE_CSUM_FEATURES; + ifp->if_capabilities = IFCAP_TXCSUM | IFCAP_RXCSUM | IFCAP_TSO4; + ifp->if_hwassist = SGE_CSUM_FEATURES | CSUM_TSO; ifp->if_capenable = ifp->if_capabilities; /* * Do MII setup. @@ -641,7 +646,7 @@ sge_attach(device_t dev) /* VLAN setup. */ if ((sc->sge_flags & SGE_FLAG_SIS190) == 0) ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING | - IFCAP_VLAN_HWCSUM; + IFCAP_VLAN_HWCSUM | IFCAP_VLAN_HWTSO; ifp->if_capabilities |= IFCAP_VLAN_MTU; ifp->if_capenable = ifp->if_capabilities; /* Tell the upper layer(s) we support long frames. */ @@ -851,8 +856,8 @@ sge_dma_alloc(struct sge_softc *sc) /* Create DMA tag for Tx buffers. */ error = bus_dma_tag_create(cd->sge_tag, 1, 0, BUS_SPACE_MAXADDR, - BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES * SGE_MAXTXSEGS, - SGE_MAXTXSEGS, MCLBYTES, 0, NULL, NULL, &cd->sge_txmbuf_tag); + BUS_SPACE_MAXADDR, NULL, NULL, SGE_TSO_MAXSIZE, SGE_MAXTXSEGS, + SGE_TSO_MAXSEGSIZE, 0, NULL, NULL, &cd->sge_txmbuf_tag); if (error != 0) { device_printf(sc->sge_dev, "could not create Tx mbuf DMA tag.\n"); @@ -1424,13 +1429,73 @@ sge_encap(struct sge_softc *sc, struct mbuf **m_head) struct sge_desc *desc; struct sge_txdesc *txd; bus_dma_segment_t txsegs[SGE_MAXTXSEGS]; - uint32_t cflags; + uint32_t cflags, mss; int error, i, nsegs, prod, si; SGE_LOCK_ASSERT(sc); si = prod = sc->sge_cdata.sge_tx_prod; txd = &sc->sge_cdata.sge_txdesc[prod]; + if (((*m_head)->m_pkthdr.csum_flags & CSUM_TSO) != 0) { + struct ether_header *eh; + struct ip *ip; + struct tcphdr *tcp; + uint32_t ip_off, poff; + + if (M_WRITABLE(*m_head) == 0) { + /* Get a writable copy. */ + m = m_dup(*m_head, M_DONTWAIT); + m_freem(*m_head); + if (m == NULL) { + *m_head = NULL; + return (ENOBUFS); + } + *m_head = m; + } + ip_off = sizeof(struct ether_header); + m = m_pullup(*m_head, ip_off); + if (m == NULL) { + *m_head = NULL; + return (ENOBUFS); + } + 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) { + *m_head = NULL; + return (ENOBUFS); + } + } + m = m_pullup(m, ip_off + sizeof(struct ip)); + if (m == NULL) { + *m_head = NULL; + return (ENOBUFS); + } + 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) { + *m_head = NULL; + return (ENOBUFS); + } + tcp = (struct tcphdr *)(mtod(m, char *) + poff); + m = m_pullup(m, poff + (tcp->th_off << 2)); + if (m == NULL) { + *m_head = NULL; + return (ENOBUFS); + } + /* + * Reset IP checksum and recompute TCP pseudo + * checksum that NDIS specification requires. + */ + ip->ip_sum = 0; + tcp->th_sum = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr, + htons(IPPROTO_TCP)); + *m_head = m; + } + error = bus_dmamap_load_mbuf_sg(sc->sge_cdata.sge_txmbuf_tag, txd->tx_dmamap, *m_head, txsegs, &nsegs, 0); if (error == EFBIG) { @@ -1462,16 +1527,23 @@ sge_encap(struct sge_softc *sc, struct mbuf **m_head) m = *m_head; cflags = 0; - if (m->m_pkthdr.csum_flags & CSUM_IP) - cflags |= TDC_IP_CSUM; - if (m->m_pkthdr.csum_flags & CSUM_TCP) - cflags |= TDC_TCP_CSUM; - if (m->m_pkthdr.csum_flags & CSUM_UDP) - cflags |= TDC_UDP_CSUM; + mss = 0; + if ((m->m_pkthdr.csum_flags & CSUM_TSO) != 0) { + cflags |= TDC_LS; + mss = (uint32_t)m->m_pkthdr.tso_segsz; + mss <<= 16; + } else { + if (m->m_pkthdr.csum_flags & CSUM_IP) + cflags |= TDC_IP_CSUM; + if (m->m_pkthdr.csum_flags & CSUM_TCP) + cflags |= TDC_TCP_CSUM; + if (m->m_pkthdr.csum_flags & CSUM_UDP) + cflags |= TDC_UDP_CSUM; + } for (i = 0; i < nsegs; i++) { desc = &sc->sge_ldata.sge_tx_ring[prod]; if (i == 0) { - desc->sge_sts_size = htole32(m->m_pkthdr.len); + desc->sge_sts_size = htole32(m->m_pkthdr.len | mss); desc->sge_cmdsts = 0; } else { desc->sge_sts_size = 0; @@ -1759,6 +1831,17 @@ sge_ioctl(struct ifnet *ifp, u_long command, caddr_t data) if ((mask & IFCAP_VLAN_HWCSUM) != 0 && (ifp->if_capabilities & IFCAP_VLAN_HWCSUM) != 0) ifp->if_capenable ^= IFCAP_VLAN_HWCSUM; + 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_HWTSO) != 0 && + (ifp->if_capabilities & IFCAP_VLAN_HWTSO) != 0) + ifp->if_capenable ^= IFCAP_VLAN_HWTSO; if ((mask & IFCAP_VLAN_HWTAGGING) != 0 && (ifp->if_capabilities & IFCAP_VLAN_HWTAGGING) != 0) { /* @@ -1766,6 +1849,9 @@ sge_ioctl(struct ifnet *ifp, u_long command, caddr_t data) * tagging require interface reinitialization. */ ifp->if_capenable ^= IFCAP_VLAN_HWTAGGING; + if ((ifp->if_capenable & IFCAP_VLAN_HWTAGGING) == 0) + ifp->if_capenable &= + ~(IFCAP_VLAN_HWTSO | IFCAP_VLAN_HWCSUM); reinit = 1; } if (reinit > 0 && (ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) { diff --git a/sys/dev/sge/if_sgereg.h b/sys/dev/sge/if_sgereg.h index c06072cdeb9..6987e99ff16 100644 --- a/sys/dev/sge/if_sgereg.h +++ b/sys/dev/sge/if_sgereg.h @@ -283,7 +283,9 @@ struct sge_desc { #define SGE_RX_RING_CNT 256 /* [8, 1024] */ #define SGE_TX_RING_CNT 256 /* [8, 8192] */ #define SGE_DESC_ALIGN 16 -#define SGE_MAXTXSEGS 16 +#define SGE_MAXTXSEGS 32 +#define SGE_TSO_MAXSIZE (65535 + sizeof(struct ether_vlan_header)) +#define SGE_TSO_MAXSEGSIZE 4096 #define SGE_RX_BUF_ALIGN sizeof(uint64_t) #define SGE_RX_RING_SZ (SGE_RX_RING_CNT * sizeof(struct sge_desc))