xref: /openbmc/linux/net/mac80211/chan.c (revision 727eff4d)
1457c8996SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2f444de05SJohannes Berg /*
3f444de05SJohannes Berg  * mac80211 - channel management
4d0a9123eSJohannes Berg  * Copyright 2020 - 2022 Intel Corporation
5f444de05SJohannes Berg  */
6f444de05SJohannes Berg 
70aaffa9bSJohannes Berg #include <linux/nl80211.h>
83448c005SJohannes Berg #include <linux/export.h>
94d76d21bSJohannes Berg #include <linux/rtnetlink.h>
103117bbdbSPaul Stewart #include <net/cfg80211.h>
11f444de05SJohannes Berg #include "ieee80211_i.h"
1235f2fce9SMichal Kazior #include "driver-ops.h"
1344b72ca8SIlan Peer #include "rate.h"
14f444de05SJohannes Berg 
15c0166da9SMichal Kazior static int ieee80211_chanctx_num_assigned(struct ieee80211_local *local,
16c0166da9SMichal Kazior 					  struct ieee80211_chanctx *ctx)
17c0166da9SMichal Kazior {
18b4f85443SJohannes Berg 	struct ieee80211_link_data *link;
19c0166da9SMichal Kazior 	int num = 0;
20c0166da9SMichal Kazior 
21c0166da9SMichal Kazior 	lockdep_assert_held(&local->chanctx_mtx);
22c0166da9SMichal Kazior 
23b4f85443SJohannes Berg 	list_for_each_entry(link, &ctx->assigned_links, assigned_chanctx_list)
24c0166da9SMichal Kazior 		num++;
25c0166da9SMichal Kazior 
26c0166da9SMichal Kazior 	return num;
27c0166da9SMichal Kazior }
28c0166da9SMichal Kazior 
29c0166da9SMichal Kazior static int ieee80211_chanctx_num_reserved(struct ieee80211_local *local,
30c0166da9SMichal Kazior 					  struct ieee80211_chanctx *ctx)
31c0166da9SMichal Kazior {
32b4f85443SJohannes Berg 	struct ieee80211_link_data *link;
33c0166da9SMichal Kazior 	int num = 0;
34c0166da9SMichal Kazior 
35c0166da9SMichal Kazior 	lockdep_assert_held(&local->chanctx_mtx);
36c0166da9SMichal Kazior 
37b4f85443SJohannes Berg 	list_for_each_entry(link, &ctx->reserved_links, reserved_chanctx_list)
38c0166da9SMichal Kazior 		num++;
39c0166da9SMichal Kazior 
40c0166da9SMichal Kazior 	return num;
41c0166da9SMichal Kazior }
42c0166da9SMichal Kazior 
43c0166da9SMichal Kazior int ieee80211_chanctx_refcount(struct ieee80211_local *local,
44c0166da9SMichal Kazior 			       struct ieee80211_chanctx *ctx)
45c0166da9SMichal Kazior {
46c0166da9SMichal Kazior 	return ieee80211_chanctx_num_assigned(local, ctx) +
47c0166da9SMichal Kazior 	       ieee80211_chanctx_num_reserved(local, ctx);
48c0166da9SMichal Kazior }
49c0166da9SMichal Kazior 
50c2b90ad8SMichal Kazior static int ieee80211_num_chanctx(struct ieee80211_local *local)
51c2b90ad8SMichal Kazior {
52c2b90ad8SMichal Kazior 	struct ieee80211_chanctx *ctx;
53c2b90ad8SMichal Kazior 	int num = 0;
54c2b90ad8SMichal Kazior 
55c2b90ad8SMichal Kazior 	lockdep_assert_held(&local->chanctx_mtx);
56c2b90ad8SMichal Kazior 
57c2b90ad8SMichal Kazior 	list_for_each_entry(ctx, &local->chanctx_list, list)
58c2b90ad8SMichal Kazior 		num++;
59c2b90ad8SMichal Kazior 
60c2b90ad8SMichal Kazior 	return num;
61c2b90ad8SMichal Kazior }
62c2b90ad8SMichal Kazior 
63c2b90ad8SMichal Kazior static bool ieee80211_can_create_new_chanctx(struct ieee80211_local *local)
64c2b90ad8SMichal Kazior {
65c2b90ad8SMichal Kazior 	lockdep_assert_held(&local->chanctx_mtx);
66c2b90ad8SMichal Kazior 	return ieee80211_num_chanctx(local) < ieee80211_max_num_channels(local);
67c2b90ad8SMichal Kazior }
68c2b90ad8SMichal Kazior 
695bcae31dSMichal Kazior static struct ieee80211_chanctx *
70d8675a63SJohannes Berg ieee80211_link_get_chanctx(struct ieee80211_link_data *link)
715bcae31dSMichal Kazior {
72d8675a63SJohannes Berg 	struct ieee80211_local *local __maybe_unused = link->sdata->local;
735bcae31dSMichal Kazior 	struct ieee80211_chanctx_conf *conf;
745bcae31dSMichal Kazior 
75d8675a63SJohannes Berg 	conf = rcu_dereference_protected(link->conf->chanctx_conf,
765bcae31dSMichal Kazior 					 lockdep_is_held(&local->chanctx_mtx));
775bcae31dSMichal Kazior 	if (!conf)
785bcae31dSMichal Kazior 		return NULL;
795bcae31dSMichal Kazior 
805bcae31dSMichal Kazior 	return container_of(conf, struct ieee80211_chanctx, conf);
815bcae31dSMichal Kazior }
825bcae31dSMichal Kazior 
830288157bSMichal Kazior static const struct cfg80211_chan_def *
840288157bSMichal Kazior ieee80211_chanctx_reserved_chandef(struct ieee80211_local *local,
850288157bSMichal Kazior 				   struct ieee80211_chanctx *ctx,
860288157bSMichal Kazior 				   const struct cfg80211_chan_def *compat)
870288157bSMichal Kazior {
88b4f85443SJohannes Berg 	struct ieee80211_link_data *link;
890288157bSMichal Kazior 
900288157bSMichal Kazior 	lockdep_assert_held(&local->chanctx_mtx);
910288157bSMichal Kazior 
92b4f85443SJohannes Berg 	list_for_each_entry(link, &ctx->reserved_links,
930288157bSMichal Kazior 			    reserved_chanctx_list) {
940288157bSMichal Kazior 		if (!compat)
95b4f85443SJohannes Berg 			compat = &link->reserved_chandef;
960288157bSMichal Kazior 
97b4f85443SJohannes Berg 		compat = cfg80211_chandef_compatible(&link->reserved_chandef,
980288157bSMichal Kazior 						     compat);
990288157bSMichal Kazior 		if (!compat)
1000288157bSMichal Kazior 			break;
1010288157bSMichal Kazior 	}
1020288157bSMichal Kazior 
1030288157bSMichal Kazior 	return compat;
1040288157bSMichal Kazior }
1050288157bSMichal Kazior 
10613f348a8SMichal Kazior static const struct cfg80211_chan_def *
10713f348a8SMichal Kazior ieee80211_chanctx_non_reserved_chandef(struct ieee80211_local *local,
10813f348a8SMichal Kazior 				       struct ieee80211_chanctx *ctx,
10913f348a8SMichal Kazior 				       const struct cfg80211_chan_def *compat)
11013f348a8SMichal Kazior {
111b4f85443SJohannes Berg 	struct ieee80211_link_data *link;
11213f348a8SMichal Kazior 
11313f348a8SMichal Kazior 	lockdep_assert_held(&local->chanctx_mtx);
11413f348a8SMichal Kazior 
115b4f85443SJohannes Berg 	list_for_each_entry(link, &ctx->assigned_links,
11613f348a8SMichal Kazior 			    assigned_chanctx_list) {
117d8675a63SJohannes Berg 		struct ieee80211_bss_conf *link_conf = link->conf;
118b4f85443SJohannes Berg 
119b4f85443SJohannes Berg 		if (link->reserved_chanctx)
12013f348a8SMichal Kazior 			continue;
12113f348a8SMichal Kazior 
12213f348a8SMichal Kazior 		if (!compat)
123b4f85443SJohannes Berg 			compat = &link_conf->chandef;
12413f348a8SMichal Kazior 
12513f348a8SMichal Kazior 		compat = cfg80211_chandef_compatible(
126b4f85443SJohannes Berg 				&link_conf->chandef, compat);
12713f348a8SMichal Kazior 		if (!compat)
12813f348a8SMichal Kazior 			break;
12913f348a8SMichal Kazior 	}
13013f348a8SMichal Kazior 
13113f348a8SMichal Kazior 	return compat;
13213f348a8SMichal Kazior }
13313f348a8SMichal Kazior 
13413f348a8SMichal Kazior static const struct cfg80211_chan_def *
13513f348a8SMichal Kazior ieee80211_chanctx_combined_chandef(struct ieee80211_local *local,
13613f348a8SMichal Kazior 				   struct ieee80211_chanctx *ctx,
13713f348a8SMichal Kazior 				   const struct cfg80211_chan_def *compat)
13813f348a8SMichal Kazior {
13913f348a8SMichal Kazior 	lockdep_assert_held(&local->chanctx_mtx);
14013f348a8SMichal Kazior 
14113f348a8SMichal Kazior 	compat = ieee80211_chanctx_reserved_chandef(local, ctx, compat);
14213f348a8SMichal Kazior 	if (!compat)
14313f348a8SMichal Kazior 		return NULL;
14413f348a8SMichal Kazior 
14513f348a8SMichal Kazior 	compat = ieee80211_chanctx_non_reserved_chandef(local, ctx, compat);
14613f348a8SMichal Kazior 	if (!compat)
14713f348a8SMichal Kazior 		return NULL;
14813f348a8SMichal Kazior 
14913f348a8SMichal Kazior 	return compat;
15013f348a8SMichal Kazior }
15113f348a8SMichal Kazior 
15213f348a8SMichal Kazior static bool
15313f348a8SMichal Kazior ieee80211_chanctx_can_reserve_chandef(struct ieee80211_local *local,
15413f348a8SMichal Kazior 				      struct ieee80211_chanctx *ctx,
15513f348a8SMichal Kazior 				      const struct cfg80211_chan_def *def)
15613f348a8SMichal Kazior {
15713f348a8SMichal Kazior 	lockdep_assert_held(&local->chanctx_mtx);
15813f348a8SMichal Kazior 
15913f348a8SMichal Kazior 	if (ieee80211_chanctx_combined_chandef(local, ctx, def))
16013f348a8SMichal Kazior 		return true;
16113f348a8SMichal Kazior 
162b4f85443SJohannes Berg 	if (!list_empty(&ctx->reserved_links) &&
16313f348a8SMichal Kazior 	    ieee80211_chanctx_reserved_chandef(local, ctx, def))
16413f348a8SMichal Kazior 		return true;
16513f348a8SMichal Kazior 
16613f348a8SMichal Kazior 	return false;
16713f348a8SMichal Kazior }
16813f348a8SMichal Kazior 
16913f348a8SMichal Kazior static struct ieee80211_chanctx *
17013f348a8SMichal Kazior ieee80211_find_reservation_chanctx(struct ieee80211_local *local,
17113f348a8SMichal Kazior 				   const struct cfg80211_chan_def *chandef,
17213f348a8SMichal Kazior 				   enum ieee80211_chanctx_mode mode)
17313f348a8SMichal Kazior {
17413f348a8SMichal Kazior 	struct ieee80211_chanctx *ctx;
17513f348a8SMichal Kazior 
17613f348a8SMichal Kazior 	lockdep_assert_held(&local->chanctx_mtx);
17713f348a8SMichal Kazior 
17813f348a8SMichal Kazior 	if (mode == IEEE80211_CHANCTX_EXCLUSIVE)
17913f348a8SMichal Kazior 		return NULL;
18013f348a8SMichal Kazior 
18113f348a8SMichal Kazior 	list_for_each_entry(ctx, &local->chanctx_list, list) {
1825bcae31dSMichal Kazior 		if (ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED)
1835bcae31dSMichal Kazior 			continue;
1845bcae31dSMichal Kazior 
18513f348a8SMichal Kazior 		if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE)
18613f348a8SMichal Kazior 			continue;
18713f348a8SMichal Kazior 
18813f348a8SMichal Kazior 		if (!ieee80211_chanctx_can_reserve_chandef(local, ctx,
18913f348a8SMichal Kazior 							   chandef))
19013f348a8SMichal Kazior 			continue;
19113f348a8SMichal Kazior 
19213f348a8SMichal Kazior 		return ctx;
19313f348a8SMichal Kazior 	}
19413f348a8SMichal Kazior 
19513f348a8SMichal Kazior 	return NULL;
19613f348a8SMichal Kazior }
19713f348a8SMichal Kazior 
198b4f85443SJohannes Berg static enum nl80211_chan_width ieee80211_get_sta_bw(struct sta_info *sta,
199b4f85443SJohannes Berg 						    unsigned int link_id)
20021f659bfSEliad Peller {
201c71420dbSJohannes Berg 	enum ieee80211_sta_rx_bandwidth width;
202c71420dbSJohannes Berg 	struct link_sta_info *link_sta;
203c71420dbSJohannes Berg 
204c71420dbSJohannes Berg 	link_sta = rcu_dereference(sta->link[link_id]);
205b4f85443SJohannes Berg 
206b4f85443SJohannes Berg 	/* no effect if this STA has no presence on this link */
207c71420dbSJohannes Berg 	if (!link_sta)
208b4f85443SJohannes Berg 		return NL80211_CHAN_WIDTH_20_NOHT;
209bbf31e88SIlan Peer 
210c71420dbSJohannes Berg 	width = ieee80211_sta_cap_rx_bw(link_sta);
211c71420dbSJohannes Berg 
212bbf31e88SIlan Peer 	switch (width) {
21321f659bfSEliad Peller 	case IEEE80211_STA_RX_BW_20:
214c71420dbSJohannes Berg 		if (link_sta->pub->ht_cap.ht_supported)
21521f659bfSEliad Peller 			return NL80211_CHAN_WIDTH_20;
21621f659bfSEliad Peller 		else
21721f659bfSEliad Peller 			return NL80211_CHAN_WIDTH_20_NOHT;
21821f659bfSEliad Peller 	case IEEE80211_STA_RX_BW_40:
21921f659bfSEliad Peller 		return NL80211_CHAN_WIDTH_40;
22021f659bfSEliad Peller 	case IEEE80211_STA_RX_BW_80:
22121f659bfSEliad Peller 		return NL80211_CHAN_WIDTH_80;
22221f659bfSEliad Peller 	case IEEE80211_STA_RX_BW_160:
22321f659bfSEliad Peller 		/*
22421f659bfSEliad Peller 		 * This applied for both 160 and 80+80. since we use
22521f659bfSEliad Peller 		 * the returned value to consider degradation of
22621f659bfSEliad Peller 		 * ctx->conf.min_def, we have to make sure to take
22721f659bfSEliad Peller 		 * the bigger one (NL80211_CHAN_WIDTH_160).
22821f659bfSEliad Peller 		 * Otherwise we might try degrading even when not
22921f659bfSEliad Peller 		 * needed, as the max required sta_bw returned (80+80)
23021f659bfSEliad Peller 		 * might be smaller than the configured bw (160).
23121f659bfSEliad Peller 		 */
23221f659bfSEliad Peller 		return NL80211_CHAN_WIDTH_160;
2335dca295dSIlan Peer 	case IEEE80211_STA_RX_BW_320:
2345dca295dSIlan Peer 		return NL80211_CHAN_WIDTH_320;
23521f659bfSEliad Peller 	default:
23621f659bfSEliad Peller 		WARN_ON(1);
23721f659bfSEliad Peller 		return NL80211_CHAN_WIDTH_20;
23821f659bfSEliad Peller 	}
23921f659bfSEliad Peller }
24021f659bfSEliad Peller 
24121f659bfSEliad Peller static enum nl80211_chan_width
242b4f85443SJohannes Berg ieee80211_get_max_required_bw(struct ieee80211_sub_if_data *sdata,
243b4f85443SJohannes Berg 			      unsigned int link_id)
24421f659bfSEliad Peller {
24521f659bfSEliad Peller 	enum nl80211_chan_width max_bw = NL80211_CHAN_WIDTH_20_NOHT;
24621f659bfSEliad Peller 	struct sta_info *sta;
24721f659bfSEliad Peller 
24821f659bfSEliad Peller 	list_for_each_entry_rcu(sta, &sdata->local->sta_list, list) {
24921f659bfSEliad Peller 		if (sdata != sta->sdata &&
25021f659bfSEliad Peller 		    !(sta->sdata->bss && sta->sdata->bss == sdata->bss))
25121f659bfSEliad Peller 			continue;
25221f659bfSEliad Peller 
253b4f85443SJohannes Berg 		max_bw = max(max_bw, ieee80211_get_sta_bw(sta, link_id));
25421f659bfSEliad Peller 	}
25521f659bfSEliad Peller 
25621f659bfSEliad Peller 	return max_bw;
25721f659bfSEliad Peller }
25821f659bfSEliad Peller 
25921f659bfSEliad Peller static enum nl80211_chan_width
260b4f85443SJohannes Berg ieee80211_get_chanctx_vif_max_required_bw(struct ieee80211_sub_if_data *sdata,
261b4f85443SJohannes Berg 					  struct ieee80211_chanctx_conf *conf)
262b4f85443SJohannes Berg {
263b4f85443SJohannes Berg 	enum nl80211_chan_width max_bw = NL80211_CHAN_WIDTH_20_NOHT;
264b4f85443SJohannes Berg 	struct ieee80211_vif *vif = &sdata->vif;
265b4f85443SJohannes Berg 	int link_id;
266b4f85443SJohannes Berg 
267d8675a63SJohannes Berg 	rcu_read_lock();
268b4f85443SJohannes Berg 	for (link_id = 0; link_id < ARRAY_SIZE(sdata->link); link_id++) {
269b4f85443SJohannes Berg 		enum nl80211_chan_width width = NL80211_CHAN_WIDTH_20_NOHT;
270b4f85443SJohannes Berg 		struct ieee80211_bss_conf *link_conf =
271d8675a63SJohannes Berg 			rcu_dereference(sdata->vif.link_conf[link_id]);
272b4f85443SJohannes Berg 
273b4f85443SJohannes Berg 		if (!link_conf)
274b4f85443SJohannes Berg 			continue;
275b4f85443SJohannes Berg 
276b4f85443SJohannes Berg 		if (rcu_access_pointer(link_conf->chanctx_conf) != conf)
277b4f85443SJohannes Berg 			continue;
278b4f85443SJohannes Berg 
279b4f85443SJohannes Berg 		switch (vif->type) {
280b4f85443SJohannes Berg 		case NL80211_IFTYPE_AP:
281b4f85443SJohannes Berg 		case NL80211_IFTYPE_AP_VLAN:
282b4f85443SJohannes Berg 			width = ieee80211_get_max_required_bw(sdata, link_id);
283b4f85443SJohannes Berg 			break;
284b4f85443SJohannes Berg 		case NL80211_IFTYPE_STATION:
285b4f85443SJohannes Berg 			/*
286b4f85443SJohannes Berg 			 * The ap's sta->bandwidth is not set yet at this
287b4f85443SJohannes Berg 			 * point, so take the width from the chandef, but
288b4f85443SJohannes Berg 			 * account also for TDLS peers
289b4f85443SJohannes Berg 			 */
290b4f85443SJohannes Berg 			width = max(link_conf->chandef.width,
291b4f85443SJohannes Berg 				    ieee80211_get_max_required_bw(sdata, link_id));
292b4f85443SJohannes Berg 			break;
293b4f85443SJohannes Berg 		case NL80211_IFTYPE_P2P_DEVICE:
294b4f85443SJohannes Berg 		case NL80211_IFTYPE_NAN:
295b4f85443SJohannes Berg 			continue;
296b4f85443SJohannes Berg 		case NL80211_IFTYPE_ADHOC:
297b4f85443SJohannes Berg 		case NL80211_IFTYPE_MESH_POINT:
298b4f85443SJohannes Berg 		case NL80211_IFTYPE_OCB:
299b4f85443SJohannes Berg 			width = link_conf->chandef.width;
300b4f85443SJohannes Berg 			break;
301b4f85443SJohannes Berg 		case NL80211_IFTYPE_WDS:
302b4f85443SJohannes Berg 		case NL80211_IFTYPE_UNSPECIFIED:
303b4f85443SJohannes Berg 		case NUM_NL80211_IFTYPES:
304b4f85443SJohannes Berg 		case NL80211_IFTYPE_MONITOR:
305b4f85443SJohannes Berg 		case NL80211_IFTYPE_P2P_CLIENT:
306b4f85443SJohannes Berg 		case NL80211_IFTYPE_P2P_GO:
307b4f85443SJohannes Berg 			WARN_ON_ONCE(1);
308b4f85443SJohannes Berg 		}
309b4f85443SJohannes Berg 
310b4f85443SJohannes Berg 		max_bw = max(max_bw, width);
311b4f85443SJohannes Berg 	}
312d8675a63SJohannes Berg 	rcu_read_unlock();
313b4f85443SJohannes Berg 
314b4f85443SJohannes Berg 	return max_bw;
315b4f85443SJohannes Berg }
316b4f85443SJohannes Berg 
317b4f85443SJohannes Berg static enum nl80211_chan_width
31821f659bfSEliad Peller ieee80211_get_chanctx_max_required_bw(struct ieee80211_local *local,
31921f659bfSEliad Peller 				      struct ieee80211_chanctx_conf *conf)
32021f659bfSEliad Peller {
32121f659bfSEliad Peller 	struct ieee80211_sub_if_data *sdata;
32221f659bfSEliad Peller 	enum nl80211_chan_width max_bw = NL80211_CHAN_WIDTH_20_NOHT;
32321f659bfSEliad Peller 
32421f659bfSEliad Peller 	rcu_read_lock();
32521f659bfSEliad Peller 	list_for_each_entry_rcu(sdata, &local->interfaces, list) {
326b4f85443SJohannes Berg 		enum nl80211_chan_width width;
32721f659bfSEliad Peller 
32821f659bfSEliad Peller 		if (!ieee80211_sdata_running(sdata))
32921f659bfSEliad Peller 			continue;
33021f659bfSEliad Peller 
331b4f85443SJohannes Berg 		width = ieee80211_get_chanctx_vif_max_required_bw(sdata, conf);
33221f659bfSEliad Peller 
33321f659bfSEliad Peller 		max_bw = max(max_bw, width);
33421f659bfSEliad Peller 	}
3351c37a72cSEliad Peller 
3361c37a72cSEliad Peller 	/* use the configured bandwidth in case of monitor interface */
3371c37a72cSEliad Peller 	sdata = rcu_dereference(local->monitor_sdata);
338b4f85443SJohannes Berg 	if (sdata &&
339d8675a63SJohannes Berg 	    rcu_access_pointer(sdata->vif.bss_conf.chanctx_conf) == conf)
3401c37a72cSEliad Peller 		max_bw = max(max_bw, conf->def.width);
3411c37a72cSEliad Peller 
34221f659bfSEliad Peller 	rcu_read_unlock();
34321f659bfSEliad Peller 
34421f659bfSEliad Peller 	return max_bw;
34521f659bfSEliad Peller }
34621f659bfSEliad Peller 
34721f659bfSEliad Peller /*
34821f659bfSEliad Peller  * recalc the min required chan width of the channel context, which is
34921f659bfSEliad Peller  * the max of min required widths of all the interfaces bound to this
35021f659bfSEliad Peller  * channel context.
35121f659bfSEliad Peller  */
352d6c37509SMordechay Goodstein static u32 _ieee80211_recalc_chanctx_min_def(struct ieee80211_local *local,
35321f659bfSEliad Peller 					     struct ieee80211_chanctx *ctx)
35421f659bfSEliad Peller {
35521f659bfSEliad Peller 	enum nl80211_chan_width max_bw;
35621f659bfSEliad Peller 	struct cfg80211_chan_def min_def;
35721f659bfSEliad Peller 
35821f659bfSEliad Peller 	lockdep_assert_held(&local->chanctx_mtx);
35921f659bfSEliad Peller 
360df78a0c0SThomas Pedersen 	/* don't optimize non-20MHz based and radar_enabled confs */
36121f659bfSEliad Peller 	if (ctx->conf.def.width == NL80211_CHAN_WIDTH_5 ||
36221f659bfSEliad Peller 	    ctx->conf.def.width == NL80211_CHAN_WIDTH_10 ||
363df78a0c0SThomas Pedersen 	    ctx->conf.def.width == NL80211_CHAN_WIDTH_1 ||
364df78a0c0SThomas Pedersen 	    ctx->conf.def.width == NL80211_CHAN_WIDTH_2 ||
365df78a0c0SThomas Pedersen 	    ctx->conf.def.width == NL80211_CHAN_WIDTH_4 ||
366df78a0c0SThomas Pedersen 	    ctx->conf.def.width == NL80211_CHAN_WIDTH_8 ||
367df78a0c0SThomas Pedersen 	    ctx->conf.def.width == NL80211_CHAN_WIDTH_16 ||
36821f659bfSEliad Peller 	    ctx->conf.radar_enabled) {
36921f659bfSEliad Peller 		ctx->conf.min_def = ctx->conf.def;
370d6c37509SMordechay Goodstein 		return 0;
37121f659bfSEliad Peller 	}
37221f659bfSEliad Peller 
37321f659bfSEliad Peller 	max_bw = ieee80211_get_chanctx_max_required_bw(local, &ctx->conf);
37421f659bfSEliad Peller 
37521f659bfSEliad Peller 	/* downgrade chandef up to max_bw */
37621f659bfSEliad Peller 	min_def = ctx->conf.def;
37721f659bfSEliad Peller 	while (min_def.width > max_bw)
37821f659bfSEliad Peller 		ieee80211_chandef_downgrade(&min_def);
37921f659bfSEliad Peller 
38021f659bfSEliad Peller 	if (cfg80211_chandef_identical(&ctx->conf.min_def, &min_def))
381d6c37509SMordechay Goodstein 		return 0;
38221f659bfSEliad Peller 
38321f659bfSEliad Peller 	ctx->conf.min_def = min_def;
38421f659bfSEliad Peller 	if (!ctx->driver_present)
385d6c37509SMordechay Goodstein 		return 0;
38621f659bfSEliad Peller 
387d6c37509SMordechay Goodstein 	return IEEE80211_CHANCTX_CHANGE_MIN_WIDTH;
38821f659bfSEliad Peller }
38921f659bfSEliad Peller 
390d6c37509SMordechay Goodstein /* calling this function is assuming that station vif is updated to
391b4f85443SJohannes Berg  * lates changes by calling ieee80211_link_update_chandef
392d6c37509SMordechay Goodstein  */
39344b72ca8SIlan Peer static void ieee80211_chan_bw_change(struct ieee80211_local *local,
394d6c37509SMordechay Goodstein 				     struct ieee80211_chanctx *ctx,
395d6c37509SMordechay Goodstein 				     bool narrowed)
39644b72ca8SIlan Peer {
39744b72ca8SIlan Peer 	struct sta_info *sta;
39844b72ca8SIlan Peer 	struct ieee80211_supported_band *sband =
39944b72ca8SIlan Peer 		local->hw.wiphy->bands[ctx->conf.def.chan->band];
40044b72ca8SIlan Peer 
40144b72ca8SIlan Peer 	rcu_read_lock();
40244b72ca8SIlan Peer 	list_for_each_entry_rcu(sta, &local->sta_list,
40344b72ca8SIlan Peer 				list) {
404b4f85443SJohannes Berg 		struct ieee80211_sub_if_data *sdata = sta->sdata;
40544b72ca8SIlan Peer 		enum ieee80211_sta_rx_bandwidth new_sta_bw;
406b4f85443SJohannes Berg 		unsigned int link_id;
40744b72ca8SIlan Peer 
40844b72ca8SIlan Peer 		if (!ieee80211_sdata_running(sta->sdata))
40944b72ca8SIlan Peer 			continue;
41044b72ca8SIlan Peer 
411b4f85443SJohannes Berg 		for (link_id = 0; link_id < ARRAY_SIZE(sta->sdata->link); link_id++) {
412b4f85443SJohannes Berg 			struct ieee80211_bss_conf *link_conf =
413d8675a63SJohannes Berg 				rcu_dereference(sdata->vif.link_conf[link_id]);
414c71420dbSJohannes Berg 			struct link_sta_info *link_sta;
415b4f85443SJohannes Berg 
416b4f85443SJohannes Berg 			if (!link_conf)
41744b72ca8SIlan Peer 				continue;
41844b72ca8SIlan Peer 
419b4f85443SJohannes Berg 			if (rcu_access_pointer(link_conf->chanctx_conf) != &ctx->conf)
420b4f85443SJohannes Berg 				continue;
421b4f85443SJohannes Berg 
422c71420dbSJohannes Berg 			link_sta = rcu_dereference(sta->link[link_id]);
423c71420dbSJohannes Berg 			if (!link_sta)
424c71420dbSJohannes Berg 				continue;
425c71420dbSJohannes Berg 
426c71420dbSJohannes Berg 			new_sta_bw = ieee80211_sta_cur_vht_bw(link_sta);
427d6c37509SMordechay Goodstein 
428d6c37509SMordechay Goodstein 			/* nothing change */
429c71420dbSJohannes Berg 			if (new_sta_bw == link_sta->pub->bandwidth)
43044b72ca8SIlan Peer 				continue;
43144b72ca8SIlan Peer 
432d6c37509SMordechay Goodstein 			/* vif changed to narrow BW and narrow BW for station wasn't
433d6c37509SMordechay Goodstein 			 * requested or vise versa */
434c71420dbSJohannes Berg 			if ((new_sta_bw < link_sta->pub->bandwidth) == !narrowed)
435d6c37509SMordechay Goodstein 				continue;
436d6c37509SMordechay Goodstein 
437c71420dbSJohannes Berg 			link_sta->pub->bandwidth = new_sta_bw;
438b4f85443SJohannes Berg 			rate_control_rate_update(local, sband, sta, link_id,
43944b72ca8SIlan Peer 						 IEEE80211_RC_BW_CHANGED);
44044b72ca8SIlan Peer 		}
441b4f85443SJohannes Berg 	}
44244b72ca8SIlan Peer 	rcu_read_unlock();
44344b72ca8SIlan Peer }
44444b72ca8SIlan Peer 
445d6c37509SMordechay Goodstein /*
446d6c37509SMordechay Goodstein  * recalc the min required chan width of the channel context, which is
447d6c37509SMordechay Goodstein  * the max of min required widths of all the interfaces bound to this
448d6c37509SMordechay Goodstein  * channel context.
449d6c37509SMordechay Goodstein  */
450d6c37509SMordechay Goodstein void ieee80211_recalc_chanctx_min_def(struct ieee80211_local *local,
451d6c37509SMordechay Goodstein 				      struct ieee80211_chanctx *ctx)
452e89a96f5SMichal Kazior {
453d6c37509SMordechay Goodstein 	u32 changed = _ieee80211_recalc_chanctx_min_def(local, ctx);
45444b72ca8SIlan Peer 
455d6c37509SMordechay Goodstein 	if (!changed)
456e89a96f5SMichal Kazior 		return;
457d6c37509SMordechay Goodstein 
458d6c37509SMordechay Goodstein 	/* check is BW narrowed */
459d6c37509SMordechay Goodstein 	ieee80211_chan_bw_change(local, ctx, true);
460d6c37509SMordechay Goodstein 
461d6c37509SMordechay Goodstein 	drv_change_chanctx(local, ctx, changed);
462d6c37509SMordechay Goodstein 
463d6c37509SMordechay Goodstein 	/* check is BW wider */
464d6c37509SMordechay Goodstein 	ieee80211_chan_bw_change(local, ctx, false);
465aa507a7bSArik Nemtsov }
466e89a96f5SMichal Kazior 
467d6c37509SMordechay Goodstein static void ieee80211_change_chanctx(struct ieee80211_local *local,
468d6c37509SMordechay Goodstein 				     struct ieee80211_chanctx *ctx,
469d6c37509SMordechay Goodstein 				     struct ieee80211_chanctx *old_ctx,
470d6c37509SMordechay Goodstein 				     const struct cfg80211_chan_def *chandef)
471d6c37509SMordechay Goodstein {
472d6c37509SMordechay Goodstein 	u32 changed;
47344b72ca8SIlan Peer 
4745dca295dSIlan Peer 	/* expected to handle only 20/40/80/160/320 channel widths */
47544b72ca8SIlan Peer 	switch (chandef->width) {
47644b72ca8SIlan Peer 	case NL80211_CHAN_WIDTH_20_NOHT:
47744b72ca8SIlan Peer 	case NL80211_CHAN_WIDTH_20:
47844b72ca8SIlan Peer 	case NL80211_CHAN_WIDTH_40:
47944b72ca8SIlan Peer 	case NL80211_CHAN_WIDTH_80:
48044b72ca8SIlan Peer 	case NL80211_CHAN_WIDTH_80P80:
48144b72ca8SIlan Peer 	case NL80211_CHAN_WIDTH_160:
4825dca295dSIlan Peer 	case NL80211_CHAN_WIDTH_320:
48344b72ca8SIlan Peer 		break;
48444b72ca8SIlan Peer 	default:
48544b72ca8SIlan Peer 		WARN_ON(1);
48644b72ca8SIlan Peer 	}
48744b72ca8SIlan Peer 
488d6c37509SMordechay Goodstein 	/* Check maybe BW narrowed - we do this _before_ calling recalc_chanctx_min_def
489d6c37509SMordechay Goodstein 	 * due to maybe not returning from it, e.g in case new context was added
490d6c37509SMordechay Goodstein 	 * first time with all parameters up to date.
491d6c37509SMordechay Goodstein 	 */
492d6c37509SMordechay Goodstein 	ieee80211_chan_bw_change(local, old_ctx, true);
49344b72ca8SIlan Peer 
494d6c37509SMordechay Goodstein 	if (cfg80211_chandef_identical(&ctx->conf.def, chandef)) {
49521f659bfSEliad Peller 		ieee80211_recalc_chanctx_min_def(local, ctx);
496d6c37509SMordechay Goodstein 		return;
497d6c37509SMordechay Goodstein 	}
498d6c37509SMordechay Goodstein 
499d6c37509SMordechay Goodstein 	WARN_ON(!cfg80211_chandef_compatible(&ctx->conf.def, chandef));
500d6c37509SMordechay Goodstein 
501d6c37509SMordechay Goodstein 	ctx->conf.def = *chandef;
502d6c37509SMordechay Goodstein 
503d6c37509SMordechay Goodstein 	/* check if min chanctx also changed */
504d6c37509SMordechay Goodstein 	changed = IEEE80211_CHANCTX_CHANGE_WIDTH |
505d6c37509SMordechay Goodstein 		  _ieee80211_recalc_chanctx_min_def(local, ctx);
506d6c37509SMordechay Goodstein 	drv_change_chanctx(local, ctx, changed);
50755de908aSJohannes Berg 
50855de908aSJohannes Berg 	if (!local->use_chanctx) {
509675a0b04SKarl Beldan 		local->_oper_chandef = *chandef;
51055de908aSJohannes Berg 		ieee80211_hw_config(local, 0);
51155de908aSJohannes Berg 	}
51244b72ca8SIlan Peer 
513d6c37509SMordechay Goodstein 	/* check is BW wider */
514d6c37509SMordechay Goodstein 	ieee80211_chan_bw_change(local, old_ctx, false);
5150aaffa9bSJohannes Berg }
516d01a1e65SMichal Kazior 
517d01a1e65SMichal Kazior static struct ieee80211_chanctx *
518d01a1e65SMichal Kazior ieee80211_find_chanctx(struct ieee80211_local *local,
5194bf88530SJohannes Berg 		       const struct cfg80211_chan_def *chandef,
520d01a1e65SMichal Kazior 		       enum ieee80211_chanctx_mode mode)
521d01a1e65SMichal Kazior {
522d01a1e65SMichal Kazior 	struct ieee80211_chanctx *ctx;
523d01a1e65SMichal Kazior 
524d01a1e65SMichal Kazior 	lockdep_assert_held(&local->chanctx_mtx);
525d01a1e65SMichal Kazior 
526d01a1e65SMichal Kazior 	if (mode == IEEE80211_CHANCTX_EXCLUSIVE)
527d01a1e65SMichal Kazior 		return NULL;
528d01a1e65SMichal Kazior 
529d01a1e65SMichal Kazior 	list_for_each_entry(ctx, &local->chanctx_list, list) {
5304bf88530SJohannes Berg 		const struct cfg80211_chan_def *compat;
531e89a96f5SMichal Kazior 
5325bcae31dSMichal Kazior 		if (ctx->replace_state != IEEE80211_CHANCTX_REPLACE_NONE)
5335bcae31dSMichal Kazior 			continue;
5345bcae31dSMichal Kazior 
535d01a1e65SMichal Kazior 		if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE)
536d01a1e65SMichal Kazior 			continue;
5374bf88530SJohannes Berg 
5384bf88530SJohannes Berg 		compat = cfg80211_chandef_compatible(&ctx->conf.def, chandef);
5394bf88530SJohannes Berg 		if (!compat)
540d01a1e65SMichal Kazior 			continue;
541d01a1e65SMichal Kazior 
5420288157bSMichal Kazior 		compat = ieee80211_chanctx_reserved_chandef(local, ctx,
5430288157bSMichal Kazior 							    compat);
5440288157bSMichal Kazior 		if (!compat)
5450288157bSMichal Kazior 			continue;
5460288157bSMichal Kazior 
547d6c37509SMordechay Goodstein 		ieee80211_change_chanctx(local, ctx, ctx, compat);
548e89a96f5SMichal Kazior 
549d01a1e65SMichal Kazior 		return ctx;
550d01a1e65SMichal Kazior 	}
551d01a1e65SMichal Kazior 
552d01a1e65SMichal Kazior 	return NULL;
553d01a1e65SMichal Kazior }
554d01a1e65SMichal Kazior 
5555cbc95a7SEliad Peller bool ieee80211_is_radar_required(struct ieee80211_local *local)
556e4746851SSimon Wunderlich {
557e4746851SSimon Wunderlich 	struct ieee80211_sub_if_data *sdata;
558e4746851SSimon Wunderlich 
559cc901de1SMichal Kazior 	lockdep_assert_held(&local->mtx);
560cc901de1SMichal Kazior 
561e4746851SSimon Wunderlich 	rcu_read_lock();
562e4746851SSimon Wunderlich 	list_for_each_entry_rcu(sdata, &local->interfaces, list) {
563b4f85443SJohannes Berg 		unsigned int link_id;
564b4f85443SJohannes Berg 
565b4f85443SJohannes Berg 		for (link_id = 0; link_id < ARRAY_SIZE(sdata->link); link_id++) {
566d8675a63SJohannes Berg 			struct ieee80211_link_data *link;
567d8675a63SJohannes Berg 
568d8675a63SJohannes Berg 			link = rcu_dereference(sdata->link[link_id]);
569d8675a63SJohannes Berg 
570d8675a63SJohannes Berg 			if (link && link->radar_required) {
571e4746851SSimon Wunderlich 				rcu_read_unlock();
572e4746851SSimon Wunderlich 				return true;
573e4746851SSimon Wunderlich 			}
574e4746851SSimon Wunderlich 		}
575b4f85443SJohannes Berg 	}
576e4746851SSimon Wunderlich 	rcu_read_unlock();
577e4746851SSimon Wunderlich 
578e4746851SSimon Wunderlich 	return false;
579e4746851SSimon Wunderlich }
580e4746851SSimon Wunderlich 
581e7f2337aSEliad Peller static bool
582e7f2337aSEliad Peller ieee80211_chanctx_radar_required(struct ieee80211_local *local,
583e7f2337aSEliad Peller 				 struct ieee80211_chanctx *ctx)
584e7f2337aSEliad Peller {
585e7f2337aSEliad Peller 	struct ieee80211_chanctx_conf *conf = &ctx->conf;
586e7f2337aSEliad Peller 	struct ieee80211_sub_if_data *sdata;
587e7f2337aSEliad Peller 	bool required = false;
588e7f2337aSEliad Peller 
589e7f2337aSEliad Peller 	lockdep_assert_held(&local->chanctx_mtx);
590e7f2337aSEliad Peller 	lockdep_assert_held(&local->mtx);
591e7f2337aSEliad Peller 
592e7f2337aSEliad Peller 	rcu_read_lock();
593e7f2337aSEliad Peller 	list_for_each_entry_rcu(sdata, &local->interfaces, list) {
594b4f85443SJohannes Berg 		unsigned int link_id;
595b4f85443SJohannes Berg 
596e7f2337aSEliad Peller 		if (!ieee80211_sdata_running(sdata))
597e7f2337aSEliad Peller 			continue;
598b4f85443SJohannes Berg 		for (link_id = 0; link_id < ARRAY_SIZE(sdata->link); link_id++) {
599d8675a63SJohannes Berg 			struct ieee80211_link_data *link;
600b4f85443SJohannes Berg 
601d8675a63SJohannes Berg 			link = rcu_dereference(sdata->link[link_id]);
602d8675a63SJohannes Berg 			if (!link)
603e7f2337aSEliad Peller 				continue;
604e7f2337aSEliad Peller 
605d8675a63SJohannes Berg 			if (rcu_access_pointer(link->conf->chanctx_conf) != conf)
606b4f85443SJohannes Berg 				continue;
607d8675a63SJohannes Berg 			if (!link->radar_required)
608b4f85443SJohannes Berg 				continue;
609e7f2337aSEliad Peller 			required = true;
610e7f2337aSEliad Peller 			break;
611e7f2337aSEliad Peller 		}
612b4f85443SJohannes Berg 
613b4f85443SJohannes Berg 		if (required)
614b4f85443SJohannes Berg 			break;
615b4f85443SJohannes Berg 	}
616e7f2337aSEliad Peller 	rcu_read_unlock();
617e7f2337aSEliad Peller 
618e7f2337aSEliad Peller 	return required;
619e7f2337aSEliad Peller }
620e7f2337aSEliad Peller 
621d01a1e65SMichal Kazior static struct ieee80211_chanctx *
622ed68ebcaSMichal Kazior ieee80211_alloc_chanctx(struct ieee80211_local *local,
6234bf88530SJohannes Berg 			const struct cfg80211_chan_def *chandef,
624d01a1e65SMichal Kazior 			enum ieee80211_chanctx_mode mode)
625d01a1e65SMichal Kazior {
626d01a1e65SMichal Kazior 	struct ieee80211_chanctx *ctx;
627d01a1e65SMichal Kazior 
628d01a1e65SMichal Kazior 	lockdep_assert_held(&local->chanctx_mtx);
629d01a1e65SMichal Kazior 
630d01a1e65SMichal Kazior 	ctx = kzalloc(sizeof(*ctx) + local->hw.chanctx_data_size, GFP_KERNEL);
631d01a1e65SMichal Kazior 	if (!ctx)
632ed68ebcaSMichal Kazior 		return NULL;
633d01a1e65SMichal Kazior 
634b4f85443SJohannes Berg 	INIT_LIST_HEAD(&ctx->assigned_links);
635b4f85443SJohannes Berg 	INIT_LIST_HEAD(&ctx->reserved_links);
6364bf88530SJohannes Berg 	ctx->conf.def = *chandef;
63704ecd257SJohannes Berg 	ctx->conf.rx_chains_static = 1;
63804ecd257SJohannes Berg 	ctx->conf.rx_chains_dynamic = 1;
639d01a1e65SMichal Kazior 	ctx->mode = mode;
640e7f2337aSEliad Peller 	ctx->conf.radar_enabled = false;
64121f659bfSEliad Peller 	ieee80211_recalc_chanctx_min_def(local, ctx);
642ed68ebcaSMichal Kazior 
643ed68ebcaSMichal Kazior 	return ctx;
644ed68ebcaSMichal Kazior }
645ed68ebcaSMichal Kazior 
646ed68ebcaSMichal Kazior static int ieee80211_add_chanctx(struct ieee80211_local *local,
647ed68ebcaSMichal Kazior 				 struct ieee80211_chanctx *ctx)
648ed68ebcaSMichal Kazior {
649ed68ebcaSMichal Kazior 	u32 changed;
650ed68ebcaSMichal Kazior 	int err;
651ed68ebcaSMichal Kazior 
652ed68ebcaSMichal Kazior 	lockdep_assert_held(&local->mtx);
653ed68ebcaSMichal Kazior 	lockdep_assert_held(&local->chanctx_mtx);
654ed68ebcaSMichal Kazior 
655e4746851SSimon Wunderlich 	if (!local->use_chanctx)
656e4746851SSimon Wunderlich 		local->hw.conf.radar_enabled = ctx->conf.radar_enabled;
657d01a1e65SMichal Kazior 
658382a103bSJohannes Berg 	/* turn idle off *before* setting channel -- some drivers need that */
659382a103bSJohannes Berg 	changed = ieee80211_idle_off(local);
660382a103bSJohannes Berg 	if (changed)
661382a103bSJohannes Berg 		ieee80211_hw_config(local, changed);
662382a103bSJohannes Berg 
66355de908aSJohannes Berg 	if (!local->use_chanctx) {
664ed68ebcaSMichal Kazior 		local->_oper_chandef = ctx->conf.def;
6659b4816f5SMichal Kazior 		ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
66655de908aSJohannes Berg 	} else {
66735f2fce9SMichal Kazior 		err = drv_add_chanctx(local, ctx);
66835f2fce9SMichal Kazior 		if (err) {
669382a103bSJohannes Berg 			ieee80211_recalc_idle(local);
670ed68ebcaSMichal Kazior 			return err;
671ed68ebcaSMichal Kazior 		}
672ed68ebcaSMichal Kazior 	}
673ed68ebcaSMichal Kazior 
674ed68ebcaSMichal Kazior 	return 0;
675ed68ebcaSMichal Kazior }
676ed68ebcaSMichal Kazior 
677ed68ebcaSMichal Kazior static struct ieee80211_chanctx *
678ed68ebcaSMichal Kazior ieee80211_new_chanctx(struct ieee80211_local *local,
679ed68ebcaSMichal Kazior 		      const struct cfg80211_chan_def *chandef,
680ed68ebcaSMichal Kazior 		      enum ieee80211_chanctx_mode mode)
681ed68ebcaSMichal Kazior {
682ed68ebcaSMichal Kazior 	struct ieee80211_chanctx *ctx;
683ed68ebcaSMichal Kazior 	int err;
684ed68ebcaSMichal Kazior 
685ed68ebcaSMichal Kazior 	lockdep_assert_held(&local->mtx);
686ed68ebcaSMichal Kazior 	lockdep_assert_held(&local->chanctx_mtx);
687ed68ebcaSMichal Kazior 
688ed68ebcaSMichal Kazior 	ctx = ieee80211_alloc_chanctx(local, chandef, mode);
689ed68ebcaSMichal Kazior 	if (!ctx)
690ed68ebcaSMichal Kazior 		return ERR_PTR(-ENOMEM);
691ed68ebcaSMichal Kazior 
692ed68ebcaSMichal Kazior 	err = ieee80211_add_chanctx(local, ctx);
693ed68ebcaSMichal Kazior 	if (err) {
694ed68ebcaSMichal Kazior 		kfree(ctx);
69534a3740dSJohannes Berg 		return ERR_PTR(err);
69635f2fce9SMichal Kazior 	}
69735f2fce9SMichal Kazior 
6983448c005SJohannes Berg 	list_add_rcu(&ctx->list, &local->chanctx_list);
699d01a1e65SMichal Kazior 	return ctx;
700d01a1e65SMichal Kazior }
701d01a1e65SMichal Kazior 
7021f0d54cdSMichal Kazior static void ieee80211_del_chanctx(struct ieee80211_local *local,
703d01a1e65SMichal Kazior 				  struct ieee80211_chanctx *ctx)
704d01a1e65SMichal Kazior {
705d01a1e65SMichal Kazior 	lockdep_assert_held(&local->chanctx_mtx);
706d01a1e65SMichal Kazior 
70755de908aSJohannes Berg 	if (!local->use_chanctx) {
708675a0b04SKarl Beldan 		struct cfg80211_chan_def *chandef = &local->_oper_chandef;
7095e480774SThomas Pedersen 		/* S1G doesn't have 20MHz, so get the correct width for the
7105e480774SThomas Pedersen 		 * current channel.
7115e480774SThomas Pedersen 		 */
7125e480774SThomas Pedersen 		if (chandef->chan->band == NL80211_BAND_S1GHZ)
7135e480774SThomas Pedersen 			chandef->width =
7145e480774SThomas Pedersen 				ieee80211_s1g_channel_width(chandef->chan);
7155e480774SThomas Pedersen 		else
716675a0b04SKarl Beldan 			chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
717675a0b04SKarl Beldan 		chandef->center_freq1 = chandef->chan->center_freq;
718b6011960SThomas Pedersen 		chandef->freq1_offset = chandef->chan->freq_offset;
719675a0b04SKarl Beldan 		chandef->center_freq2 = 0;
720e4746851SSimon Wunderlich 
721e4746851SSimon Wunderlich 		/* NOTE: Disabling radar is only valid here for
722e4746851SSimon Wunderlich 		 * single channel context. To be sure, check it ...
723e4746851SSimon Wunderlich 		 */
7241f0d54cdSMichal Kazior 		WARN_ON(local->hw.conf.radar_enabled &&
7251f0d54cdSMichal Kazior 			!list_empty(&local->chanctx_list));
7261f0d54cdSMichal Kazior 
727e4746851SSimon Wunderlich 		local->hw.conf.radar_enabled = false;
728e4746851SSimon Wunderlich 
7299b4816f5SMichal Kazior 		ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
73055de908aSJohannes Berg 	} else {
73135f2fce9SMichal Kazior 		drv_remove_chanctx(local, ctx);
73255de908aSJohannes Berg 	}
73335f2fce9SMichal Kazior 
734fd0f979aSJohannes Berg 	ieee80211_recalc_idle(local);
735d01a1e65SMichal Kazior }
736d01a1e65SMichal Kazior 
7371f0d54cdSMichal Kazior static void ieee80211_free_chanctx(struct ieee80211_local *local,
738d01a1e65SMichal Kazior 				   struct ieee80211_chanctx *ctx)
739d01a1e65SMichal Kazior {
740d01a1e65SMichal Kazior 	lockdep_assert_held(&local->chanctx_mtx);
741d01a1e65SMichal Kazior 
742c0166da9SMichal Kazior 	WARN_ON_ONCE(ieee80211_chanctx_refcount(local, ctx) != 0);
74335f2fce9SMichal Kazior 
7441f0d54cdSMichal Kazior 	list_del_rcu(&ctx->list);
7451f0d54cdSMichal Kazior 	ieee80211_del_chanctx(local, ctx);
7461f0d54cdSMichal Kazior 	kfree_rcu(ctx, rcu_head);
747d01a1e65SMichal Kazior }
748d01a1e65SMichal Kazior 
7490fabfaafSArik Nemtsov void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local,
750e89a96f5SMichal Kazior 				       struct ieee80211_chanctx *ctx)
751e89a96f5SMichal Kazior {
752e89a96f5SMichal Kazior 	struct ieee80211_chanctx_conf *conf = &ctx->conf;
753e89a96f5SMichal Kazior 	struct ieee80211_sub_if_data *sdata;
7544bf88530SJohannes Berg 	const struct cfg80211_chan_def *compat = NULL;
7550fabfaafSArik Nemtsov 	struct sta_info *sta;
756e89a96f5SMichal Kazior 
757e89a96f5SMichal Kazior 	lockdep_assert_held(&local->chanctx_mtx);
758e89a96f5SMichal Kazior 
759e89a96f5SMichal Kazior 	rcu_read_lock();
760e89a96f5SMichal Kazior 	list_for_each_entry_rcu(sdata, &local->interfaces, list) {
761b4f85443SJohannes Berg 		int link_id;
7624bf88530SJohannes Berg 
763e89a96f5SMichal Kazior 		if (!ieee80211_sdata_running(sdata))
764e89a96f5SMichal Kazior 			continue;
765b4f85443SJohannes Berg 
7660e67c136SFelix Fietkau 		if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
7670e67c136SFelix Fietkau 			continue;
768e89a96f5SMichal Kazior 
769b4f85443SJohannes Berg 		for (link_id = 0; link_id < ARRAY_SIZE(sdata->link); link_id++) {
770b4f85443SJohannes Berg 			struct ieee80211_bss_conf *link_conf =
771d8675a63SJohannes Berg 				rcu_dereference(sdata->vif.link_conf[link_id]);
7724bf88530SJohannes Berg 
773b4f85443SJohannes Berg 			if (!link_conf)
774b4f85443SJohannes Berg 				continue;
775b4f85443SJohannes Berg 
776b4f85443SJohannes Berg 			if (rcu_access_pointer(link_conf->chanctx_conf) != conf)
777b4f85443SJohannes Berg 				continue;
778b4f85443SJohannes Berg 
779b4f85443SJohannes Berg 			if (!compat)
780b4f85443SJohannes Berg 				compat = &link_conf->chandef;
781b4f85443SJohannes Berg 
782b4f85443SJohannes Berg 			compat = cfg80211_chandef_compatible(&link_conf->chandef,
783b4f85443SJohannes Berg 							     compat);
784a00f4f6eSMichal Kazior 			if (WARN_ON_ONCE(!compat))
7854bf88530SJohannes Berg 				break;
786e89a96f5SMichal Kazior 		}
787b4f85443SJohannes Berg 	}
7880fabfaafSArik Nemtsov 
7890fabfaafSArik Nemtsov 	/* TDLS peers can sometimes affect the chandef width */
7900fabfaafSArik Nemtsov 	list_for_each_entry_rcu(sta, &local->sta_list, list) {
7910fabfaafSArik Nemtsov 		if (!sta->uploaded ||
7920fabfaafSArik Nemtsov 		    !test_sta_flag(sta, WLAN_STA_TDLS_WIDER_BW) ||
7930fabfaafSArik Nemtsov 		    !test_sta_flag(sta, WLAN_STA_AUTHORIZED) ||
7940fabfaafSArik Nemtsov 		    !sta->tdls_chandef.chan)
7950fabfaafSArik Nemtsov 			continue;
7960fabfaafSArik Nemtsov 
7970fabfaafSArik Nemtsov 		compat = cfg80211_chandef_compatible(&sta->tdls_chandef,
7980fabfaafSArik Nemtsov 						     compat);
7990fabfaafSArik Nemtsov 		if (WARN_ON_ONCE(!compat))
8000fabfaafSArik Nemtsov 			break;
8010fabfaafSArik Nemtsov 	}
802e89a96f5SMichal Kazior 	rcu_read_unlock();
803e89a96f5SMichal Kazior 
804a00f4f6eSMichal Kazior 	if (!compat)
8054bf88530SJohannes Berg 		return;
806e89a96f5SMichal Kazior 
807d6c37509SMordechay Goodstein 	ieee80211_change_chanctx(local, ctx, ctx, compat);
808e89a96f5SMichal Kazior }
809e89a96f5SMichal Kazior 
810367bbd10SJohannes Berg static void ieee80211_recalc_radar_chanctx(struct ieee80211_local *local,
811367bbd10SJohannes Berg 					   struct ieee80211_chanctx *chanctx)
812367bbd10SJohannes Berg {
813367bbd10SJohannes Berg 	bool radar_enabled;
814367bbd10SJohannes Berg 
815367bbd10SJohannes Berg 	lockdep_assert_held(&local->chanctx_mtx);
8165cbc95a7SEliad Peller 	/* for ieee80211_is_radar_required */
81734a3740dSJohannes Berg 	lockdep_assert_held(&local->mtx);
818367bbd10SJohannes Berg 
819e7f2337aSEliad Peller 	radar_enabled = ieee80211_chanctx_radar_required(local, chanctx);
820367bbd10SJohannes Berg 
821367bbd10SJohannes Berg 	if (radar_enabled == chanctx->conf.radar_enabled)
822367bbd10SJohannes Berg 		return;
823367bbd10SJohannes Berg 
824367bbd10SJohannes Berg 	chanctx->conf.radar_enabled = radar_enabled;
825367bbd10SJohannes Berg 
826367bbd10SJohannes Berg 	if (!local->use_chanctx) {
827367bbd10SJohannes Berg 		local->hw.conf.radar_enabled = chanctx->conf.radar_enabled;
828367bbd10SJohannes Berg 		ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
829367bbd10SJohannes Berg 	}
830367bbd10SJohannes Berg 
831367bbd10SJohannes Berg 	drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RADAR);
832367bbd10SJohannes Berg }
833367bbd10SJohannes Berg 
834b4f85443SJohannes Berg static int ieee80211_assign_link_chanctx(struct ieee80211_link_data *link,
83577eeba97SLuciano Coelho 					 struct ieee80211_chanctx *new_ctx)
836d01a1e65SMichal Kazior {
837b4f85443SJohannes Berg 	struct ieee80211_sub_if_data *sdata = link->sdata;
83835f2fce9SMichal Kazior 	struct ieee80211_local *local = sdata->local;
83977eeba97SLuciano Coelho 	struct ieee80211_chanctx_conf *conf;
84077eeba97SLuciano Coelho 	struct ieee80211_chanctx *curr_ctx = NULL;
84177eeba97SLuciano Coelho 	int ret = 0;
842d01a1e65SMichal Kazior 
843708d50edSAyala Beker 	if (WARN_ON(sdata->vif.type == NL80211_IFTYPE_NAN))
844708d50edSAyala Beker 		return -ENOTSUPP;
845708d50edSAyala Beker 
846d8675a63SJohannes Berg 	conf = rcu_dereference_protected(link->conf->chanctx_conf,
84777eeba97SLuciano Coelho 					 lockdep_is_held(&local->chanctx_mtx));
848d01a1e65SMichal Kazior 
84977eeba97SLuciano Coelho 	if (conf) {
85077eeba97SLuciano Coelho 		curr_ctx = container_of(conf, struct ieee80211_chanctx, conf);
85135f2fce9SMichal Kazior 
852*727eff4dSGregory Greenman 		drv_unassign_vif_chanctx(local, sdata, link->conf, curr_ctx);
85377eeba97SLuciano Coelho 		conf = NULL;
854b4f85443SJohannes Berg 		list_del(&link->assigned_chanctx_list);
85577eeba97SLuciano Coelho 	}
85677eeba97SLuciano Coelho 
85777eeba97SLuciano Coelho 	if (new_ctx) {
858*727eff4dSGregory Greenman 		ret = drv_assign_vif_chanctx(local, sdata, link->conf, new_ctx);
85977eeba97SLuciano Coelho 		if (ret)
86077eeba97SLuciano Coelho 			goto out;
86177eeba97SLuciano Coelho 
86277eeba97SLuciano Coelho 		conf = &new_ctx->conf;
863b4f85443SJohannes Berg 		list_add(&link->assigned_chanctx_list,
864b4f85443SJohannes Berg 			 &new_ctx->assigned_links);
86577eeba97SLuciano Coelho 	}
86677eeba97SLuciano Coelho 
86777eeba97SLuciano Coelho out:
868d8675a63SJohannes Berg 	rcu_assign_pointer(link->conf->chanctx_conf, conf);
86977eeba97SLuciano Coelho 
870f276e20bSJohannes Berg 	sdata->vif.cfg.idle = !conf;
87177eeba97SLuciano Coelho 
872c0166da9SMichal Kazior 	if (curr_ctx && ieee80211_chanctx_num_assigned(local, curr_ctx) > 0) {
87377eeba97SLuciano Coelho 		ieee80211_recalc_chanctx_chantype(local, curr_ctx);
87477eeba97SLuciano Coelho 		ieee80211_recalc_smps_chanctx(local, curr_ctx);
87577eeba97SLuciano Coelho 		ieee80211_recalc_radar_chanctx(local, curr_ctx);
87677eeba97SLuciano Coelho 		ieee80211_recalc_chanctx_min_def(local, curr_ctx);
87777eeba97SLuciano Coelho 	}
87877eeba97SLuciano Coelho 
879c0166da9SMichal Kazior 	if (new_ctx && ieee80211_chanctx_num_assigned(local, new_ctx) > 0) {
880db82d8a9SLorenzo Bianconi 		ieee80211_recalc_txpower(sdata, false);
88177eeba97SLuciano Coelho 		ieee80211_recalc_chanctx_min_def(local, new_ctx);
88277eeba97SLuciano Coelho 	}
8835bbe754dSJohannes Berg 
8845bbe754dSJohannes Berg 	if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE &&
8855bbe754dSJohannes Berg 	    sdata->vif.type != NL80211_IFTYPE_MONITOR)
8867b7090b4SJohannes Berg 		ieee80211_vif_cfg_change_notify(sdata, BSS_CHANGED_IDLE);
887fd0f979aSJohannes Berg 
88817c18bf8SJohannes Berg 	ieee80211_check_fast_xmit_iface(sdata);
88917c18bf8SJohannes Berg 
89077eeba97SLuciano Coelho 	return ret;
891d01a1e65SMichal Kazior }
892d01a1e65SMichal Kazior 
89304ecd257SJohannes Berg void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local,
89404ecd257SJohannes Berg 				   struct ieee80211_chanctx *chanctx)
89504ecd257SJohannes Berg {
89604ecd257SJohannes Berg 	struct ieee80211_sub_if_data *sdata;
89704ecd257SJohannes Berg 	u8 rx_chains_static, rx_chains_dynamic;
89804ecd257SJohannes Berg 
89904ecd257SJohannes Berg 	lockdep_assert_held(&local->chanctx_mtx);
90004ecd257SJohannes Berg 
90104ecd257SJohannes Berg 	rx_chains_static = 1;
90204ecd257SJohannes Berg 	rx_chains_dynamic = 1;
90304ecd257SJohannes Berg 
90404ecd257SJohannes Berg 	rcu_read_lock();
90504ecd257SJohannes Berg 	list_for_each_entry_rcu(sdata, &local->interfaces, list) {
90604ecd257SJohannes Berg 		u8 needed_static, needed_dynamic;
907b4f85443SJohannes Berg 		unsigned int link_id;
90804ecd257SJohannes Berg 
90904ecd257SJohannes Berg 		if (!ieee80211_sdata_running(sdata))
91004ecd257SJohannes Berg 			continue;
91104ecd257SJohannes Berg 
91204ecd257SJohannes Berg 		switch (sdata->vif.type) {
91304ecd257SJohannes Berg 		case NL80211_IFTYPE_STATION:
91404ecd257SJohannes Berg 			if (!sdata->u.mgd.associated)
91504ecd257SJohannes Berg 				continue;
91604ecd257SJohannes Berg 			break;
91704ecd257SJohannes Berg 		case NL80211_IFTYPE_AP:
91804ecd257SJohannes Berg 		case NL80211_IFTYPE_ADHOC:
91904ecd257SJohannes Berg 		case NL80211_IFTYPE_MESH_POINT:
920239281f8SRostislav Lisovy 		case NL80211_IFTYPE_OCB:
92104ecd257SJohannes Berg 			break;
92204ecd257SJohannes Berg 		default:
923b4f85443SJohannes Berg 			continue;
92404ecd257SJohannes Berg 		}
92504ecd257SJohannes Berg 
926b4f85443SJohannes Berg 		for (link_id = 0; link_id < ARRAY_SIZE(sdata->link); link_id++) {
927d8675a63SJohannes Berg 			struct ieee80211_link_data *link;
928b4f85443SJohannes Berg 
929d8675a63SJohannes Berg 			link = rcu_dereference(sdata->link[link_id]);
930d8675a63SJohannes Berg 
931d8675a63SJohannes Berg 			if (!link)
932b4f85443SJohannes Berg 				continue;
933b4f85443SJohannes Berg 
934d8675a63SJohannes Berg 			if (rcu_access_pointer(link->conf->chanctx_conf) != &chanctx->conf)
935b4f85443SJohannes Berg 				continue;
936b4f85443SJohannes Berg 
937b4f85443SJohannes Berg 			switch (link->smps_mode) {
93804ecd257SJohannes Berg 			default:
93904ecd257SJohannes Berg 				WARN_ONCE(1, "Invalid SMPS mode %d\n",
940b4f85443SJohannes Berg 					  link->smps_mode);
941fc0561dcSGustavo A. R. Silva 				fallthrough;
94204ecd257SJohannes Berg 			case IEEE80211_SMPS_OFF:
943b4f85443SJohannes Berg 				needed_static = link->needed_rx_chains;
944b4f85443SJohannes Berg 				needed_dynamic = link->needed_rx_chains;
94504ecd257SJohannes Berg 				break;
94604ecd257SJohannes Berg 			case IEEE80211_SMPS_DYNAMIC:
94704ecd257SJohannes Berg 				needed_static = 1;
948b4f85443SJohannes Berg 				needed_dynamic = link->needed_rx_chains;
94904ecd257SJohannes Berg 				break;
95004ecd257SJohannes Berg 			case IEEE80211_SMPS_STATIC:
95104ecd257SJohannes Berg 				needed_static = 1;
95204ecd257SJohannes Berg 				needed_dynamic = 1;
95304ecd257SJohannes Berg 				break;
95404ecd257SJohannes Berg 			}
95504ecd257SJohannes Berg 
95604ecd257SJohannes Berg 			rx_chains_static = max(rx_chains_static, needed_static);
95704ecd257SJohannes Berg 			rx_chains_dynamic = max(rx_chains_dynamic, needed_dynamic);
95804ecd257SJohannes Berg 		}
959b4f85443SJohannes Berg 	}
9607b8a9cddSIdo Yariv 
9617b8a9cddSIdo Yariv 	/* Disable SMPS for the monitor interface */
9627b8a9cddSIdo Yariv 	sdata = rcu_dereference(local->monitor_sdata);
9637b8a9cddSIdo Yariv 	if (sdata &&
964d8675a63SJohannes Berg 	    rcu_access_pointer(sdata->vif.bss_conf.chanctx_conf) == &chanctx->conf)
9657b8a9cddSIdo Yariv 		rx_chains_dynamic = rx_chains_static = local->rx_chains;
9667b8a9cddSIdo Yariv 
96704ecd257SJohannes Berg 	rcu_read_unlock();
96804ecd257SJohannes Berg 
96904ecd257SJohannes Berg 	if (!local->use_chanctx) {
97004ecd257SJohannes Berg 		if (rx_chains_static > 1)
97104ecd257SJohannes Berg 			local->smps_mode = IEEE80211_SMPS_OFF;
97204ecd257SJohannes Berg 		else if (rx_chains_dynamic > 1)
97304ecd257SJohannes Berg 			local->smps_mode = IEEE80211_SMPS_DYNAMIC;
97404ecd257SJohannes Berg 		else
97504ecd257SJohannes Berg 			local->smps_mode = IEEE80211_SMPS_STATIC;
97604ecd257SJohannes Berg 		ieee80211_hw_config(local, 0);
97704ecd257SJohannes Berg 	}
97804ecd257SJohannes Berg 
97904ecd257SJohannes Berg 	if (rx_chains_static == chanctx->conf.rx_chains_static &&
98004ecd257SJohannes Berg 	    rx_chains_dynamic == chanctx->conf.rx_chains_dynamic)
98104ecd257SJohannes Berg 		return;
98204ecd257SJohannes Berg 
98304ecd257SJohannes Berg 	chanctx->conf.rx_chains_static = rx_chains_static;
98404ecd257SJohannes Berg 	chanctx->conf.rx_chains_dynamic = rx_chains_dynamic;
98504ecd257SJohannes Berg 	drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RX_CHAINS);
98604ecd257SJohannes Berg }
98704ecd257SJohannes Berg 
98811335a55SLuciano Coelho static void
989b4f85443SJohannes Berg __ieee80211_link_copy_chanctx_to_vlans(struct ieee80211_link_data *link,
99011335a55SLuciano Coelho 				       bool clear)
99111335a55SLuciano Coelho {
992b4f85443SJohannes Berg 	struct ieee80211_sub_if_data *sdata = link->sdata;
993b4f85443SJohannes Berg 	unsigned int link_id = link->link_id;
994d8675a63SJohannes Berg 	struct ieee80211_bss_conf *link_conf = link->conf;
99533926eb7SJohannes Berg 	struct ieee80211_local *local __maybe_unused = sdata->local;
99611335a55SLuciano Coelho 	struct ieee80211_sub_if_data *vlan;
99711335a55SLuciano Coelho 	struct ieee80211_chanctx_conf *conf;
99811335a55SLuciano Coelho 
99911335a55SLuciano Coelho 	if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP))
100011335a55SLuciano Coelho 		return;
100111335a55SLuciano Coelho 
100211335a55SLuciano Coelho 	lockdep_assert_held(&local->mtx);
100311335a55SLuciano Coelho 
100411335a55SLuciano Coelho 	/* Check that conf exists, even when clearing this function
100511335a55SLuciano Coelho 	 * must be called with the AP's channel context still there
100611335a55SLuciano Coelho 	 * as it would otherwise cause VLANs to have an invalid
100711335a55SLuciano Coelho 	 * channel context pointer for a while, possibly pointing
100811335a55SLuciano Coelho 	 * to a channel context that has already been freed.
100911335a55SLuciano Coelho 	 */
1010b4f85443SJohannes Berg 	conf = rcu_dereference_protected(link_conf->chanctx_conf,
101111335a55SLuciano Coelho 					 lockdep_is_held(&local->chanctx_mtx));
101211335a55SLuciano Coelho 	WARN_ON(!conf);
101311335a55SLuciano Coelho 
101411335a55SLuciano Coelho 	if (clear)
101511335a55SLuciano Coelho 		conf = NULL;
101611335a55SLuciano Coelho 
1017d8675a63SJohannes Berg 	rcu_read_lock();
1018d8675a63SJohannes Berg 	list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) {
1019d8675a63SJohannes Berg 		struct ieee80211_bss_conf *vlan_conf;
1020d8675a63SJohannes Berg 
1021d8675a63SJohannes Berg 		vlan_conf = rcu_dereference(vlan->vif.link_conf[link_id]);
1022d8675a63SJohannes Berg 		if (WARN_ON(!vlan_conf))
1023d8675a63SJohannes Berg 			continue;
1024d8675a63SJohannes Berg 
1025d8675a63SJohannes Berg 		rcu_assign_pointer(vlan_conf->chanctx_conf, conf);
1026d8675a63SJohannes Berg 	}
1027d8675a63SJohannes Berg 	rcu_read_unlock();
102811335a55SLuciano Coelho }
102911335a55SLuciano Coelho 
1030b4f85443SJohannes Berg void ieee80211_link_copy_chanctx_to_vlans(struct ieee80211_link_data *link,
103111335a55SLuciano Coelho 					  bool clear)
103211335a55SLuciano Coelho {
1033b4f85443SJohannes Berg 	struct ieee80211_local *local = link->sdata->local;
103411335a55SLuciano Coelho 
103511335a55SLuciano Coelho 	mutex_lock(&local->chanctx_mtx);
103611335a55SLuciano Coelho 
1037b4f85443SJohannes Berg 	__ieee80211_link_copy_chanctx_to_vlans(link, clear);
103811335a55SLuciano Coelho 
103911335a55SLuciano Coelho 	mutex_unlock(&local->chanctx_mtx);
104011335a55SLuciano Coelho }
104111335a55SLuciano Coelho 
1042b4f85443SJohannes Berg int ieee80211_link_unreserve_chanctx(struct ieee80211_link_data *link)
104311335a55SLuciano Coelho {
1044b4f85443SJohannes Berg 	struct ieee80211_sub_if_data *sdata = link->sdata;
1045b4f85443SJohannes Berg 	struct ieee80211_chanctx *ctx = link->reserved_chanctx;
1046e3afb920SMichal Kazior 
104711335a55SLuciano Coelho 	lockdep_assert_held(&sdata->local->chanctx_mtx);
104811335a55SLuciano Coelho 
1049e3afb920SMichal Kazior 	if (WARN_ON(!ctx))
105011335a55SLuciano Coelho 		return -EINVAL;
105111335a55SLuciano Coelho 
1052b4f85443SJohannes Berg 	list_del(&link->reserved_chanctx_list);
1053b4f85443SJohannes Berg 	link->reserved_chanctx = NULL;
105411335a55SLuciano Coelho 
10555bcae31dSMichal Kazior 	if (ieee80211_chanctx_refcount(sdata->local, ctx) == 0) {
10565bcae31dSMichal Kazior 		if (ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER) {
10575bcae31dSMichal Kazior 			if (WARN_ON(!ctx->replace_ctx))
10585bcae31dSMichal Kazior 				return -EINVAL;
10595bcae31dSMichal Kazior 
10605bcae31dSMichal Kazior 			WARN_ON(ctx->replace_ctx->replace_state !=
10615bcae31dSMichal Kazior 			        IEEE80211_CHANCTX_WILL_BE_REPLACED);
10625bcae31dSMichal Kazior 			WARN_ON(ctx->replace_ctx->replace_ctx != ctx);
10635bcae31dSMichal Kazior 
10645bcae31dSMichal Kazior 			ctx->replace_ctx->replace_ctx = NULL;
10655bcae31dSMichal Kazior 			ctx->replace_ctx->replace_state =
10665bcae31dSMichal Kazior 					IEEE80211_CHANCTX_REPLACE_NONE;
10675bcae31dSMichal Kazior 
10685bcae31dSMichal Kazior 			list_del_rcu(&ctx->list);
10695bcae31dSMichal Kazior 			kfree_rcu(ctx, rcu_head);
10705bcae31dSMichal Kazior 		} else {
1071e3afb920SMichal Kazior 			ieee80211_free_chanctx(sdata->local, ctx);
10725bcae31dSMichal Kazior 		}
10735bcae31dSMichal Kazior 	}
1074e3afb920SMichal Kazior 
107511335a55SLuciano Coelho 	return 0;
107611335a55SLuciano Coelho }
107711335a55SLuciano Coelho 
1078b4f85443SJohannes Berg int ieee80211_link_reserve_chanctx(struct ieee80211_link_data *link,
107911335a55SLuciano Coelho 				   const struct cfg80211_chan_def *chandef,
108009332481SMichal Kazior 				   enum ieee80211_chanctx_mode mode,
108109332481SMichal Kazior 				   bool radar_required)
108211335a55SLuciano Coelho {
1083b4f85443SJohannes Berg 	struct ieee80211_sub_if_data *sdata = link->sdata;
108411335a55SLuciano Coelho 	struct ieee80211_local *local = sdata->local;
10855bcae31dSMichal Kazior 	struct ieee80211_chanctx *new_ctx, *curr_ctx, *ctx;
108611335a55SLuciano Coelho 
10875bcae31dSMichal Kazior 	lockdep_assert_held(&local->chanctx_mtx);
108811335a55SLuciano Coelho 
1089b4f85443SJohannes Berg 	curr_ctx = ieee80211_link_get_chanctx(link);
10905bcae31dSMichal Kazior 	if (curr_ctx && local->use_chanctx && !local->ops->switch_vif_chanctx)
10915bcae31dSMichal Kazior 		return -ENOTSUPP;
109211335a55SLuciano Coelho 
109313f348a8SMichal Kazior 	new_ctx = ieee80211_find_reservation_chanctx(local, chandef, mode);
109411335a55SLuciano Coelho 	if (!new_ctx) {
10955bcae31dSMichal Kazior 		if (ieee80211_can_create_new_chanctx(local)) {
109611335a55SLuciano Coelho 			new_ctx = ieee80211_new_chanctx(local, chandef, mode);
10975bcae31dSMichal Kazior 			if (IS_ERR(new_ctx))
10985bcae31dSMichal Kazior 				return PTR_ERR(new_ctx);
1099c2b90ad8SMichal Kazior 		} else {
11005bcae31dSMichal Kazior 			if (!curr_ctx ||
11015bcae31dSMichal Kazior 			    (curr_ctx->replace_state ==
11025bcae31dSMichal Kazior 			     IEEE80211_CHANCTX_WILL_BE_REPLACED) ||
1103b4f85443SJohannes Berg 			    !list_empty(&curr_ctx->reserved_links)) {
11045bcae31dSMichal Kazior 				/*
1105b4f85443SJohannes Berg 				 * Another link already requested this context
11065bcae31dSMichal Kazior 				 * for a reservation. Find another one hoping
1107b4f85443SJohannes Berg 				 * all links assigned to it will also switch
11085bcae31dSMichal Kazior 				 * soon enough.
11095bcae31dSMichal Kazior 				 *
11105bcae31dSMichal Kazior 				 * TODO: This needs a little more work as some
11115bcae31dSMichal Kazior 				 * cases (more than 2 chanctx capable devices)
11125bcae31dSMichal Kazior 				 * may fail which could otherwise succeed
11135bcae31dSMichal Kazior 				 * provided some channel context juggling was
11145bcae31dSMichal Kazior 				 * performed.
11155bcae31dSMichal Kazior 				 *
1116b4f85443SJohannes Berg 				 * Consider ctx1..3, link1..6, each ctx has 2
1117b4f85443SJohannes Berg 				 * links. link1 and link2 from ctx1 request new
11185bcae31dSMichal Kazior 				 * different chandefs starting 2 in-place
11195bcae31dSMichal Kazior 				 * reserations with ctx4 and ctx5 replacing
1120b4f85443SJohannes Berg 				 * ctx1 and ctx2 respectively. Next link5 and
1121b4f85443SJohannes Berg 				 * link6 from ctx3 reserve ctx4. If link3 and
1122b4f85443SJohannes Berg 				 * link4 remain on ctx2 as they are then this
11235bcae31dSMichal Kazior 				 * fails unless `replace_ctx` from ctx5 is
11245bcae31dSMichal Kazior 				 * replaced with ctx3.
11255bcae31dSMichal Kazior 				 */
11265bcae31dSMichal Kazior 				list_for_each_entry(ctx, &local->chanctx_list,
11275bcae31dSMichal Kazior 						    list) {
11285bcae31dSMichal Kazior 					if (ctx->replace_state !=
11295bcae31dSMichal Kazior 					    IEEE80211_CHANCTX_REPLACE_NONE)
11305bcae31dSMichal Kazior 						continue;
11315bcae31dSMichal Kazior 
1132b4f85443SJohannes Berg 					if (!list_empty(&ctx->reserved_links))
11335bcae31dSMichal Kazior 						continue;
11345bcae31dSMichal Kazior 
11355bcae31dSMichal Kazior 					curr_ctx = ctx;
11365bcae31dSMichal Kazior 					break;
11375bcae31dSMichal Kazior 				}
11385bcae31dSMichal Kazior 			}
11395bcae31dSMichal Kazior 
11405bcae31dSMichal Kazior 			/*
11415bcae31dSMichal Kazior 			 * If that's true then all available contexts already
11425bcae31dSMichal Kazior 			 * have reservations and cannot be used.
11435bcae31dSMichal Kazior 			 */
11445bcae31dSMichal Kazior 			if (!curr_ctx ||
11455bcae31dSMichal Kazior 			    (curr_ctx->replace_state ==
11465bcae31dSMichal Kazior 			     IEEE80211_CHANCTX_WILL_BE_REPLACED) ||
1147b4f85443SJohannes Berg 			    !list_empty(&curr_ctx->reserved_links))
11485bcae31dSMichal Kazior 				return -EBUSY;
11495bcae31dSMichal Kazior 
11505bcae31dSMichal Kazior 			new_ctx = ieee80211_alloc_chanctx(local, chandef, mode);
11515bcae31dSMichal Kazior 			if (!new_ctx)
11525bcae31dSMichal Kazior 				return -ENOMEM;
11535bcae31dSMichal Kazior 
11545bcae31dSMichal Kazior 			new_ctx->replace_ctx = curr_ctx;
11555bcae31dSMichal Kazior 			new_ctx->replace_state =
11565bcae31dSMichal Kazior 					IEEE80211_CHANCTX_REPLACES_OTHER;
11575bcae31dSMichal Kazior 
11585bcae31dSMichal Kazior 			curr_ctx->replace_ctx = new_ctx;
11595bcae31dSMichal Kazior 			curr_ctx->replace_state =
11605bcae31dSMichal Kazior 					IEEE80211_CHANCTX_WILL_BE_REPLACED;
11615bcae31dSMichal Kazior 
11625bcae31dSMichal Kazior 			list_add_rcu(&new_ctx->list, &local->chanctx_list);
116311335a55SLuciano Coelho 		}
11645d52ee81SLuciano Coelho 	}
116511335a55SLuciano Coelho 
1166b4f85443SJohannes Berg 	list_add(&link->reserved_chanctx_list, &new_ctx->reserved_links);
1167b4f85443SJohannes Berg 	link->reserved_chanctx = new_ctx;
1168b4f85443SJohannes Berg 	link->reserved_chandef = *chandef;
1169b4f85443SJohannes Berg 	link->reserved_radar_required = radar_required;
1170b4f85443SJohannes Berg 	link->reserved_ready = false;
11715bcae31dSMichal Kazior 
11725bcae31dSMichal Kazior 	return 0;
117311335a55SLuciano Coelho }
117411335a55SLuciano Coelho 
117503078de4SMichal Kazior static void
1176b4f85443SJohannes Berg ieee80211_link_chanctx_reservation_complete(struct ieee80211_link_data *link)
117703078de4SMichal Kazior {
1178b4f85443SJohannes Berg 	struct ieee80211_sub_if_data *sdata = link->sdata;
1179b4f85443SJohannes Berg 
118003078de4SMichal Kazior 	switch (sdata->vif.type) {
118103078de4SMichal Kazior 	case NL80211_IFTYPE_ADHOC:
118203078de4SMichal Kazior 	case NL80211_IFTYPE_AP:
118303078de4SMichal Kazior 	case NL80211_IFTYPE_MESH_POINT:
11846e0bd6c3SRostislav Lisovy 	case NL80211_IFTYPE_OCB:
118503078de4SMichal Kazior 		ieee80211_queue_work(&sdata->local->hw,
1186b4f85443SJohannes Berg 				     &link->csa_finalize_work);
118703078de4SMichal Kazior 		break;
118803078de4SMichal Kazior 	case NL80211_IFTYPE_STATION:
11894c3ebc56SMichal Kazior 		ieee80211_queue_work(&sdata->local->hw,
11905bd5666dSJohannes Berg 				     &link->u.mgd.chswitch_work);
11914c3ebc56SMichal Kazior 		break;
11924c3ebc56SMichal Kazior 	case NL80211_IFTYPE_UNSPECIFIED:
119303078de4SMichal Kazior 	case NL80211_IFTYPE_AP_VLAN:
119403078de4SMichal Kazior 	case NL80211_IFTYPE_WDS:
119503078de4SMichal Kazior 	case NL80211_IFTYPE_MONITOR:
119603078de4SMichal Kazior 	case NL80211_IFTYPE_P2P_CLIENT:
119703078de4SMichal Kazior 	case NL80211_IFTYPE_P2P_GO:
119803078de4SMichal Kazior 	case NL80211_IFTYPE_P2P_DEVICE:
1199cb3b7d87SAyala Beker 	case NL80211_IFTYPE_NAN:
120003078de4SMichal Kazior 	case NUM_NL80211_IFTYPES:
120103078de4SMichal Kazior 		WARN_ON(1);
120203078de4SMichal Kazior 		break;
120303078de4SMichal Kazior 	}
120403078de4SMichal Kazior }
120503078de4SMichal Kazior 
12062967e031SFelix Fietkau static void
1207b4f85443SJohannes Berg ieee80211_link_update_chandef(struct ieee80211_link_data *link,
12082967e031SFelix Fietkau 			      const struct cfg80211_chan_def *chandef)
12092967e031SFelix Fietkau {
1210b4f85443SJohannes Berg 	struct ieee80211_sub_if_data *sdata = link->sdata;
1211b4f85443SJohannes Berg 	unsigned int link_id = link->link_id;
12122967e031SFelix Fietkau 	struct ieee80211_sub_if_data *vlan;
12132967e031SFelix Fietkau 
1214d8675a63SJohannes Berg 	link->conf->chandef = *chandef;
12152967e031SFelix Fietkau 
12162967e031SFelix Fietkau 	if (sdata->vif.type != NL80211_IFTYPE_AP)
12172967e031SFelix Fietkau 		return;
12182967e031SFelix Fietkau 
1219d8675a63SJohannes Berg 	rcu_read_lock();
1220d8675a63SJohannes Berg 	list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) {
1221d8675a63SJohannes Berg 		struct ieee80211_bss_conf *vlan_conf;
1222d8675a63SJohannes Berg 
1223d8675a63SJohannes Berg 		vlan_conf = rcu_dereference(vlan->vif.link_conf[link_id]);
1224d8675a63SJohannes Berg 		if (WARN_ON(!vlan_conf))
1225d8675a63SJohannes Berg 			continue;
1226d8675a63SJohannes Berg 
1227d8675a63SJohannes Berg 		vlan_conf->chandef = *chandef;
1228d8675a63SJohannes Berg 	}
1229d8675a63SJohannes Berg 	rcu_read_unlock();
12302967e031SFelix Fietkau }
12312967e031SFelix Fietkau 
12325bcae31dSMichal Kazior static int
1233b4f85443SJohannes Berg ieee80211_link_use_reserved_reassign(struct ieee80211_link_data *link)
123411335a55SLuciano Coelho {
1235b4f85443SJohannes Berg 	struct ieee80211_sub_if_data *sdata = link->sdata;
1236d8675a63SJohannes Berg 	struct ieee80211_bss_conf *link_conf = link->conf;
123711335a55SLuciano Coelho 	struct ieee80211_local *local = sdata->local;
12385bcae31dSMichal Kazior 	struct ieee80211_vif_chanctx_switch vif_chsw[1] = {};
12395bcae31dSMichal Kazior 	struct ieee80211_chanctx *old_ctx, *new_ctx;
12405bcae31dSMichal Kazior 	const struct cfg80211_chan_def *chandef;
12415bcae31dSMichal Kazior 	u32 changed = 0;
12425bcae31dSMichal Kazior 	int err;
124311335a55SLuciano Coelho 
124411335a55SLuciano Coelho 	lockdep_assert_held(&local->mtx);
12455bcae31dSMichal Kazior 	lockdep_assert_held(&local->chanctx_mtx);
124611335a55SLuciano Coelho 
1247b4f85443SJohannes Berg 	new_ctx = link->reserved_chanctx;
1248b4f85443SJohannes Berg 	old_ctx = ieee80211_link_get_chanctx(link);
124911335a55SLuciano Coelho 
1250b4f85443SJohannes Berg 	if (WARN_ON(!link->reserved_ready))
12515bcae31dSMichal Kazior 		return -EBUSY;
125211335a55SLuciano Coelho 
12535bcae31dSMichal Kazior 	if (WARN_ON(!new_ctx))
12545bcae31dSMichal Kazior 		return -EINVAL;
125511335a55SLuciano Coelho 
12565bcae31dSMichal Kazior 	if (WARN_ON(!old_ctx))
12575bcae31dSMichal Kazior 		return -EINVAL;
125811335a55SLuciano Coelho 
12595bcae31dSMichal Kazior 	if (WARN_ON(new_ctx->replace_state ==
12605bcae31dSMichal Kazior 		    IEEE80211_CHANCTX_REPLACES_OTHER))
12615bcae31dSMichal Kazior 		return -EINVAL;
126211335a55SLuciano Coelho 
12635bcae31dSMichal Kazior 	chandef = ieee80211_chanctx_non_reserved_chandef(local, new_ctx,
1264b4f85443SJohannes Berg 				&link->reserved_chandef);
12655bcae31dSMichal Kazior 	if (WARN_ON(!chandef))
12665bcae31dSMichal Kazior 		return -EINVAL;
126711335a55SLuciano Coelho 
1268b4f85443SJohannes Berg 	if (link_conf->chandef.width != link->reserved_chandef.width)
1269d6c37509SMordechay Goodstein 		changed = BSS_CHANGED_BANDWIDTH;
127044b72ca8SIlan Peer 
1271b4f85443SJohannes Berg 	ieee80211_link_update_chandef(link, &link->reserved_chandef);
1272bf45a242SAndrei Otcheretianski 
1273d6c37509SMordechay Goodstein 	ieee80211_change_chanctx(local, new_ctx, old_ctx, chandef);
127444b72ca8SIlan Peer 
12755bcae31dSMichal Kazior 	vif_chsw[0].vif = &sdata->vif;
12765bcae31dSMichal Kazior 	vif_chsw[0].old_ctx = &old_ctx->conf;
12775bcae31dSMichal Kazior 	vif_chsw[0].new_ctx = &new_ctx->conf;
1278*727eff4dSGregory Greenman 	vif_chsw[0].link_conf = link->conf;
12795bcae31dSMichal Kazior 
1280b4f85443SJohannes Berg 	list_del(&link->reserved_chanctx_list);
1281b4f85443SJohannes Berg 	link->reserved_chanctx = NULL;
128211335a55SLuciano Coelho 
12835bcae31dSMichal Kazior 	err = drv_switch_vif_chanctx(local, vif_chsw, 1,
12845bcae31dSMichal Kazior 				     CHANCTX_SWMODE_REASSIGN_VIF);
12855bcae31dSMichal Kazior 	if (err) {
12865bcae31dSMichal Kazior 		if (ieee80211_chanctx_refcount(local, new_ctx) == 0)
12875bcae31dSMichal Kazior 			ieee80211_free_chanctx(local, new_ctx);
12885bcae31dSMichal Kazior 
128903078de4SMichal Kazior 		goto out;
129011335a55SLuciano Coelho 	}
129111335a55SLuciano Coelho 
1292b4f85443SJohannes Berg 	list_move(&link->assigned_chanctx_list, &new_ctx->assigned_links);
1293b4f85443SJohannes Berg 	rcu_assign_pointer(link_conf->chanctx_conf, &new_ctx->conf);
12945bcae31dSMichal Kazior 
129511335a55SLuciano Coelho 	if (sdata->vif.type == NL80211_IFTYPE_AP)
1296b4f85443SJohannes Berg 		__ieee80211_link_copy_chanctx_to_vlans(link, false);
12975bcae31dSMichal Kazior 
129817c18bf8SJohannes Berg 	ieee80211_check_fast_xmit_iface(sdata);
129917c18bf8SJohannes Berg 
13005bcae31dSMichal Kazior 	if (ieee80211_chanctx_refcount(local, old_ctx) == 0)
13015bcae31dSMichal Kazior 		ieee80211_free_chanctx(local, old_ctx);
13025bcae31dSMichal Kazior 
1303d6c37509SMordechay Goodstein 	ieee80211_recalc_chanctx_min_def(local, new_ctx);
1304722ddb0dSEmmanuel Grumbach 	ieee80211_recalc_smps_chanctx(local, new_ctx);
1305722ddb0dSEmmanuel Grumbach 	ieee80211_recalc_radar_chanctx(local, new_ctx);
1306722ddb0dSEmmanuel Grumbach 
13075bcae31dSMichal Kazior 	if (changed)
1308d8675a63SJohannes Berg 		ieee80211_link_info_change_notify(sdata, link, changed);
13095bcae31dSMichal Kazior 
131003078de4SMichal Kazior out:
1311b4f85443SJohannes Berg 	ieee80211_link_chanctx_reservation_complete(link);
13125bcae31dSMichal Kazior 	return err;
13135d52ee81SLuciano Coelho }
131411335a55SLuciano Coelho 
13155bcae31dSMichal Kazior static int
1316b4f85443SJohannes Berg ieee80211_link_use_reserved_assign(struct ieee80211_link_data *link)
13175bcae31dSMichal Kazior {
1318b4f85443SJohannes Berg 	struct ieee80211_sub_if_data *sdata = link->sdata;
13195bcae31dSMichal Kazior 	struct ieee80211_local *local = sdata->local;
13205bcae31dSMichal Kazior 	struct ieee80211_chanctx *old_ctx, *new_ctx;
13215bcae31dSMichal Kazior 	const struct cfg80211_chan_def *chandef;
13225bcae31dSMichal Kazior 	int err;
13235bcae31dSMichal Kazior 
1324d8675a63SJohannes Berg 	old_ctx = ieee80211_link_get_chanctx(link);
1325b4f85443SJohannes Berg 	new_ctx = link->reserved_chanctx;
13265bcae31dSMichal Kazior 
1327b4f85443SJohannes Berg 	if (WARN_ON(!link->reserved_ready))
13285bcae31dSMichal Kazior 		return -EINVAL;
13295bcae31dSMichal Kazior 
13305bcae31dSMichal Kazior 	if (WARN_ON(old_ctx))
13315bcae31dSMichal Kazior 		return -EINVAL;
13325bcae31dSMichal Kazior 
13335bcae31dSMichal Kazior 	if (WARN_ON(!new_ctx))
13345bcae31dSMichal Kazior 		return -EINVAL;
13355bcae31dSMichal Kazior 
13365bcae31dSMichal Kazior 	if (WARN_ON(new_ctx->replace_state ==
13375bcae31dSMichal Kazior 		    IEEE80211_CHANCTX_REPLACES_OTHER))
13385bcae31dSMichal Kazior 		return -EINVAL;
13395bcae31dSMichal Kazior 
13405bcae31dSMichal Kazior 	chandef = ieee80211_chanctx_non_reserved_chandef(local, new_ctx,
1341b4f85443SJohannes Berg 				&link->reserved_chandef);
13425bcae31dSMichal Kazior 	if (WARN_ON(!chandef))
13435bcae31dSMichal Kazior 		return -EINVAL;
13445bcae31dSMichal Kazior 
1345d6c37509SMordechay Goodstein 	ieee80211_change_chanctx(local, new_ctx, new_ctx, chandef);
1346bf45a242SAndrei Otcheretianski 
1347b4f85443SJohannes Berg 	list_del(&link->reserved_chanctx_list);
1348b4f85443SJohannes Berg 	link->reserved_chanctx = NULL;
13495bcae31dSMichal Kazior 
1350b4f85443SJohannes Berg 	err = ieee80211_assign_link_chanctx(link, new_ctx);
13515bcae31dSMichal Kazior 	if (err) {
13525bcae31dSMichal Kazior 		if (ieee80211_chanctx_refcount(local, new_ctx) == 0)
13535bcae31dSMichal Kazior 			ieee80211_free_chanctx(local, new_ctx);
13545bcae31dSMichal Kazior 
13555bcae31dSMichal Kazior 		goto out;
13565bcae31dSMichal Kazior 	}
13575bcae31dSMichal Kazior 
13585bcae31dSMichal Kazior out:
1359b4f85443SJohannes Berg 	ieee80211_link_chanctx_reservation_complete(link);
13605bcae31dSMichal Kazior 	return err;
13615bcae31dSMichal Kazior }
13625bcae31dSMichal Kazior 
13635bcae31dSMichal Kazior static bool
1364b4f85443SJohannes Berg ieee80211_link_has_in_place_reservation(struct ieee80211_link_data *link)
13655bcae31dSMichal Kazior {
1366b4f85443SJohannes Berg 	struct ieee80211_sub_if_data *sdata = link->sdata;
13675bcae31dSMichal Kazior 	struct ieee80211_chanctx *old_ctx, *new_ctx;
13685bcae31dSMichal Kazior 
13695bcae31dSMichal Kazior 	lockdep_assert_held(&sdata->local->chanctx_mtx);
13705bcae31dSMichal Kazior 
1371b4f85443SJohannes Berg 	new_ctx = link->reserved_chanctx;
1372b4f85443SJohannes Berg 	old_ctx = ieee80211_link_get_chanctx(link);
13735bcae31dSMichal Kazior 
13745bcae31dSMichal Kazior 	if (!old_ctx)
13755bcae31dSMichal Kazior 		return false;
13765bcae31dSMichal Kazior 
13775bcae31dSMichal Kazior 	if (WARN_ON(!new_ctx))
13785bcae31dSMichal Kazior 		return false;
13795bcae31dSMichal Kazior 
13805bcae31dSMichal Kazior 	if (old_ctx->replace_state != IEEE80211_CHANCTX_WILL_BE_REPLACED)
13815bcae31dSMichal Kazior 		return false;
13825bcae31dSMichal Kazior 
13835bcae31dSMichal Kazior 	if (new_ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
13845bcae31dSMichal Kazior 		return false;
13855bcae31dSMichal Kazior 
13865bcae31dSMichal Kazior 	return true;
13875bcae31dSMichal Kazior }
13885bcae31dSMichal Kazior 
13895bcae31dSMichal Kazior static int ieee80211_chsw_switch_hwconf(struct ieee80211_local *local,
13905bcae31dSMichal Kazior 					struct ieee80211_chanctx *new_ctx)
13915bcae31dSMichal Kazior {
13925bcae31dSMichal Kazior 	const struct cfg80211_chan_def *chandef;
13935bcae31dSMichal Kazior 
13945bcae31dSMichal Kazior 	lockdep_assert_held(&local->mtx);
13955bcae31dSMichal Kazior 	lockdep_assert_held(&local->chanctx_mtx);
13965bcae31dSMichal Kazior 
13975bcae31dSMichal Kazior 	chandef = ieee80211_chanctx_reserved_chandef(local, new_ctx, NULL);
13985bcae31dSMichal Kazior 	if (WARN_ON(!chandef))
13995bcae31dSMichal Kazior 		return -EINVAL;
14005bcae31dSMichal Kazior 
14015bcae31dSMichal Kazior 	local->hw.conf.radar_enabled = new_ctx->conf.radar_enabled;
14025bcae31dSMichal Kazior 	local->_oper_chandef = *chandef;
14035bcae31dSMichal Kazior 	ieee80211_hw_config(local, 0);
14045bcae31dSMichal Kazior 
14055bcae31dSMichal Kazior 	return 0;
14065bcae31dSMichal Kazior }
14075bcae31dSMichal Kazior 
14085bcae31dSMichal Kazior static int ieee80211_chsw_switch_vifs(struct ieee80211_local *local,
14095bcae31dSMichal Kazior 				      int n_vifs)
14105bcae31dSMichal Kazior {
14115bcae31dSMichal Kazior 	struct ieee80211_vif_chanctx_switch *vif_chsw;
1412b4f85443SJohannes Berg 	struct ieee80211_link_data *link;
14135bcae31dSMichal Kazior 	struct ieee80211_chanctx *ctx, *old_ctx;
14145bcae31dSMichal Kazior 	int i, err;
14155bcae31dSMichal Kazior 
14165bcae31dSMichal Kazior 	lockdep_assert_held(&local->mtx);
14175bcae31dSMichal Kazior 	lockdep_assert_held(&local->chanctx_mtx);
14185bcae31dSMichal Kazior 
14196396bb22SKees Cook 	vif_chsw = kcalloc(n_vifs, sizeof(vif_chsw[0]), GFP_KERNEL);
14205bcae31dSMichal Kazior 	if (!vif_chsw)
14215bcae31dSMichal Kazior 		return -ENOMEM;
14225bcae31dSMichal Kazior 
14235bcae31dSMichal Kazior 	i = 0;
14245bcae31dSMichal Kazior 	list_for_each_entry(ctx, &local->chanctx_list, list) {
14255bcae31dSMichal Kazior 		if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
14265bcae31dSMichal Kazior 			continue;
14275bcae31dSMichal Kazior 
14285bcae31dSMichal Kazior 		if (WARN_ON(!ctx->replace_ctx)) {
14295bcae31dSMichal Kazior 			err = -EINVAL;
14305bcae31dSMichal Kazior 			goto out;
14315bcae31dSMichal Kazior 		}
14325bcae31dSMichal Kazior 
1433b4f85443SJohannes Berg 		list_for_each_entry(link, &ctx->reserved_links,
14345bcae31dSMichal Kazior 				    reserved_chanctx_list) {
1435b4f85443SJohannes Berg 			if (!ieee80211_link_has_in_place_reservation(link))
14365bcae31dSMichal Kazior 				continue;
14375bcae31dSMichal Kazior 
1438b4f85443SJohannes Berg 			old_ctx = ieee80211_link_get_chanctx(link);
1439b4f85443SJohannes Berg 			vif_chsw[i].vif = &link->sdata->vif;
14405bcae31dSMichal Kazior 			vif_chsw[i].old_ctx = &old_ctx->conf;
14415bcae31dSMichal Kazior 			vif_chsw[i].new_ctx = &ctx->conf;
1442*727eff4dSGregory Greenman 			vif_chsw[i].link_conf = link->conf;
14435bcae31dSMichal Kazior 
14445bcae31dSMichal Kazior 			i++;
14455bcae31dSMichal Kazior 		}
14465bcae31dSMichal Kazior 	}
14475bcae31dSMichal Kazior 
14485bcae31dSMichal Kazior 	err = drv_switch_vif_chanctx(local, vif_chsw, n_vifs,
14495bcae31dSMichal Kazior 				     CHANCTX_SWMODE_SWAP_CONTEXTS);
14505bcae31dSMichal Kazior 
14515bcae31dSMichal Kazior out:
14525bcae31dSMichal Kazior 	kfree(vif_chsw);
14535bcae31dSMichal Kazior 	return err;
14545bcae31dSMichal Kazior }
14555bcae31dSMichal Kazior 
14565bcae31dSMichal Kazior static int ieee80211_chsw_switch_ctxs(struct ieee80211_local *local)
14575bcae31dSMichal Kazior {
14585bcae31dSMichal Kazior 	struct ieee80211_chanctx *ctx;
14595bcae31dSMichal Kazior 	int err;
14605bcae31dSMichal Kazior 
14615bcae31dSMichal Kazior 	lockdep_assert_held(&local->mtx);
14625bcae31dSMichal Kazior 	lockdep_assert_held(&local->chanctx_mtx);
14635bcae31dSMichal Kazior 
14645bcae31dSMichal Kazior 	list_for_each_entry(ctx, &local->chanctx_list, list) {
14655bcae31dSMichal Kazior 		if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
14665bcae31dSMichal Kazior 			continue;
14675bcae31dSMichal Kazior 
1468b4f85443SJohannes Berg 		if (!list_empty(&ctx->replace_ctx->assigned_links))
14695bcae31dSMichal Kazior 			continue;
14705bcae31dSMichal Kazior 
14715bcae31dSMichal Kazior 		ieee80211_del_chanctx(local, ctx->replace_ctx);
14725bcae31dSMichal Kazior 		err = ieee80211_add_chanctx(local, ctx);
14735bcae31dSMichal Kazior 		if (err)
14745bcae31dSMichal Kazior 			goto err;
14755bcae31dSMichal Kazior 	}
14765bcae31dSMichal Kazior 
14775bcae31dSMichal Kazior 	return 0;
14785bcae31dSMichal Kazior 
14795bcae31dSMichal Kazior err:
14805bcae31dSMichal Kazior 	WARN_ON(ieee80211_add_chanctx(local, ctx));
14815bcae31dSMichal Kazior 	list_for_each_entry_continue_reverse(ctx, &local->chanctx_list, list) {
14825bcae31dSMichal Kazior 		if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
14835bcae31dSMichal Kazior 			continue;
14845bcae31dSMichal Kazior 
1485b4f85443SJohannes Berg 		if (!list_empty(&ctx->replace_ctx->assigned_links))
14865bcae31dSMichal Kazior 			continue;
14875bcae31dSMichal Kazior 
14885bcae31dSMichal Kazior 		ieee80211_del_chanctx(local, ctx);
14895bcae31dSMichal Kazior 		WARN_ON(ieee80211_add_chanctx(local, ctx->replace_ctx));
14905bcae31dSMichal Kazior 	}
14915bcae31dSMichal Kazior 
14925bcae31dSMichal Kazior 	return err;
14935bcae31dSMichal Kazior }
14945bcae31dSMichal Kazior 
1495649b2a4dSJohannes Berg static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local)
14965bcae31dSMichal Kazior {
14975bcae31dSMichal Kazior 	struct ieee80211_chanctx *ctx, *ctx_tmp, *old_ctx;
14985bcae31dSMichal Kazior 	struct ieee80211_chanctx *new_ctx = NULL;
1499b7f2405cSKirtika Ruchandani 	int err, n_assigned, n_reserved, n_ready;
15005bcae31dSMichal Kazior 	int n_ctx = 0, n_vifs_switch = 0, n_vifs_assign = 0, n_vifs_ctxless = 0;
15015bcae31dSMichal Kazior 
15025bcae31dSMichal Kazior 	lockdep_assert_held(&local->mtx);
15035bcae31dSMichal Kazior 	lockdep_assert_held(&local->chanctx_mtx);
15045bcae31dSMichal Kazior 
15055bcae31dSMichal Kazior 	/*
15065bcae31dSMichal Kazior 	 * If there are 2 independent pairs of channel contexts performing
15075bcae31dSMichal Kazior 	 * cross-switch of their vifs this code will still wait until both are
15085bcae31dSMichal Kazior 	 * ready even though it could be possible to switch one before the
15095bcae31dSMichal Kazior 	 * other is ready.
15105bcae31dSMichal Kazior 	 *
15115bcae31dSMichal Kazior 	 * For practical reasons and code simplicity just do a single huge
15125bcae31dSMichal Kazior 	 * switch.
15135bcae31dSMichal Kazior 	 */
15145bcae31dSMichal Kazior 
15155bcae31dSMichal Kazior 	/*
15165bcae31dSMichal Kazior 	 * Verify if the reservation is still feasible.
15175bcae31dSMichal Kazior 	 *  - if it's not then disconnect
15185bcae31dSMichal Kazior 	 *  - if it is but not all vifs necessary are ready then defer
15195bcae31dSMichal Kazior 	 */
15205bcae31dSMichal Kazior 
15215bcae31dSMichal Kazior 	list_for_each_entry(ctx, &local->chanctx_list, list) {
1522b4f85443SJohannes Berg 		struct ieee80211_link_data *link;
1523b4f85443SJohannes Berg 
15245bcae31dSMichal Kazior 		if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
15255bcae31dSMichal Kazior 			continue;
15265bcae31dSMichal Kazior 
15275bcae31dSMichal Kazior 		if (WARN_ON(!ctx->replace_ctx)) {
15285bcae31dSMichal Kazior 			err = -EINVAL;
15295bcae31dSMichal Kazior 			goto err;
15305bcae31dSMichal Kazior 		}
15315bcae31dSMichal Kazior 
15325bcae31dSMichal Kazior 		if (!local->use_chanctx)
15335bcae31dSMichal Kazior 			new_ctx = ctx;
15345bcae31dSMichal Kazior 
15355bcae31dSMichal Kazior 		n_ctx++;
15365bcae31dSMichal Kazior 
15375bcae31dSMichal Kazior 		n_assigned = 0;
15385bcae31dSMichal Kazior 		n_reserved = 0;
15395bcae31dSMichal Kazior 		n_ready = 0;
15405bcae31dSMichal Kazior 
1541b4f85443SJohannes Berg 		list_for_each_entry(link, &ctx->replace_ctx->assigned_links,
15425bcae31dSMichal Kazior 				    assigned_chanctx_list) {
15435bcae31dSMichal Kazior 			n_assigned++;
1544b4f85443SJohannes Berg 			if (link->reserved_chanctx) {
15455bcae31dSMichal Kazior 				n_reserved++;
1546b4f85443SJohannes Berg 				if (link->reserved_ready)
15475bcae31dSMichal Kazior 					n_ready++;
15485bcae31dSMichal Kazior 			}
15495bcae31dSMichal Kazior 		}
15505bcae31dSMichal Kazior 
15515bcae31dSMichal Kazior 		if (n_assigned != n_reserved) {
15525bcae31dSMichal Kazior 			if (n_ready == n_reserved) {
15535bcae31dSMichal Kazior 				wiphy_info(local->hw.wiphy,
15545bcae31dSMichal Kazior 					   "channel context reservation cannot be finalized because some interfaces aren't switching\n");
15555bcae31dSMichal Kazior 				err = -EBUSY;
15565bcae31dSMichal Kazior 				goto err;
15575bcae31dSMichal Kazior 			}
15585bcae31dSMichal Kazior 
15595bcae31dSMichal Kazior 			return -EAGAIN;
15605bcae31dSMichal Kazior 		}
15615bcae31dSMichal Kazior 
15625bcae31dSMichal Kazior 		ctx->conf.radar_enabled = false;
1563b4f85443SJohannes Berg 		list_for_each_entry(link, &ctx->reserved_links,
15645bcae31dSMichal Kazior 				    reserved_chanctx_list) {
1565b4f85443SJohannes Berg 			if (ieee80211_link_has_in_place_reservation(link) &&
1566b4f85443SJohannes Berg 			    !link->reserved_ready)
15675bcae31dSMichal Kazior 				return -EAGAIN;
15685bcae31dSMichal Kazior 
1569b4f85443SJohannes Berg 			old_ctx = ieee80211_link_get_chanctx(link);
15705bcae31dSMichal Kazior 			if (old_ctx) {
15715bcae31dSMichal Kazior 				if (old_ctx->replace_state ==
15725bcae31dSMichal Kazior 				    IEEE80211_CHANCTX_WILL_BE_REPLACED)
15735bcae31dSMichal Kazior 					n_vifs_switch++;
15745bcae31dSMichal Kazior 				else
15755bcae31dSMichal Kazior 					n_vifs_assign++;
15765bcae31dSMichal Kazior 			} else {
15775bcae31dSMichal Kazior 				n_vifs_ctxless++;
15785bcae31dSMichal Kazior 			}
15795bcae31dSMichal Kazior 
1580b4f85443SJohannes Berg 			if (link->reserved_radar_required)
15815bcae31dSMichal Kazior 				ctx->conf.radar_enabled = true;
15825bcae31dSMichal Kazior 		}
15835bcae31dSMichal Kazior 	}
15845bcae31dSMichal Kazior 
15855bcae31dSMichal Kazior 	if (WARN_ON(n_ctx == 0) ||
15865bcae31dSMichal Kazior 	    WARN_ON(n_vifs_switch == 0 &&
15875bcae31dSMichal Kazior 		    n_vifs_assign == 0 &&
15885bcae31dSMichal Kazior 		    n_vifs_ctxless == 0) ||
15895bcae31dSMichal Kazior 	    WARN_ON(n_ctx > 1 && !local->use_chanctx) ||
15905bcae31dSMichal Kazior 	    WARN_ON(!new_ctx && !local->use_chanctx)) {
15915bcae31dSMichal Kazior 		err = -EINVAL;
15925bcae31dSMichal Kazior 		goto err;
15935bcae31dSMichal Kazior 	}
15945bcae31dSMichal Kazior 
15955bcae31dSMichal Kazior 	/*
15965bcae31dSMichal Kazior 	 * All necessary vifs are ready. Perform the switch now depending on
15975bcae31dSMichal Kazior 	 * reservations and driver capabilities.
15985bcae31dSMichal Kazior 	 */
15995bcae31dSMichal Kazior 
16005bcae31dSMichal Kazior 	if (local->use_chanctx) {
16015bcae31dSMichal Kazior 		if (n_vifs_switch > 0) {
16025bcae31dSMichal Kazior 			err = ieee80211_chsw_switch_vifs(local, n_vifs_switch);
16035bcae31dSMichal Kazior 			if (err)
16045bcae31dSMichal Kazior 				goto err;
16055bcae31dSMichal Kazior 		}
16065bcae31dSMichal Kazior 
16075bcae31dSMichal Kazior 		if (n_vifs_assign > 0 || n_vifs_ctxless > 0) {
16085bcae31dSMichal Kazior 			err = ieee80211_chsw_switch_ctxs(local);
16095bcae31dSMichal Kazior 			if (err)
16105bcae31dSMichal Kazior 				goto err;
16115bcae31dSMichal Kazior 		}
16125bcae31dSMichal Kazior 	} else {
16135bcae31dSMichal Kazior 		err = ieee80211_chsw_switch_hwconf(local, new_ctx);
16145bcae31dSMichal Kazior 		if (err)
16155bcae31dSMichal Kazior 			goto err;
16165bcae31dSMichal Kazior 	}
16175bcae31dSMichal Kazior 
16185bcae31dSMichal Kazior 	/*
16195bcae31dSMichal Kazior 	 * Update all structures, values and pointers to point to new channel
16205bcae31dSMichal Kazior 	 * context(s).
16215bcae31dSMichal Kazior 	 */
16225bcae31dSMichal Kazior 	list_for_each_entry(ctx, &local->chanctx_list, list) {
1623b4f85443SJohannes Berg 		struct ieee80211_link_data *link, *link_tmp;
1624b4f85443SJohannes Berg 
16255bcae31dSMichal Kazior 		if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
16265bcae31dSMichal Kazior 			continue;
16275bcae31dSMichal Kazior 
16285bcae31dSMichal Kazior 		if (WARN_ON(!ctx->replace_ctx)) {
16295bcae31dSMichal Kazior 			err = -EINVAL;
16305bcae31dSMichal Kazior 			goto err;
16315bcae31dSMichal Kazior 		}
16325bcae31dSMichal Kazior 
1633b4f85443SJohannes Berg 		list_for_each_entry(link, &ctx->reserved_links,
16345bcae31dSMichal Kazior 				    reserved_chanctx_list) {
1635b4f85443SJohannes Berg 			struct ieee80211_sub_if_data *sdata = link->sdata;
1636d8675a63SJohannes Berg 			struct ieee80211_bss_conf *link_conf = link->conf;
16375bcae31dSMichal Kazior 			u32 changed = 0;
16385bcae31dSMichal Kazior 
1639b4f85443SJohannes Berg 			if (!ieee80211_link_has_in_place_reservation(link))
16405bcae31dSMichal Kazior 				continue;
16415bcae31dSMichal Kazior 
1642b4f85443SJohannes Berg 			rcu_assign_pointer(link_conf->chanctx_conf,
1643d0a9123eSJohannes Berg 					   &ctx->conf);
16445bcae31dSMichal Kazior 
16455bcae31dSMichal Kazior 			if (sdata->vif.type == NL80211_IFTYPE_AP)
1646b4f85443SJohannes Berg 				__ieee80211_link_copy_chanctx_to_vlans(link,
16475bcae31dSMichal Kazior 								       false);
16485bcae31dSMichal Kazior 
164917c18bf8SJohannes Berg 			ieee80211_check_fast_xmit_iface(sdata);
165017c18bf8SJohannes Berg 
1651b4f85443SJohannes Berg 			link->radar_required = link->reserved_radar_required;
16525bcae31dSMichal Kazior 
1653b4f85443SJohannes Berg 			if (link_conf->chandef.width != link->reserved_chandef.width)
16545bcae31dSMichal Kazior 				changed = BSS_CHANGED_BANDWIDTH;
16555bcae31dSMichal Kazior 
1656b4f85443SJohannes Berg 			ieee80211_link_update_chandef(link, &link->reserved_chandef);
16575bcae31dSMichal Kazior 			if (changed)
1658b4f85443SJohannes Berg 				ieee80211_link_info_change_notify(sdata,
1659d8675a63SJohannes Berg 								  link,
16605bcae31dSMichal Kazior 								  changed);
16615bcae31dSMichal Kazior 
1662db82d8a9SLorenzo Bianconi 			ieee80211_recalc_txpower(sdata, false);
16635bcae31dSMichal Kazior 		}
166411335a55SLuciano Coelho 
166511335a55SLuciano Coelho 		ieee80211_recalc_chanctx_chantype(local, ctx);
166611335a55SLuciano Coelho 		ieee80211_recalc_smps_chanctx(local, ctx);
166711335a55SLuciano Coelho 		ieee80211_recalc_radar_chanctx(local, ctx);
166811335a55SLuciano Coelho 		ieee80211_recalc_chanctx_min_def(local, ctx);
16695bcae31dSMichal Kazior 
1670b4f85443SJohannes Berg 		list_for_each_entry_safe(link, link_tmp, &ctx->reserved_links,
16715bcae31dSMichal Kazior 					 reserved_chanctx_list) {
1672b4f85443SJohannes Berg 			if (ieee80211_link_get_chanctx(link) != ctx)
16735bcae31dSMichal Kazior 				continue;
16745bcae31dSMichal Kazior 
1675b4f85443SJohannes Berg 			list_del(&link->reserved_chanctx_list);
1676b4f85443SJohannes Berg 			list_move(&link->assigned_chanctx_list,
1677b4f85443SJohannes Berg 				  &ctx->assigned_links);
1678b4f85443SJohannes Berg 			link->reserved_chanctx = NULL;
167903078de4SMichal Kazior 
1680b4f85443SJohannes Berg 			ieee80211_link_chanctx_reservation_complete(link);
16815bcae31dSMichal Kazior 		}
16825bcae31dSMichal Kazior 
16835bcae31dSMichal Kazior 		/*
16845bcae31dSMichal Kazior 		 * This context might have been a dependency for an already
16855bcae31dSMichal Kazior 		 * ready re-assign reservation interface that was deferred. Do
16865bcae31dSMichal Kazior 		 * not propagate error to the caller though. The in-place
16875bcae31dSMichal Kazior 		 * reservation for originally requested interface has already
16885bcae31dSMichal Kazior 		 * succeeded at this point.
16895bcae31dSMichal Kazior 		 */
1690b4f85443SJohannes Berg 		list_for_each_entry_safe(link, link_tmp, &ctx->reserved_links,
16915bcae31dSMichal Kazior 					 reserved_chanctx_list) {
1692b4f85443SJohannes Berg 			if (WARN_ON(ieee80211_link_has_in_place_reservation(link)))
16935bcae31dSMichal Kazior 				continue;
16945bcae31dSMichal Kazior 
1695b4f85443SJohannes Berg 			if (WARN_ON(link->reserved_chanctx != ctx))
16965bcae31dSMichal Kazior 				continue;
16975bcae31dSMichal Kazior 
1698b4f85443SJohannes Berg 			if (!link->reserved_ready)
16995bcae31dSMichal Kazior 				continue;
17005bcae31dSMichal Kazior 
1701b4f85443SJohannes Berg 			if (ieee80211_link_get_chanctx(link))
1702b4f85443SJohannes Berg 				err = ieee80211_link_use_reserved_reassign(link);
17035bcae31dSMichal Kazior 			else
1704b4f85443SJohannes Berg 				err = ieee80211_link_use_reserved_assign(link);
17055bcae31dSMichal Kazior 
17065bcae31dSMichal Kazior 			if (err) {
1707b4f85443SJohannes Berg 				link_info(link,
17085bcae31dSMichal Kazior 					  "failed to finalize (re-)assign reservation (err=%d)\n",
17095bcae31dSMichal Kazior 					  err);
1710b4f85443SJohannes Berg 				ieee80211_link_unreserve_chanctx(link);
17115bcae31dSMichal Kazior 				cfg80211_stop_iface(local->hw.wiphy,
1712b4f85443SJohannes Berg 						    &link->sdata->wdev,
17135bcae31dSMichal Kazior 						    GFP_KERNEL);
17145bcae31dSMichal Kazior 			}
17155bcae31dSMichal Kazior 		}
17165bcae31dSMichal Kazior 	}
17175bcae31dSMichal Kazior 
17185bcae31dSMichal Kazior 	/*
17195bcae31dSMichal Kazior 	 * Finally free old contexts
17205bcae31dSMichal Kazior 	 */
17215bcae31dSMichal Kazior 
17225bcae31dSMichal Kazior 	list_for_each_entry_safe(ctx, ctx_tmp, &local->chanctx_list, list) {
17235bcae31dSMichal Kazior 		if (ctx->replace_state != IEEE80211_CHANCTX_WILL_BE_REPLACED)
17245bcae31dSMichal Kazior 			continue;
17255bcae31dSMichal Kazior 
17265bcae31dSMichal Kazior 		ctx->replace_ctx->replace_ctx = NULL;
17275bcae31dSMichal Kazior 		ctx->replace_ctx->replace_state =
17285bcae31dSMichal Kazior 				IEEE80211_CHANCTX_REPLACE_NONE;
17295bcae31dSMichal Kazior 
17305bcae31dSMichal Kazior 		list_del_rcu(&ctx->list);
17315bcae31dSMichal Kazior 		kfree_rcu(ctx, rcu_head);
17325bcae31dSMichal Kazior 	}
17335bcae31dSMichal Kazior 
17345bcae31dSMichal Kazior 	return 0;
17355bcae31dSMichal Kazior 
17365bcae31dSMichal Kazior err:
17375bcae31dSMichal Kazior 	list_for_each_entry(ctx, &local->chanctx_list, list) {
1738b4f85443SJohannes Berg 		struct ieee80211_link_data *link, *link_tmp;
1739b4f85443SJohannes Berg 
17405bcae31dSMichal Kazior 		if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
17415bcae31dSMichal Kazior 			continue;
17425bcae31dSMichal Kazior 
1743b4f85443SJohannes Berg 		list_for_each_entry_safe(link, link_tmp, &ctx->reserved_links,
174403078de4SMichal Kazior 					 reserved_chanctx_list) {
1745b4f85443SJohannes Berg 			ieee80211_link_unreserve_chanctx(link);
1746b4f85443SJohannes Berg 			ieee80211_link_chanctx_reservation_complete(link);
174703078de4SMichal Kazior 		}
17485bcae31dSMichal Kazior 	}
17495bcae31dSMichal Kazior 
17505bcae31dSMichal Kazior 	return err;
17515bcae31dSMichal Kazior }
17525bcae31dSMichal Kazior 
1753b4f85443SJohannes Berg static void __ieee80211_link_release_channel(struct ieee80211_link_data *link)
1754649b2a4dSJohannes Berg {
1755b4f85443SJohannes Berg 	struct ieee80211_sub_if_data *sdata = link->sdata;
1756d8675a63SJohannes Berg 	struct ieee80211_bss_conf *link_conf = link->conf;
1757649b2a4dSJohannes Berg 	struct ieee80211_local *local = sdata->local;
1758649b2a4dSJohannes Berg 	struct ieee80211_chanctx_conf *conf;
1759649b2a4dSJohannes Berg 	struct ieee80211_chanctx *ctx;
1760649b2a4dSJohannes Berg 	bool use_reserved_switch = false;
1761649b2a4dSJohannes Berg 
1762649b2a4dSJohannes Berg 	lockdep_assert_held(&local->chanctx_mtx);
1763649b2a4dSJohannes Berg 
1764b4f85443SJohannes Berg 	conf = rcu_dereference_protected(link_conf->chanctx_conf,
1765649b2a4dSJohannes Berg 					 lockdep_is_held(&local->chanctx_mtx));
1766649b2a4dSJohannes Berg 	if (!conf)
1767649b2a4dSJohannes Berg 		return;
1768649b2a4dSJohannes Berg 
1769649b2a4dSJohannes Berg 	ctx = container_of(conf, struct ieee80211_chanctx, conf);
1770649b2a4dSJohannes Berg 
1771b4f85443SJohannes Berg 	if (link->reserved_chanctx) {
1772b4f85443SJohannes Berg 		if (link->reserved_chanctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER &&
1773b4f85443SJohannes Berg 		    ieee80211_chanctx_num_reserved(local, link->reserved_chanctx) > 1)
1774649b2a4dSJohannes Berg 			use_reserved_switch = true;
1775649b2a4dSJohannes Berg 
1776b4f85443SJohannes Berg 		ieee80211_link_unreserve_chanctx(link);
1777649b2a4dSJohannes Berg 	}
1778649b2a4dSJohannes Berg 
1779b4f85443SJohannes Berg 	ieee80211_assign_link_chanctx(link, NULL);
1780649b2a4dSJohannes Berg 	if (ieee80211_chanctx_refcount(local, ctx) == 0)
1781649b2a4dSJohannes Berg 		ieee80211_free_chanctx(local, ctx);
1782649b2a4dSJohannes Berg 
1783b4f85443SJohannes Berg 	link->radar_required = false;
1784104f5a62SEliad Peller 
1785649b2a4dSJohannes Berg 	/* Unreserving may ready an in-place reservation. */
1786649b2a4dSJohannes Berg 	if (use_reserved_switch)
1787649b2a4dSJohannes Berg 		ieee80211_vif_use_reserved_switch(local);
1788649b2a4dSJohannes Berg }
1789649b2a4dSJohannes Berg 
1790b4f85443SJohannes Berg int ieee80211_link_use_channel(struct ieee80211_link_data *link,
1791649b2a4dSJohannes Berg 			       const struct cfg80211_chan_def *chandef,
1792649b2a4dSJohannes Berg 			       enum ieee80211_chanctx_mode mode)
1793649b2a4dSJohannes Berg {
1794b4f85443SJohannes Berg 	struct ieee80211_sub_if_data *sdata = link->sdata;
1795649b2a4dSJohannes Berg 	struct ieee80211_local *local = sdata->local;
1796649b2a4dSJohannes Berg 	struct ieee80211_chanctx *ctx;
1797649b2a4dSJohannes Berg 	u8 radar_detect_width = 0;
1798649b2a4dSJohannes Berg 	int ret;
1799649b2a4dSJohannes Berg 
1800649b2a4dSJohannes Berg 	lockdep_assert_held(&local->mtx);
1801649b2a4dSJohannes Berg 
1802649b2a4dSJohannes Berg 	mutex_lock(&local->chanctx_mtx);
1803649b2a4dSJohannes Berg 
1804649b2a4dSJohannes Berg 	ret = cfg80211_chandef_dfs_required(local->hw.wiphy,
1805649b2a4dSJohannes Berg 					    chandef,
1806649b2a4dSJohannes Berg 					    sdata->wdev.iftype);
1807649b2a4dSJohannes Berg 	if (ret < 0)
1808649b2a4dSJohannes Berg 		goto out;
1809649b2a4dSJohannes Berg 	if (ret > 0)
1810649b2a4dSJohannes Berg 		radar_detect_width = BIT(chandef->width);
1811649b2a4dSJohannes Berg 
1812d8675a63SJohannes Berg 	link->radar_required = ret;
1813649b2a4dSJohannes Berg 
1814649b2a4dSJohannes Berg 	ret = ieee80211_check_combinations(sdata, chandef, mode,
1815649b2a4dSJohannes Berg 					   radar_detect_width);
1816649b2a4dSJohannes Berg 	if (ret < 0)
1817649b2a4dSJohannes Berg 		goto out;
1818649b2a4dSJohannes Berg 
1819b4f85443SJohannes Berg 	__ieee80211_link_release_channel(link);
1820649b2a4dSJohannes Berg 
1821649b2a4dSJohannes Berg 	ctx = ieee80211_find_chanctx(local, chandef, mode);
1822649b2a4dSJohannes Berg 	if (!ctx)
1823649b2a4dSJohannes Berg 		ctx = ieee80211_new_chanctx(local, chandef, mode);
1824649b2a4dSJohannes Berg 	if (IS_ERR(ctx)) {
1825649b2a4dSJohannes Berg 		ret = PTR_ERR(ctx);
1826649b2a4dSJohannes Berg 		goto out;
1827649b2a4dSJohannes Berg 	}
1828649b2a4dSJohannes Berg 
1829b4f85443SJohannes Berg 	ieee80211_link_update_chandef(link, chandef);
1830649b2a4dSJohannes Berg 
1831b4f85443SJohannes Berg 	ret = ieee80211_assign_link_chanctx(link, ctx);
1832649b2a4dSJohannes Berg 	if (ret) {
1833649b2a4dSJohannes Berg 		/* if assign fails refcount stays the same */
1834649b2a4dSJohannes Berg 		if (ieee80211_chanctx_refcount(local, ctx) == 0)
1835649b2a4dSJohannes Berg 			ieee80211_free_chanctx(local, ctx);
1836649b2a4dSJohannes Berg 		goto out;
1837649b2a4dSJohannes Berg 	}
1838649b2a4dSJohannes Berg 
1839649b2a4dSJohannes Berg 	ieee80211_recalc_smps_chanctx(local, ctx);
1840649b2a4dSJohannes Berg 	ieee80211_recalc_radar_chanctx(local, ctx);
1841649b2a4dSJohannes Berg  out:
1842104f5a62SEliad Peller 	if (ret)
1843b4f85443SJohannes Berg 		link->radar_required = false;
1844104f5a62SEliad Peller 
1845649b2a4dSJohannes Berg 	mutex_unlock(&local->chanctx_mtx);
1846649b2a4dSJohannes Berg 	return ret;
1847649b2a4dSJohannes Berg }
1848649b2a4dSJohannes Berg 
1849b4f85443SJohannes Berg int ieee80211_link_use_reserved_context(struct ieee80211_link_data *link)
18505bcae31dSMichal Kazior {
1851b4f85443SJohannes Berg 	struct ieee80211_sub_if_data *sdata = link->sdata;
18525bcae31dSMichal Kazior 	struct ieee80211_local *local = sdata->local;
18535bcae31dSMichal Kazior 	struct ieee80211_chanctx *new_ctx;
18545bcae31dSMichal Kazior 	struct ieee80211_chanctx *old_ctx;
18555bcae31dSMichal Kazior 	int err;
18565bcae31dSMichal Kazior 
18575bcae31dSMichal Kazior 	lockdep_assert_held(&local->mtx);
18585bcae31dSMichal Kazior 	lockdep_assert_held(&local->chanctx_mtx);
18595bcae31dSMichal Kazior 
1860b4f85443SJohannes Berg 	new_ctx = link->reserved_chanctx;
1861b4f85443SJohannes Berg 	old_ctx = ieee80211_link_get_chanctx(link);
18625bcae31dSMichal Kazior 
18635bcae31dSMichal Kazior 	if (WARN_ON(!new_ctx))
18645bcae31dSMichal Kazior 		return -EINVAL;
18655bcae31dSMichal Kazior 
18665bcae31dSMichal Kazior 	if (WARN_ON(new_ctx->replace_state ==
18675bcae31dSMichal Kazior 		    IEEE80211_CHANCTX_WILL_BE_REPLACED))
18685bcae31dSMichal Kazior 		return -EINVAL;
18695bcae31dSMichal Kazior 
1870b4f85443SJohannes Berg 	if (WARN_ON(link->reserved_ready))
18715bcae31dSMichal Kazior 		return -EINVAL;
18725bcae31dSMichal Kazior 
1873b4f85443SJohannes Berg 	link->reserved_ready = true;
18745bcae31dSMichal Kazior 
18755bcae31dSMichal Kazior 	if (new_ctx->replace_state == IEEE80211_CHANCTX_REPLACE_NONE) {
18765bcae31dSMichal Kazior 		if (old_ctx)
1877b4f85443SJohannes Berg 			return ieee80211_link_use_reserved_reassign(link);
18785bcae31dSMichal Kazior 
1879b4f85443SJohannes Berg 		return ieee80211_link_use_reserved_assign(link);
18805bcae31dSMichal Kazior 	}
18815bcae31dSMichal Kazior 
18825bcae31dSMichal Kazior 	/*
18835bcae31dSMichal Kazior 	 * In-place reservation may need to be finalized now either if:
18845bcae31dSMichal Kazior 	 *  a) sdata is taking part in the swapping itself and is the last one
18855bcae31dSMichal Kazior 	 *  b) sdata has switched with a re-assign reservation to an existing
18865bcae31dSMichal Kazior 	 *     context readying in-place switching of old_ctx
18875bcae31dSMichal Kazior 	 *
18885bcae31dSMichal Kazior 	 * In case of (b) do not propagate the error up because the requested
18895bcae31dSMichal Kazior 	 * sdata already switched successfully. Just spill an extra warning.
18905bcae31dSMichal Kazior 	 * The ieee80211_vif_use_reserved_switch() already stops all necessary
18915bcae31dSMichal Kazior 	 * interfaces upon failure.
18925bcae31dSMichal Kazior 	 */
18935bcae31dSMichal Kazior 	if ((old_ctx &&
18945bcae31dSMichal Kazior 	     old_ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED) ||
18955bcae31dSMichal Kazior 	    new_ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER) {
18965bcae31dSMichal Kazior 		err = ieee80211_vif_use_reserved_switch(local);
18975bcae31dSMichal Kazior 		if (err && err != -EAGAIN) {
18985bcae31dSMichal Kazior 			if (new_ctx->replace_state ==
18995bcae31dSMichal Kazior 			    IEEE80211_CHANCTX_REPLACES_OTHER)
19005bcae31dSMichal Kazior 				return err;
19015bcae31dSMichal Kazior 
19025bcae31dSMichal Kazior 			wiphy_info(local->hw.wiphy,
19035bcae31dSMichal Kazior 				   "depending in-place reservation failed (err=%d)\n",
19045bcae31dSMichal Kazior 				   err);
19055bcae31dSMichal Kazior 		}
19065bcae31dSMichal Kazior 	}
19075bcae31dSMichal Kazior 
19085bcae31dSMichal Kazior 	return 0;
190973da7d5bSSimon Wunderlich }
191073da7d5bSSimon Wunderlich 
1911b4f85443SJohannes Berg int ieee80211_link_change_bandwidth(struct ieee80211_link_data *link,
19122c9b7359SJohannes Berg 				    const struct cfg80211_chan_def *chandef,
19132c9b7359SJohannes Berg 				    u32 *changed)
19142c9b7359SJohannes Berg {
1915b4f85443SJohannes Berg 	struct ieee80211_sub_if_data *sdata = link->sdata;
1916d8675a63SJohannes Berg 	struct ieee80211_bss_conf *link_conf = link->conf;
19172c9b7359SJohannes Berg 	struct ieee80211_local *local = sdata->local;
19182c9b7359SJohannes Berg 	struct ieee80211_chanctx_conf *conf;
19192c9b7359SJohannes Berg 	struct ieee80211_chanctx *ctx;
19205bcae31dSMichal Kazior 	const struct cfg80211_chan_def *compat;
19212c9b7359SJohannes Berg 	int ret;
19222c9b7359SJohannes Berg 
19232c9b7359SJohannes Berg 	if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef,
19242c9b7359SJohannes Berg 				     IEEE80211_CHAN_DISABLED))
19252c9b7359SJohannes Berg 		return -EINVAL;
19262c9b7359SJohannes Berg 
19272c9b7359SJohannes Berg 	mutex_lock(&local->chanctx_mtx);
1928b4f85443SJohannes Berg 	if (cfg80211_chandef_identical(chandef, &link_conf->chandef)) {
19292c9b7359SJohannes Berg 		ret = 0;
19302c9b7359SJohannes Berg 		goto out;
19312c9b7359SJohannes Berg 	}
19322c9b7359SJohannes Berg 
19332c9b7359SJohannes Berg 	if (chandef->width == NL80211_CHAN_WIDTH_20_NOHT ||
1934b4f85443SJohannes Berg 	    link_conf->chandef.width == NL80211_CHAN_WIDTH_20_NOHT) {
19352c9b7359SJohannes Berg 		ret = -EINVAL;
19362c9b7359SJohannes Berg 		goto out;
19372c9b7359SJohannes Berg 	}
19382c9b7359SJohannes Berg 
1939b4f85443SJohannes Berg 	conf = rcu_dereference_protected(link_conf->chanctx_conf,
19402c9b7359SJohannes Berg 					 lockdep_is_held(&local->chanctx_mtx));
19412c9b7359SJohannes Berg 	if (!conf) {
19422c9b7359SJohannes Berg 		ret = -EINVAL;
19432c9b7359SJohannes Berg 		goto out;
19442c9b7359SJohannes Berg 	}
19452c9b7359SJohannes Berg 
19462c9b7359SJohannes Berg 	ctx = container_of(conf, struct ieee80211_chanctx, conf);
19475bcae31dSMichal Kazior 
19485bcae31dSMichal Kazior 	compat = cfg80211_chandef_compatible(&conf->def, chandef);
19495bcae31dSMichal Kazior 	if (!compat) {
19502c9b7359SJohannes Berg 		ret = -EINVAL;
19512c9b7359SJohannes Berg 		goto out;
19522c9b7359SJohannes Berg 	}
19532c9b7359SJohannes Berg 
19545bcae31dSMichal Kazior 	switch (ctx->replace_state) {
19555bcae31dSMichal Kazior 	case IEEE80211_CHANCTX_REPLACE_NONE:
19565bcae31dSMichal Kazior 		if (!ieee80211_chanctx_reserved_chandef(local, ctx, compat)) {
19575bcae31dSMichal Kazior 			ret = -EBUSY;
19585bcae31dSMichal Kazior 			goto out;
19595bcae31dSMichal Kazior 		}
19605bcae31dSMichal Kazior 		break;
19615bcae31dSMichal Kazior 	case IEEE80211_CHANCTX_WILL_BE_REPLACED:
1962d070f913SStephen Hemminger 		/* TODO: Perhaps the bandwidth change could be treated as a
19635bcae31dSMichal Kazior 		 * reservation itself? */
19645bcae31dSMichal Kazior 		ret = -EBUSY;
19655bcae31dSMichal Kazior 		goto out;
19665bcae31dSMichal Kazior 	case IEEE80211_CHANCTX_REPLACES_OTHER:
19675bcae31dSMichal Kazior 		/* channel context that is going to replace another channel
19685bcae31dSMichal Kazior 		 * context doesn't really exist and shouldn't be assigned
19695bcae31dSMichal Kazior 		 * anywhere yet */
19705bcae31dSMichal Kazior 		WARN_ON(1);
19715bcae31dSMichal Kazior 		break;
19725bcae31dSMichal Kazior 	}
19735bcae31dSMichal Kazior 
1974b4f85443SJohannes Berg 	ieee80211_link_update_chandef(link, chandef);
19752c9b7359SJohannes Berg 
19762c9b7359SJohannes Berg 	ieee80211_recalc_chanctx_chantype(local, ctx);
19772c9b7359SJohannes Berg 
19782c9b7359SJohannes Berg 	*changed |= BSS_CHANGED_BANDWIDTH;
19792c9b7359SJohannes Berg 	ret = 0;
19802c9b7359SJohannes Berg  out:
19812c9b7359SJohannes Berg 	mutex_unlock(&local->chanctx_mtx);
19822c9b7359SJohannes Berg 	return ret;
19832c9b7359SJohannes Berg }
19842c9b7359SJohannes Berg 
1985b4f85443SJohannes Berg void ieee80211_link_release_channel(struct ieee80211_link_data *link)
1986d01a1e65SMichal Kazior {
1987b4f85443SJohannes Berg 	struct ieee80211_sub_if_data *sdata = link->sdata;
1988b4f85443SJohannes Berg 
1989d01a1e65SMichal Kazior 	mutex_lock(&sdata->local->chanctx_mtx);
1990939c4c7eSJohannes Berg 	if (rcu_access_pointer(link->conf->chanctx_conf)) {
1991939c4c7eSJohannes Berg 		lockdep_assert_held(&sdata->local->mtx);
1992b4f85443SJohannes Berg 		__ieee80211_link_release_channel(link);
1993939c4c7eSJohannes Berg 	}
1994d01a1e65SMichal Kazior 	mutex_unlock(&sdata->local->chanctx_mtx);
1995d01a1e65SMichal Kazior }
19963448c005SJohannes Berg 
1997b4f85443SJohannes Berg void ieee80211_link_vlan_copy_chanctx(struct ieee80211_link_data *link)
19984d76d21bSJohannes Berg {
1999b4f85443SJohannes Berg 	struct ieee80211_sub_if_data *sdata = link->sdata;
2000b4f85443SJohannes Berg 	unsigned int link_id = link->link_id;
2001d8675a63SJohannes Berg 	struct ieee80211_bss_conf *link_conf = link->conf;
2002d8675a63SJohannes Berg 	struct ieee80211_bss_conf *ap_conf;
20034d76d21bSJohannes Berg 	struct ieee80211_local *local = sdata->local;
20044d76d21bSJohannes Berg 	struct ieee80211_sub_if_data *ap;
20054d76d21bSJohannes Berg 	struct ieee80211_chanctx_conf *conf;
20064d76d21bSJohannes Berg 
20074d76d21bSJohannes Berg 	if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP_VLAN || !sdata->bss))
20084d76d21bSJohannes Berg 		return;
20094d76d21bSJohannes Berg 
20104d76d21bSJohannes Berg 	ap = container_of(sdata->bss, struct ieee80211_sub_if_data, u.ap);
20114d76d21bSJohannes Berg 
20124d76d21bSJohannes Berg 	mutex_lock(&local->chanctx_mtx);
20134d76d21bSJohannes Berg 
2014d8675a63SJohannes Berg 	rcu_read_lock();
2015d8675a63SJohannes Berg 	ap_conf = rcu_dereference(ap->vif.link_conf[link_id]);
2016d8675a63SJohannes Berg 	conf = rcu_dereference_protected(ap_conf->chanctx_conf,
20174d76d21bSJohannes Berg 					 lockdep_is_held(&local->chanctx_mtx));
2018b4f85443SJohannes Berg 	rcu_assign_pointer(link_conf->chanctx_conf, conf);
2019d8675a63SJohannes Berg 	rcu_read_unlock();
20204d76d21bSJohannes Berg 	mutex_unlock(&local->chanctx_mtx);
20214d76d21bSJohannes Berg }
20224d76d21bSJohannes Berg 
20233448c005SJohannes Berg void ieee80211_iter_chan_contexts_atomic(
20243448c005SJohannes Berg 	struct ieee80211_hw *hw,
20253448c005SJohannes Berg 	void (*iter)(struct ieee80211_hw *hw,
20263448c005SJohannes Berg 		     struct ieee80211_chanctx_conf *chanctx_conf,
20273448c005SJohannes Berg 		     void *data),
20283448c005SJohannes Berg 	void *iter_data)
20293448c005SJohannes Berg {
20303448c005SJohannes Berg 	struct ieee80211_local *local = hw_to_local(hw);
20313448c005SJohannes Berg 	struct ieee80211_chanctx *ctx;
20323448c005SJohannes Berg 
20333448c005SJohannes Berg 	rcu_read_lock();
20343448c005SJohannes Berg 	list_for_each_entry_rcu(ctx, &local->chanctx_list, list)
20358a61af65SJohannes Berg 		if (ctx->driver_present)
20363448c005SJohannes Berg 			iter(hw, &ctx->conf, iter_data);
20373448c005SJohannes Berg 	rcu_read_unlock();
20383448c005SJohannes Berg }
20393448c005SJohannes Berg EXPORT_SYMBOL_GPL(ieee80211_iter_chan_contexts_atomic);
2040