aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorstephen hemminger <shemminger@vyatta.com>2012-04-30 02:47:37 -0400
committerLuis Henriques <luis.henriques@canonical.com>2012-05-25 12:24:45 -0400
commit3278c90dcc07e0e6c748f20465486ab093654100 (patch)
tree62aa5cee45bd1a564cda8b852e59cb2fe6e9818f
parent334f10f50800f92e721d5a5a80dfcaec862204b1 (diff)
sky2: fix receive length error in mixed non-VLAN/VLAN traffic
BugLink: http://bugs.launchpad.net/bugs/1002880 [ Upstream commit e072b3fad5f3915102c94628b4971f52ff99dd05 ] Bug: The VLAN bit of the MAC RX Status Word is unreliable in several older supported chips. Sometimes the VLAN bit is not set for valid VLAN packets and also sometimes the VLAN bit is set for non-VLAN packets that came after a VLAN packet. This results in a receive length error when VLAN hardware tagging is enabled. Fix: Variation on original fix proposed by Mirko. The VLAN information is decoded in the status loop, and can be applied to the received SKB there. This eliminates the need for the separate tag field in the interface data structure. The tag has to be copied and cleared if packet is copied. This version checked out with vlan and normal traffic. Note: vlan_tx_tag_present should be renamed vlan_tag_present, but that is outside scope of this. Reported-by: Mirko Lindner <mlindner@marvell.com> Signed-off-by: Stephen Hemminger <shemminger@vyatta.com> Signed-off-by: David S. Miller <davem@davemloft.net> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Signed-off-by: Tim Gardner <tim.gardner@canonical.com>
-rw-r--r--drivers/net/sky2.c28
-rw-r--r--drivers/net/sky2.h1
2 files changed, 17 insertions, 12 deletions
diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c
index 88e287fd072..f2e31c87d9f 100644
--- a/drivers/net/sky2.c
+++ b/drivers/net/sky2.c
@@ -2345,9 +2345,11 @@ static struct sk_buff *receive_copy(struct sky2_port *sky2,
2345 skb->ip_summed = re->skb->ip_summed; 2345 skb->ip_summed = re->skb->ip_summed;
2346 skb->csum = re->skb->csum; 2346 skb->csum = re->skb->csum;
2347 skb->rxhash = re->skb->rxhash; 2347 skb->rxhash = re->skb->rxhash;
2348 skb->vlan_tci = re->skb->vlan_tci;
2348 2349
2349 pci_dma_sync_single_for_device(sky2->hw->pdev, re->data_addr, 2350 pci_dma_sync_single_for_device(sky2->hw->pdev, re->data_addr,
2350 length, PCI_DMA_FROMDEVICE); 2351 length, PCI_DMA_FROMDEVICE);
2352 re->skb->vlan_tci = 0;
2351 re->skb->rxhash = 0; 2353 re->skb->rxhash = 0;
2352 re->skb->ip_summed = CHECKSUM_NONE; 2354 re->skb->ip_summed = CHECKSUM_NONE;
2353 skb_put(skb, length); 2355 skb_put(skb, length);
@@ -2433,9 +2435,6 @@ static struct sk_buff *sky2_receive(struct net_device *dev,
2433 struct sk_buff *skb = NULL; 2435 struct sk_buff *skb = NULL;
2434 u16 count = (status & GMR_FS_LEN) >> 16; 2436 u16 count = (status & GMR_FS_LEN) >> 16;
2435 2437
2436 if (status & GMR_FS_VLAN)
2437 count -= VLAN_HLEN; /* Account for vlan tag */
2438
2439 netif_printk(sky2, rx_status, KERN_DEBUG, dev, 2438 netif_printk(sky2, rx_status, KERN_DEBUG, dev,
2440 "rx slot %u status 0x%x len %d\n", 2439 "rx slot %u status 0x%x len %d\n",
2441 sky2->rx_next, status, length); 2440 sky2->rx_next, status, length);
@@ -2443,6 +2442,9 @@ static struct sk_buff *sky2_receive(struct net_device *dev,
2443 sky2->rx_next = (sky2->rx_next + 1) % sky2->rx_pending; 2442 sky2->rx_next = (sky2->rx_next + 1) % sky2->rx_pending;
2444 prefetch(sky2->rx_ring + sky2->rx_next); 2443 prefetch(sky2->rx_ring + sky2->rx_next);
2445 2444
2445 if (vlan_tx_tag_present(re->skb))
2446 count -= VLAN_HLEN; /* Account for vlan tag */
2447
2446 /* This chip has hardware problems that generates bogus status. 2448 /* This chip has hardware problems that generates bogus status.
2447 * So do only marginal checking and expect higher level protocols 2449 * So do only marginal checking and expect higher level protocols
2448 * to handle crap frames. 2450 * to handle crap frames.
@@ -2500,11 +2502,8 @@ static inline void sky2_tx_done(struct net_device *dev, u16 last)
2500} 2502}
2501 2503
2502static inline void sky2_skb_rx(const struct sky2_port *sky2, 2504static inline void sky2_skb_rx(const struct sky2_port *sky2,
2503 u32 status, struct sk_buff *skb) 2505 struct sk_buff *skb)
2504{ 2506{
2505 if (status & GMR_FS_VLAN)
2506 __vlan_hwaccel_put_tag(skb, be16_to_cpu(sky2->rx_tag));
2507
2508 if (skb->ip_summed == CHECKSUM_NONE) 2507 if (skb->ip_summed == CHECKSUM_NONE)
2509 netif_receive_skb(skb); 2508 netif_receive_skb(skb);
2510 else 2509 else
@@ -2558,6 +2557,14 @@ static void sky2_rx_checksum(struct sky2_port *sky2, u32 status)
2558 } 2557 }
2559} 2558}
2560 2559
2560static void sky2_rx_tag(struct sky2_port *sky2, u16 length)
2561{
2562 struct sk_buff *skb;
2563
2564 skb = sky2->rx_ring[sky2->rx_next].skb;
2565 __vlan_hwaccel_put_tag(skb, be16_to_cpu(length));
2566}
2567
2561static void sky2_rx_hash(struct sky2_port *sky2, u32 status) 2568static void sky2_rx_hash(struct sky2_port *sky2, u32 status)
2562{ 2569{
2563 struct sk_buff *skb; 2570 struct sk_buff *skb;
@@ -2616,8 +2623,7 @@ static int sky2_status_intr(struct sky2_hw *hw, int to_do, u16 idx)
2616 } 2623 }
2617 2624
2618 skb->protocol = eth_type_trans(skb, dev); 2625 skb->protocol = eth_type_trans(skb, dev);
2619 2626 sky2_skb_rx(sky2, skb);
2620 sky2_skb_rx(sky2, status, skb);
2621 2627
2622 /* Stop after net poll weight */ 2628 /* Stop after net poll weight */
2623 if (++work_done >= to_do) 2629 if (++work_done >= to_do)
@@ -2625,11 +2631,11 @@ static int sky2_status_intr(struct sky2_hw *hw, int to_do, u16 idx)
2625 break; 2631 break;
2626 2632
2627 case OP_RXVLAN: 2633 case OP_RXVLAN:
2628 sky2->rx_tag = length; 2634 sky2_rx_tag(sky2, length);
2629 break; 2635 break;
2630 2636
2631 case OP_RXCHKSVLAN: 2637 case OP_RXCHKSVLAN:
2632 sky2->rx_tag = length; 2638 sky2_rx_tag(sky2, length);
2633 /* fall through */ 2639 /* fall through */
2634 case OP_RXCHKS: 2640 case OP_RXCHKS:
2635 if (likely(dev->features & NETIF_F_RXCSUM)) 2641 if (likely(dev->features & NETIF_F_RXCSUM))
diff --git a/drivers/net/sky2.h b/drivers/net/sky2.h
index 318c9ae7bf9..a79a1662ea9 100644
--- a/drivers/net/sky2.h
+++ b/drivers/net/sky2.h
@@ -2236,7 +2236,6 @@ struct sky2_port {
2236 u16 rx_pending; 2236 u16 rx_pending;
2237 u16 rx_data_size; 2237 u16 rx_data_size;
2238 u16 rx_nfrags; 2238 u16 rx_nfrags;
2239 u16 rx_tag;
2240 2239
2241 struct { 2240 struct {
2242 unsigned long last; 2241 unsigned long last;