summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorandre <andre@FreeBSD.org>2006-09-29 13:33:30 +0000
committerandre <andre@FreeBSD.org>2006-09-29 13:33:30 +0000
commite596041984ed0c3594657189cd5e309af208e043 (patch)
treea61c7fec5d7b6934b951edbdaae518127787862f
parentfe1a7e4ebbd78dd0665a2b92670f9ac511ddc501 (diff)
downloadFreeBSD-src-e596041984ed0c3594657189cd5e309af208e043.zip
FreeBSD-src-e596041984ed0c3594657189cd5e309af208e043.tar.gz
Change em_tso_setup() to deal with already inserted vlan headers, IP options
and add skeleton IPv6 support. The new code structure can also be easily enhanced to support new/more protocols (SCTP) and IP fragmentation in the future. In em_encap() only try to do TSO if 'dotso' is true. Reviewed by: jfv
-rw-r--r--sys/dev/em/if_em.c156
1 files changed, 120 insertions, 36 deletions
diff --git a/sys/dev/em/if_em.c b/sys/dev/em/if_em.c
index e62271c..aff12d5 100644
--- a/sys/dev/em/if_em.c
+++ b/sys/dev/em/if_em.c
@@ -69,6 +69,7 @@ POSSIBILITY OF SUCH DAMAGE.
#include <netinet/in.h>
#include <netinet/if_ether.h>
#include <netinet/ip.h>
+#include <netinet/ip6.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
@@ -1591,11 +1592,12 @@ em_encap(struct adapter *adapter, struct mbuf **m_headp)
bus_dmamap_unload(adapter->txtag, map);
return (ENOBUFS);
}
+ m_head = *m_headp;
/* Do hardware assists */
- m_head = *m_headp;
- if (ifp->if_hwassist > 0) {
- if (em_tso_setup(adapter, m_head, &txd_upper, &txd_lower)) {
+ if (ifp->if_hwassist) {
+ if (do_tso &&
+ em_tso_setup(adapter, m_head, &txd_upper, &txd_lower)) {
/* we need to make a final sentinel transmit desc */
tso_desc = TRUE;
} else
@@ -2873,58 +2875,140 @@ em_tso_setup(struct adapter *adapter, struct mbuf *mp, uint32_t *txd_upper,
{
struct em_context_desc *TXD;
struct em_buffer *tx_buffer;
+ struct ether_vlan_header *eh;
struct ip *ip;
+ struct ip6_hdr *ip6;
struct tcphdr *th;
- int curr_txd, hdr_len, ip_hlen, tcp_hlen;
+ int curr_txd, ehdrlen, hdr_len, ip_hlen, isip6;
+ uint16_t etype;
+
+ /*
+ * XXX: This is not really correct as the stack would not have
+ * set up all checksums.
+ * XXX: Return FALSE is not sufficient as we may have to return
+ * in true failure cases as well. Should do -1 (failure), 0 (no)
+ * and 1 (success).
+ */
+ if (mp->m_pkthdr.len <= E1000_TX_BUFFER_SIZE)
+ return FALSE; /* 0 */
- if (((mp->m_pkthdr.csum_flags & CSUM_TSO) == 0) ||
- (mp->m_pkthdr.len <= E1000_TX_BUFFER_SIZE)) {
+ /*
+ * This function could/should be extended to support IP/IPv6
+ * fragmentation as well. But as they say, one step at a time.
+ */
+
+ /*
+ * Determine where frame payload starts.
+ * Jump over vlan headers if already present,
+ * helpful for QinQ too.
+ */
+ eh = mtod(mp, struct ether_vlan_header *);
+ if (eh->evl_encap_proto == htons(ETHERTYPE_VLAN)) {
+ etype = ntohs(eh->evl_proto);
+ ehdrlen = ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
+ } else {
+ etype = ntohs(eh->evl_encap_proto);
+ ehdrlen = ETHER_HDR_LEN;
+ }
+
+ /* Ensure we have at least the IP+TCP header in the first mbuf. */
+ if (mp->m_len < ehdrlen + sizeof(struct ip) + sizeof(struct tcphdr))
+ return FALSE; /* -1 */
+
+ /*
+ * We only support TCP for IPv4 and IPv6 (notyet) for the moment.
+ * TODO: Support SCTP too when it hits the tree.
+ */
+ switch (etype) {
+ case ETHERTYPE_IP:
+ isip6 = 0;
+ ip = (struct ip *)(mp->m_data + ehdrlen);
+ if (ip->ip_p != IPPROTO_TCP)
+ return FALSE; /* 0 */
+ ip->ip_len = 0;
+ ip->ip_sum = 0;
+ ip_hlen = ip->ip_hl << 2;
+ if (mp->m_len < ehdrlen + ip_hlen + sizeof(struct tcphdr))
+ return FALSE; /* -1 */
+ th = (struct tcphdr *)((caddr_t)ip + ip_hlen);
+#if 1
+ th->th_sum = in_pseudo(ip->ip_src.s_addr,
+ ip->ip_dst.s_addr, htons(IPPROTO_TCP));
+#else
+ th->th_sum = mp->m_pkthdr.csum_data;
+#endif
+ break;
+ case ETHERTYPE_IPV6:
+ isip6 = 1;
+ return FALSE; /* Not supported yet. */
+ ip6 = (struct ip6_hdr *)(mp->m_data + ehdrlen);
+ if (ip6->ip6_nxt != IPPROTO_TCP)
+ return FALSE; /* 0 */
+ ip6->ip6_plen = 0;
+ ip_hlen = sizeof(struct ip6_hdr); /* XXX: no header stacking. */
+ if (mp->m_len < ehdrlen + ip_hlen + sizeof(struct tcphdr))
+ return FALSE; /* -1 */
+ th = (struct tcphdr *)((caddr_t)ip6 + ip_hlen);
+#if 0
+ th->th_sum = in6_pseudo(ip6->ip6_src, ip->ip6_dst,
+ htons(IPPROTO_TCP)); /* XXX: function notyet. */
+#else
+ th->th_sum = mp->m_pkthdr.csum_data;
+#endif
+ break;
+ default:
return FALSE;
}
+ hdr_len = ehdrlen + ip_hlen + (th->th_off << 2);
- *txd_lower = (E1000_TXD_CMD_DEXT |
- E1000_TXD_DTYP_D |
- E1000_TXD_CMD_TSE);
+ *txd_lower = (E1000_TXD_CMD_DEXT | /* Extended descr type */
+ E1000_TXD_DTYP_D | /* Data descr type */
+ E1000_TXD_CMD_TSE); /* Do TSE on this packet */
- *txd_upper = (E1000_TXD_POPTS_IXSM |
+ /* IP and/or TCP header checksum calculation and insertion. */
+ *txd_upper = ((isip6 ? 0 : E1000_TXD_POPTS_IXSM) |
E1000_TXD_POPTS_TXSM) << 8;
curr_txd = adapter->next_avail_tx_desc;
tx_buffer = &adapter->tx_buffer_area[curr_txd];
TXD = (struct em_context_desc *) &adapter->tx_desc_base[curr_txd];
- mp->m_data += sizeof(struct ether_header);
- ip = mtod(mp, struct ip *);
- ip->ip_len = 0;
- ip->ip_sum = 0;
- ip_hlen = ip->ip_hl << 2 ;
- th = (struct tcphdr *)((caddr_t)ip + ip_hlen);
- tcp_hlen = th->th_off << 2;
-
- hdr_len = ETHER_HDR_LEN + ip_hlen + tcp_hlen;
- th->th_sum = in_pseudo(ip->ip_src.s_addr,
- ip->ip_dst.s_addr, htons(IPPROTO_TCP));
-
- mp->m_data -= sizeof(struct ether_header);
- TXD->lower_setup.ip_fields.ipcss = ETHER_HDR_LEN;
- TXD->lower_setup.ip_fields.ipcso =
- ETHER_HDR_LEN + offsetof(struct ip, ip_sum);
- TXD->lower_setup.ip_fields.ipcse =
- htole16(ETHER_HDR_LEN + ip_hlen - 1);
-
+ /* IPv6 doesn't have a header checksum. */
+ if (!isip6) {
+ /*
+ * Start offset for header checksum calculation.
+ * End offset for header checksum calculation.
+ * Offset of place put the checksum.
+ */
+ TXD->lower_setup.ip_fields.ipcss = ehdrlen;
+ TXD->lower_setup.ip_fields.ipcse =
+ htole16(ehdrlen + ip_hlen - 1);
+ TXD->lower_setup.ip_fields.ipcso =
+ ehdrlen + offsetof(struct ip, ip_sum);
+ }
+ /*
+ * Start offset for payload checksum calculation.
+ * End offset for payload checksum calculation.
+ * Offset of place to put the checksum.
+ */
TXD->upper_setup.tcp_fields.tucss =
- ETHER_HDR_LEN + ip_hlen;
+ ehdrlen + ip_hlen;
TXD->upper_setup.tcp_fields.tucse = 0;
TXD->upper_setup.tcp_fields.tucso =
- ETHER_HDR_LEN + ip_hlen +
- offsetof(struct tcphdr, th_sum);
+ ehdrlen + ip_hlen + offsetof(struct tcphdr, th_sum);
+ /*
+ * Payload size per packet w/o any headers.
+ * Length of all headers up to payload.
+ */
TXD->tcp_seg_setup.fields.mss = htole16(mp->m_pkthdr.tso_segsz);
TXD->tcp_seg_setup.fields.hdr_len = hdr_len;
+
TXD->cmd_and_length = htole32(adapter->txd_cmd |
- E1000_TXD_CMD_DEXT |
- E1000_TXD_CMD_TSE |
- E1000_TXD_CMD_IP | E1000_TXD_CMD_TCP |
- (mp->m_pkthdr.len - (hdr_len)));
+ E1000_TXD_CMD_DEXT | /* Extended descr */
+ E1000_TXD_CMD_TSE | /* TSE context */
+ (isip6 ? 0 : E1000_TXD_CMD_IP) | /* Do IP csum */
+ E1000_TXD_CMD_TCP | /* Do TCP checksum */
+ (mp->m_pkthdr.len - (hdr_len))); /* Total len */
tx_buffer->m_head = NULL;
OpenPOWER on IntegriCloud