cfg80211/mac80211: avoid bounce back mac->cfg->mac on sched_scan_stopped

When sched_scan_stopped was called by the driver, mac80211 calls
cfg80211, which in turn was calling mac80211 back with a flag
"driver_initiated".  This flag was used so that mac80211 would do the
necessary cleanup but would not call the driver.  This was enough to
prevent the bounce back between the driver and mac80211, but not
between mac80211 and cfg80211.

To fix this, we now do the cleanup in mac80211 before calling
cfg80211.  To help with locking issues, the workqueue was moved from
cfg80211 to mac80211.

Reported-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: Luciano Coelho <coelho@ti.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index ea44a8e..d20046b 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -902,8 +902,7 @@
 	return ret;
 }
 
-int ieee80211_request_sched_scan_stop(struct ieee80211_sub_if_data *sdata,
-				      bool driver_initiated)
+int ieee80211_request_sched_scan_stop(struct ieee80211_sub_if_data *sdata)
 {
 	struct ieee80211_local *local = sdata->local;
 	int ret = 0, i;
@@ -919,11 +918,9 @@
 		for (i = 0; i < IEEE80211_NUM_BANDS; i++)
 			kfree(local->sched_scan_ies.ie[i]);
 
-		if (!driver_initiated)
-			drv_sched_scan_stop(local, sdata);
+		drv_sched_scan_stop(local, sdata);
 		local->sched_scanning = false;
 	}
-
 out:
 	mutex_unlock(&sdata->local->mtx);
 
@@ -940,12 +937,36 @@
 }
 EXPORT_SYMBOL(ieee80211_sched_scan_results);
 
+void ieee80211_sched_scan_stopped_work(struct work_struct *work)
+{
+	struct ieee80211_local *local =
+		container_of(work, struct ieee80211_local,
+			     sched_scan_stopped_work);
+	int i;
+
+	mutex_lock(&local->mtx);
+
+	if (!local->sched_scanning) {
+		mutex_unlock(&local->mtx);
+		return;
+	}
+
+	for (i = 0; i < IEEE80211_NUM_BANDS; i++)
+		kfree(local->sched_scan_ies.ie[i]);
+
+	local->sched_scanning = false;
+
+	mutex_unlock(&local->mtx);
+
+	cfg80211_sched_scan_stopped(local->hw.wiphy);
+}
+
 void ieee80211_sched_scan_stopped(struct ieee80211_hw *hw)
 {
 	struct ieee80211_local *local = hw_to_local(hw);
 
 	trace_api_sched_scan_stopped(local);
 
-	cfg80211_sched_scan_stopped(hw->wiphy);
+	ieee80211_queue_work(&local->hw, &local->sched_scan_stopped_work);
 }
 EXPORT_SYMBOL(ieee80211_sched_scan_stopped);