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(-)

diff -puN drivers/net/pcmcia/3c574_cs.c~3c574_cs-deadlock-fix drivers/net/pcmcia/3c574_cs.c
--- 25/drivers/net/pcmcia/3c574_cs.c~3c574_cs-deadlock-fix	2003-12-14 19:47:54.000000000 -0800
+++ 25-akpm/drivers/net/pcmcia/3c574_cs.c	2003-12-14 19:48:48.000000000 -0800
@@ -1092,8 +1092,12 @@ static struct net_device_stats *el3_get_
 {
 	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);
+		spin_unlock_irqrestore(&lp->window_lock, flags);
+	}
 	return &lp->stats;
 }
 
@@ -1105,7 +1109,6 @@ static void update_stats(struct net_devi
 {
 	struct el3_private *lp = (struct el3_private *)dev->priv;
 	ioaddr_t ioaddr = dev->base_addr;
-	unsigned long flags;
 	u8 rx, tx, up;
 
 	DEBUG(2, "%s: updating the statistics.\n", dev->name);
@@ -1113,8 +1116,6 @@ static void update_stats(struct net_devi
 	if (inw(ioaddr+EL3_STATUS) == 0xffff) /* No card. */
 		return;
 		
-	spin_lock_irqsave(&lp->window_lock, flags);
-
 	/* Unlike the 3c509 we need not turn off stats updates while reading. */
 	/* Switch to the stats window, and read everything. */
 	EL3WINDOW(6);
@@ -1139,7 +1140,6 @@ static void update_stats(struct net_devi
 	lp->stats.tx_bytes 			+= tx + ((up & 0xf0) << 12);
 
 	EL3WINDOW(1);
-	spin_unlock_irqrestore(&lp->window_lock, flags);
 }
 
 static int el3_rx(struct net_device *dev, int worklimit)
@@ -1281,6 +1281,8 @@ static int el3_close(struct net_device *
 	DEBUG(2, "%s: shutting down ethercard.\n", dev->name);
 	
 	if (DEV_OK(link)) {
+		unsigned long flags;
+
 		/* Turn off statistics ASAP.  We update lp->stats below. */
 		outw(StatsDisable, ioaddr + EL3_CMD);
 		
@@ -1290,8 +1292,9 @@ static int el3_close(struct net_device *
 		
 		/* Note: Switching to window 0 may disable the IRQ. */
 		EL3WINDOW(0);
-		
+		spin_lock_irqsave(&lp->window_lock, flags);
 		update_stats(dev);
+		spin_unlock_irqrestore(&lp->window_lock, flags);
 	}
 
 	link->open--;

_