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>
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index e6c1f11..fe575a2 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -18,19 +18,14 @@
 
 #define IEEE80211_SCAN_RESULT_EXPIRE	(15 * HZ)
 
-void __cfg80211_scan_done(struct work_struct *wk)
+void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev)
 {
-	struct cfg80211_registered_device *rdev;
 	struct cfg80211_scan_request *request;
 	struct net_device *dev;
 #ifdef CONFIG_WIRELESS_EXT
 	union iwreq_data wrqu;
 #endif
 
-	rdev = container_of(wk, struct cfg80211_registered_device,
-			    scan_done_wk);
-
-	mutex_lock(&rdev->mtx);
 	request = rdev->scan_req;
 
 	dev = request->dev;
@@ -43,9 +38,9 @@
 	cfg80211_sme_scan_done(dev);
 
 	if (request->aborted)
-		nl80211_send_scan_aborted(wiphy_to_dev(request->wiphy), dev);
+		nl80211_send_scan_aborted(rdev, dev);
 	else
-		nl80211_send_scan_done(wiphy_to_dev(request->wiphy), dev);
+		nl80211_send_scan_done(rdev, dev);
 
 #ifdef CONFIG_WIRELESS_EXT
 	if (!request->aborted) {
@@ -57,11 +52,22 @@
 
 	dev_put(dev);
 
-	cfg80211_unlock_rdev(rdev);
-	wiphy_to_dev(request->wiphy)->scan_req = NULL;
+	rdev->scan_req = NULL;
 	kfree(request);
 }
 
+void __cfg80211_scan_done(struct work_struct *wk)
+{
+	struct cfg80211_registered_device *rdev;
+
+	rdev = container_of(wk, struct cfg80211_registered_device,
+			    scan_done_wk);
+
+	cfg80211_lock_rdev(rdev);
+	___cfg80211_scan_done(rdev);
+	cfg80211_unlock_rdev(rdev);
+}
+
 void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted)
 {
 	WARN_ON(request != wiphy_to_dev(request->wiphy)->scan_req);