summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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