| .. | .. |
|---|
| 387 | 387 | struct arcnet_local *lp = from_timer(lp, t, timer); |
|---|
| 388 | 388 | struct net_device *dev = lp->dev; |
|---|
| 389 | 389 | |
|---|
| 390 | | - if (!netif_carrier_ok(dev)) { |
|---|
| 390 | + spin_lock_irq(&lp->lock); |
|---|
| 391 | + |
|---|
| 392 | + if (!lp->reset_in_progress && !netif_carrier_ok(dev)) { |
|---|
| 391 | 393 | netif_carrier_on(dev); |
|---|
| 392 | 394 | netdev_info(dev, "link up\n"); |
|---|
| 393 | 395 | } |
|---|
| 396 | + |
|---|
| 397 | + spin_unlock_irq(&lp->lock); |
|---|
| 398 | +} |
|---|
| 399 | + |
|---|
| 400 | +static void reset_device_work(struct work_struct *work) |
|---|
| 401 | +{ |
|---|
| 402 | + struct arcnet_local *lp; |
|---|
| 403 | + struct net_device *dev; |
|---|
| 404 | + |
|---|
| 405 | + lp = container_of(work, struct arcnet_local, reset_work); |
|---|
| 406 | + dev = lp->dev; |
|---|
| 407 | + |
|---|
| 408 | + /* Do not bring the network interface back up if an ifdown |
|---|
| 409 | + * was already done. |
|---|
| 410 | + */ |
|---|
| 411 | + if (!netif_running(dev) || !lp->reset_in_progress) |
|---|
| 412 | + return; |
|---|
| 413 | + |
|---|
| 414 | + rtnl_lock(); |
|---|
| 415 | + |
|---|
| 416 | + /* Do another check, in case of an ifdown that was triggered in |
|---|
| 417 | + * the small race window between the exit condition above and |
|---|
| 418 | + * acquiring RTNL. |
|---|
| 419 | + */ |
|---|
| 420 | + if (!netif_running(dev) || !lp->reset_in_progress) |
|---|
| 421 | + goto out; |
|---|
| 422 | + |
|---|
| 423 | + dev_close(dev); |
|---|
| 424 | + dev_open(dev, NULL); |
|---|
| 425 | + |
|---|
| 426 | +out: |
|---|
| 427 | + rtnl_unlock(); |
|---|
| 394 | 428 | } |
|---|
| 395 | 429 | |
|---|
| 396 | 430 | static void arcnet_reply_tasklet(unsigned long data) |
|---|
| .. | .. |
|---|
| 452 | 486 | lp->dev = dev; |
|---|
| 453 | 487 | spin_lock_init(&lp->lock); |
|---|
| 454 | 488 | timer_setup(&lp->timer, arcnet_timer, 0); |
|---|
| 489 | + INIT_WORK(&lp->reset_work, reset_device_work); |
|---|
| 455 | 490 | } |
|---|
| 456 | 491 | |
|---|
| 457 | 492 | return dev; |
|---|
| 458 | 493 | } |
|---|
| 459 | 494 | EXPORT_SYMBOL(alloc_arcdev); |
|---|
| 495 | + |
|---|
| 496 | +void free_arcdev(struct net_device *dev) |
|---|
| 497 | +{ |
|---|
| 498 | + struct arcnet_local *lp = netdev_priv(dev); |
|---|
| 499 | + |
|---|
| 500 | + /* Do not cancel this at ->ndo_close(), as the workqueue itself |
|---|
| 501 | + * indirectly calls the ifdown path through dev_close(). |
|---|
| 502 | + */ |
|---|
| 503 | + cancel_work_sync(&lp->reset_work); |
|---|
| 504 | + free_netdev(dev); |
|---|
| 505 | +} |
|---|
| 506 | +EXPORT_SYMBOL(free_arcdev); |
|---|
| 460 | 507 | |
|---|
| 461 | 508 | /* Open/initialize the board. This is called sometime after booting when |
|---|
| 462 | 509 | * the 'ifconfig' program is run. |
|---|
| .. | .. |
|---|
| 587 | 634 | |
|---|
| 588 | 635 | /* shut down the card */ |
|---|
| 589 | 636 | lp->hw.close(dev); |
|---|
| 637 | + |
|---|
| 638 | + /* reset counters */ |
|---|
| 639 | + lp->reset_in_progress = 0; |
|---|
| 640 | + |
|---|
| 590 | 641 | module_put(lp->hw.owner); |
|---|
| 591 | 642 | return 0; |
|---|
| 592 | 643 | } |
|---|
| .. | .. |
|---|
| 763 | 814 | } |
|---|
| 764 | 815 | |
|---|
| 765 | 816 | /* Called by the kernel when transmit times out */ |
|---|
| 766 | | -void arcnet_timeout(struct net_device *dev) |
|---|
| 817 | +void arcnet_timeout(struct net_device *dev, unsigned int txqueue) |
|---|
| 767 | 818 | { |
|---|
| 768 | 819 | unsigned long flags; |
|---|
| 769 | 820 | struct arcnet_local *lp = netdev_priv(dev); |
|---|
| .. | .. |
|---|
| 820 | 871 | |
|---|
| 821 | 872 | spin_lock_irqsave(&lp->lock, flags); |
|---|
| 822 | 873 | |
|---|
| 874 | + if (lp->reset_in_progress) |
|---|
| 875 | + goto out; |
|---|
| 876 | + |
|---|
| 823 | 877 | /* RESET flag was enabled - if device is not running, we must |
|---|
| 824 | 878 | * clear it right away (but nothing else). |
|---|
| 825 | 879 | */ |
|---|
| .. | .. |
|---|
| 852 | 906 | if (status & RESETflag) { |
|---|
| 853 | 907 | arc_printk(D_NORMAL, dev, "spurious reset (status=%Xh)\n", |
|---|
| 854 | 908 | status); |
|---|
| 855 | | - arcnet_close(dev); |
|---|
| 856 | | - arcnet_open(dev); |
|---|
| 909 | + |
|---|
| 910 | + lp->reset_in_progress = 1; |
|---|
| 911 | + netif_stop_queue(dev); |
|---|
| 912 | + netif_carrier_off(dev); |
|---|
| 913 | + schedule_work(&lp->reset_work); |
|---|
| 857 | 914 | |
|---|
| 858 | 915 | /* get out of the interrupt handler! */ |
|---|
| 859 | | - break; |
|---|
| 916 | + goto out; |
|---|
| 860 | 917 | } |
|---|
| 861 | 918 | /* RX is inhibited - we must have received something. |
|---|
| 862 | 919 | * Prepare to receive into the next buffer. |
|---|
| .. | .. |
|---|
| 1052 | 1109 | udelay(1); |
|---|
| 1053 | 1110 | lp->hw.intmask(dev, lp->intmask); |
|---|
| 1054 | 1111 | |
|---|
| 1112 | +out: |
|---|
| 1055 | 1113 | spin_unlock_irqrestore(&lp->lock, flags); |
|---|
| 1056 | 1114 | return retval; |
|---|
| 1057 | 1115 | } |
|---|