Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

bnx2: Reinsert VLAN tag when necessary.

In certain cases when ASF or other management firmware is running, the
chip may be configured to always strip out the VLAN tag even when
VLAN acceleration is not enabled. This causes some VLAN tagged
packets to be received by the host stack without any knowledge that
the original packet was VLAN tagged.

We fix this by re-inserting the VLAN tag into the packet when necessary.

Signed-off-by: Michael Chan <mchan@broadcom.com>
Signed-off-by: Benjamin Li <benli@broadcom.com>
Signed-off-by: Matt Carlson <mcarlson@broadcom.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Michael Chan and committed by
David S. Miller
f22828e8 729b85cd

+27 -8
+27 -8
drivers/net/bnx2.c
··· 2876 2876 struct sw_bd *rx_buf; 2877 2877 struct sk_buff *skb; 2878 2878 dma_addr_t dma_addr; 2879 + u16 vtag = 0; 2880 + int hw_vlan __maybe_unused = 0; 2879 2881 2880 2882 sw_ring_cons = RX_RING_IDX(sw_cons); 2881 2883 sw_ring_prod = RX_RING_IDX(sw_prod); ··· 2921 2919 if (len <= bp->rx_copy_thresh) { 2922 2920 struct sk_buff *new_skb; 2923 2921 2924 - new_skb = netdev_alloc_skb(bp->dev, len + 2); 2922 + new_skb = netdev_alloc_skb(bp->dev, len + 6); 2925 2923 if (new_skb == NULL) { 2926 2924 bnx2_reuse_rx_skb(bp, rxr, skb, sw_ring_cons, 2927 2925 sw_ring_prod); ··· 2930 2928 2931 2929 /* aligned copy */ 2932 2930 skb_copy_from_linear_data_offset(skb, 2933 - BNX2_RX_OFFSET - 2, 2934 - new_skb->data, len + 2); 2935 - skb_reserve(new_skb, 2); 2931 + BNX2_RX_OFFSET - 6, 2932 + new_skb->data, len + 6); 2933 + skb_reserve(new_skb, 6); 2936 2934 skb_put(new_skb, len); 2937 2935 2938 2936 bnx2_reuse_rx_skb(bp, rxr, skb, ··· 2942 2940 } else if (unlikely(bnx2_rx_skb(bp, rxr, skb, len, hdr_len, 2943 2941 dma_addr, (sw_ring_cons << 16) | sw_ring_prod))) 2944 2942 goto next_rx; 2943 + 2944 + if ((status & L2_FHDR_STATUS_L2_VLAN_TAG) && 2945 + !(bp->rx_mode & BNX2_EMAC_RX_MODE_KEEP_VLAN_TAG)) { 2946 + vtag = rx_hdr->l2_fhdr_vlan_tag; 2947 + #ifdef BCM_VLAN 2948 + if (bp->vlgrp) 2949 + hw_vlan = 1; 2950 + else 2951 + #endif 2952 + { 2953 + struct vlan_ethhdr *ve = (struct vlan_ethhdr *) 2954 + __skb_push(skb, 4); 2955 + 2956 + memmove(ve, skb->data + 4, ETH_ALEN * 2); 2957 + ve->h_vlan_proto = htons(ETH_P_8021Q); 2958 + ve->h_vlan_TCI = htons(vtag); 2959 + len += 4; 2960 + } 2961 + } 2945 2962 2946 2963 skb->protocol = eth_type_trans(skb, bp->dev); 2947 2964 ··· 2983 2962 } 2984 2963 2985 2964 #ifdef BCM_VLAN 2986 - if ((status & L2_FHDR_STATUS_L2_VLAN_TAG) && bp->vlgrp) { 2987 - vlan_hwaccel_receive_skb(skb, bp->vlgrp, 2988 - rx_hdr->l2_fhdr_vlan_tag); 2989 - } 2965 + if (hw_vlan) 2966 + vlan_hwaccel_receive_skb(skb, bp->vlgrp, vtag); 2990 2967 else 2991 2968 #endif 2992 2969 netif_receive_skb(skb);