diff options
author | Francois Romieu <romieu@fr.zoreil.com> | 2007-02-15 17:37:21 -0500 |
---|---|---|
committer | Jeff Garzik <jeff@garzik.org> | 2007-02-20 11:18:12 -0500 |
commit | eb2a021c4710b98081daa797d5a729ac23c240cd (patch) | |
tree | 93f0d67ed4fef48fa324f33890bec242e35fbfe0 | |
parent | 1ca949299260aa49eeba34ff912e2321c8b1f647 (diff) |
r8169: RTNL and flush_scheduled_work deadlock
flush_scheduled_work() in net_device->close has a slight tendency
to deadlock with tasks on the workqueue that hold RTNL.
rtl8169_close/down simply need the recovery tasks to not meddle
with the hardware while the device is going down.
Signed-off-by: Francois Romieu <romieu@fr.zoreil.com>
Signed-off-by: Jeff Garzik <jeff@garzik.org>
-rw-r--r-- | drivers/net/r8169.c | 25 |
1 files changed, 18 insertions, 7 deletions
diff --git a/drivers/net/r8169.c b/drivers/net/r8169.c index 5598d86380b4..13cf06ee97f7 100644 --- a/drivers/net/r8169.c +++ b/drivers/net/r8169.c | |||
@@ -1733,6 +1733,8 @@ rtl8169_remove_one(struct pci_dev *pdev) | |||
1733 | assert(dev != NULL); | 1733 | assert(dev != NULL); |
1734 | assert(tp != NULL); | 1734 | assert(tp != NULL); |
1735 | 1735 | ||
1736 | flush_scheduled_work(); | ||
1737 | |||
1736 | unregister_netdev(dev); | 1738 | unregister_netdev(dev); |
1737 | rtl8169_release_board(pdev, dev, tp->mmio_addr); | 1739 | rtl8169_release_board(pdev, dev, tp->mmio_addr); |
1738 | pci_set_drvdata(pdev, NULL); | 1740 | pci_set_drvdata(pdev, NULL); |
@@ -2161,10 +2163,13 @@ static void rtl8169_reinit_task(struct work_struct *work) | |||
2161 | struct net_device *dev = tp->dev; | 2163 | struct net_device *dev = tp->dev; |
2162 | int ret; | 2164 | int ret; |
2163 | 2165 | ||
2164 | if (netif_running(dev)) { | 2166 | rtnl_lock(); |
2165 | rtl8169_wait_for_quiescence(dev); | 2167 | |
2166 | rtl8169_close(dev); | 2168 | if (!netif_running(dev)) |
2167 | } | 2169 | goto out_unlock; |
2170 | |||
2171 | rtl8169_wait_for_quiescence(dev); | ||
2172 | rtl8169_close(dev); | ||
2168 | 2173 | ||
2169 | ret = rtl8169_open(dev); | 2174 | ret = rtl8169_open(dev); |
2170 | if (unlikely(ret < 0)) { | 2175 | if (unlikely(ret < 0)) { |
@@ -2179,6 +2184,9 @@ static void rtl8169_reinit_task(struct work_struct *work) | |||
2179 | } | 2184 | } |
2180 | rtl8169_schedule_work(dev, rtl8169_reinit_task); | 2185 | rtl8169_schedule_work(dev, rtl8169_reinit_task); |
2181 | } | 2186 | } |
2187 | |||
2188 | out_unlock: | ||
2189 | rtnl_unlock(); | ||
2182 | } | 2190 | } |
2183 | 2191 | ||
2184 | static void rtl8169_reset_task(struct work_struct *work) | 2192 | static void rtl8169_reset_task(struct work_struct *work) |
@@ -2187,8 +2195,10 @@ static void rtl8169_reset_task(struct work_struct *work) | |||
2187 | container_of(work, struct rtl8169_private, task.work); | 2195 | container_of(work, struct rtl8169_private, task.work); |
2188 | struct net_device *dev = tp->dev; | 2196 | struct net_device *dev = tp->dev; |
2189 | 2197 | ||
2198 | rtnl_lock(); | ||
2199 | |||
2190 | if (!netif_running(dev)) | 2200 | if (!netif_running(dev)) |
2191 | return; | 2201 | goto out_unlock; |
2192 | 2202 | ||
2193 | rtl8169_wait_for_quiescence(dev); | 2203 | rtl8169_wait_for_quiescence(dev); |
2194 | 2204 | ||
@@ -2210,6 +2220,9 @@ static void rtl8169_reset_task(struct work_struct *work) | |||
2210 | } | 2220 | } |
2211 | rtl8169_schedule_work(dev, rtl8169_reset_task); | 2221 | rtl8169_schedule_work(dev, rtl8169_reset_task); |
2212 | } | 2222 | } |
2223 | |||
2224 | out_unlock: | ||
2225 | rtnl_unlock(); | ||
2213 | } | 2226 | } |
2214 | 2227 | ||
2215 | static void rtl8169_tx_timeout(struct net_device *dev) | 2228 | static void rtl8169_tx_timeout(struct net_device *dev) |
@@ -2722,8 +2735,6 @@ static void rtl8169_down(struct net_device *dev) | |||
2722 | 2735 | ||
2723 | netif_stop_queue(dev); | 2736 | netif_stop_queue(dev); |
2724 | 2737 | ||
2725 | flush_scheduled_work(); | ||
2726 | |||
2727 | core_down: | 2738 | core_down: |
2728 | spin_lock_irq(&tp->lock); | 2739 | spin_lock_irq(&tp->lock); |
2729 | 2740 | ||