[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>
This commit is contained in:
committed by
Francois Romieu
parent
56a645cc1b
commit
791917deb6
@@ -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)
|
|||||||
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)
|
|||||||
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:
|
|||||||
}
|
}
|
||||||
|
|
||||||
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)
|
|||||||
|
|
||||||
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)
|
|||||||
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)
|
|||||||
|
|
||||||
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,
|
|||||||
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 {
|
|||||||
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;
|
||||||
|
Reference in New Issue
Block a user