From df2de84e2958059d777f55358f1c7113a7f43a7b Mon Sep 17 00:00:00 2001 From: Andrew Gospodarek Date: Thu, 10 Apr 2008 16:14:34 -0400 Subject: [PATCH] tulip down race fix RHBZ 194502 not currently upstream --- drivers/net/tulip/interrupt.c | 8 ++++++++ drivers/net/tulip/media.c | 31 +++++++++++++++++++++++++++---- drivers/net/tulip/tulip.h | 3 ++- drivers/net/tulip/tulip_core.c | 39 ++++++++++++++++++++++++++++----------- 4 files changed, 65 insertions(+), 16 deletions(-) diff --git a/drivers/net/tulip/interrupt.c b/drivers/net/tulip/interrupt.c index 5a70319..135f7fb 100644 --- a/drivers/net/tulip/interrupt.c +++ b/drivers/net/tulip/interrupt.c @@ -529,6 +529,12 @@ irqreturn_t tulip_interrupt(int irq, void *dev_instance, struct pt_regs *regs) tp->nir++; + if (!down_read_trylock(&tp->shutdown)) { + /* Shutdown in progress, ack interrupts and return */ + outl(csr5 & 0x0001ff3f, ioaddr + CSR5); + return IRQ_HANDLED; + } + do { #ifdef CONFIG_TULIP_NAPI @@ -782,5 +788,7 @@ irqreturn_t tulip_interrupt(int irq, void *dev_instance, struct pt_regs *regs) printk(KERN_DEBUG "%s: exiting interrupt, csr5=%#4.4x.\n", dev->name, inl(ioaddr + CSR5)); + up_read(&tp->shutdown); + return IRQ_HANDLED; } diff --git a/drivers/net/tulip/media.c b/drivers/net/tulip/media.c index 79d08fe..a49929d 100644 --- a/drivers/net/tulip/media.c +++ b/drivers/net/tulip/media.c @@ -74,8 +74,20 @@ int tulip_mdio_read(struct net_device *dev, int phy_id, int location) inl(ioaddr + 0xA0); while (--i > 0) { barrier(); - if ( ! ((retval = inl(ioaddr + 0xA0)) & 0x80000000)) - break; + if ( ! ((retval = inl(ioaddr + 0xA0)) + & 0x80000000)) { + /* + * Possible bug in 82c168 rev 17 - + * sometimes bit 31 is unstable and + * clears before actually finished. + * Delay and check if bit 31 is still + * cleared before believing it. + */ + udelay(10); + if ( ! ((retval = inl(ioaddr + 0xA0)) + & 0x80000000)) + break; + } } spin_unlock_irqrestore(&tp->mii_lock, flags); return retval & 0xffff; @@ -134,8 +146,19 @@ void tulip_mdio_write(struct net_device *dev, int phy_id, int location, int val) outl(cmd, ioaddr + 0xA0); do { barrier(); - if ( ! (inl(ioaddr + 0xA0) & 0x80000000)) - break; + if ( ! (inl(ioaddr + 0xA0) & 0x80000000)) { + /* + * Possible bug in 82c168 rev 17 - + * sometimes bit 31 is unstable and + * clears before actually finished. + * Delay and check if bit 31 is still + * cleared before believing it. + */ + udelay(10); + if ( ! (inl(ioaddr + 0xA0) + & 0x80000000)) + break; + } } while (--i > 0); spin_unlock_irqrestore(&tp->mii_lock, flags); return; diff --git a/drivers/net/tulip/tulip.h b/drivers/net/tulip/tulip.h index 625ebba..24f6db5 100644 --- a/drivers/net/tulip/tulip.h +++ b/drivers/net/tulip/tulip.h @@ -384,7 +384,8 @@ struct tulip_private { unsigned long nir; unsigned long base_addr; int csr12_shadow; - int pad0; /* Used for 8-byte alignment */ + struct rw_semaphore shutdown; /* Hold as reader when enabling DMA + or interrupts */ }; diff --git a/drivers/net/tulip/tulip_core.c b/drivers/net/tulip/tulip_core.c index 6f0d3fd..4581ac8 100644 --- a/drivers/net/tulip/tulip_core.c +++ b/drivers/net/tulip/tulip_core.c @@ -728,31 +728,47 @@ static void tulip_down (struct net_device *dev) { long ioaddr = dev->base_addr; struct tulip_private *tp = netdev_priv(dev); - unsigned long flags; + + /* + * We have to be careful during shutdown not to race with + * other code that enables either DMA or interrupts - namely, + * timers, and the irq handler. + * + * del_timer_sync() takes care of timers. + * + * The irq handler is a bit harder. We grab a semaphore for + * writing, tp->shutdown, to prevent new irq handlers from + * starting. The interrupt handler must hold tp->shutdown as + * reader while it is re-enabling DMA or interrupts. + * + * We don't use the driver spinlock tp->lock here because by + * the time we touch anything that it protects, we are the + * only thread running. -VAL + */ + + /* Take the semaphore now; sometimes interrupts reschedule + * timers. */ + down_write(&tp->shutdown); del_timer_sync (&tp->timer); #ifdef CONFIG_TULIP_NAPI del_timer_sync (&tp->oom_timer); #endif - spin_lock_irqsave (&tp->lock, flags); + tulip_stop_rxtx(tp); /* stop DMA */ /* Disable interrupts by clearing the interrupt mask. */ outl (0x00000000, ioaddr + CSR7); - - /* Stop the Tx and Rx processes. */ - tulip_stop_rxtx(tp); - - /* prepare receive buffers */ + inl (ioaddr + CSR7); /* flush posted write */ + free_irq (dev->irq, dev); + /* Now we are the only thread running. */ + up_write(&tp->shutdown); + /* Put driver back into the state we start with */ tulip_refill_rx(dev); - - /* release any unconsumed transmit buffers */ tulip_clean_tx_ring(tp); if (inl (ioaddr + CSR6) != 0xffffffff) tp->stats.rx_missed_errors += inl (ioaddr + CSR8) & 0xffff; - spin_unlock_irqrestore (&tp->lock, flags); - init_timer(&tp->timer); tp->timer.data = (unsigned long)dev; tp->timer.function = tulip_tbl[tp->chip_id].media_timer; @@ -1396,6 +1412,7 @@ static int __devinit tulip_init_one (struct pci_dev *pdev, tp->csr0 = csr0; spin_lock_init(&tp->lock); spin_lock_init(&tp->mii_lock); + init_rwsem(&tp->shutdown); init_timer(&tp->timer); tp->timer.data = (unsigned long)dev; tp->timer.function = tulip_tbl[tp->chip_id].media_timer; -- 1.5.2.1