diff options
author | Stephen Hemminger <shemminger@osdl.org> | 2006-02-22 13:28:35 -0500 |
---|---|---|
committer | Francois Romieu <romieu@fr.zoreil.com> | 2006-02-23 17:07:08 -0500 |
commit | 80dd857daca1cf541b10118991569470d62c1d38 (patch) | |
tree | a02a62684dee38af8bb91bb261de8659f08e9d55 /drivers/net | |
parent | 0781191cf69b7635e0d3ea55c6019e789d1936fa (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>
Diffstat (limited to 'drivers/net')
-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 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 | ||
2873 | static irqreturn_t skge_intr(int irq, void *dev_id, struct pt_regs *regs) | 2878 | static 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 | ||
2407 | enum { | 2408 | enum { |