diff options
author | Somnath Kotur <somnath.kotur@emulex.com> | 2012-06-26 18:32:10 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2012-06-27 04:35:49 -0400 |
commit | 93040ae5cc8dcc893eca4a4366dc8415af278edf (patch) | |
tree | e178db19e2948d1faa58d1e6f5b0c03b9cc6b5c0 | |
parent | 525b8075edda9c2ab4b81e210505bd7487ea6e56 (diff) |
be2net: Fix to trim skb for padded vlan packets to workaround an ASIC Bug
Fixed spelling error in a comment as pointed out by DaveM.
Also refactored existing code a bit to provide placeholders for another ASIC
Bug workaround that will be checked-in soon after this.
Signed-off-by: Somnath Kotur <somnath.kotur@emulex.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | drivers/net/ethernet/emulex/benet/be.h | 5 | ||||
-rw-r--r-- | drivers/net/ethernet/emulex/benet/be_main.c | 56 |
2 files changed, 47 insertions, 14 deletions
diff --git a/drivers/net/ethernet/emulex/benet/be.h b/drivers/net/ethernet/emulex/benet/be.h index 7b5cc2b212e5..7a71fb66c6dc 100644 --- a/drivers/net/ethernet/emulex/benet/be.h +++ b/drivers/net/ethernet/emulex/benet/be.h | |||
@@ -573,6 +573,11 @@ static inline u8 is_udp_pkt(struct sk_buff *skb) | |||
573 | return val; | 573 | return val; |
574 | } | 574 | } |
575 | 575 | ||
576 | static inline bool is_ipv4_pkt(struct sk_buff *skb) | ||
577 | { | ||
578 | return skb->protocol == ntohs(ETH_P_IP) && ip_hdr(skb)->version == 4; | ||
579 | } | ||
580 | |||
576 | static inline void be_vf_eth_addr_generate(struct be_adapter *adapter, u8 *mac) | 581 | static inline void be_vf_eth_addr_generate(struct be_adapter *adapter, u8 *mac) |
577 | { | 582 | { |
578 | u32 addr; | 583 | u32 addr; |
diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c index a28896d4411d..edce7af97c87 100644 --- a/drivers/net/ethernet/emulex/benet/be_main.c +++ b/drivers/net/ethernet/emulex/benet/be_main.c | |||
@@ -577,6 +577,11 @@ static inline u16 be_get_tx_vlan_tag(struct be_adapter *adapter, | |||
577 | return vlan_tag; | 577 | return vlan_tag; |
578 | } | 578 | } |
579 | 579 | ||
580 | static int be_vlan_tag_chk(struct be_adapter *adapter, struct sk_buff *skb) | ||
581 | { | ||
582 | return vlan_tx_tag_present(skb) || adapter->pvid; | ||
583 | } | ||
584 | |||
580 | static void wrb_fill_hdr(struct be_adapter *adapter, struct be_eth_hdr_wrb *hdr, | 585 | static void wrb_fill_hdr(struct be_adapter *adapter, struct be_eth_hdr_wrb *hdr, |
581 | struct sk_buff *skb, u32 wrb_cnt, u32 len) | 586 | struct sk_buff *skb, u32 wrb_cnt, u32 len) |
582 | { | 587 | { |
@@ -704,33 +709,56 @@ dma_err: | |||
704 | return 0; | 709 | return 0; |
705 | } | 710 | } |
706 | 711 | ||
712 | static struct sk_buff *be_insert_vlan_in_pkt(struct be_adapter *adapter, | ||
713 | struct sk_buff *skb) | ||
714 | { | ||
715 | u16 vlan_tag = 0; | ||
716 | |||
717 | skb = skb_share_check(skb, GFP_ATOMIC); | ||
718 | if (unlikely(!skb)) | ||
719 | return skb; | ||
720 | |||
721 | if (vlan_tx_tag_present(skb)) { | ||
722 | vlan_tag = be_get_tx_vlan_tag(adapter, skb); | ||
723 | __vlan_put_tag(skb, vlan_tag); | ||
724 | skb->vlan_tci = 0; | ||
725 | } | ||
726 | |||
727 | return skb; | ||
728 | } | ||
729 | |||
707 | static netdev_tx_t be_xmit(struct sk_buff *skb, | 730 | static netdev_tx_t be_xmit(struct sk_buff *skb, |
708 | struct net_device *netdev) | 731 | struct net_device *netdev) |
709 | { | 732 | { |
710 | struct be_adapter *adapter = netdev_priv(netdev); | 733 | struct be_adapter *adapter = netdev_priv(netdev); |
711 | struct be_tx_obj *txo = &adapter->tx_obj[skb_get_queue_mapping(skb)]; | 734 | struct be_tx_obj *txo = &adapter->tx_obj[skb_get_queue_mapping(skb)]; |
712 | struct be_queue_info *txq = &txo->q; | 735 | struct be_queue_info *txq = &txo->q; |
736 | struct iphdr *ip = NULL; | ||
713 | u32 wrb_cnt = 0, copied = 0; | 737 | u32 wrb_cnt = 0, copied = 0; |
714 | u32 start = txq->head; | 738 | u32 start = txq->head, eth_hdr_len; |
715 | bool dummy_wrb, stopped = false; | 739 | bool dummy_wrb, stopped = false; |
716 | 740 | ||
717 | /* For vlan tagged pkts, BE | 741 | eth_hdr_len = ntohs(skb->protocol) == ETH_P_8021Q ? |
718 | * 1) calculates checksum even when CSO is not requested | 742 | VLAN_ETH_HLEN : ETH_HLEN; |
719 | * 2) calculates checksum wrongly for padded pkt less than | 743 | |
720 | * 60 bytes long. | 744 | /* HW has a bug which considers padding bytes as legal |
721 | * As a workaround disable TX vlan offloading in such cases. | 745 | * and modifies the IPv4 hdr's 'tot_len' field |
722 | */ | 746 | */ |
723 | if (vlan_tx_tag_present(skb) && | 747 | if (skb->len <= 60 && be_vlan_tag_chk(adapter, skb) && |
724 | (skb->ip_summed != CHECKSUM_PARTIAL || skb->len <= 60)) { | 748 | is_ipv4_pkt(skb)) { |
725 | skb = skb_share_check(skb, GFP_ATOMIC); | 749 | ip = (struct iphdr *)ip_hdr(skb); |
726 | if (unlikely(!skb)) | 750 | pskb_trim(skb, eth_hdr_len + ntohs(ip->tot_len)); |
727 | goto tx_drop; | 751 | } |
728 | 752 | ||
729 | skb = __vlan_put_tag(skb, be_get_tx_vlan_tag(adapter, skb)); | 753 | /* HW has a bug wherein it will calculate CSUM for VLAN |
754 | * pkts even though it is disabled. | ||
755 | * Manually insert VLAN in pkt. | ||
756 | */ | ||
757 | if (skb->ip_summed != CHECKSUM_PARTIAL && | ||
758 | be_vlan_tag_chk(adapter, skb)) { | ||
759 | skb = be_insert_vlan_in_pkt(adapter, skb); | ||
730 | if (unlikely(!skb)) | 760 | if (unlikely(!skb)) |
731 | goto tx_drop; | 761 | goto tx_drop; |
732 | |||
733 | skb->vlan_tci = 0; | ||
734 | } | 762 | } |
735 | 763 | ||
736 | wrb_cnt = wrb_cnt_for_skb(adapter, skb, &dummy_wrb); | 764 | wrb_cnt = wrb_cnt_for_skb(adapter, skb, &dummy_wrb); |