summaryrefslogtreecommitdiff
path: root/usr/src/uts/common/io/igb/igb_tx.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/uts/common/io/igb/igb_tx.c')
-rw-r--r--usr/src/uts/common/io/igb/igb_tx.c61
1 files changed, 45 insertions, 16 deletions
diff --git a/usr/src/uts/common/io/igb/igb_tx.c b/usr/src/uts/common/io/igb/igb_tx.c
index 0496bf59fe..5245312adc 100644
--- a/usr/src/uts/common/io/igb/igb_tx.c
+++ b/usr/src/uts/common/io/igb/igb_tx.c
@@ -598,6 +598,7 @@ igb_get_tx_context(mblk_t *mp, tx_context_t *ctx)
uint32_t start;
uint32_t flags;
uint32_t lso_flag;
+ uint32_t lso_cksum;
uint32_t mss;
uint32_t len;
uint32_t size;
@@ -622,19 +623,6 @@ igb_get_tx_context(mblk_t *mp, tx_context_t *ctx)
ctx->mss = mss;
ctx->lso_flag = (lso_flag == HW_LSO);
- /*
- * LSO relies on tx h/w checksum, so here the packet will be
- * dropped if the h/w checksum flags are not set.
- */
- if (ctx->lso_flag) {
- if (!((ctx->hcksum_flags & HCK_PARTIALCKSUM) &&
- (ctx->hcksum_flags & HCK_IPV4_HDRCKSUM))) {
- igb_log(NULL, IGB_LOG_INFO, "igb_tx: h/w "
- "checksum flags are not set for LSO");
- return (TX_CXT_E_LSO_CSUM);
- }
- }
-
etype = 0;
mac_hdr_len = 0;
l4_proto = 0;
@@ -679,6 +667,8 @@ igb_get_tx_context(mblk_t *mp, tx_context_t *ctx)
* Here we assume the IP(V6) header is fully included in one
* mblk fragment.
*/
+ lso_cksum = HCK_PARTIALCKSUM;
+ ctx->l3_proto = etype;
switch (etype) {
case ETHERTYPE_IP:
offset = mac_hdr_len;
@@ -703,11 +693,27 @@ igb_get_tx_context(mblk_t *mp, tx_context_t *ctx)
* with zero. Currently the tcp/ip stack has done
* these.
*/
+ lso_cksum |= HCK_IPV4_HDRCKSUM;
}
l4_proto = *(uint8_t *)(pos + offsetof(ipha_t, ipha_protocol));
break;
case ETHERTYPE_IPV6:
+ /*
+ * We need to zero out the length in the header.
+ */
+ if (ctx->lso_flag) {
+ offset = offsetof(ip6_t, ip6_plen) + mac_hdr_len;
+ while (size <= offset) {
+ mp = mp->b_cont;
+ ASSERT(mp != NULL);
+ len = MBLKL(mp);
+ size += len;
+ }
+ pos = mp->b_rptr + offset + len - size;
+ *((uint16_t *)(uintptr_t)(pos)) = 0;
+ }
+
offset = offsetof(ip6_t, ip6_nxt) + mac_hdr_len;
while (size <= offset) {
mp = mp->b_cont;
@@ -727,6 +733,18 @@ igb_get_tx_context(mblk_t *mp, tx_context_t *ctx)
}
if (ctx->lso_flag) {
+ /*
+ * LSO relies on tx h/w checksum, so here the packet will be
+ * dropped if the h/w checksum flags are not set.
+ */
+ if ((ctx->hcksum_flags & lso_cksum) != lso_cksum) {
+ igb_log(NULL, IGB_LOG_INFO, "igb_tx: h/w "
+ "checksum flags are not set for LSO, found "
+ "0x%x, needed bits 0x%x", ctx->hcksum_flags,
+ lso_cksum);
+ return (TX_CXT_E_LSO_CSUM);
+ }
+
offset = mac_hdr_len + start;
while (size <= offset) {
mp = mp->b_cont;
@@ -771,6 +789,7 @@ igb_check_tx_context(igb_tx_ring_t *tx_ring, tx_context_t *ctx)
* need to be checked are:
* hcksum_flags
* l4_proto
+ * l3_proto
* mss (only check for LSO)
* l4_hdr_len (only check for LSO)
* ip_hdr_len
@@ -783,6 +802,7 @@ igb_check_tx_context(igb_tx_ring_t *tx_ring, tx_context_t *ctx)
if (ctx->hcksum_flags != 0) {
if ((ctx->hcksum_flags != last->hcksum_flags) ||
(ctx->l4_proto != last->l4_proto) ||
+ (ctx->l3_proto != last->l3_proto) ||
(ctx->lso_flag && ((ctx->mss != last->mss) ||
(ctx->l4_hdr_len != last->l4_hdr_len))) ||
(ctx->ip_hdr_len != last->ip_hdr_len) ||
@@ -814,10 +834,19 @@ igb_fill_tx_context(struct e1000_adv_tx_context_desc *ctx_tbd,
ctx_tbd->type_tucmd_mlhl =
E1000_ADVTXD_DCMD_DEXT | E1000_ADVTXD_DTYP_CTXT;
- if (ctx->hcksum_flags & HCK_IPV4_HDRCKSUM)
- ctx_tbd->type_tucmd_mlhl |= E1000_ADVTXD_TUCMD_IPV4;
+ /*
+ * When we have a TX context set up, we enforce that the ethertype is
+ * either IPv4 or IPv6 in igb_get_tx_context().
+ */
+ if (ctx->lso_flag || ctx->hcksum_flags & HCK_IPV4_HDRCKSUM) {
+ if (ctx->l3_proto == ETHERTYPE_IP) {
+ ctx_tbd->type_tucmd_mlhl |= E1000_ADVTXD_TUCMD_IPV4;
+ } else {
+ ctx_tbd->type_tucmd_mlhl |= E1000_ADVTXD_TUCMD_IPV6;
+ }
+ }
- if (ctx->hcksum_flags & HCK_PARTIALCKSUM) {
+ if (ctx->lso_flag || ctx->hcksum_flags & HCK_PARTIALCKSUM) {
switch (ctx->l4_proto) {
case IPPROTO_TCP:
ctx_tbd->type_tucmd_mlhl |= E1000_ADVTXD_TUCMD_L4T_TCP;