diff options
author | Stephen Hemminger <shemminger@osdl.org> | 2006-02-22 13:52:35 -0800 |
---|---|---|
committer | Chris Wright <chrisw@sous-sol.org> | 2006-03-01 14:36:37 -0800 |
commit | 94069fb3035f4e9de4ce33f5910be0dded06677c (patch) | |
tree | cce363e2dd58076a48a142c4fc29cf674439d46d /drivers | |
parent | ed31b30cb77fd23f37d5cac1144fa5dddc6b7a00 (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.c | 21 | ||||
-rw-r--r-- | drivers/net/skge.h | 1 |
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 { |