summaryrefslogtreecommitdiffstats
path: root/sys/dev/bce
diff options
context:
space:
mode:
authoryongari <yongari@FreeBSD.org>2010-02-26 21:19:46 +0000
committeryongari <yongari@FreeBSD.org>2010-02-26 21:19:46 +0000
commit142d4bad6742190307c16cf0996ca20d233188ab (patch)
tree75480a9f752933746a836bb40927e1013851c310 /sys/dev/bce
parent90740e4631e4ed0c337597c310c9984b01a8560a (diff)
downloadFreeBSD-src-142d4bad6742190307c16cf0996ca20d233188ab.zip
FreeBSD-src-142d4bad6742190307c16cf0996ca20d233188ab.tar.gz
Move TSO setup to new function bce_tso_setup(). Also remove VLAN
parsing code in TSO path as the controller requires VLAN hardware tagging to make TSO work over VLANs. While parsing the mbuf in TSO patch, always perform check for writable mbuf as bce(4) have to reset IP length and IP checksum field of IP header and make sure to ensure contiguous buffer before accessing IP/TCP headers. While I'm here replace magic number 40 to more readable sizeof(struct ip) + sizeof(struct tcphdr). Reviewed by: davidch
Diffstat (limited to 'sys/dev/bce')
-rw-r--r--sys/dev/bce/if_bce.c183
1 files changed, 114 insertions, 69 deletions
diff --git a/sys/dev/bce/if_bce.c b/sys/dev/bce/if_bce.c
index 2decf2d..b6cf634 100644
--- a/sys/dev/bce/if_bce.c
+++ b/sys/dev/bce/if_bce.c
@@ -403,6 +403,7 @@ static void bce_fill_pg_chain (struct bce_softc *);
static void bce_free_pg_chain (struct bce_softc *);
#endif
+static struct mbuf *bce_tso_setup (struct bce_softc *, struct mbuf **, u16 *);
static int bce_tx_encap (struct bce_softc *, struct mbuf **);
static void bce_start_locked (struct ifnet *);
static void bce_start (struct ifnet *);
@@ -6585,6 +6586,110 @@ bce_init(void *xsc)
}
+static struct mbuf *
+bce_tso_setup(struct bce_softc *sc, struct mbuf **m_head, u16 *flags)
+{
+ struct mbuf *m;
+ struct ether_header *eh;
+ struct ip *ip;
+ struct tcphdr *th;
+ u16 etype;
+ int hdr_len, ip_hlen = 0, tcp_hlen = 0, ip_len = 0;
+
+ DBRUN(sc->requested_tso_frames++);
+ /* Controller requires to monify mbuf chains. */
+ if (M_WRITABLE(*m_head) == 0) {
+ m = m_dup(*m_head, M_DONTWAIT);
+ m_freem(*m_head);
+ if (m == NULL) {
+ sc->mbuf_alloc_failed_count++;
+ *m_head = NULL;
+ return (NULL);
+ }
+ *m_head = m;
+ }
+ /*
+ * For TSO the controller needs two pieces of info,
+ * the MSS and the IP+TCP options length.
+ */
+ m = m_pullup(*m_head, sizeof(struct ether_header) + sizeof(struct ip));
+ if (m == NULL) {
+ *m_head = NULL;
+ return (NULL);
+ }
+ eh = mtod(m, struct ether_header *);
+ etype = ntohs(eh->ether_type);
+
+ /* Check for supported TSO Ethernet types (only IPv4 for now) */
+ switch (etype) {
+ case ETHERTYPE_IP:
+ ip = (struct ip *)(m->m_data + sizeof(struct ether_header));
+ /* TSO only supported for TCP protocol. */
+ if (ip->ip_p != IPPROTO_TCP) {
+ BCE_PRINTF("%s(%d): TSO enabled for non-TCP frame!.\n",
+ __FILE__, __LINE__);
+ m_freem(*m_head);
+ *m_head = NULL;
+ return (NULL);
+ }
+
+ /* Get IP header length in bytes (min 20) */
+ ip_hlen = ip->ip_hl << 2;
+ m = m_pullup(*m_head, sizeof(struct ether_header) + ip_hlen +
+ sizeof(struct tcphdr));
+ if (m == NULL) {
+ *m_head = NULL;
+ return (NULL);
+ }
+
+ /* Get the TCP header length in bytes (min 20) */
+ th = (struct tcphdr *)((caddr_t)ip + ip_hlen);
+ tcp_hlen = (th->th_off << 2);
+
+ /* Make sure all IP/TCP options live in the same buffer. */
+ m = m_pullup(*m_head, sizeof(struct ether_header)+ ip_hlen +
+ tcp_hlen);
+ if (m == NULL) {
+ *m_head = NULL;
+ return (NULL);
+ }
+
+ /* IP header length and checksum will be calc'd by hardware */
+ ip_len = ip->ip_len;
+ ip->ip_len = 0;
+ ip->ip_sum = 0;
+ break;
+ case ETHERTYPE_IPV6:
+ BCE_PRINTF("%s(%d): TSO over IPv6 not supported!.\n",
+ __FILE__, __LINE__);
+ m_freem(*m_head);
+ *m_head = NULL;
+ return (NULL);
+ /* NOT REACHED */
+ default:
+ BCE_PRINTF("%s(%d): TSO enabled for unsupported protocol!.\n",
+ __FILE__, __LINE__);
+ m_freem(*m_head);
+ *m_head = NULL;
+ return (NULL);
+ }
+
+ hdr_len = sizeof(struct ether_header) + ip_hlen + tcp_hlen;
+
+ DBPRINT(sc, BCE_EXTREME_SEND, "%s(): hdr_len = %d, e_hlen = %d, "
+ "ip_hlen = %d, tcp_hlen = %d, ip_len = %d\n",
+ __FUNCTION__, hdr_len, sizeof(struct ether_header), ip_hlen,
+ tcp_hlen, ip_len);
+
+ /* Set the LSO flag in the TX BD */
+ *flags |= TX_BD_FLAGS_SW_LSO;
+ /* Set the length of IP + TCP options (in 32 bit words) */
+ *flags |= (((ip_hlen + tcp_hlen - sizeof(struct ip) -
+ sizeof(struct tcphdr)) >> 2) << 8);
+ return (*m_head);
+}
+
+
/****************************************************************************/
/* Encapsultes an mbuf cluster into the tx_bd chain structure and makes the */
/* memory visible to the controller. */
@@ -6601,12 +6706,8 @@ bce_tx_encap(struct bce_softc *sc, struct mbuf **m_head)
bus_dmamap_t map;
struct tx_bd *txbd = NULL;
struct mbuf *m0;
- struct ether_vlan_header *eh;
- struct ip *ip;
- struct tcphdr *th;
- u16 prod, chain_prod, etype, mss = 0, vlan_tag = 0, flags = 0;
+ u16 prod, chain_prod, mss = 0, vlan_tag = 0, flags = 0;
u32 prod_bseq;
- int hdr_len = 0, e_hlen = 0, ip_hlen = 0, tcp_hlen = 0, ip_len = 0;
#ifdef BCE_DEBUG
u16 debug_prod;
@@ -6623,72 +6724,16 @@ bce_tx_encap(struct bce_softc *sc, struct mbuf **m_head)
/* Transfer any checksum offload flags to the bd. */
m0 = *m_head;
if (m0->m_pkthdr.csum_flags) {
- if (m0->m_pkthdr.csum_flags & CSUM_IP)
- flags |= TX_BD_FLAGS_IP_CKSUM;
- if (m0->m_pkthdr.csum_flags & (CSUM_TCP | CSUM_UDP))
- flags |= TX_BD_FLAGS_TCP_UDP_CKSUM;
if (m0->m_pkthdr.csum_flags & CSUM_TSO) {
- /* For TSO the controller needs two pieces of info, */
- /* the MSS and the IP+TCP options length. */
+ m0 = bce_tso_setup(sc, m_head, &flags);
+ if (m0 == NULL)
+ goto bce_tx_encap_exit;
mss = htole16(m0->m_pkthdr.tso_segsz);
-
- /* Map the header and find the Ethernet type & header length */
- eh = mtod(m0, struct ether_vlan_header *);
- if (eh->evl_encap_proto == htons(ETHERTYPE_VLAN)) {
- etype = ntohs(eh->evl_proto);
- e_hlen = ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
- } else {
- etype = ntohs(eh->evl_encap_proto);
- e_hlen = ETHER_HDR_LEN;
- }
-
- /* Check for supported TSO Ethernet types (only IPv4 for now) */
- switch (etype) {
- case ETHERTYPE_IP:
- ip = (struct ip *)(m0->m_data + e_hlen);
-
- /* TSO only supported for TCP protocol */
- if (ip->ip_p != IPPROTO_TCP) {
- BCE_PRINTF("%s(%d): TSO enabled for non-TCP frame!.\n",
- __FILE__, __LINE__);
- goto bce_tx_encap_skip_tso;
- }
-
- /* Get IP header length in bytes (min 20) */
- ip_hlen = ip->ip_hl << 2;
-
- /* Get the TCP header length in bytes (min 20) */
- th = (struct tcphdr *)((caddr_t)ip + ip_hlen);
- tcp_hlen = (th->th_off << 2);
-
- /* IP header length and checksum will be calc'd by hardware */
- ip_len = ip->ip_len;
- ip->ip_len = 0;
- ip->ip_sum = 0;
- break;
- case ETHERTYPE_IPV6:
- BCE_PRINTF("%s(%d): TSO over IPv6 not supported!.\n",
- __FILE__, __LINE__);
- goto bce_tx_encap_skip_tso;
- default:
- BCE_PRINTF("%s(%d): TSO enabled for unsupported protocol!.\n",
- __FILE__, __LINE__);
- goto bce_tx_encap_skip_tso;
- }
-
- hdr_len = e_hlen + ip_hlen + tcp_hlen;
-
- DBPRINT(sc, BCE_EXTREME_SEND,
- "%s(): hdr_len = %d, e_hlen = %d, ip_hlen = %d, tcp_hlen = %d, ip_len = %d\n",
- __FUNCTION__, hdr_len, e_hlen, ip_hlen, tcp_hlen, ip_len);
-
- /* Set the LSO flag in the TX BD */
- flags |= TX_BD_FLAGS_SW_LSO;
- /* Set the length of IP + TCP options (in 32 bit words) */
- flags |= (((ip_hlen + tcp_hlen - 40) >> 2) << 8);
-
-bce_tx_encap_skip_tso:
- DBRUN(sc->requested_tso_frames++);
+ } else {
+ if (m0->m_pkthdr.csum_flags & CSUM_IP)
+ flags |= TX_BD_FLAGS_IP_CKSUM;
+ if (m0->m_pkthdr.csum_flags & (CSUM_TCP | CSUM_UDP))
+ flags |= TX_BD_FLAGS_TCP_UDP_CKSUM;
}
}
OpenPOWER on IntegriCloud