Skip to content

Commit

Permalink
sky2: transmit timeout
Browse files Browse the repository at this point in the history
The transmit timeout code could hang, and it would not clear out
problems if the hardware was stuck.  Change the code to effectively do
a device down/up similar to the suspend/resume code.

Signed-off-by: Stephen Hemminger <shemminger@linux-foundation.org>
Signed-off-by: Jeff Garzik <jeff@garzik.org>
  • Loading branch information
Stephen Hemminger authored and Jeff Garzik committed Feb 17, 2007
1 parent da4c1ff commit 8190679
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 16 deletions.
65 changes: 49 additions & 16 deletions drivers/net/sky2.c
Original file line number Diff line number Diff line change
Expand Up @@ -1866,16 +1866,13 @@ static void sky2_phy_intr(struct sky2_hw *hw, unsigned port)
spin_unlock(&sky2->phy_lock);
}


/* Transmit timeout is only called if we are running, carrier is up
* and tx queue is full (stopped).
* Called with netif_tx_lock held.
*/
static void sky2_tx_timeout(struct net_device *dev)
{
struct sky2_port *sky2 = netdev_priv(dev);
struct sky2_hw *hw = sky2->hw;
u32 imask;

if (netif_msg_timer(sky2))
printk(KERN_ERR PFX "%s: tx timeout\n", dev->name);
Expand All @@ -1885,19 +1882,8 @@ static void sky2_tx_timeout(struct net_device *dev)
sky2_read16(hw, sky2->port == 0 ? STAT_TXA1_RIDX : STAT_TXA2_RIDX),
sky2_read16(hw, Q_ADDR(txqaddr[sky2->port], Q_DONE)));

imask = sky2_read32(hw, B0_IMSK); /* block IRQ in hw */
sky2_write32(hw, B0_IMSK, 0);
sky2_read32(hw, B0_IMSK);

netif_poll_disable(hw->dev[0]); /* stop NAPI poll */
synchronize_irq(hw->pdev->irq);

netif_start_queue(dev); /* don't wakeup during flush */
sky2_tx_complete(sky2, sky2->tx_prod); /* Flush transmit queue */

sky2_write32(hw, B0_IMSK, imask);

sky2_phy_reinit(sky2); /* this clears flow control etc */
/* can't restart safely under softirq */
schedule_work(&hw->restart_work);
}

static int sky2_change_mtu(struct net_device *dev, int new_mtu)
Expand Down Expand Up @@ -2651,6 +2637,49 @@ static void sky2_reset(struct sky2_hw *hw)
sky2_write8(hw, STAT_ISR_TIMER_CTRL, TIM_START);
}

static void sky2_restart(struct work_struct *work)
{
struct sky2_hw *hw = container_of(work, struct sky2_hw, restart_work);
struct net_device *dev;
int i, err;

dev_dbg(&hw->pdev->dev, "restarting\n");

del_timer_sync(&hw->idle_timer);

rtnl_lock();
sky2_write32(hw, B0_IMSK, 0);
sky2_read32(hw, B0_IMSK);

netif_poll_disable(hw->dev[0]);

for (i = 0; i < hw->ports; i++) {
dev = hw->dev[i];
if (netif_running(dev))
sky2_down(dev);
}

sky2_reset(hw);
sky2_write32(hw, B0_IMSK, Y2_IS_BASE);
netif_poll_enable(hw->dev[0]);

for (i = 0; i < hw->ports; i++) {
dev = hw->dev[i];
if (netif_running(dev)) {
err = sky2_up(dev);
if (err) {
printk(KERN_INFO PFX "%s: could not restart %d\n",
dev->name, err);
dev_close(dev);
}
}
}

sky2_idle_start(hw);

rtnl_unlock();
}

static inline u8 sky2_wol_supported(const struct sky2_hw *hw)
{
return sky2_is_copper(hw) ? (WAKE_PHY | WAKE_MAGIC) : 0;
Expand Down Expand Up @@ -3613,6 +3642,8 @@ static int __devinit sky2_probe(struct pci_dev *pdev,
}

setup_timer(&hw->idle_timer, sky2_idle, (unsigned long) hw);
INIT_WORK(&hw->restart_work, sky2_restart);

sky2_idle_start(hw);

pci_set_drvdata(pdev, hw);
Expand Down Expand Up @@ -3649,6 +3680,8 @@ static void __devexit sky2_remove(struct pci_dev *pdev)

del_timer_sync(&hw->idle_timer);

flush_scheduled_work();

sky2_write32(hw, B0_IMSK, 0);
synchronize_irq(hw->pdev->irq);

Expand Down
1 change: 1 addition & 0 deletions drivers/net/sky2.h
Original file line number Diff line number Diff line change
Expand Up @@ -1933,6 +1933,7 @@ struct sky2_hw {
dma_addr_t st_dma;

struct timer_list idle_timer;
struct work_struct restart_work;
int msi;
wait_queue_head_t msi_wait;
};
Expand Down

0 comments on commit 8190679

Please sign in to comment.