aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStephen Hemminger <shemminger@osdl.org>2006-02-22 13:28:35 -0500
committerFrancois Romieu <romieu@fr.zoreil.com>2006-02-23 17:07:08 -0500
commit80dd857daca1cf541b10118991569470d62c1d38 (patch)
treea02a62684dee38af8bb91bb261de8659f08e9d55
parent0781191cf69b7635e0d3ea55c6019e789d1936fa (diff)
skge: protect interrupt mask
There is a race between updating the irq mask and setting it which can be triggered on SMP with a bad cable. Similar patch from Ingo Molnar and Thomas Gleixner Signed-off-by: Stephen Hemminger <shemminger@osdl.org>
-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 af2e6782031b..25e028b7ce48 100644
--- a/drivers/net/skge.c
+++ b/drivers/net/skge.c
@@ -2185,8 +2185,10 @@ static int skge_up(struct net_device *dev)
2185 skge->tx_avail = skge->tx_ring.count - 1; 2185 skge->tx_avail = skge->tx_ring.count - 1;
2186 2186
2187 /* Enable IRQ from port */ 2187 /* Enable IRQ from port */
2188 spin_lock_irq(&hw->hw_lock);
2188 hw->intr_mask |= portirqmask[port]; 2189 hw->intr_mask |= portirqmask[port];
2189 skge_write32(hw, B0_IMSK, hw->intr_mask); 2190 skge_write32(hw, B0_IMSK, hw->intr_mask);
2191 spin_unlock_irq(&hw->hw_lock);
2190 2192
2191 /* Initialize MAC */ 2193 /* Initialize MAC */
2192 spin_lock_bh(&hw->phy_lock); 2194 spin_lock_bh(&hw->phy_lock);
@@ -2244,8 +2246,10 @@ static int skge_down(struct net_device *dev)
2244 else 2246 else
2245 yukon_stop(skge); 2247 yukon_stop(skge);
2246 2248
2249 spin_lock_irq(&hw->hw_lock);
2247 hw->intr_mask &= ~portirqmask[skge->port]; 2250 hw->intr_mask &= ~portirqmask[skge->port];
2248 skge_write32(hw, B0_IMSK, hw->intr_mask); 2251 skge_write32(hw, B0_IMSK, hw->intr_mask);
2252 spin_unlock_irq(&hw->hw_lock);
2249 2253
2250 /* Stop transmitter */ 2254 /* Stop transmitter */
2251 skge_write8(hw, Q_ADDR(txqaddr[port], Q_CSR), CSR_STOP); 2255 skge_write8(hw, Q_ADDR(txqaddr[port], Q_CSR), CSR_STOP);
@@ -2701,10 +2705,11 @@ static int skge_poll(struct net_device *dev, int *budget)
2701 if (work_done >= to_do) 2705 if (work_done >= to_do)
2702 return 1; /* not done */ 2706 return 1; /* not done */
2703 2707
2704 netif_rx_complete(dev); 2708 spin_lock_irq(&hw->hw_lock);
2705 hw->intr_mask |= portirqmask[skge->port]; 2709 __netif_rx_complete(dev);
2706 skge_write32(hw, B0_IMSK, hw->intr_mask); 2710 hw->intr_mask |= portirqmask[skge->port];
2707 skge_read32(hw, B0_IMSK); 2711 skge_write32(hw, B0_IMSK, hw->intr_mask);
2712 spin_unlock_irq(&hw->hw_lock);
2708 2713
2709 return 0; 2714 return 0;
2710} 2715}
@@ -2864,10 +2869,10 @@ static void skge_extirq(unsigned long data)
2864 } 2869 }
2865 spin_unlock(&hw->phy_lock); 2870 spin_unlock(&hw->phy_lock);
2866 2871
2867 local_irq_disable(); 2872 spin_lock_irq(&hw->hw_lock);
2868 hw->intr_mask |= IS_EXT_REG; 2873 hw->intr_mask |= IS_EXT_REG;
2869 skge_write32(hw, B0_IMSK, hw->intr_mask); 2874 skge_write32(hw, B0_IMSK, hw->intr_mask);
2870 local_irq_enable(); 2875 spin_unlock_irq(&hw->hw_lock);
2871} 2876}
2872 2877
2873static irqreturn_t skge_intr(int irq, void *dev_id, struct pt_regs *regs) 2878static irqreturn_t skge_intr(int irq, void *dev_id, struct pt_regs *regs)
@@ -2878,7 +2883,7 @@ static irqreturn_t skge_intr(int irq, void *dev_id, struct pt_regs *regs)
2878 if (status == 0 || status == ~0) /* hotplug or shared irq */ 2883 if (status == 0 || status == ~0) /* hotplug or shared irq */
2879 return IRQ_NONE; 2884 return IRQ_NONE;
2880 2885
2881 status &= hw->intr_mask; 2886 spin_lock(&hw->hw_lock);
2882 if (status & IS_R1_F) { 2887 if (status & IS_R1_F) {
2883 skge_write8(hw, Q_ADDR(Q_R1, Q_CSR), CSR_IRQ_CL_F); 2888 skge_write8(hw, Q_ADDR(Q_R1, Q_CSR), CSR_IRQ_CL_F);
2884 hw->intr_mask &= ~IS_R1_F; 2889 hw->intr_mask &= ~IS_R1_F;
@@ -2930,6 +2935,7 @@ static irqreturn_t skge_intr(int irq, void *dev_id, struct pt_regs *regs)
2930 } 2935 }
2931 2936
2932 skge_write32(hw, B0_IMSK, hw->intr_mask); 2937 skge_write32(hw, B0_IMSK, hw->intr_mask);
2938 spin_unlock(&hw->hw_lock);
2933 2939
2934 return IRQ_HANDLED; 2940 return IRQ_HANDLED;
2935} 2941}
@@ -3298,6 +3304,7 @@ static int __devinit skge_probe(struct pci_dev *pdev,
3298 3304
3299 hw->pdev = pdev; 3305 hw->pdev = pdev;
3300 spin_lock_init(&hw->phy_lock); 3306 spin_lock_init(&hw->phy_lock);
3307 spin_lock_init(&hw->hw_lock);
3301 tasklet_init(&hw->ext_tasklet, skge_extirq, (unsigned long) hw); 3308 tasklet_init(&hw->ext_tasklet, skge_extirq, (unsigned long) hw);
3302 3309
3303 hw->regs = ioremap_nocache(pci_resource_start(pdev, 0), 0x4000); 3310 hw->regs = ioremap_nocache(pci_resource_start(pdev, 0), 0x4000);
diff --git a/drivers/net/skge.h b/drivers/net/skge.h
index 2efdacc290e5..941f12a333b6 100644
--- a/drivers/net/skge.h
+++ b/drivers/net/skge.h
@@ -2402,6 +2402,7 @@ struct skge_hw {
2402 2402
2403 struct tasklet_struct ext_tasklet; 2403 struct tasklet_struct ext_tasklet;
2404 spinlock_t phy_lock; 2404 spinlock_t phy_lock;
2405 spinlock_t hw_lock;
2405}; 2406};
2406 2407
2407enum { 2408enum {