From: Stephen Hemminger Date: Wed, 22 Feb 2006 21:52:35 +0000 (-0800) Subject: [PATCH] skge: fix SMP race X-Git-Tag: v2.6.15.5~8 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=94069fb3035f4e9de4ce33f5910be0dded06677c;p=thirdparty%2Fkernel%2Fstable.git [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 Signed-off-by: Chris Wright --- diff --git a/drivers/net/skge.c b/drivers/net/skge.c index f3294d00290c4..6a4e055a3cb60 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 ee123c15f5450..29d9ecbf62519 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 {