summaryrefslogtreecommitdiffstats
path: root/net/wireless
diff options
context:
space:
mode:
authorJohannes Berg <johannes@sipsolutions.net>2009-08-17 12:25:37 +0200
committerJohn W. Linville <linville@tuxdriver.com>2009-08-20 11:35:56 -0400
commit0ff6ce7b36199f67f709c750e9a2a66659a4babe (patch)
tree3b023b960e010b7ce84d35d8e9b516f5cb285f77 /net/wireless
parent96909e97716de1d86e6e24d6aabce09980372771 (diff)
downloadlinux-0ff6ce7b36199f67f709c750e9a2a66659a4babe.tar.bz2
cfg80211: fix deadlock
When removing an interface with nl80211, cfg80211 will deadlock in the netdev notifier because we're already holding rdev->mtx and try to acquire it again to verify the scan has been done. This bug was introduced by my patch "cfg80211: check for and abort dangling scan requests". To fix this, move the dangling scan request check into wiphy_unregister(). This will not be able to catch all cases right away, but if the scan problem happens with a manual ifdown or so it will be possible to remedy it by removing the module/device. Additionally, add comments about the deadlock scenario. Reported-by: Christian Lamparter <chunkeey@web.de> Signed-off-by: Johannes Berg <johannes@sipsolutions.net> Tested-by: Christian Lamparter <chunkeey@web.de> Tested-by: Kalle Valo <kalle.valo@iki.fi> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/wireless')
-rw-r--r--net/wireless/core.c30
1 files changed, 18 insertions, 12 deletions
diff --git a/net/wireless/core.c b/net/wireless/core.c
index bc99e4ec7463..69a185ba9ff1 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -586,9 +586,14 @@ void wiphy_unregister(struct wiphy *wiphy)
* get to lock contention here if userspace issues a command
* that identified the hardware by wiphy index.
*/
- mutex_lock(&rdev->mtx);
- /* unlock again before freeing */
- mutex_unlock(&rdev->mtx);
+ cfg80211_lock_rdev(rdev);
+
+ if (WARN_ON(rdev->scan_req)) {
+ rdev->scan_req->aborted = true;
+ ___cfg80211_scan_done(rdev);
+ }
+
+ cfg80211_unlock_rdev(rdev);
cfg80211_debugfs_rdev_del(rdev);
@@ -605,7 +610,6 @@ void wiphy_unregister(struct wiphy *wiphy)
flush_work(&rdev->scan_done_wk);
cancel_work_sync(&rdev->conn_work);
- kfree(rdev->scan_req);
flush_work(&rdev->event_work);
}
EXPORT_SYMBOL(wiphy_unregister);
@@ -653,6 +657,11 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb,
switch (state) {
case NETDEV_REGISTER:
+ /*
+ * NB: cannot take rdev->mtx here because this may be
+ * called within code protected by it when interfaces
+ * are added with nl80211.
+ */
mutex_init(&wdev->mtx);
INIT_LIST_HEAD(&wdev->event_list);
spin_lock_init(&wdev->event_lock);
@@ -730,13 +739,11 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb,
#endif
break;
case NETDEV_UNREGISTER:
- cfg80211_lock_rdev(rdev);
-
- if (WARN_ON(rdev->scan_req && rdev->scan_req->dev == dev)) {
- rdev->scan_req->aborted = true;
- ___cfg80211_scan_done(rdev);
- }
-
+ /*
+ * NB: cannot take rdev->mtx here because this may be
+ * called within code protected by it when interfaces
+ * are removed with nl80211.
+ */
mutex_lock(&rdev->devlist_mtx);
/*
* It is possible to get NETDEV_UNREGISTER
@@ -755,7 +762,6 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb,
#endif
}
mutex_unlock(&rdev->devlist_mtx);
- cfg80211_unlock_rdev(rdev);
break;
case NETDEV_PRE_UP:
if (!(wdev->wiphy->interface_modes & BIT(wdev->iftype)))