summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--usr/src/uts/common/io/i40e/i40e_transceiver.c180
-rw-r--r--usr/src/uts/common/io/mac/mac_provider.c156
-rw-r--r--usr/src/uts/common/io/vioif/vioif.c64
-rw-r--r--usr/src/uts/common/mapfiles/mac.mapfile1
-rw-r--r--usr/src/uts/common/sys/mac_provider.h25
5 files changed, 245 insertions, 181 deletions
diff --git a/usr/src/uts/common/io/i40e/i40e_transceiver.c b/usr/src/uts/common/io/i40e/i40e_transceiver.c
index caafa3e102..9662cb58f5 100644
--- a/usr/src/uts/common/io/i40e/i40e_transceiver.c
+++ b/usr/src/uts/common/io/i40e/i40e_transceiver.c
@@ -1663,186 +1663,6 @@ i40e_ring_rx_poll(void *arg, int poll_bytes)
}
/*
- * This is a structure I wish someone would fill out for me for dorking with the
- * checksums. When we get some more experience with this, we should go ahead and
- * consider adding this to MAC.
- */
-typedef enum mac_ether_offload_flags {
- MEOI_L2INFO_SET = 0x01,
- MEOI_VLAN_TAGGED = 0x02,
- MEOI_L3INFO_SET = 0x04,
- MEOI_L3CKSUM_SET = 0x08,
- MEOI_L4INFO_SET = 0x10,
- MEOI_L4CKSUM_SET = 0x20
-} mac_ether_offload_flags_t;
-
-typedef struct mac_ether_offload_info {
- mac_ether_offload_flags_t meoi_flags;
- uint8_t meoi_l2hlen; /* How long is the Ethernet header? */
- uint16_t meoi_l3proto; /* What's the Ethertype */
- uint8_t meoi_l3hlen; /* How long is the header? */
- uint8_t meoi_l4proto; /* What is the payload type? */
- uint8_t meoi_l4hlen; /* How long is the L4 header */
- mblk_t *meoi_l3ckmp; /* Which mblk has the l3 checksum */
- off_t meoi_l3ckoff; /* What's the offset to it */
- mblk_t *meoi_l4ckmp; /* Which mblk has the L4 checksum */
- off_t meoi_l4off; /* What is the offset to it? */
-} mac_ether_offload_info_t;
-
-/*
- * This is something that we'd like to make a general MAC function. Before we do
- * that, we should add support for TSO.
- *
- * We should really keep track of our offset and not walk everything every
- * time. I can't imagine that this will be kind to us at high packet rates;
- * however, for the moment, let's leave that.
- *
- * This walks a message block chain without pulling up to fill in the context
- * information. Note that the data we care about could be hidden across more
- * than one mblk_t.
- */
-static int
-i40e_meoi_get_uint8(mblk_t *mp, off_t off, uint8_t *out)
-{
- size_t mpsize;
- uint8_t *bp;
-
- mpsize = msgsize(mp);
- /* Check for overflow */
- if (off + sizeof (uint16_t) > mpsize)
- return (-1);
-
- mpsize = MBLKL(mp);
- while (off >= mpsize) {
- mp = mp->b_cont;
- off -= mpsize;
- mpsize = MBLKL(mp);
- }
-
- bp = mp->b_rptr + off;
- *out = *bp;
- return (0);
-
-}
-
-static int
-i40e_meoi_get_uint16(mblk_t *mp, off_t off, uint16_t *out)
-{
- size_t mpsize;
- uint8_t *bp;
-
- mpsize = msgsize(mp);
- /* Check for overflow */
- if (off + sizeof (uint16_t) > mpsize)
- return (-1);
-
- mpsize = MBLKL(mp);
- while (off >= mpsize) {
- mp = mp->b_cont;
- off -= mpsize;
- mpsize = MBLKL(mp);
- }
-
- /*
- * Data is in network order. Note the second byte of data might be in
- * the next mp.
- */
- bp = mp->b_rptr + off;
- *out = *bp << 8;
- if (off + 1 == mpsize) {
- mp = mp->b_cont;
- bp = mp->b_rptr;
- } else {
- bp++;
- }
-
- *out |= *bp;
- return (0);
-
-}
-
-static int
-mac_ether_offload_info(mblk_t *mp, mac_ether_offload_info_t *meoi)
-{
- size_t off;
- uint16_t ether;
- uint8_t ipproto, iplen, l4len, maclen;
-
- bzero(meoi, sizeof (mac_ether_offload_info_t));
-
- off = offsetof(struct ether_header, ether_type);
- if (i40e_meoi_get_uint16(mp, off, &ether) != 0)
- return (-1);
-
- if (ether == ETHERTYPE_VLAN) {
- off = offsetof(struct ether_vlan_header, ether_type);
- if (i40e_meoi_get_uint16(mp, off, &ether) != 0)
- return (-1);
- meoi->meoi_flags |= MEOI_VLAN_TAGGED;
- maclen = sizeof (struct ether_vlan_header);
- } else {
- maclen = sizeof (struct ether_header);
- }
- meoi->meoi_flags |= MEOI_L2INFO_SET;
- meoi->meoi_l2hlen = maclen;
- meoi->meoi_l3proto = ether;
-
- switch (ether) {
- case ETHERTYPE_IP:
- /*
- * For IPv4 we need to get the length of the header, as it can
- * be variable.
- */
- off = offsetof(ipha_t, ipha_version_and_hdr_length) + maclen;
- if (i40e_meoi_get_uint8(mp, off, &iplen) != 0)
- return (-1);
- iplen &= 0x0f;
- if (iplen < 5 || iplen > 0x0f)
- return (-1);
- iplen *= 4;
- off = offsetof(ipha_t, ipha_protocol) + maclen;
- if (i40e_meoi_get_uint8(mp, off, &ipproto) == -1)
- return (-1);
- break;
- case ETHERTYPE_IPV6:
- iplen = 40;
- off = offsetof(ip6_t, ip6_nxt) + maclen;
- if (i40e_meoi_get_uint8(mp, off, &ipproto) == -1)
- return (-1);
- break;
- default:
- return (0);
- }
- meoi->meoi_l3hlen = iplen;
- meoi->meoi_l4proto = ipproto;
- meoi->meoi_flags |= MEOI_L3INFO_SET;
-
- switch (ipproto) {
- case IPPROTO_TCP:
- off = offsetof(tcph_t, th_offset_and_rsrvd) + maclen + iplen;
- if (i40e_meoi_get_uint8(mp, off, &l4len) == -1)
- return (-1);
- l4len = (l4len & 0xf0) >> 4;
- if (l4len < 5 || l4len > 0xf)
- return (-1);
- l4len *= 4;
- break;
- case IPPROTO_UDP:
- l4len = sizeof (struct udphdr);
- break;
- case IPPROTO_SCTP:
- l4len = sizeof (sctp_hdr_t);
- break;
- default:
- return (0);
- }
-
- meoi->meoi_l4hlen = l4len;
- meoi->meoi_flags |= MEOI_L4INFO_SET;
- return (0);
-}
-
-/*
* Attempt to put togther the information we'll need to feed into a descriptor
* to properly program the hardware for checksum offload as well as the
* generally required flags.
diff --git a/usr/src/uts/common/io/mac/mac_provider.c b/usr/src/uts/common/io/mac/mac_provider.c
index a3f0ca89ed..36359bb0a9 100644
--- a/usr/src/uts/common/io/mac/mac_provider.c
+++ b/usr/src/uts/common/io/mac/mac_provider.c
@@ -58,6 +58,10 @@
#include <sys/pattr.h>
#include <sys/strsun.h>
#include <sys/vlan.h>
+#include <inet/ip.h>
+#include <inet/tcp.h>
+#include <netinet/udp.h>
+#include <netinet/sctp.h>
/*
* MAC Provider Interface.
@@ -1554,3 +1558,155 @@ mac_transceiver_info_set_usable(mac_transceiver_info_t *infop,
{
infop->mti_usable = usable;
}
+
+/*
+ * We should really keep track of our offset and not walk everything every
+ * time. I can't imagine that this will be kind to us at high packet rates;
+ * however, for the moment, let's leave that.
+ *
+ * This walks a message block chain without pulling up to fill in the context
+ * information. Note that the data we care about could be hidden across more
+ * than one mblk_t.
+ */
+static int
+mac_meoi_get_uint8(mblk_t *mp, off_t off, uint8_t *out)
+{
+ size_t mpsize;
+ uint8_t *bp;
+
+ mpsize = msgsize(mp);
+ /* Check for overflow */
+ if (off + sizeof (uint16_t) > mpsize)
+ return (-1);
+
+ mpsize = MBLKL(mp);
+ while (off >= mpsize) {
+ mp = mp->b_cont;
+ off -= mpsize;
+ mpsize = MBLKL(mp);
+ }
+
+ bp = mp->b_rptr + off;
+ *out = *bp;
+ return (0);
+
+}
+
+static int
+mac_meoi_get_uint16(mblk_t *mp, off_t off, uint16_t *out)
+{
+ size_t mpsize;
+ uint8_t *bp;
+
+ mpsize = msgsize(mp);
+ /* Check for overflow */
+ if (off + sizeof (uint16_t) > mpsize)
+ return (-1);
+
+ mpsize = MBLKL(mp);
+ while (off >= mpsize) {
+ mp = mp->b_cont;
+ off -= mpsize;
+ mpsize = MBLKL(mp);
+ }
+
+ /*
+ * Data is in network order. Note the second byte of data might be in
+ * the next mp.
+ */
+ bp = mp->b_rptr + off;
+ *out = *bp << 8;
+ if (off + 1 == mpsize) {
+ mp = mp->b_cont;
+ bp = mp->b_rptr;
+ } else {
+ bp++;
+ }
+
+ *out |= *bp;
+ return (0);
+
+}
+
+
+int
+mac_ether_offload_info(mblk_t *mp, mac_ether_offload_info_t *meoi)
+{
+ size_t off;
+ uint16_t ether;
+ uint8_t ipproto, iplen, l4len, maclen;
+
+ bzero(meoi, sizeof (mac_ether_offload_info_t));
+
+ meoi->meoi_len = msgsize(mp);
+ off = offsetof(struct ether_header, ether_type);
+ if (mac_meoi_get_uint16(mp, off, &ether) != 0)
+ return (-1);
+
+ if (ether == ETHERTYPE_VLAN) {
+ off = offsetof(struct ether_vlan_header, ether_type);
+ if (mac_meoi_get_uint16(mp, off, &ether) != 0)
+ return (-1);
+ meoi->meoi_flags |= MEOI_VLAN_TAGGED;
+ maclen = sizeof (struct ether_vlan_header);
+ } else {
+ maclen = sizeof (struct ether_header);
+ }
+ meoi->meoi_flags |= MEOI_L2INFO_SET;
+ meoi->meoi_l2hlen = maclen;
+ meoi->meoi_l3proto = ether;
+
+ switch (ether) {
+ case ETHERTYPE_IP:
+ /*
+ * For IPv4 we need to get the length of the header, as it can
+ * be variable.
+ */
+ off = offsetof(ipha_t, ipha_version_and_hdr_length) + maclen;
+ if (mac_meoi_get_uint8(mp, off, &iplen) != 0)
+ return (-1);
+ iplen &= 0x0f;
+ if (iplen < 5 || iplen > 0x0f)
+ return (-1);
+ iplen *= 4;
+ off = offsetof(ipha_t, ipha_protocol) + maclen;
+ if (mac_meoi_get_uint8(mp, off, &ipproto) == -1)
+ return (-1);
+ break;
+ case ETHERTYPE_IPV6:
+ iplen = 40;
+ off = offsetof(ip6_t, ip6_nxt) + maclen;
+ if (mac_meoi_get_uint8(mp, off, &ipproto) == -1)
+ return (-1);
+ break;
+ default:
+ return (0);
+ }
+ meoi->meoi_l3hlen = iplen;
+ meoi->meoi_l4proto = ipproto;
+ meoi->meoi_flags |= MEOI_L3INFO_SET;
+
+ switch (ipproto) {
+ case IPPROTO_TCP:
+ off = offsetof(tcph_t, th_offset_and_rsrvd) + maclen + iplen;
+ if (mac_meoi_get_uint8(mp, off, &l4len) == -1)
+ return (-1);
+ l4len = (l4len & 0xf0) >> 4;
+ if (l4len < 5 || l4len > 0xf)
+ return (-1);
+ l4len *= 4;
+ break;
+ case IPPROTO_UDP:
+ l4len = sizeof (struct udphdr);
+ break;
+ case IPPROTO_SCTP:
+ l4len = sizeof (sctp_hdr_t);
+ break;
+ default:
+ return (0);
+ }
+
+ meoi->meoi_l4hlen = l4len;
+ meoi->meoi_flags |= MEOI_L4INFO_SET;
+ return (0);
+}
diff --git a/usr/src/uts/common/io/vioif/vioif.c b/usr/src/uts/common/io/vioif/vioif.c
index e1535182b3..ea3d812c96 100644
--- a/usr/src/uts/common/io/vioif/vioif.c
+++ b/usr/src/uts/common/io/vioif/vioif.c
@@ -74,6 +74,7 @@
#include <sys/random.h>
#include <sys/containerof.h>
#include <sys/stream.h>
+#include <inet/tcp.h>
#include <sys/mac.h>
#include <sys/mac_provider.h>
@@ -1084,8 +1085,69 @@ vioif_send(vioif_t *vif, mblk_t *mp)
* Setup LSO fields if required.
*/
if (lso_required) {
- vnh->vnh_gso_type = VIRTIO_NET_HDR_GSO_TCPV4;
+ mac_ether_offload_flags_t needed;
+ mac_ether_offload_info_t meo;
+ uint32_t cksum;
+ size_t len;
+ mblk_t *pullmp = NULL;
+ tcpha_t *tcpha;
+
+ if (mac_ether_offload_info(mp, &meo) != 0) {
+ goto fail;
+ }
+
+ needed = MEOI_L2INFO_SET | MEOI_L3INFO_SET | MEOI_L4INFO_SET;
+ if ((meo.meoi_flags & needed) != needed) {
+ goto fail;
+ }
+
+ if (meo.meoi_l4proto != IPPROTO_TCP) {
+ goto fail;
+ }
+
+ if (meo.meoi_l3proto == ETHERTYPE_IP) {
+ vnh->vnh_gso_type = VIRTIO_NET_HDR_GSO_TCPV4;
+ } else if (meo.meoi_l3proto == ETHERTYPE_IPV6) {
+ vnh->vnh_gso_type = VIRTIO_NET_HDR_GSO_TCPV6;
+ } else {
+ goto fail;
+ }
+
+ /*
+ * The TCP stack does not include the length in the TCP
+ * pseudo-header when it is performing LSO since hardware
+ * generally asks for it to be removed (as it'll change).
+ * Unfortunately, for virtio, we actually need it. This means we
+ * need to go through and calculate the actual length and fix
+ * things up. Because the virtio spec cares about the ECN flag
+ * and indicating that, at least this means we'll have that
+ * available as well.
+ */
+ if (MBLKL(mp) < vnh->vnh_hdr_len) {
+ pullmp = msgpullup(mp, vnh->vnh_hdr_len);
+ if (pullmp == NULL)
+ goto fail;
+ tcpha = (tcpha_t *)(pullmp->b_rptr + meo.meoi_l2hlen +
+ meo.meoi_l3hlen);
+ } else {
+ tcpha = (tcpha_t *)(mp->b_rptr + meo.meoi_l2hlen +
+ meo.meoi_l3hlen);
+ }
+
+ len = meo.meoi_len - meo.meoi_l2hlen - meo.meoi_l3hlen;
+ cksum = ntohs(tcpha->tha_sum) + len;
+ cksum = (cksum >> 16) + (cksum & 0xffff);
+ cksum = (cksum >> 16) + (cksum & 0xffff);
+ tcpha->tha_sum = htons(cksum);
+
+ if (tcpha->tha_flags & TH_CWR) {
+ vnh->vnh_gso_type |= VIRTIO_NET_HDR_GSO_ECN;
+ }
vnh->vnh_gso_size = (uint16_t)lso_mss;
+ vnh->vnh_hdr_len = meo.meoi_l2hlen + meo.meoi_l3hlen +
+ meo.meoi_l4hlen;
+
+ freemsg(pullmp);
}
/*
diff --git a/usr/src/uts/common/mapfiles/mac.mapfile b/usr/src/uts/common/mapfiles/mac.mapfile
index d40c09b311..d5ba3ae2e5 100644
--- a/usr/src/uts/common/mapfiles/mac.mapfile
+++ b/usr/src/uts/common/mapfiles/mac.mapfile
@@ -32,6 +32,7 @@ $mapfile_version 2
SYMBOL_SCOPE {
global:
mac_alloc { FLAGS = EXTERN };
+ mac_ether_offload_info { FLAGS = EXTERN };
mac_fini_ops { FLAGS = EXTERN };
mac_free { FLAGS = EXTERN };
mac_hcksum_get { FLAGS = EXTERN };
diff --git a/usr/src/uts/common/sys/mac_provider.h b/usr/src/uts/common/sys/mac_provider.h
index 8e00dfced6..b5f0c0c870 100644
--- a/usr/src/uts/common/sys/mac_provider.h
+++ b/usr/src/uts/common/sys/mac_provider.h
@@ -645,6 +645,31 @@ extern void mac_transceiver_info_set_usable(
mac_transceiver_info_t *,
boolean_t);
+/*
+ * This represents a provisional set of currently illumos-private APIs to get
+ * information about a mblk_t chain's type. This is an evolving interface.
+ */
+typedef enum mac_ether_offload_flags {
+ MEOI_L2INFO_SET = 1 << 0,
+ MEOI_VLAN_TAGGED = 1 << 1,
+ MEOI_L3INFO_SET = 1 << 2,
+ MEOI_L4INFO_SET = 1 << 3
+} mac_ether_offload_flags_t;
+
+typedef struct mac_ether_offload_info {
+ mac_ether_offload_flags_t meoi_flags; /* What's valid? */
+ size_t meoi_len; /* Total message length */
+ uint8_t meoi_l2hlen; /* How long is the Ethernet header? */
+ uint16_t meoi_l3proto; /* What's the Ethertype */
+ uint8_t meoi_l3hlen; /* How long is the header? */
+ uint8_t meoi_l4proto; /* What is the payload type? */
+ uint8_t meoi_l4hlen; /* How long is the L4 header */
+} mac_ether_offload_info_t;
+
+extern int mac_ether_offload_info(mblk_t *,
+ mac_ether_offload_info_t *);
+
+
#endif /* _KERNEL */
#ifdef __cplusplus