Commit a5b02872 authored by Andrew Morton's avatar Andrew Morton Committed by Jeff Garzik

[PATCH] Re: Deadlock in 3c574_cs.c (fwd)

Patch looks fine to me, thanks.   I've queued up the below.


From: Ville Nuorvala <vnuorval@tcs.hut.fi>

I've experienced random lockups witch become almost certain under heavy
loads, like when doing ping6 -f. The culprit seems to be the 3c574_cs
driver, which locks lp->window_lock twice when calling update_stats() from
el3_interrupt().



 drivers/net/pcmcia/3c574_cs.c |   15 +++++++++------
 1 files changed, 9 insertions(+), 6 deletions(-)
parent 39ceafe3
...@@ -1092,8 +1092,12 @@ static struct net_device_stats *el3_get_stats(struct net_device *dev) ...@@ -1092,8 +1092,12 @@ static struct net_device_stats *el3_get_stats(struct net_device *dev)
{ {
struct el3_private *lp = (struct el3_private *)dev->priv; struct el3_private *lp = (struct el3_private *)dev->priv;
if (netif_device_present(dev)) if (netif_device_present(dev)) {
unsigned long flags;
spin_lock_irqsave(&lp->window_lock, flags);
update_stats(dev); update_stats(dev);
spin_unlock_irqrestore(&lp->window_lock, flags);
}
return &lp->stats; return &lp->stats;
} }
...@@ -1105,7 +1109,6 @@ static void update_stats(struct net_device *dev) ...@@ -1105,7 +1109,6 @@ static void update_stats(struct net_device *dev)
{ {
struct el3_private *lp = (struct el3_private *)dev->priv; struct el3_private *lp = (struct el3_private *)dev->priv;
ioaddr_t ioaddr = dev->base_addr; ioaddr_t ioaddr = dev->base_addr;
unsigned long flags;
u8 rx, tx, up; u8 rx, tx, up;
DEBUG(2, "%s: updating the statistics.\n", dev->name); DEBUG(2, "%s: updating the statistics.\n", dev->name);
...@@ -1113,8 +1116,6 @@ static void update_stats(struct net_device *dev) ...@@ -1113,8 +1116,6 @@ static void update_stats(struct net_device *dev)
if (inw(ioaddr+EL3_STATUS) == 0xffff) /* No card. */ if (inw(ioaddr+EL3_STATUS) == 0xffff) /* No card. */
return; return;
spin_lock_irqsave(&lp->window_lock, flags);
/* Unlike the 3c509 we need not turn off stats updates while reading. */ /* Unlike the 3c509 we need not turn off stats updates while reading. */
/* Switch to the stats window, and read everything. */ /* Switch to the stats window, and read everything. */
EL3WINDOW(6); EL3WINDOW(6);
...@@ -1139,7 +1140,6 @@ static void update_stats(struct net_device *dev) ...@@ -1139,7 +1140,6 @@ static void update_stats(struct net_device *dev)
lp->stats.tx_bytes += tx + ((up & 0xf0) << 12); lp->stats.tx_bytes += tx + ((up & 0xf0) << 12);
EL3WINDOW(1); EL3WINDOW(1);
spin_unlock_irqrestore(&lp->window_lock, flags);
} }
static int el3_rx(struct net_device *dev, int worklimit) static int el3_rx(struct net_device *dev, int worklimit)
...@@ -1281,6 +1281,8 @@ static int el3_close(struct net_device *dev) ...@@ -1281,6 +1281,8 @@ static int el3_close(struct net_device *dev)
DEBUG(2, "%s: shutting down ethercard.\n", dev->name); DEBUG(2, "%s: shutting down ethercard.\n", dev->name);
if (DEV_OK(link)) { if (DEV_OK(link)) {
unsigned long flags;
/* Turn off statistics ASAP. We update lp->stats below. */ /* Turn off statistics ASAP. We update lp->stats below. */
outw(StatsDisable, ioaddr + EL3_CMD); outw(StatsDisable, ioaddr + EL3_CMD);
...@@ -1290,8 +1292,9 @@ static int el3_close(struct net_device *dev) ...@@ -1290,8 +1292,9 @@ static int el3_close(struct net_device *dev)
/* Note: Switching to window 0 may disable the IRQ. */ /* Note: Switching to window 0 may disable the IRQ. */
EL3WINDOW(0); EL3WINDOW(0);
spin_lock_irqsave(&lp->window_lock, flags);
update_stats(dev); update_stats(dev);
spin_unlock_irqrestore(&lp->window_lock, flags);
} }
link->open--; link->open--;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment