Skip to content

Commit

Permalink
korina: fix deadlock on RX FIFO overrun
Browse files Browse the repository at this point in the history
By calling korina_restart(), the IRQ handler tries to disable the
interrupt it's currently serving. This leads to a deadlock since
disable_irq() waits for any running IRQ handlers to finish before
returning. This patch addresses the issue by turning korina_restart()
into a workqueue task, which is then scheduled when needed.

Reproducing the deadlock is easily done using e.g. GNU netcat to send
large amounts of UDP data to the host running this driver.

Note that the same problem (and fix) applies to TX FIFO underruns, but
apparently these are less easy to trigger.

Signed-off-by: Phil Sutter <phil@nwl.cc>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Phil Sutter authored and David S. Miller committed Jun 2, 2010
1 parent 2df4a0f commit ceb3d23
Showing 1 changed file with 13 additions and 14 deletions.
27 changes: 13 additions & 14 deletions drivers/net/korina.c
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ struct korina_private {
struct napi_struct napi;
struct timer_list media_check_timer;
struct mii_if_info mii_if;
struct work_struct restart_task;
struct net_device *dev;
int phy_addr;
};
Expand Down Expand Up @@ -890,12 +891,12 @@ static int korina_init(struct net_device *dev)

/*
* Restart the RC32434 ethernet controller.
* FIXME: check the return status where we call it
*/
static int korina_restart(struct net_device *dev)
static void korina_restart_task(struct work_struct *work)
{
struct korina_private *lp = netdev_priv(dev);
int ret;
struct korina_private *lp = container_of(work,
struct korina_private, restart_task);
struct net_device *dev = lp->dev;

/*
* Disable interrupts
Expand All @@ -916,19 +917,16 @@ static int korina_restart(struct net_device *dev)

napi_disable(&lp->napi);

ret = korina_init(dev);
if (ret < 0) {
if (korina_init(dev) < 0) {
printk(KERN_ERR "%s: cannot restart device\n", dev->name);
return ret;
return;
}
korina_multicast_list(dev);

enable_irq(lp->und_irq);
enable_irq(lp->ovr_irq);
enable_irq(lp->tx_irq);
enable_irq(lp->rx_irq);

return ret;
}

static void korina_clear_and_restart(struct net_device *dev, u32 value)
Expand All @@ -937,7 +935,7 @@ static void korina_clear_and_restart(struct net_device *dev, u32 value)

netif_stop_queue(dev);
writel(value, &lp->eth_regs->ethintfc);
korina_restart(dev);
schedule_work(&lp->restart_task);
}

/* Ethernet Tx Underflow interrupt */
Expand All @@ -962,11 +960,8 @@ static irqreturn_t korina_und_interrupt(int irq, void *dev_id)
static void korina_tx_timeout(struct net_device *dev)
{
struct korina_private *lp = netdev_priv(dev);
unsigned long flags;

spin_lock_irqsave(&lp->lock, flags);
korina_restart(dev);
spin_unlock_irqrestore(&lp->lock, flags);
schedule_work(&lp->restart_task);
}

/* Ethernet Rx Overflow interrupt */
Expand Down Expand Up @@ -1086,6 +1081,8 @@ static int korina_close(struct net_device *dev)

napi_disable(&lp->napi);

cancel_work_sync(&lp->restart_task);

free_irq(lp->rx_irq, dev);
free_irq(lp->tx_irq, dev);
free_irq(lp->ovr_irq, dev);
Expand Down Expand Up @@ -1198,6 +1195,8 @@ static int korina_probe(struct platform_device *pdev)
}
setup_timer(&lp->media_check_timer, korina_poll_media, (unsigned long) dev);

INIT_WORK(&lp->restart_task, korina_restart_task);

printk(KERN_INFO "%s: " DRV_NAME "-" DRV_VERSION " " DRV_RELDATE "\n",
dev->name);
out:
Expand Down

0 comments on commit ceb3d23

Please sign in to comment.