summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorStephen Hemminger <shemminger@osdl.org>2006-02-22 22:45:03 +0300
committerFrancois Romieu <romieu@fr.zoreil.com>2006-02-23 03:25:26 +0300
commit791917deb63c6d8beb3f347ea0911371deff1624 (patch)
tree170bc081f03326f75b6b5ccce7d7d1df28a2c833 /drivers
parent56a645cc1bc16ab33b33a3e0854a46c5d2c864f3 (diff)
downloadlinux-791917deb63c6d8beb3f347ea0911371deff1624.tar.xz
[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>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/net/sky2.c21
-rw-r--r--drivers/net/sky2.h3
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)
goto err_out;
/* 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;
sky2_write32(hw, B0_IMSK, hw->intr_mask);
+ spin_unlock_irq(&hw->hw_lock);
return 0;
err_out:
@@ -1380,10 +1382,10 @@ static int sky2_down(struct net_device *dev)
netif_stop_queue(dev);
/* 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);
sky2_write32(hw, B0_IMSK, hw->intr_mask);
- local_irq_enable();
+ spin_unlock_irq(&hw->hw_lock);
flush_scheduled_work();
@@ -1665,10 +1667,10 @@ static void sky2_phy_task(void *arg)
out:
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;
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)) {
- netif_rx_complete(dev0);
+ spin_lock_irq(&hw->hw_lock);
+ __netif_rx_complete(dev0);
+
hw->intr_mask |= Y2_IS_STAT_BMU;
sky2_write32(hw, B0_IMSK, hw->intr_mask);
+ spin_unlock_irq(&hw->hw_lock);
+
return 0;
} else {
*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);
sky2_write32(hw, B0_IMSK, hw->intr_mask);
+
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)
return IRQ_NONE;
+ spin_lock(&hw->hw_lock);
if (status & Y2_IS_HW_ERR)
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_read32(hw, B0_IMSK);
+ spin_unlock(&hw->hw_lock);
return IRQ_HANDLED;
}
@@ -3241,6 +3249,7 @@ static int __devinit sky2_probe(struct pci_dev *pdev,
goto err_out_free_hw;
}
hw->pm_cap = pm_cap;
+ spin_lock_init(&hw->hw_lock);
#ifdef __BIG_ENDIAN
/* 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 {
struct sky2_hw {
void __iomem *regs;
struct pci_dev *pdev;
- u32 intr_mask;
struct net_device *dev[2];
+ spinlock_t hw_lock;
+ u32 intr_mask;
int pm_cap;
int msi;