summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStephen Hemminger <shemminger@linux-foundation.org>2007-11-06 02:52:09 +0300
committerJeff Garzik <jeff@garzik.org>2007-11-10 12:25:08 +0300
commitab5adecb2d02f3688719dfb5936a82833fcc3955 (patch)
treed76a14137b6b86a8e4500a6c03cb4201af975bd5
parentac93a3946b676025fa55356180e8321639744b31 (diff)
downloadlinux-ab5adecb2d02f3688719dfb5936a82833fcc3955.tar.xz
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>
-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)
do {
struct sky2_port *sky2;
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 sk_buff *skb;
u32 status;
u16 length;
+ u8 opcode = le->opcode;
+
+ if (!(opcode & HW_OWNER))
+ break;
hw->st_idx = RING_NEXT(hw->st_idx, STATUS_RING_SIZE);
+ port = le->css & CSS_LINK_BIT;
dev = hw->dev[port];
sky2 = netdev_priv(dev);
length = le16_to_cpu(le->length);
status = le32_to_cpu(le->status);
- switch (le->opcode & ~HW_OWNER) {
+ le->opcode = 0;
+ switch (opcode & ~HW_OWNER) {
case OP_RXSTAT:
++rx[port];
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:
if (net_ratelimit())
printk(KERN_WARNING PFX
- "unknown status opcode 0x%x\n", le->opcode);
+ "unknown status opcode 0x%x\n", opcode);
}
} while (hw->st_idx != idx);