From a5b0287268e3bbf10049255a5f2427ba2c557b12 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Mon, 15 Dec 2003 14:52:31 -0500 Subject: [PATCH] [PATCH] Re: Deadlock in 3c574_cs.c (fwd) Patch looks fine to me, thanks. I've queued up the below. From: Ville Nuorvala 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(-) --- drivers/net/pcmcia/3c574_cs.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/net/pcmcia/3c574_cs.c b/drivers/net/pcmcia/3c574_cs.c index 756ce28013760..89a44c4c67381 100644 --- a/drivers/net/pcmcia/3c574_cs.c +++ b/drivers/net/pcmcia/3c574_cs.c @@ -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; - 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_device *dev) { 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_device *dev) 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_device *dev) 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 *dev) 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 *dev) /* 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--; -- 2.53.0