aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/sky2.c
diff options
context:
space:
mode:
authorStephen Hemminger <shemminger@osdl.org>2006-03-20 15:48:21 -0800
committerJeff Garzik <jeff@garzik.org>2006-03-21 16:00:52 -0500
commit8f24664da64f8db094cd9d379b16fc1d8776d1df (patch)
treec954d1ac501e3f4f4c5473cdc96bf46894b129d9 /drivers/net/sky2.c
parent[PATCH] sky2: whitespace fixes (diff)
downloadlinux-dev-8f24664da64f8db094cd9d379b16fc1d8776d1df.tar.xz
linux-dev-8f24664da64f8db094cd9d379b16fc1d8776d1df.zip
[PATCH] sky2: transmit recovery
This patch decodes state and revovers from any races in the transmit timeout and NAPI logic. It should never trigger, but if it does then do the right thing. Signed-off-by: Stephen Hemminger <shemminger@osdl.org> Signed-off-by: Jeff Garzik <jeff@garzik.org>
Diffstat (limited to 'drivers/net/sky2.c')
-rw-r--r--drivers/net/sky2.c37
1 files changed, 31 insertions, 6 deletions
diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c
index ffe3aa7e2ab2..ab36a7460a26 100644
--- a/drivers/net/sky2.c
+++ b/drivers/net/sky2.c
@@ -1328,7 +1328,7 @@ static void sky2_tx_complete(struct sky2_port *sky2, u16 done)
}
sky2->tx_cons = put;
- if (netif_queue_stopped(dev) && tx_avail(sky2) > MAX_SKB_TX_LE)
+ if (tx_avail(sky2) > MAX_SKB_TX_LE)
netif_wake_queue(dev);
}
@@ -1651,17 +1651,40 @@ static void sky2_tx_timeout(struct net_device *dev)
struct sky2_port *sky2 = netdev_priv(dev);
struct sky2_hw *hw = sky2->hw;
unsigned txq = txqaddr[sky2->port];
+ u16 report, done;
if (netif_msg_timer(sky2))
printk(KERN_ERR PFX "%s: tx timeout\n", dev->name);
- sky2_write32(hw, Q_ADDR(txq, Q_CSR), BMU_STOP);
- sky2_write32(hw, Y2_QADDR(txq, PREF_UNIT_CTRL), PREF_UNIT_RST_SET);
+ report = sky2_read16(hw, sky2->port == 0 ? STAT_TXA1_RIDX : STAT_TXA2_RIDX);
+ done = sky2_read16(hw, Q_ADDR(txq, Q_DONE));
- sky2_tx_clean(sky2);
+ printk(KERN_DEBUG PFX "%s: transmit ring %u .. %u report=%u done=%u\n",
+ dev->name,
+ sky2->tx_cons, sky2->tx_prod, report, done);
+
+ if (report != done) {
+ printk(KERN_INFO PFX "status burst pending (irq moderation?)\n");
+
+ sky2_write8(hw, STAT_TX_TIMER_CTRL, TIM_STOP);
+ sky2_write8(hw, STAT_TX_TIMER_CTRL, TIM_START);
+ } else if (report != sky2->tx_cons) {
+ printk(KERN_INFO PFX "status report lost?\n");
- sky2_qset(hw, txq);
- sky2_prefetch_init(hw, txq, sky2->tx_le_map, TX_RING_SIZE - 1);
+ spin_lock_bh(&sky2->tx_lock);
+ sky2_tx_complete(sky2, report);
+ spin_unlock_bh(&sky2->tx_lock);
+ } else {
+ printk(KERN_INFO PFX "hardware hung? flushing\n");
+
+ sky2_write32(hw, Q_ADDR(txq, Q_CSR), BMU_STOP);
+ sky2_write32(hw, Y2_QADDR(txq, PREF_UNIT_CTRL), PREF_UNIT_RST_SET);
+
+ sky2_tx_clean(sky2);
+
+ sky2_qset(hw, txq);
+ sky2_prefetch_init(hw, txq, sky2->tx_le_map, TX_RING_SIZE - 1);
+ }
}
@@ -2097,6 +2120,8 @@ static irqreturn_t sky2_intr(int irq, void *dev_id, struct pt_regs *regs)
prefetch(&hw->st_le[hw->st_idx]);
if (likely(__netif_rx_schedule_prep(dev0)))
__netif_rx_schedule(dev0);
+ else
+ printk(KERN_DEBUG PFX "irq race detected\n");
return IRQ_HANDLED;
}