diff options
author | Johannes Berg <johannes@sipsolutions.net> | 2009-08-12 16:21:21 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2009-08-14 09:14:07 -0400 |
commit | 36e6fea84905512ea776707e82b5b435220efc17 (patch) | |
tree | db588e7dbbfcf0fa47f4954344a03961e960c898 /net | |
parent | 70bdb6b275d789ddf05c3a858e6b57715539394b (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')
-rw-r--r-- | net/wireless/core.c | 10 | ||||
-rw-r--r-- | net/wireless/core.h | 1 | ||||
-rw-r--r-- | net/wireless/scan.c | 26 |
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); | |||
368 | void cfg80211_sme_rx_auth(struct net_device *dev, const u8 *buf, size_t len); | 368 | void cfg80211_sme_rx_auth(struct net_device *dev, const u8 *buf, size_t len); |
369 | void cfg80211_sme_disassoc(struct net_device *dev, int idx); | 369 | void cfg80211_sme_disassoc(struct net_device *dev, int idx); |
370 | void __cfg80211_scan_done(struct work_struct *wk); | 370 | void __cfg80211_scan_done(struct work_struct *wk); |
371 | void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev); | ||
371 | void cfg80211_upload_connect_keys(struct wireless_dev *wdev); | 372 | void cfg80211_upload_connect_keys(struct wireless_dev *wdev); |
372 | 373 | ||
373 | struct ieee80211_channel * | 374 | struct 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 | ||
21 | void __cfg80211_scan_done(struct work_struct *wk) | 21 | void ___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 | ||
59 | void __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 | |||
65 | void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted) | 71 | void 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); |