summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorStephen Hemminger <shemminger@osdl.org>2006-02-22 13:52:35 -0800
committerChris Wright <chrisw@sous-sol.org>2006-03-01 14:36:37 -0800
commit94069fb3035f4e9de4ce33f5910be0dded06677c (patch)
treecce363e2dd58076a48a142c4fc29cf674439d46d /drivers
parented31b30cb77fd23f37d5cac1144fa5dddc6b7a00 (diff)
[PATCH] skge: fix SMP race
If skge is attached to a bad cable, that goes up/down. It exposes an SMP race with the management of IRQ mask Signed-off-by: Stephen Hemminger <shemminger@osdl.org> Signed-off-by: Chris Wright <chrisw@sous-sol.org>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/net/skge.c21
-rw-r--r--drivers/net/skge.h1
2 files changed, 15 insertions, 7 deletions
diff --git a/drivers/net/skge.c b/drivers/net/skge.c
index f3294d00290c..6a4e055a3cb6 100644
--- a/drivers/net/skge.c
+++ b/drivers/net/skge.c
@@ -2182,8 +2182,10 @@ static int skge_up(struct net_device *dev)
skge->tx_avail = skge->tx_ring.count - 1;
/* Enable IRQ from port */
+ spin_lock_irq(&hw->hw_lock);
hw->intr_mask |= portirqmask[port];
skge_write32(hw, B0_IMSK, hw->intr_mask);
+ spin_unlock_irq(&hw->hw_lock);
/* Initialize MAC */
spin_lock_bh(&hw->phy_lock);
@@ -2241,8 +2243,10 @@ static int skge_down(struct net_device *dev)
else
yukon_stop(skge);
+ spin_lock_irq(&hw->hw_lock);
hw->intr_mask &= ~portirqmask[skge->port];
skge_write32(hw, B0_IMSK, hw->intr_mask);
+ spin_unlock_irq(&hw->hw_lock);
/* Stop transmitter */
skge_write8(hw, Q_ADDR(txqaddr[port], Q_CSR), CSR_STOP);
@@ -2698,10 +2702,11 @@ static int skge_poll(struct net_device *dev, int *budget)
if (work_done >= to_do)
return 1; /* not done */
- netif_rx_complete(dev);
- hw->intr_mask |= portirqmask[skge->port];
- skge_write32(hw, B0_IMSK, hw->intr_mask);
- skge_read32(hw, B0_IMSK);
+ spin_lock_irq(&hw->hw_lock);
+ __netif_rx_complete(dev);
+ hw->intr_mask |= portirqmask[skge->port];
+ skge_write32(hw, B0_IMSK, hw->intr_mask);
+ spin_unlock_irq(&hw->hw_lock);
return 0;
}
@@ -2861,10 +2866,10 @@ static void skge_extirq(unsigned long data)
}
spin_unlock(&hw->phy_lock);
- local_irq_disable();
+ spin_lock_irq(&hw->hw_lock);
hw->intr_mask |= IS_EXT_REG;
skge_write32(hw, B0_IMSK, hw->intr_mask);
- local_irq_enable();
+ spin_unlock_irq(&hw->hw_lock);
}
static irqreturn_t skge_intr(int irq, void *dev_id, struct pt_regs *regs)
@@ -2875,7 +2880,7 @@ static irqreturn_t skge_intr(int irq, void *dev_id, struct pt_regs *regs)
if (status == 0 || status == ~0) /* hotplug or shared irq */
return IRQ_NONE;
- status &= hw->intr_mask;
+ spin_lock(&hw->hw_lock);
if (status & IS_R1_F) {
skge_write8(hw, Q_ADDR(Q_R1, Q_CSR), CSR_IRQ_CL_F);
hw->intr_mask &= ~IS_R1_F;
@@ -2927,6 +2932,7 @@ static irqreturn_t skge_intr(int irq, void *dev_id, struct pt_regs *regs)
}
skge_write32(hw, B0_IMSK, hw->intr_mask);
+ spin_unlock(&hw->hw_lock);
return IRQ_HANDLED;
}
@@ -3285,6 +3291,7 @@ static int __devinit skge_probe(struct pci_dev *pdev,
hw->pdev = pdev;
spin_lock_init(&hw->phy_lock);
+ spin_lock_init(&hw->hw_lock);
tasklet_init(&hw->ext_tasklet, skge_extirq, (unsigned long) hw);
hw->regs = ioremap_nocache(pci_resource_start(pdev, 0), 0x4000);
diff --git a/drivers/net/skge.h b/drivers/net/skge.h
index ee123c15f545..29d9ecbf6251 100644
--- a/drivers/net/skge.h
+++ b/drivers/net/skge.h
@@ -2473,6 +2473,7 @@ struct skge_hw {
struct tasklet_struct ext_tasklet;
spinlock_t phy_lock;
+ spinlock_t hw_lock;
};
enum {