summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/dev/bge/if_bge.c171
-rw-r--r--sys/dev/bge/if_bgereg.h10
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
OpenPOWER on IntegriCloud