diff options
author | Stephen Hemminger <shemminger@osdl.org> | 2006-02-22 14:45:03 -0500 |
---|---|---|
committer | Francois Romieu <romieu@fr.zoreil.com> | 2006-02-22 19:25:26 -0500 |
commit | 791917deb63c6d8beb3f347ea0911371deff1624 (patch) | |
tree | 170bc081f03326f75b6b5ccce7d7d1df28a2c833 | |
parent | 56a645cc1bc16ab33b33a3e0854a46c5d2c864f3 (diff) |
[PATCH] sky2: close race on IRQ mask update.
Need to avoid race in updating IRQ mask. This can probably be replaced
smarter use of the interrupt control registers (if/when chipset
docs are available).
Signed-off-by: Stephen Hemminger <shemminger@osdl.org>
-rw-r--r-- | drivers/net/sky2.c | 21 | ||||
-rw-r--r-- | drivers/net/sky2.h | 3 |
2 files changed, 17 insertions, 7 deletions
diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index fbbc85532c2e..ca8160d68229 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c | |||
@@ -1079,8 +1079,10 @@ static int sky2_up(struct net_device *dev) | |||
1079 | goto err_out; | 1079 | goto err_out; |
1080 | 1080 | ||
1081 | /* Enable interrupts from phy/mac for port */ | 1081 | /* Enable interrupts from phy/mac for port */ |
1082 | spin_lock_irq(&hw->hw_lock); | ||
1082 | hw->intr_mask |= (port == 0) ? Y2_IS_PORT_1 : Y2_IS_PORT_2; | 1083 | hw->intr_mask |= (port == 0) ? Y2_IS_PORT_1 : Y2_IS_PORT_2; |
1083 | sky2_write32(hw, B0_IMSK, hw->intr_mask); | 1084 | sky2_write32(hw, B0_IMSK, hw->intr_mask); |
1085 | spin_unlock_irq(&hw->hw_lock); | ||
1084 | return 0; | 1086 | return 0; |
1085 | 1087 | ||
1086 | err_out: | 1088 | err_out: |
@@ -1380,10 +1382,10 @@ static int sky2_down(struct net_device *dev) | |||
1380 | netif_stop_queue(dev); | 1382 | netif_stop_queue(dev); |
1381 | 1383 | ||
1382 | /* Disable port IRQ */ | 1384 | /* Disable port IRQ */ |
1383 | local_irq_disable(); | 1385 | spin_lock_irq(&hw->hw_lock); |
1384 | hw->intr_mask &= ~((sky2->port == 0) ? Y2_IS_IRQ_PHY1 : Y2_IS_IRQ_PHY2); | 1386 | hw->intr_mask &= ~((sky2->port == 0) ? Y2_IS_IRQ_PHY1 : Y2_IS_IRQ_PHY2); |
1385 | sky2_write32(hw, B0_IMSK, hw->intr_mask); | 1387 | sky2_write32(hw, B0_IMSK, hw->intr_mask); |
1386 | local_irq_enable(); | 1388 | spin_unlock_irq(&hw->hw_lock); |
1387 | 1389 | ||
1388 | flush_scheduled_work(); | 1390 | flush_scheduled_work(); |
1389 | 1391 | ||
@@ -1665,10 +1667,10 @@ static void sky2_phy_task(void *arg) | |||
1665 | out: | 1667 | out: |
1666 | up(&sky2->phy_sema); | 1668 | up(&sky2->phy_sema); |
1667 | 1669 | ||
1668 | local_irq_disable(); | 1670 | spin_lock_irq(&hw->hw_lock); |
1669 | hw->intr_mask |= (sky2->port == 0) ? Y2_IS_IRQ_PHY1 : Y2_IS_IRQ_PHY2; | 1671 | hw->intr_mask |= (sky2->port == 0) ? Y2_IS_IRQ_PHY1 : Y2_IS_IRQ_PHY2; |
1670 | sky2_write32(hw, B0_IMSK, hw->intr_mask); | 1672 | sky2_write32(hw, B0_IMSK, hw->intr_mask); |
1671 | local_irq_enable(); | 1673 | spin_unlock_irq(&hw->hw_lock); |
1672 | } | 1674 | } |
1673 | 1675 | ||
1674 | 1676 | ||
@@ -1994,9 +1996,13 @@ exit_loop: | |||
1994 | } | 1996 | } |
1995 | 1997 | ||
1996 | if (likely(work_done < to_do)) { | 1998 | if (likely(work_done < to_do)) { |
1997 | netif_rx_complete(dev0); | 1999 | spin_lock_irq(&hw->hw_lock); |
2000 | __netif_rx_complete(dev0); | ||
2001 | |||
1998 | hw->intr_mask |= Y2_IS_STAT_BMU; | 2002 | hw->intr_mask |= Y2_IS_STAT_BMU; |
1999 | sky2_write32(hw, B0_IMSK, hw->intr_mask); | 2003 | sky2_write32(hw, B0_IMSK, hw->intr_mask); |
2004 | spin_unlock_irq(&hw->hw_lock); | ||
2005 | |||
2000 | return 0; | 2006 | return 0; |
2001 | } else { | 2007 | } else { |
2002 | *budget -= work_done; | 2008 | *budget -= work_done; |
@@ -2128,6 +2134,7 @@ static void sky2_phy_intr(struct sky2_hw *hw, unsigned port) | |||
2128 | 2134 | ||
2129 | hw->intr_mask &= ~(port == 0 ? Y2_IS_IRQ_PHY1 : Y2_IS_IRQ_PHY2); | 2135 | hw->intr_mask &= ~(port == 0 ? Y2_IS_IRQ_PHY1 : Y2_IS_IRQ_PHY2); |
2130 | sky2_write32(hw, B0_IMSK, hw->intr_mask); | 2136 | sky2_write32(hw, B0_IMSK, hw->intr_mask); |
2137 | |||
2131 | schedule_work(&sky2->phy_task); | 2138 | schedule_work(&sky2->phy_task); |
2132 | } | 2139 | } |
2133 | 2140 | ||
@@ -2141,6 +2148,7 @@ static irqreturn_t sky2_intr(int irq, void *dev_id, struct pt_regs *regs) | |||
2141 | if (status == 0 || status == ~0) | 2148 | if (status == 0 || status == ~0) |
2142 | return IRQ_NONE; | 2149 | return IRQ_NONE; |
2143 | 2150 | ||
2151 | spin_lock(&hw->hw_lock); | ||
2144 | if (status & Y2_IS_HW_ERR) | 2152 | if (status & Y2_IS_HW_ERR) |
2145 | sky2_hw_intr(hw); | 2153 | sky2_hw_intr(hw); |
2146 | 2154 | ||
@@ -2169,7 +2177,7 @@ static irqreturn_t sky2_intr(int irq, void *dev_id, struct pt_regs *regs) | |||
2169 | 2177 | ||
2170 | sky2_write32(hw, B0_Y2_SP_ICR, 2); | 2178 | sky2_write32(hw, B0_Y2_SP_ICR, 2); |
2171 | 2179 | ||
2172 | sky2_read32(hw, B0_IMSK); | 2180 | spin_unlock(&hw->hw_lock); |
2173 | 2181 | ||
2174 | return IRQ_HANDLED; | 2182 | return IRQ_HANDLED; |
2175 | } | 2183 | } |
@@ -3241,6 +3249,7 @@ static int __devinit sky2_probe(struct pci_dev *pdev, | |||
3241 | goto err_out_free_hw; | 3249 | goto err_out_free_hw; |
3242 | } | 3250 | } |
3243 | hw->pm_cap = pm_cap; | 3251 | hw->pm_cap = pm_cap; |
3252 | spin_lock_init(&hw->hw_lock); | ||
3244 | 3253 | ||
3245 | #ifdef __BIG_ENDIAN | 3254 | #ifdef __BIG_ENDIAN |
3246 | /* byte swap descriptors in hardware */ | 3255 | /* byte swap descriptors in hardware */ |
diff --git a/drivers/net/sky2.h b/drivers/net/sky2.h index 9e40766150b4..3edb98075e0a 100644 --- a/drivers/net/sky2.h +++ b/drivers/net/sky2.h | |||
@@ -1876,8 +1876,9 @@ struct sky2_port { | |||
1876 | struct sky2_hw { | 1876 | struct sky2_hw { |
1877 | void __iomem *regs; | 1877 | void __iomem *regs; |
1878 | struct pci_dev *pdev; | 1878 | struct pci_dev *pdev; |
1879 | u32 intr_mask; | ||
1880 | struct net_device *dev[2]; | 1879 | struct net_device *dev[2]; |
1880 | spinlock_t hw_lock; | ||
1881 | u32 intr_mask; | ||
1881 | 1882 | ||
1882 | int pm_cap; | 1883 | int pm_cap; |
1883 | int msi; | 1884 | int msi; |