summaryrefslogtreecommitdiffstats
path: root/sys/dev/ale/if_ale.c
diff options
context:
space:
mode:
authoryongari <yongari@FreeBSD.org>2010-04-26 21:08:15 +0000
committeryongari <yongari@FreeBSD.org>2010-04-26 21:08:15 +0000
commitbba9d9c06c699a401091f437b2dced6938a6accc (patch)
treecfd98997980dd3331533dbfa8a408e41b29f538f /sys/dev/ale/if_ale.c
parenta768cbcadec7189b9947e9f3cde39fe806bbc1d7 (diff)
downloadFreeBSD-src-bba9d9c06c699a401091f437b2dced6938a6accc.zip
FreeBSD-src-bba9d9c06c699a401091f437b2dced6938a6accc.tar.gz
It seems ale(4) controllers do not like to see TCP payload in the
first descriptor in TSO case. Otherwise controller can generate bad frames during TSO. To address it, make sure to pull up ethernet + IP + TCP header with options in first buffer. Also ensure the buffer length of the first descriptor for TSO covers entire ethernet + IP + TCP with options and setup additional Tx descriptor if the first buffer includes TCP payload. Tested by: Amar Takhar <verm <> darkbeer dot org > MFC after: 1 week
Diffstat (limited to 'sys/dev/ale/if_ale.c')
-rw-r--r--sys/dev/ale/if_ale.c37
1 files changed, 33 insertions, 4 deletions
diff --git a/sys/dev/ale/if_ale.c b/sys/dev/ale/if_ale.c
index b3e7187..ea6b53b 100644
--- a/sys/dev/ale/if_ale.c
+++ b/sys/dev/ale/if_ale.c
@@ -1585,7 +1585,7 @@ ale_encap(struct ale_softc *sc, struct mbuf **m_head)
struct tcphdr *tcp;
bus_dma_segment_t txsegs[ALE_MAXTXSEGS];
bus_dmamap_t map;
- uint32_t cflags, ip_off, poff, vtag;
+ uint32_t cflags, hdrlen, ip_off, poff, vtag;
int error, i, nsegs, prod, si;
ALE_LOCK_ASSERT(sc);
@@ -1678,6 +1678,11 @@ ale_encap(struct ale_softc *sc, struct mbuf **m_head)
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);
+ }
/*
* AR81xx requires IP/TCP header size and offset as
* well as TCP pseudo checksum which complicates
@@ -1730,7 +1735,7 @@ ale_encap(struct ale_softc *sc, struct mbuf **m_head)
}
/* Check descriptor overrun. */
- if (sc->ale_cdata.ale_tx_cnt + nsegs >= ALE_TX_RING_CNT - 2) {
+ if (sc->ale_cdata.ale_tx_cnt + nsegs >= ALE_TX_RING_CNT - 3) {
bus_dmamap_unload(sc->ale_cdata.ale_tx_tag, map);
return (ENOBUFS);
}
@@ -1782,8 +1787,32 @@ ale_encap(struct ale_softc *sc, struct mbuf **m_head)
cflags |= ALE_TD_INSERT_VLAN_TAG;
}
- desc = NULL;
- for (i = 0; i < nsegs; i++) {
+ i = 0;
+ if ((m->m_pkthdr.csum_flags & CSUM_TSO) != 0) {
+ /*
+ * Make sure the first fragment contains
+ * only ethernet and IP/TCP header with options.
+ */
+ hdrlen = poff + (tcp->th_off << 2);
+ desc = &sc->ale_cdata.ale_tx_ring[prod];
+ desc->addr = htole64(txsegs[i].ds_addr);
+ desc->len = htole32(ALE_TX_BYTES(hdrlen) | vtag);
+ desc->flags = htole32(cflags);
+ sc->ale_cdata.ale_tx_cnt++;
+ ALE_DESC_INC(prod, ALE_TX_RING_CNT);
+ if (m->m_len - hdrlen > 0) {
+ /* Handle remaining payload of the first fragment. */
+ desc = &sc->ale_cdata.ale_tx_ring[prod];
+ desc->addr = htole64(txsegs[i].ds_addr + hdrlen);
+ desc->len = htole32(ALE_TX_BYTES(m->m_len - hdrlen) |
+ vtag);
+ desc->flags = htole32(cflags);
+ sc->ale_cdata.ale_tx_cnt++;
+ ALE_DESC_INC(prod, ALE_TX_RING_CNT);
+ }
+ i = 1;
+ }
+ for (; i < nsegs; i++) {
desc = &sc->ale_cdata.ale_tx_ring[prod];
desc->addr = htole64(txsegs[i].ds_addr);
desc->len = htole32(ALE_TX_BYTES(txsegs[i].ds_len) | vtag);
OpenPOWER on IntegriCloud