diff options
Diffstat (limited to 'sys')
-rw-r--r-- | sys/dev/bce/if_bce.c | 183 |
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; } } |