Commit 791917de authored by Stephen Hemminger's avatar Stephen Hemminger Committed by Francois Romieu

[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: default avatarStephen Hemminger <shemminger@osdl.org>
parent 56a645cc
...@@ -1079,8 +1079,10 @@ static int sky2_up(struct net_device *dev) ...@@ -1079,8 +1079,10 @@ static int sky2_up(struct net_device *dev)
goto err_out; goto err_out;
/* Enable interrupts from phy/mac for port */ /* Enable interrupts from phy/mac for port */
spin_lock_irq(&hw->hw_lock);
hw->intr_mask |= (port == 0) ? Y2_IS_PORT_1 : Y2_IS_PORT_2; hw->intr_mask |= (port == 0) ? Y2_IS_PORT_1 : Y2_IS_PORT_2;
sky2_write32(hw, B0_IMSK, hw->intr_mask); sky2_write32(hw, B0_IMSK, hw->intr_mask);
spin_unlock_irq(&hw->hw_lock);
return 0; return 0;
err_out: err_out:
...@@ -1380,10 +1382,10 @@ static int sky2_down(struct net_device *dev) ...@@ -1380,10 +1382,10 @@ static int sky2_down(struct net_device *dev)
netif_stop_queue(dev); netif_stop_queue(dev);
/* Disable port IRQ */ /* Disable port IRQ */
local_irq_disable(); spin_lock_irq(&hw->hw_lock);
hw->intr_mask &= ~((sky2->port == 0) ? Y2_IS_IRQ_PHY1 : Y2_IS_IRQ_PHY2); hw->intr_mask &= ~((sky2->port == 0) ? Y2_IS_IRQ_PHY1 : Y2_IS_IRQ_PHY2);
sky2_write32(hw, B0_IMSK, hw->intr_mask); sky2_write32(hw, B0_IMSK, hw->intr_mask);
local_irq_enable(); spin_unlock_irq(&hw->hw_lock);
flush_scheduled_work(); flush_scheduled_work();
...@@ -1665,10 +1667,10 @@ static void sky2_phy_task(void *arg) ...@@ -1665,10 +1667,10 @@ static void sky2_phy_task(void *arg)
out: out:
up(&sky2->phy_sema); up(&sky2->phy_sema);
local_irq_disable(); spin_lock_irq(&hw->hw_lock);
hw->intr_mask |= (sky2->port == 0) ? Y2_IS_IRQ_PHY1 : Y2_IS_IRQ_PHY2; hw->intr_mask |= (sky2->port == 0) ? Y2_IS_IRQ_PHY1 : Y2_IS_IRQ_PHY2;
sky2_write32(hw, B0_IMSK, hw->intr_mask); sky2_write32(hw, B0_IMSK, hw->intr_mask);
local_irq_enable(); spin_unlock_irq(&hw->hw_lock);
} }
...@@ -1994,9 +1996,13 @@ exit_loop: ...@@ -1994,9 +1996,13 @@ exit_loop:
} }
if (likely(work_done < to_do)) { if (likely(work_done < to_do)) {
netif_rx_complete(dev0); spin_lock_irq(&hw->hw_lock);
__netif_rx_complete(dev0);
hw->intr_mask |= Y2_IS_STAT_BMU; hw->intr_mask |= Y2_IS_STAT_BMU;
sky2_write32(hw, B0_IMSK, hw->intr_mask); sky2_write32(hw, B0_IMSK, hw->intr_mask);
spin_unlock_irq(&hw->hw_lock);
return 0; return 0;
} else { } else {
*budget -= work_done; *budget -= work_done;
...@@ -2128,6 +2134,7 @@ static void sky2_phy_intr(struct sky2_hw *hw, unsigned port) ...@@ -2128,6 +2134,7 @@ static void sky2_phy_intr(struct sky2_hw *hw, unsigned port)
hw->intr_mask &= ~(port == 0 ? Y2_IS_IRQ_PHY1 : Y2_IS_IRQ_PHY2); hw->intr_mask &= ~(port == 0 ? Y2_IS_IRQ_PHY1 : Y2_IS_IRQ_PHY2);
sky2_write32(hw, B0_IMSK, hw->intr_mask); sky2_write32(hw, B0_IMSK, hw->intr_mask);
schedule_work(&sky2->phy_task); schedule_work(&sky2->phy_task);
} }
...@@ -2141,6 +2148,7 @@ static irqreturn_t sky2_intr(int irq, void *dev_id, struct pt_regs *regs) ...@@ -2141,6 +2148,7 @@ static irqreturn_t sky2_intr(int irq, void *dev_id, struct pt_regs *regs)
if (status == 0 || status == ~0) if (status == 0 || status == ~0)
return IRQ_NONE; return IRQ_NONE;
spin_lock(&hw->hw_lock);
if (status & Y2_IS_HW_ERR) if (status & Y2_IS_HW_ERR)
sky2_hw_intr(hw); sky2_hw_intr(hw);
...@@ -2169,7 +2177,7 @@ static irqreturn_t sky2_intr(int irq, void *dev_id, struct pt_regs *regs) ...@@ -2169,7 +2177,7 @@ static irqreturn_t sky2_intr(int irq, void *dev_id, struct pt_regs *regs)
sky2_write32(hw, B0_Y2_SP_ICR, 2); sky2_write32(hw, B0_Y2_SP_ICR, 2);
sky2_read32(hw, B0_IMSK); spin_unlock(&hw->hw_lock);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
...@@ -3241,6 +3249,7 @@ static int __devinit sky2_probe(struct pci_dev *pdev, ...@@ -3241,6 +3249,7 @@ static int __devinit sky2_probe(struct pci_dev *pdev,
goto err_out_free_hw; goto err_out_free_hw;
} }
hw->pm_cap = pm_cap; hw->pm_cap = pm_cap;
spin_lock_init(&hw->hw_lock);
#ifdef __BIG_ENDIAN #ifdef __BIG_ENDIAN
/* byte swap descriptors in hardware */ /* byte swap descriptors in hardware */
......
...@@ -1876,8 +1876,9 @@ struct sky2_port { ...@@ -1876,8 +1876,9 @@ struct sky2_port {
struct sky2_hw { struct sky2_hw {
void __iomem *regs; void __iomem *regs;
struct pci_dev *pdev; struct pci_dev *pdev;
u32 intr_mask;
struct net_device *dev[2]; struct net_device *dev[2];
spinlock_t hw_lock;
u32 intr_mask;
int pm_cap; int pm_cap;
int msi; int msi;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment