diff options
author | Francois Romieu <romieu@fr.zoreil.com> | 2007-02-15 17:37:44 -0500 |
---|---|---|
committer | Jeff Garzik <jeff@garzik.org> | 2007-02-20 11:18:12 -0500 |
commit | 83cbb4d2577174e27a91e63a47a2a27c3af50d4e (patch) | |
tree | 115297292e852d352ca21b3ce66fd2f46e4d9bd6 /drivers/net/8139too.c | |
parent | c014f6c8f870271a8dcfe6e4139d6a651633aaf4 (diff) |
8139too: RTNL and flush_scheduled_work deadlock
Your usual dont-flush_scheduled_work-with-RTNL-held stuff.
It is a bit different here since the thread runs permanently
or is only occasionally kicked for recovery depending on the
hardware revision.
Signed-off-by: Francois Romieu <romieu@fr.zoreil.com>
Signed-off-by: Jeff Garzik <jeff@garzik.org>
Diffstat (limited to 'drivers/net/8139too.c')
-rw-r--r-- | drivers/net/8139too.c | 40 |
1 files changed, 17 insertions, 23 deletions
diff --git a/drivers/net/8139too.c b/drivers/net/8139too.c index 35ad5cff18e6..99304b2aa86e 100644 --- a/drivers/net/8139too.c +++ b/drivers/net/8139too.c | |||
@@ -1109,6 +1109,8 @@ static void __devexit rtl8139_remove_one (struct pci_dev *pdev) | |||
1109 | 1109 | ||
1110 | assert (dev != NULL); | 1110 | assert (dev != NULL); |
1111 | 1111 | ||
1112 | flush_scheduled_work(); | ||
1113 | |||
1112 | unregister_netdev (dev); | 1114 | unregister_netdev (dev); |
1113 | 1115 | ||
1114 | __rtl8139_cleanup_dev (dev); | 1116 | __rtl8139_cleanup_dev (dev); |
@@ -1603,18 +1605,21 @@ static void rtl8139_thread (struct work_struct *work) | |||
1603 | struct net_device *dev = tp->mii.dev; | 1605 | struct net_device *dev = tp->mii.dev; |
1604 | unsigned long thr_delay = next_tick; | 1606 | unsigned long thr_delay = next_tick; |
1605 | 1607 | ||
1608 | rtnl_lock(); | ||
1609 | |||
1610 | if (!netif_running(dev)) | ||
1611 | goto out_unlock; | ||
1612 | |||
1606 | if (tp->watchdog_fired) { | 1613 | if (tp->watchdog_fired) { |
1607 | tp->watchdog_fired = 0; | 1614 | tp->watchdog_fired = 0; |
1608 | rtl8139_tx_timeout_task(work); | 1615 | rtl8139_tx_timeout_task(work); |
1609 | } else if (rtnl_trylock()) { | 1616 | } else |
1610 | rtl8139_thread_iter (dev, tp, tp->mmio_addr); | 1617 | rtl8139_thread_iter(dev, tp, tp->mmio_addr); |
1611 | rtnl_unlock (); | ||
1612 | } else { | ||
1613 | /* unlikely race. mitigate with fast poll. */ | ||
1614 | thr_delay = HZ / 2; | ||
1615 | } | ||
1616 | 1618 | ||
1617 | schedule_delayed_work(&tp->thread, thr_delay); | 1619 | if (tp->have_thread) |
1620 | schedule_delayed_work(&tp->thread, thr_delay); | ||
1621 | out_unlock: | ||
1622 | rtnl_unlock (); | ||
1618 | } | 1623 | } |
1619 | 1624 | ||
1620 | static void rtl8139_start_thread(struct rtl8139_private *tp) | 1625 | static void rtl8139_start_thread(struct rtl8139_private *tp) |
@@ -1626,19 +1631,11 @@ static void rtl8139_start_thread(struct rtl8139_private *tp) | |||
1626 | return; | 1631 | return; |
1627 | 1632 | ||
1628 | tp->have_thread = 1; | 1633 | tp->have_thread = 1; |
1634 | tp->watchdog_fired = 0; | ||
1629 | 1635 | ||
1630 | schedule_delayed_work(&tp->thread, next_tick); | 1636 | schedule_delayed_work(&tp->thread, next_tick); |
1631 | } | 1637 | } |
1632 | 1638 | ||
1633 | static void rtl8139_stop_thread(struct rtl8139_private *tp) | ||
1634 | { | ||
1635 | if (tp->have_thread) { | ||
1636 | cancel_rearming_delayed_work(&tp->thread); | ||
1637 | tp->have_thread = 0; | ||
1638 | } else | ||
1639 | flush_scheduled_work(); | ||
1640 | } | ||
1641 | |||
1642 | static inline void rtl8139_tx_clear (struct rtl8139_private *tp) | 1639 | static inline void rtl8139_tx_clear (struct rtl8139_private *tp) |
1643 | { | 1640 | { |
1644 | tp->cur_tx = 0; | 1641 | tp->cur_tx = 0; |
@@ -1696,12 +1693,11 @@ static void rtl8139_tx_timeout (struct net_device *dev) | |||
1696 | { | 1693 | { |
1697 | struct rtl8139_private *tp = netdev_priv(dev); | 1694 | struct rtl8139_private *tp = netdev_priv(dev); |
1698 | 1695 | ||
1696 | tp->watchdog_fired = 1; | ||
1699 | if (!tp->have_thread) { | 1697 | if (!tp->have_thread) { |
1700 | INIT_DELAYED_WORK(&tp->thread, rtl8139_tx_timeout_task); | 1698 | INIT_DELAYED_WORK(&tp->thread, rtl8139_thread); |
1701 | schedule_delayed_work(&tp->thread, next_tick); | 1699 | schedule_delayed_work(&tp->thread, next_tick); |
1702 | } else | 1700 | } |
1703 | tp->watchdog_fired = 1; | ||
1704 | |||
1705 | } | 1701 | } |
1706 | 1702 | ||
1707 | static int rtl8139_start_xmit (struct sk_buff *skb, struct net_device *dev) | 1703 | static int rtl8139_start_xmit (struct sk_buff *skb, struct net_device *dev) |
@@ -2233,8 +2229,6 @@ static int rtl8139_close (struct net_device *dev) | |||
2233 | 2229 | ||
2234 | netif_stop_queue (dev); | 2230 | netif_stop_queue (dev); |
2235 | 2231 | ||
2236 | rtl8139_stop_thread(tp); | ||
2237 | |||
2238 | if (netif_msg_ifdown(tp)) | 2232 | if (netif_msg_ifdown(tp)) |
2239 | printk(KERN_DEBUG "%s: Shutting down ethercard, status was 0x%4.4x.\n", | 2233 | printk(KERN_DEBUG "%s: Shutting down ethercard, status was 0x%4.4x.\n", |
2240 | dev->name, RTL_R16 (IntrStatus)); | 2234 | dev->name, RTL_R16 (IntrStatus)); |