mac80211: fix recalc_radar hwconf sync problem

local->hw.conf maybe not be synced when recalcing whether radar is
enabled, sometimes leaving radar enabled even if it's not neccesary
anymore.

Fix this by:
 * setting radar_enabled when creating the chanctx
 * turning radar_enabled off before destroying the last channel context

Reported-by: Zefir Kurtisi <zefir.kurtisi@neratec.com>
Signed-off-by: Simon Wunderlich <siwu@hrz.tu-chemnitz.de>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
index 8024874..166165e 100644
--- a/net/mac80211/chan.c
+++ b/net/mac80211/chan.c
@@ -57,6 +57,22 @@
 	return NULL;
 }
 
+static bool ieee80211_is_radar_required(struct ieee80211_local *local)
+{
+	struct ieee80211_sub_if_data *sdata;
+
+	rcu_read_lock();
+	list_for_each_entry_rcu(sdata, &local->interfaces, list) {
+		if (sdata->radar_required) {
+			rcu_read_unlock();
+			return true;
+		}
+	}
+	rcu_read_unlock();
+
+	return false;
+}
+
 static struct ieee80211_chanctx *
 ieee80211_new_chanctx(struct ieee80211_local *local,
 		      const struct cfg80211_chan_def *chandef,
@@ -75,6 +91,9 @@
 	ctx->conf.rx_chains_static = 1;
 	ctx->conf.rx_chains_dynamic = 1;
 	ctx->mode = mode;
+	ctx->conf.radar_enabled = ieee80211_is_radar_required(local);
+	if (!local->use_chanctx)
+		local->hw.conf.radar_enabled = ctx->conf.radar_enabled;
 
 	if (!local->use_chanctx) {
 		local->_oper_chandef = *chandef;
@@ -99,6 +118,7 @@
 static void ieee80211_free_chanctx(struct ieee80211_local *local,
 				   struct ieee80211_chanctx *ctx)
 {
+	bool check_single_channel = false;
 	lockdep_assert_held(&local->chanctx_mtx);
 
 	WARN_ON_ONCE(ctx->refcount != 0);
@@ -108,6 +128,14 @@
 		chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
 		chandef->center_freq1 = chandef->chan->center_freq;
 		chandef->center_freq2 = 0;
+
+		/* NOTE: Disabling radar is only valid here for
+		 * single channel context. To be sure, check it ...
+		 */
+		if (local->hw.conf.radar_enabled)
+			check_single_channel = true;
+		local->hw.conf.radar_enabled = false;
+
 		ieee80211_hw_config(local, 0);
 	} else {
 		drv_remove_chanctx(local, ctx);
@@ -116,6 +144,9 @@
 	list_del_rcu(&ctx->list);
 	kfree_rcu(ctx, rcu_head);
 
+	/* throw a warning if this wasn't the only channel context. */
+	WARN_ON(check_single_channel && !list_empty(&local->chanctx_list));
+
 	mutex_lock(&local->mtx);
 	ieee80211_recalc_idle(local);
 	mutex_unlock(&local->mtx);
@@ -227,19 +258,11 @@
 void ieee80211_recalc_radar_chanctx(struct ieee80211_local *local,
 				    struct ieee80211_chanctx *chanctx)
 {
-	struct ieee80211_sub_if_data *sdata;
-	bool radar_enabled = false;
+	bool radar_enabled;
 
 	lockdep_assert_held(&local->chanctx_mtx);
 
-	rcu_read_lock();
-	list_for_each_entry_rcu(sdata, &local->interfaces, list) {
-		if (sdata->radar_required) {
-			radar_enabled = true;
-			break;
-		}
-	}
-	rcu_read_unlock();
+	radar_enabled = ieee80211_is_radar_required(local);
 
 	if (radar_enabled == chanctx->conf.radar_enabled)
 		return;