aboutsummaryrefslogtreecommitdiffstats
path: root/net/wireless
diff options
context:
space:
mode:
authorJohannes Berg <johannes@sipsolutions.net>2009-08-12 16:21:21 -0400
committerJohn W. Linville <linville@tuxdriver.com>2009-08-14 09:14:07 -0400
commit36e6fea84905512ea776707e82b5b435220efc17 (patch)
treedb588e7dbbfcf0fa47f4954344a03961e960c898 /net/wireless
parent70bdb6b275d789ddf05c3a858e6b57715539394b (diff)
cfg80211: check for and abort dangling scan requests
If you trigger a scan request on an interface and then take it down, or rmmod the module or unplug the device the driver might "forget" to cancel the scan request. That is a bug in the driver, but the current behaviour is that we just hang endlessly waiting for the netdev refcount to become 0 which it never will. To improve robustness, check for this situation in cfg80211, warn about it and clean up behind the driver. I don't just clean up silently because it's likely that the driver also has some internal state it has now leaked. Additionally, this fixes a locking bug, clearing the scan_req pointer should be done under the rdev lock. Finally, we also need to _wait_ for the scan work and not just abort it since it might be pending and wanting to do a cleanup. Signed-off-by: Johannes Berg <johannes@sipsolutions.net> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/wireless')
-rw-r--r--net/wireless/core.c10
-rw-r--r--net/wireless/core.h1
-rw-r--r--net/wireless/scan.c26
3 files changed, 26 insertions, 11 deletions
diff --git a/net/wireless/core.c b/net/wireless/core.c
index e630648fef79..35d83bedfe5b 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -601,8 +601,8 @@ void wiphy_unregister(struct wiphy *wiphy)
601 601
602 mutex_unlock(&cfg80211_mutex); 602 mutex_unlock(&cfg80211_mutex);
603 603
604 flush_work(&rdev->scan_done_wk);
604 cancel_work_sync(&rdev->conn_work); 605 cancel_work_sync(&rdev->conn_work);
605 cancel_work_sync(&rdev->scan_done_wk);
606 kfree(rdev->scan_req); 606 kfree(rdev->scan_req);
607 flush_work(&rdev->event_work); 607 flush_work(&rdev->event_work);
608} 608}
@@ -728,6 +728,13 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb,
728#endif 728#endif
729 break; 729 break;
730 case NETDEV_UNREGISTER: 730 case NETDEV_UNREGISTER:
731 cfg80211_lock_rdev(rdev);
732
733 if (WARN_ON(rdev->scan_req && rdev->scan_req->dev == dev)) {
734 rdev->scan_req->aborted = true;
735 ___cfg80211_scan_done(rdev);
736 }
737
731 mutex_lock(&rdev->devlist_mtx); 738 mutex_lock(&rdev->devlist_mtx);
732 /* 739 /*
733 * It is possible to get NETDEV_UNREGISTER 740 * It is possible to get NETDEV_UNREGISTER
@@ -746,6 +753,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb,
746#endif 753#endif
747 } 754 }
748 mutex_unlock(&rdev->devlist_mtx); 755 mutex_unlock(&rdev->devlist_mtx);
756 cfg80211_unlock_rdev(rdev);
749 break; 757 break;
750 case NETDEV_PRE_UP: 758 case NETDEV_PRE_UP:
751 if (!(wdev->wiphy->interface_modes & BIT(wdev->iftype))) 759 if (!(wdev->wiphy->interface_modes & BIT(wdev->iftype)))
diff --git a/net/wireless/core.h b/net/wireless/core.h
index f7be3a9b427e..c603f5286326 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -368,6 +368,7 @@ void cfg80211_sme_scan_done(struct net_device *dev);
368void cfg80211_sme_rx_auth(struct net_device *dev, const u8 *buf, size_t len); 368void cfg80211_sme_rx_auth(struct net_device *dev, const u8 *buf, size_t len);
369void cfg80211_sme_disassoc(struct net_device *dev, int idx); 369void cfg80211_sme_disassoc(struct net_device *dev, int idx);
370void __cfg80211_scan_done(struct work_struct *wk); 370void __cfg80211_scan_done(struct work_struct *wk);
371void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev);
371void cfg80211_upload_connect_keys(struct wireless_dev *wdev); 372void cfg80211_upload_connect_keys(struct wireless_dev *wdev);
372 373
373struct ieee80211_channel * 374struct ieee80211_channel *
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index e6c1f11595da..fe575a24c95c 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -18,19 +18,14 @@
18 18
19#define IEEE80211_SCAN_RESULT_EXPIRE (15 * HZ) 19#define IEEE80211_SCAN_RESULT_EXPIRE (15 * HZ)
20 20
21void __cfg80211_scan_done(struct work_struct *wk) 21void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev)
22{ 22{
23 struct cfg80211_registered_device *rdev;
24 struct cfg80211_scan_request *request; 23 struct cfg80211_scan_request *request;
25 struct net_device *dev; 24 struct net_device *dev;
26#ifdef CONFIG_WIRELESS_EXT 25#ifdef CONFIG_WIRELESS_EXT
27 union iwreq_data wrqu; 26 union iwreq_data wrqu;
28#endif 27#endif
29 28
30 rdev = container_of(wk, struct cfg80211_registered_device,
31 scan_done_wk);
32
33 mutex_lock(&rdev->mtx);
34 request = rdev->scan_req; 29 request = rdev->scan_req;
35 30
36 dev = request->dev; 31 dev = request->dev;
@@ -43,9 +38,9 @@ void __cfg80211_scan_done(struct work_struct *wk)
43 cfg80211_sme_scan_done(dev); 38 cfg80211_sme_scan_done(dev);
44 39
45 if (request->aborted) 40 if (request->aborted)
46 nl80211_send_scan_aborted(wiphy_to_dev(request->wiphy), dev); 41 nl80211_send_scan_aborted(rdev, dev);
47 else 42 else
48 nl80211_send_scan_done(wiphy_to_dev(request->wiphy), dev); 43 nl80211_send_scan_done(rdev, dev);
49 44
50#ifdef CONFIG_WIRELESS_EXT 45#ifdef CONFIG_WIRELESS_EXT
51 if (!request->aborted) { 46 if (!request->aborted) {
@@ -57,11 +52,22 @@ void __cfg80211_scan_done(struct work_struct *wk)
57 52
58 dev_put(dev); 53 dev_put(dev);
59 54
60 cfg80211_unlock_rdev(rdev); 55 rdev->scan_req = NULL;
61 wiphy_to_dev(request->wiphy)->scan_req = NULL;
62 kfree(request); 56 kfree(request);
63} 57}
64 58
59void __cfg80211_scan_done(struct work_struct *wk)
60{
61 struct cfg80211_registered_device *rdev;
62
63 rdev = container_of(wk, struct cfg80211_registered_device,
64 scan_done_wk);
65
66 cfg80211_lock_rdev(rdev);
67 ___cfg80211_scan_done(rdev);
68 cfg80211_unlock_rdev(rdev);
69}
70
65void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted) 71void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted)
66{ 72{
67 WARN_ON(request != wiphy_to_dev(request->wiphy)->scan_req); 73 WARN_ON(request != wiphy_to_dev(request->wiphy)->scan_req);