aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLennert Buytenhek <buytenh@wantstofly.org>2012-12-17 22:57:00 -0500
committerDavid S. Miller <davem@davemloft.net>2012-12-19 15:44:45 -0500
commit4945106d21926eadaaa1c5465d26d9a0d26a2420 (patch)
treef0ed2f5271c33a59e227b92360ecfe0fffdba727
parent341abdbe38ca22f351c9db68a69382fa57350066 (diff)
ksz884x: fix receive polling race condition
The ksz884x driver does receive processing in a custom tasklet, and seems to be assuming that since it takes its private interface spinlock with spin_lock_irq(), it won't be running concurrently with its own interrupt handler, as it cannot be preempted by it, but since its interrupt handler doesn't do any locking whatsoever, the receive processing tasklet and interrupt handler can end up running concurrently on different CPUs. As a result of this, the ksz884x receive path ends up locking up fairly easily, when the receive processing tasklet's reenabling of receive interrupts (due to it being done with polling the receive ring) races with the interrupt handler's disabling of receive interrupts (due to a new receive interrupt coming in) resulting in the receive interrupt being masked but the receive processing tasklet not being scheduled. Fix this by making the ksz884x interrupt handler take its private interface spinlock. This requires upgrading the spin_lock() in the transmit cleanup tasklet to a spin_lock_irq(), as otherwise the IRQ handler can preempt transmit cleanup and deadlock the system, but with those two changes, no more receive lockups have been observed. Reported-by: Chris Healy <cphealy@gmail.com> Signed-off-by: Lennert Buytenhek <buytenh@wantstofly.org> ---- Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--drivers/net/ethernet/micrel/ksz884x.c12
1 files changed, 9 insertions, 3 deletions
diff --git a/drivers/net/ethernet/micrel/ksz884x.c b/drivers/net/ethernet/micrel/ksz884x.c
index 83f0ea929d3d..8ebc352bcbe6 100644
--- a/drivers/net/ethernet/micrel/ksz884x.c
+++ b/drivers/net/ethernet/micrel/ksz884x.c
@@ -4761,7 +4761,7 @@ static void transmit_cleanup(struct dev_info *hw_priv, int normal)
4761 struct ksz_dma_buf *dma_buf; 4761 struct ksz_dma_buf *dma_buf;
4762 struct net_device *dev = NULL; 4762 struct net_device *dev = NULL;
4763 4763
4764 spin_lock(&hw_priv->hwlock); 4764 spin_lock_irq(&hw_priv->hwlock);
4765 last = info->last; 4765 last = info->last;
4766 4766
4767 while (info->avail < info->alloc) { 4767 while (info->avail < info->alloc) {
@@ -4795,7 +4795,7 @@ static void transmit_cleanup(struct dev_info *hw_priv, int normal)
4795 info->avail++; 4795 info->avail++;
4796 } 4796 }
4797 info->last = last; 4797 info->last = last;
4798 spin_unlock(&hw_priv->hwlock); 4798 spin_unlock_irq(&hw_priv->hwlock);
4799 4799
4800 /* Notify the network subsystem that the packet has been sent. */ 4800 /* Notify the network subsystem that the packet has been sent. */
4801 if (dev) 4801 if (dev)
@@ -5259,11 +5259,15 @@ static irqreturn_t netdev_intr(int irq, void *dev_id)
5259 struct dev_info *hw_priv = priv->adapter; 5259 struct dev_info *hw_priv = priv->adapter;
5260 struct ksz_hw *hw = &hw_priv->hw; 5260 struct ksz_hw *hw = &hw_priv->hw;
5261 5261
5262 spin_lock(&hw_priv->hwlock);
5263
5262 hw_read_intr(hw, &int_enable); 5264 hw_read_intr(hw, &int_enable);
5263 5265
5264 /* Not our interrupt! */ 5266 /* Not our interrupt! */
5265 if (!int_enable) 5267 if (!int_enable) {
5268 spin_unlock(&hw_priv->hwlock);
5266 return IRQ_NONE; 5269 return IRQ_NONE;
5270 }
5267 5271
5268 do { 5272 do {
5269 hw_ack_intr(hw, int_enable); 5273 hw_ack_intr(hw, int_enable);
@@ -5310,6 +5314,8 @@ static irqreturn_t netdev_intr(int irq, void *dev_id)
5310 5314
5311 hw_ena_intr(hw); 5315 hw_ena_intr(hw);
5312 5316
5317 spin_unlock(&hw_priv->hwlock);
5318
5313 return IRQ_HANDLED; 5319 return IRQ_HANDLED;
5314} 5320}
5315 5321