浏览代码

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>
Stephen Hemminger 17 年之前
父节点
当前提交
ab5adecb2d
共有 1 个文件被更改,包括 9 次插入3 次删除
  1. 9 3
      drivers/net/sky2.c

+ 9 - 3
drivers/net/sky2.c

@@ -2247,20 +2247,26 @@ static int sky2_status_intr(struct sky2_hw *hw, int to_do, u16 idx)
 	do {
 	do {
 		struct sky2_port *sky2;
 		struct sky2_port *sky2;
 		struct sky2_status_le *le  = hw->st_le + hw->st_idx;
 		struct sky2_status_le *le  = hw->st_le + hw->st_idx;
-		unsigned port = le->css & CSS_LINK_BIT;
+		unsigned port;
 		struct net_device *dev;
 		struct net_device *dev;
 		struct sk_buff *skb;
 		struct sk_buff *skb;
 		u32 status;
 		u32 status;
 		u16 length;
 		u16 length;
+		u8 opcode = le->opcode;
+
+		if (!(opcode & HW_OWNER))
+			break;
 
 
 		hw->st_idx = RING_NEXT(hw->st_idx, STATUS_RING_SIZE);
 		hw->st_idx = RING_NEXT(hw->st_idx, STATUS_RING_SIZE);
 
 
+		port = le->css & CSS_LINK_BIT;
 		dev = hw->dev[port];
 		dev = hw->dev[port];
 		sky2 = netdev_priv(dev);
 		sky2 = netdev_priv(dev);
 		length = le16_to_cpu(le->length);
 		length = le16_to_cpu(le->length);
 		status = le32_to_cpu(le->status);
 		status = le32_to_cpu(le->status);
 
 
-		switch (le->opcode & ~HW_OWNER) {
+		le->opcode = 0;
+		switch (opcode & ~HW_OWNER) {
 		case OP_RXSTAT:
 		case OP_RXSTAT:
 			++rx[port];
 			++rx[port];
 			skb = sky2_receive(dev, length, status);
 			skb = sky2_receive(dev, length, status);
@@ -2353,7 +2359,7 @@ static int sky2_status_intr(struct sky2_hw *hw, int to_do, u16 idx)
 		default:
 		default:
 			if (net_ratelimit())
 			if (net_ratelimit())
 				printk(KERN_WARNING PFX
 				printk(KERN_WARNING PFX
-				       "unknown status opcode 0x%x\n", le->opcode);
+				       "unknown status opcode 0x%x\n", opcode);
 		}
 		}
 	} while (hw->st_idx != idx);
 	} while (hw->st_idx != idx);