diff options
Diffstat (limited to 'drivers/net/arcnet/arcnet.c')
| -rw-r--r-- | drivers/net/arcnet/arcnet.c | 66 | 
1 files changed, 62 insertions, 4 deletions
diff --git a/drivers/net/arcnet/arcnet.c b/drivers/net/arcnet/arcnet.c index e04efc0a5c97..d76dd7d14299 100644 --- a/drivers/net/arcnet/arcnet.c +++ b/drivers/net/arcnet/arcnet.c @@ -387,10 +387,44 @@ static void arcnet_timer(struct timer_list *t)  	struct arcnet_local *lp = from_timer(lp, t, timer);  	struct net_device *dev = lp->dev; -	if (!netif_carrier_ok(dev)) { +	spin_lock_irq(&lp->lock); + +	if (!lp->reset_in_progress && !netif_carrier_ok(dev)) {  		netif_carrier_on(dev);  		netdev_info(dev, "link up\n");  	} + +	spin_unlock_irq(&lp->lock); +} + +static void reset_device_work(struct work_struct *work) +{ +	struct arcnet_local *lp; +	struct net_device *dev; + +	lp = container_of(work, struct arcnet_local, reset_work); +	dev = lp->dev; + +	/* Do not bring the network interface back up if an ifdown +	 * was already done. +	 */ +	if (!netif_running(dev) || !lp->reset_in_progress) +		return; + +	rtnl_lock(); + +	/* Do another check, in case of an ifdown that was triggered in +	 * the small race window between the exit condition above and +	 * acquiring RTNL. +	 */ +	if (!netif_running(dev) || !lp->reset_in_progress) +		goto out; + +	dev_close(dev); +	dev_open(dev, NULL); + +out: +	rtnl_unlock();  }  static void arcnet_reply_tasklet(unsigned long data) @@ -452,12 +486,25 @@ struct net_device *alloc_arcdev(const char *name)  		lp->dev = dev;  		spin_lock_init(&lp->lock);  		timer_setup(&lp->timer, arcnet_timer, 0); +		INIT_WORK(&lp->reset_work, reset_device_work);  	}  	return dev;  }  EXPORT_SYMBOL(alloc_arcdev); +void free_arcdev(struct net_device *dev) +{ +	struct arcnet_local *lp = netdev_priv(dev); + +	/* Do not cancel this at ->ndo_close(), as the workqueue itself +	 * indirectly calls the ifdown path through dev_close(). +	 */ +	cancel_work_sync(&lp->reset_work); +	free_netdev(dev); +} +EXPORT_SYMBOL(free_arcdev); +  /* Open/initialize the board.  This is called sometime after booting when   * the 'ifconfig' program is run.   * @@ -587,6 +634,10 @@ int arcnet_close(struct net_device *dev)  	/* shut down the card */  	lp->hw.close(dev); + +	/* reset counters */ +	lp->reset_in_progress = 0; +  	module_put(lp->hw.owner);  	return 0;  } @@ -820,6 +871,9 @@ irqreturn_t arcnet_interrupt(int irq, void *dev_id)  	spin_lock_irqsave(&lp->lock, flags); +	if (lp->reset_in_progress) +		goto out; +  	/* RESET flag was enabled - if device is not running, we must  	 * clear it right away (but nothing else).  	 */ @@ -852,11 +906,14 @@ irqreturn_t arcnet_interrupt(int irq, void *dev_id)  		if (status & RESETflag) {  			arc_printk(D_NORMAL, dev, "spurious reset (status=%Xh)\n",  				   status); -			arcnet_close(dev); -			arcnet_open(dev); + +			lp->reset_in_progress = 1; +			netif_stop_queue(dev); +			netif_carrier_off(dev); +			schedule_work(&lp->reset_work);  			/* get out of the interrupt handler! */ -			break; +			goto out;  		}  		/* RX is inhibited - we must have received something.  		 * Prepare to receive into the next buffer. @@ -1052,6 +1109,7 @@ irqreturn_t arcnet_interrupt(int irq, void *dev_id)  	udelay(1);  	lp->hw.intmask(dev, lp->intmask); +out:  	spin_unlock_irqrestore(&lp->lock, flags);  	return retval;  }  | 
