aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net
diff options
context:
space:
mode:
authorStephen Hemminger <shemminger@linux-foundation.org>2007-11-05 18:52:09 -0500
committerJeff Garzik <jeff@garzik.org>2007-11-10 04:25:08 -0500
commitab5adecb2d02f3688719dfb5936a82833fcc3955 (patch)
treed76a14137b6b86a8e4500a6c03cb4201af975bd5 /drivers/net
parentac93a3946b676025fa55356180e8321639744b31 (diff)
sky2: status ring race fix
The D-Link PCI-X board (and maybe others) can lie about status ring entries. It seems it will update the register for last status index before completing the DMA for the ring entry. To avoid reading stale data, zap the old entry and check. Signed-off-by: Stephen Hemminger <shemminger@linux-foundation.org> Signed-off-by: Jeff Garzik <jeff@garzik.org>
Diffstat (limited to 'drivers/net')
-rw-r--r--drivers/net/sky2.c12
1 files changed, 9 insertions, 3 deletions
diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c
index 4f41a9445961..706884ae8fcd 100644
--- a/drivers/net/sky2.c
+++ b/drivers/net/sky2.c
@@ -2247,20 +2247,26 @@ static int sky2_status_intr(struct sky2_hw *hw, int to_do, u16 idx)
2247 do { 2247 do {
2248 struct sky2_port *sky2; 2248 struct sky2_port *sky2;
2249 struct sky2_status_le *le = hw->st_le + hw->st_idx; 2249 struct sky2_status_le *le = hw->st_le + hw->st_idx;
2250 unsigned port = le->css & CSS_LINK_BIT; 2250 unsigned port;
2251 struct net_device *dev; 2251 struct net_device *dev;
2252 struct sk_buff *skb; 2252 struct sk_buff *skb;
2253 u32 status; 2253 u32 status;
2254 u16 length; 2254 u16 length;
2255 u8 opcode = le->opcode;
2256
2257 if (!(opcode & HW_OWNER))
2258 break;
2255 2259
2256 hw->st_idx = RING_NEXT(hw->st_idx, STATUS_RING_SIZE); 2260 hw->st_idx = RING_NEXT(hw->st_idx, STATUS_RING_SIZE);
2257 2261
2262 port = le->css & CSS_LINK_BIT;
2258 dev = hw->dev[port]; 2263 dev = hw->dev[port];
2259 sky2 = netdev_priv(dev); 2264 sky2 = netdev_priv(dev);
2260 length = le16_to_cpu(le->length); 2265 length = le16_to_cpu(le->length);
2261 status = le32_to_cpu(le->status); 2266 status = le32_to_cpu(le->status);
2262 2267
2263 switch (le->opcode & ~HW_OWNER) { 2268 le->opcode = 0;
2269 switch (opcode & ~HW_OWNER) {
2264 case OP_RXSTAT: 2270 case OP_RXSTAT:
2265 ++rx[port]; 2271 ++rx[port];
2266 skb = sky2_receive(dev, length, status); 2272 skb = sky2_receive(dev, length, status);
@@ -2353,7 +2359,7 @@ static int sky2_status_intr(struct sky2_hw *hw, int to_do, u16 idx)
2353 default: 2359 default:
2354 if (net_ratelimit()) 2360 if (net_ratelimit())
2355 printk(KERN_WARNING PFX 2361 printk(KERN_WARNING PFX
2356 "unknown status opcode 0x%x\n", le->opcode); 2362 "unknown status opcode 0x%x\n", opcode);
2357 } 2363 }
2358 } while (hw->st_idx != idx); 2364 } while (hw->st_idx != idx);
2359 2365