diff options
author | Ajit Khaparde <ajit.khaparde@emulex.com> | 2011-12-09 08:53:17 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2011-12-11 18:25:15 -0500 |
commit | 1ded132d4c3442aa3a619c94c245d7b5e0eb9731 (patch) | |
tree | c804509859f12b892686c5ce7c26bf0d0d9794cb | |
parent | 02fe7027961969a052fbbe453304f329d4e9735a (diff) |
be2net: workaround to fix a bug in BE
disable Tx vlan offloading in certain cases.
Signed-off-by: Ajit Khaparde <ajit.khaparde@emulex.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | drivers/net/ethernet/emulex/benet/be_main.c | 46 |
1 files changed, 38 insertions, 8 deletions
diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c index 73fe38928c2b..62f55145fa44 100644 --- a/drivers/net/ethernet/emulex/benet/be_main.c +++ b/drivers/net/ethernet/emulex/benet/be_main.c | |||
@@ -552,11 +552,26 @@ static inline void wrb_fill(struct be_eth_wrb *wrb, u64 addr, int len) | |||
552 | wrb->frag_len = len & ETH_WRB_FRAG_LEN_MASK; | 552 | wrb->frag_len = len & ETH_WRB_FRAG_LEN_MASK; |
553 | } | 553 | } |
554 | 554 | ||
555 | static inline u16 be_get_tx_vlan_tag(struct be_adapter *adapter, | ||
556 | struct sk_buff *skb) | ||
557 | { | ||
558 | u8 vlan_prio; | ||
559 | u16 vlan_tag; | ||
560 | |||
561 | vlan_tag = vlan_tx_tag_get(skb); | ||
562 | vlan_prio = (vlan_tag & VLAN_PRIO_MASK) >> VLAN_PRIO_SHIFT; | ||
563 | /* If vlan priority provided by OS is NOT in available bmap */ | ||
564 | if (!(adapter->vlan_prio_bmap & (1 << vlan_prio))) | ||
565 | vlan_tag = (vlan_tag & ~VLAN_PRIO_MASK) | | ||
566 | adapter->recommended_prio; | ||
567 | |||
568 | return vlan_tag; | ||
569 | } | ||
570 | |||
555 | static void wrb_fill_hdr(struct be_adapter *adapter, struct be_eth_hdr_wrb *hdr, | 571 | static void wrb_fill_hdr(struct be_adapter *adapter, struct be_eth_hdr_wrb *hdr, |
556 | struct sk_buff *skb, u32 wrb_cnt, u32 len) | 572 | struct sk_buff *skb, u32 wrb_cnt, u32 len) |
557 | { | 573 | { |
558 | u8 vlan_prio = 0; | 574 | u16 vlan_tag; |
559 | u16 vlan_tag = 0; | ||
560 | 575 | ||
561 | memset(hdr, 0, sizeof(*hdr)); | 576 | memset(hdr, 0, sizeof(*hdr)); |
562 | 577 | ||
@@ -587,12 +602,7 @@ static void wrb_fill_hdr(struct be_adapter *adapter, struct be_eth_hdr_wrb *hdr, | |||
587 | 602 | ||
588 | if (vlan_tx_tag_present(skb)) { | 603 | if (vlan_tx_tag_present(skb)) { |
589 | AMAP_SET_BITS(struct amap_eth_hdr_wrb, vlan, hdr, 1); | 604 | AMAP_SET_BITS(struct amap_eth_hdr_wrb, vlan, hdr, 1); |
590 | vlan_tag = vlan_tx_tag_get(skb); | 605 | vlan_tag = be_get_tx_vlan_tag(adapter, skb); |
591 | vlan_prio = (vlan_tag & VLAN_PRIO_MASK) >> VLAN_PRIO_SHIFT; | ||
592 | /* If vlan priority provided by OS is NOT in available bmap */ | ||
593 | if (!(adapter->vlan_prio_bmap & (1 << vlan_prio))) | ||
594 | vlan_tag = (vlan_tag & ~VLAN_PRIO_MASK) | | ||
595 | adapter->recommended_prio; | ||
596 | AMAP_SET_BITS(struct amap_eth_hdr_wrb, vlan_tag, hdr, vlan_tag); | 606 | AMAP_SET_BITS(struct amap_eth_hdr_wrb, vlan_tag, hdr, vlan_tag); |
597 | } | 607 | } |
598 | 608 | ||
@@ -695,6 +705,25 @@ static netdev_tx_t be_xmit(struct sk_buff *skb, | |||
695 | u32 start = txq->head; | 705 | u32 start = txq->head; |
696 | bool dummy_wrb, stopped = false; | 706 | bool dummy_wrb, stopped = false; |
697 | 707 | ||
708 | /* For vlan tagged pkts, BE | ||
709 | * 1) calculates checksum even when CSO is not requested | ||
710 | * 2) calculates checksum wrongly for padded pkt less than | ||
711 | * 60 bytes long. | ||
712 | * As a workaround disable TX vlan offloading in such cases. | ||
713 | */ | ||
714 | if (unlikely(vlan_tx_tag_present(skb) && | ||
715 | (skb->ip_summed != CHECKSUM_PARTIAL || skb->len <= 60))) { | ||
716 | skb = skb_share_check(skb, GFP_ATOMIC); | ||
717 | if (unlikely(!skb)) | ||
718 | goto tx_drop; | ||
719 | |||
720 | skb = __vlan_put_tag(skb, be_get_tx_vlan_tag(adapter, skb)); | ||
721 | if (unlikely(!skb)) | ||
722 | goto tx_drop; | ||
723 | |||
724 | skb->vlan_tci = 0; | ||
725 | } | ||
726 | |||
698 | wrb_cnt = wrb_cnt_for_skb(adapter, skb, &dummy_wrb); | 727 | wrb_cnt = wrb_cnt_for_skb(adapter, skb, &dummy_wrb); |
699 | 728 | ||
700 | copied = make_tx_wrbs(adapter, txq, skb, wrb_cnt, dummy_wrb); | 729 | copied = make_tx_wrbs(adapter, txq, skb, wrb_cnt, dummy_wrb); |
@@ -722,6 +751,7 @@ static netdev_tx_t be_xmit(struct sk_buff *skb, | |||
722 | txq->head = start; | 751 | txq->head = start; |
723 | dev_kfree_skb_any(skb); | 752 | dev_kfree_skb_any(skb); |
724 | } | 753 | } |
754 | tx_drop: | ||
725 | return NETDEV_TX_OK; | 755 | return NETDEV_TX_OK; |
726 | } | 756 | } |
727 | 757 | ||