xref: /openbmc/linux/net/mac80211/chan.c (revision 5bd5666d)
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;
838b4f85443SJohannes Berg 	unsigned int link_id = link->link_id;
83935f2fce9SMichal Kazior 	struct ieee80211_local *local = sdata->local;
84077eeba97SLuciano Coelho 	struct ieee80211_chanctx_conf *conf;
84177eeba97SLuciano Coelho 	struct ieee80211_chanctx *curr_ctx = NULL;
84277eeba97SLuciano Coelho 	int ret = 0;
843d01a1e65SMichal Kazior 
844708d50edSAyala Beker 	if (WARN_ON(sdata->vif.type == NL80211_IFTYPE_NAN))
845708d50edSAyala Beker 		return -ENOTSUPP;
846708d50edSAyala Beker 
847d8675a63SJohannes Berg 	conf = rcu_dereference_protected(link->conf->chanctx_conf,
84877eeba97SLuciano Coelho 					 lockdep_is_held(&local->chanctx_mtx));
849d01a1e65SMichal Kazior 
85077eeba97SLuciano Coelho 	if (conf) {
85177eeba97SLuciano Coelho 		curr_ctx = container_of(conf, struct ieee80211_chanctx, conf);
85235f2fce9SMichal Kazior 
853b4f85443SJohannes Berg 		drv_unassign_vif_chanctx(local, sdata, link_id, curr_ctx);
85477eeba97SLuciano Coelho 		conf = NULL;
855b4f85443SJohannes Berg 		list_del(&link->assigned_chanctx_list);
85677eeba97SLuciano Coelho 	}
85777eeba97SLuciano Coelho 
85877eeba97SLuciano Coelho 	if (new_ctx) {
859b4f85443SJohannes Berg 		ret = drv_assign_vif_chanctx(local, sdata, link_id, new_ctx);
86077eeba97SLuciano Coelho 		if (ret)
86177eeba97SLuciano Coelho 			goto out;
86277eeba97SLuciano Coelho 
86377eeba97SLuciano Coelho 		conf = &new_ctx->conf;
864b4f85443SJohannes Berg 		list_add(&link->assigned_chanctx_list,
865b4f85443SJohannes Berg 			 &new_ctx->assigned_links);
86677eeba97SLuciano Coelho 	}
86777eeba97SLuciano Coelho 
86877eeba97SLuciano Coelho out:
869d8675a63SJohannes Berg 	rcu_assign_pointer(link->conf->chanctx_conf, conf);
87077eeba97SLuciano Coelho 
871f276e20bSJohannes Berg 	sdata->vif.cfg.idle = !conf;
87277eeba97SLuciano Coelho 
873c0166da9SMichal Kazior 	if (curr_ctx && ieee80211_chanctx_num_assigned(local, curr_ctx) > 0) {
87477eeba97SLuciano Coelho 		ieee80211_recalc_chanctx_chantype(local, curr_ctx);
87577eeba97SLuciano Coelho 		ieee80211_recalc_smps_chanctx(local, curr_ctx);
87677eeba97SLuciano Coelho 		ieee80211_recalc_radar_chanctx(local, curr_ctx);
87777eeba97SLuciano Coelho 		ieee80211_recalc_chanctx_min_def(local, curr_ctx);
87877eeba97SLuciano Coelho 	}
87977eeba97SLuciano Coelho 
880c0166da9SMichal Kazior 	if (new_ctx && ieee80211_chanctx_num_assigned(local, new_ctx) > 0) {
881db82d8a9SLorenzo Bianconi 		ieee80211_recalc_txpower(sdata, false);
88277eeba97SLuciano Coelho 		ieee80211_recalc_chanctx_min_def(local, new_ctx);
88377eeba97SLuciano Coelho 	}
8845bbe754dSJohannes Berg 
8855bbe754dSJohannes Berg 	if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE &&
8865bbe754dSJohannes Berg 	    sdata->vif.type != NL80211_IFTYPE_MONITOR)
8877b7090b4SJohannes Berg 		ieee80211_vif_cfg_change_notify(sdata, BSS_CHANGED_IDLE);
888fd0f979aSJohannes Berg 
88917c18bf8SJohannes Berg 	ieee80211_check_fast_xmit_iface(sdata);
89017c18bf8SJohannes Berg 
89177eeba97SLuciano Coelho 	return ret;
892d01a1e65SMichal Kazior }
893d01a1e65SMichal Kazior 
89404ecd257SJohannes Berg void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local,
89504ecd257SJohannes Berg 				   struct ieee80211_chanctx *chanctx)
89604ecd257SJohannes Berg {
89704ecd257SJohannes Berg 	struct ieee80211_sub_if_data *sdata;
89804ecd257SJohannes Berg 	u8 rx_chains_static, rx_chains_dynamic;
89904ecd257SJohannes Berg 
90004ecd257SJohannes Berg 	lockdep_assert_held(&local->chanctx_mtx);
90104ecd257SJohannes Berg 
90204ecd257SJohannes Berg 	rx_chains_static = 1;
90304ecd257SJohannes Berg 	rx_chains_dynamic = 1;
90404ecd257SJohannes Berg 
90504ecd257SJohannes Berg 	rcu_read_lock();
90604ecd257SJohannes Berg 	list_for_each_entry_rcu(sdata, &local->interfaces, list) {
90704ecd257SJohannes Berg 		u8 needed_static, needed_dynamic;
908b4f85443SJohannes Berg 		unsigned int link_id;
90904ecd257SJohannes Berg 
91004ecd257SJohannes Berg 		if (!ieee80211_sdata_running(sdata))
91104ecd257SJohannes Berg 			continue;
91204ecd257SJohannes Berg 
91304ecd257SJohannes Berg 		switch (sdata->vif.type) {
91404ecd257SJohannes Berg 		case NL80211_IFTYPE_STATION:
91504ecd257SJohannes Berg 			if (!sdata->u.mgd.associated)
91604ecd257SJohannes Berg 				continue;
91704ecd257SJohannes Berg 			break;
91804ecd257SJohannes Berg 		case NL80211_IFTYPE_AP:
91904ecd257SJohannes Berg 		case NL80211_IFTYPE_ADHOC:
92004ecd257SJohannes Berg 		case NL80211_IFTYPE_MESH_POINT:
921239281f8SRostislav Lisovy 		case NL80211_IFTYPE_OCB:
92204ecd257SJohannes Berg 			break;
92304ecd257SJohannes Berg 		default:
924b4f85443SJohannes Berg 			continue;
92504ecd257SJohannes Berg 		}
92604ecd257SJohannes Berg 
927b4f85443SJohannes Berg 		for (link_id = 0; link_id < ARRAY_SIZE(sdata->link); link_id++) {
928d8675a63SJohannes Berg 			struct ieee80211_link_data *link;
929b4f85443SJohannes Berg 
930d8675a63SJohannes Berg 			link = rcu_dereference(sdata->link[link_id]);
931d8675a63SJohannes Berg 
932d8675a63SJohannes Berg 			if (!link)
933b4f85443SJohannes Berg 				continue;
934b4f85443SJohannes Berg 
935d8675a63SJohannes Berg 			if (rcu_access_pointer(link->conf->chanctx_conf) != &chanctx->conf)
936b4f85443SJohannes Berg 				continue;
937b4f85443SJohannes Berg 
938b4f85443SJohannes Berg 			switch (link->smps_mode) {
93904ecd257SJohannes Berg 			default:
94004ecd257SJohannes Berg 				WARN_ONCE(1, "Invalid SMPS mode %d\n",
941b4f85443SJohannes Berg 					  link->smps_mode);
942fc0561dcSGustavo A. R. Silva 				fallthrough;
94304ecd257SJohannes Berg 			case IEEE80211_SMPS_OFF:
944b4f85443SJohannes Berg 				needed_static = link->needed_rx_chains;
945b4f85443SJohannes Berg 				needed_dynamic = link->needed_rx_chains;
94604ecd257SJohannes Berg 				break;
94704ecd257SJohannes Berg 			case IEEE80211_SMPS_DYNAMIC:
94804ecd257SJohannes Berg 				needed_static = 1;
949b4f85443SJohannes Berg 				needed_dynamic = link->needed_rx_chains;
95004ecd257SJohannes Berg 				break;
95104ecd257SJohannes Berg 			case IEEE80211_SMPS_STATIC:
95204ecd257SJohannes Berg 				needed_static = 1;
95304ecd257SJohannes Berg 				needed_dynamic = 1;
95404ecd257SJohannes Berg 				break;
95504ecd257SJohannes Berg 			}
95604ecd257SJohannes Berg 
95704ecd257SJohannes Berg 			rx_chains_static = max(rx_chains_static, needed_static);
95804ecd257SJohannes Berg 			rx_chains_dynamic = max(rx_chains_dynamic, needed_dynamic);
95904ecd257SJohannes Berg 		}
960b4f85443SJohannes Berg 	}
9617b8a9cddSIdo Yariv 
9627b8a9cddSIdo Yariv 	/* Disable SMPS for the monitor interface */
9637b8a9cddSIdo Yariv 	sdata = rcu_dereference(local->monitor_sdata);
9647b8a9cddSIdo Yariv 	if (sdata &&
965d8675a63SJohannes Berg 	    rcu_access_pointer(sdata->vif.bss_conf.chanctx_conf) == &chanctx->conf)
9667b8a9cddSIdo Yariv 		rx_chains_dynamic = rx_chains_static = local->rx_chains;
9677b8a9cddSIdo Yariv 
96804ecd257SJohannes Berg 	rcu_read_unlock();
96904ecd257SJohannes Berg 
97004ecd257SJohannes Berg 	if (!local->use_chanctx) {
97104ecd257SJohannes Berg 		if (rx_chains_static > 1)
97204ecd257SJohannes Berg 			local->smps_mode = IEEE80211_SMPS_OFF;
97304ecd257SJohannes Berg 		else if (rx_chains_dynamic > 1)
97404ecd257SJohannes Berg 			local->smps_mode = IEEE80211_SMPS_DYNAMIC;
97504ecd257SJohannes Berg 		else
97604ecd257SJohannes Berg 			local->smps_mode = IEEE80211_SMPS_STATIC;
97704ecd257SJohannes Berg 		ieee80211_hw_config(local, 0);
97804ecd257SJohannes Berg 	}
97904ecd257SJohannes Berg 
98004ecd257SJohannes Berg 	if (rx_chains_static == chanctx->conf.rx_chains_static &&
98104ecd257SJohannes Berg 	    rx_chains_dynamic == chanctx->conf.rx_chains_dynamic)
98204ecd257SJohannes Berg 		return;
98304ecd257SJohannes Berg 
98404ecd257SJohannes Berg 	chanctx->conf.rx_chains_static = rx_chains_static;
98504ecd257SJohannes Berg 	chanctx->conf.rx_chains_dynamic = rx_chains_dynamic;
98604ecd257SJohannes Berg 	drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RX_CHAINS);
98704ecd257SJohannes Berg }
98804ecd257SJohannes Berg 
98911335a55SLuciano Coelho static void
990b4f85443SJohannes Berg __ieee80211_link_copy_chanctx_to_vlans(struct ieee80211_link_data *link,
99111335a55SLuciano Coelho 				       bool clear)
99211335a55SLuciano Coelho {
993b4f85443SJohannes Berg 	struct ieee80211_sub_if_data *sdata = link->sdata;
994b4f85443SJohannes Berg 	unsigned int link_id = link->link_id;
995d8675a63SJohannes Berg 	struct ieee80211_bss_conf *link_conf = link->conf;
99633926eb7SJohannes Berg 	struct ieee80211_local *local __maybe_unused = sdata->local;
99711335a55SLuciano Coelho 	struct ieee80211_sub_if_data *vlan;
99811335a55SLuciano Coelho 	struct ieee80211_chanctx_conf *conf;
99911335a55SLuciano Coelho 
100011335a55SLuciano Coelho 	if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP))
100111335a55SLuciano Coelho 		return;
100211335a55SLuciano Coelho 
100311335a55SLuciano Coelho 	lockdep_assert_held(&local->mtx);
100411335a55SLuciano Coelho 
100511335a55SLuciano Coelho 	/* Check that conf exists, even when clearing this function
100611335a55SLuciano Coelho 	 * must be called with the AP's channel context still there
100711335a55SLuciano Coelho 	 * as it would otherwise cause VLANs to have an invalid
100811335a55SLuciano Coelho 	 * channel context pointer for a while, possibly pointing
100911335a55SLuciano Coelho 	 * to a channel context that has already been freed.
101011335a55SLuciano Coelho 	 */
1011b4f85443SJohannes Berg 	conf = rcu_dereference_protected(link_conf->chanctx_conf,
101211335a55SLuciano Coelho 					 lockdep_is_held(&local->chanctx_mtx));
101311335a55SLuciano Coelho 	WARN_ON(!conf);
101411335a55SLuciano Coelho 
101511335a55SLuciano Coelho 	if (clear)
101611335a55SLuciano Coelho 		conf = NULL;
101711335a55SLuciano Coelho 
1018d8675a63SJohannes Berg 	rcu_read_lock();
1019d8675a63SJohannes Berg 	list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) {
1020d8675a63SJohannes Berg 		struct ieee80211_bss_conf *vlan_conf;
1021d8675a63SJohannes Berg 
1022d8675a63SJohannes Berg 		vlan_conf = rcu_dereference(vlan->vif.link_conf[link_id]);
1023d8675a63SJohannes Berg 		if (WARN_ON(!vlan_conf))
1024d8675a63SJohannes Berg 			continue;
1025d8675a63SJohannes Berg 
1026d8675a63SJohannes Berg 		rcu_assign_pointer(vlan_conf->chanctx_conf, conf);
1027d8675a63SJohannes Berg 	}
1028d8675a63SJohannes Berg 	rcu_read_unlock();
102911335a55SLuciano Coelho }
103011335a55SLuciano Coelho 
1031b4f85443SJohannes Berg void ieee80211_link_copy_chanctx_to_vlans(struct ieee80211_link_data *link,
103211335a55SLuciano Coelho 					  bool clear)
103311335a55SLuciano Coelho {
1034b4f85443SJohannes Berg 	struct ieee80211_local *local = link->sdata->local;
103511335a55SLuciano Coelho 
103611335a55SLuciano Coelho 	mutex_lock(&local->chanctx_mtx);
103711335a55SLuciano Coelho 
1038b4f85443SJohannes Berg 	__ieee80211_link_copy_chanctx_to_vlans(link, clear);
103911335a55SLuciano Coelho 
104011335a55SLuciano Coelho 	mutex_unlock(&local->chanctx_mtx);
104111335a55SLuciano Coelho }
104211335a55SLuciano Coelho 
1043b4f85443SJohannes Berg int ieee80211_link_unreserve_chanctx(struct ieee80211_link_data *link)
104411335a55SLuciano Coelho {
1045b4f85443SJohannes Berg 	struct ieee80211_sub_if_data *sdata = link->sdata;
1046b4f85443SJohannes Berg 	struct ieee80211_chanctx *ctx = link->reserved_chanctx;
1047e3afb920SMichal Kazior 
104811335a55SLuciano Coelho 	lockdep_assert_held(&sdata->local->chanctx_mtx);
104911335a55SLuciano Coelho 
1050e3afb920SMichal Kazior 	if (WARN_ON(!ctx))
105111335a55SLuciano Coelho 		return -EINVAL;
105211335a55SLuciano Coelho 
1053b4f85443SJohannes Berg 	list_del(&link->reserved_chanctx_list);
1054b4f85443SJohannes Berg 	link->reserved_chanctx = NULL;
105511335a55SLuciano Coelho 
10565bcae31dSMichal Kazior 	if (ieee80211_chanctx_refcount(sdata->local, ctx) == 0) {
10575bcae31dSMichal Kazior 		if (ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER) {
10585bcae31dSMichal Kazior 			if (WARN_ON(!ctx->replace_ctx))
10595bcae31dSMichal Kazior 				return -EINVAL;
10605bcae31dSMichal Kazior 
10615bcae31dSMichal Kazior 			WARN_ON(ctx->replace_ctx->replace_state !=
10625bcae31dSMichal Kazior 			        IEEE80211_CHANCTX_WILL_BE_REPLACED);
10635bcae31dSMichal Kazior 			WARN_ON(ctx->replace_ctx->replace_ctx != ctx);
10645bcae31dSMichal Kazior 
10655bcae31dSMichal Kazior 			ctx->replace_ctx->replace_ctx = NULL;
10665bcae31dSMichal Kazior 			ctx->replace_ctx->replace_state =
10675bcae31dSMichal Kazior 					IEEE80211_CHANCTX_REPLACE_NONE;
10685bcae31dSMichal Kazior 
10695bcae31dSMichal Kazior 			list_del_rcu(&ctx->list);
10705bcae31dSMichal Kazior 			kfree_rcu(ctx, rcu_head);
10715bcae31dSMichal Kazior 		} else {
1072e3afb920SMichal Kazior 			ieee80211_free_chanctx(sdata->local, ctx);
10735bcae31dSMichal Kazior 		}
10745bcae31dSMichal Kazior 	}
1075e3afb920SMichal Kazior 
107611335a55SLuciano Coelho 	return 0;
107711335a55SLuciano Coelho }
107811335a55SLuciano Coelho 
1079b4f85443SJohannes Berg int ieee80211_link_reserve_chanctx(struct ieee80211_link_data *link,
108011335a55SLuciano Coelho 				   const struct cfg80211_chan_def *chandef,
108109332481SMichal Kazior 				   enum ieee80211_chanctx_mode mode,
108209332481SMichal Kazior 				   bool radar_required)
108311335a55SLuciano Coelho {
1084b4f85443SJohannes Berg 	struct ieee80211_sub_if_data *sdata = link->sdata;
108511335a55SLuciano Coelho 	struct ieee80211_local *local = sdata->local;
10865bcae31dSMichal Kazior 	struct ieee80211_chanctx *new_ctx, *curr_ctx, *ctx;
108711335a55SLuciano Coelho 
10885bcae31dSMichal Kazior 	lockdep_assert_held(&local->chanctx_mtx);
108911335a55SLuciano Coelho 
1090b4f85443SJohannes Berg 	curr_ctx = ieee80211_link_get_chanctx(link);
10915bcae31dSMichal Kazior 	if (curr_ctx && local->use_chanctx && !local->ops->switch_vif_chanctx)
10925bcae31dSMichal Kazior 		return -ENOTSUPP;
109311335a55SLuciano Coelho 
109413f348a8SMichal Kazior 	new_ctx = ieee80211_find_reservation_chanctx(local, chandef, mode);
109511335a55SLuciano Coelho 	if (!new_ctx) {
10965bcae31dSMichal Kazior 		if (ieee80211_can_create_new_chanctx(local)) {
109711335a55SLuciano Coelho 			new_ctx = ieee80211_new_chanctx(local, chandef, mode);
10985bcae31dSMichal Kazior 			if (IS_ERR(new_ctx))
10995bcae31dSMichal Kazior 				return PTR_ERR(new_ctx);
1100c2b90ad8SMichal Kazior 		} else {
11015bcae31dSMichal Kazior 			if (!curr_ctx ||
11025bcae31dSMichal Kazior 			    (curr_ctx->replace_state ==
11035bcae31dSMichal Kazior 			     IEEE80211_CHANCTX_WILL_BE_REPLACED) ||
1104b4f85443SJohannes Berg 			    !list_empty(&curr_ctx->reserved_links)) {
11055bcae31dSMichal Kazior 				/*
1106b4f85443SJohannes Berg 				 * Another link already requested this context
11075bcae31dSMichal Kazior 				 * for a reservation. Find another one hoping
1108b4f85443SJohannes Berg 				 * all links assigned to it will also switch
11095bcae31dSMichal Kazior 				 * soon enough.
11105bcae31dSMichal Kazior 				 *
11115bcae31dSMichal Kazior 				 * TODO: This needs a little more work as some
11125bcae31dSMichal Kazior 				 * cases (more than 2 chanctx capable devices)
11135bcae31dSMichal Kazior 				 * may fail which could otherwise succeed
11145bcae31dSMichal Kazior 				 * provided some channel context juggling was
11155bcae31dSMichal Kazior 				 * performed.
11165bcae31dSMichal Kazior 				 *
1117b4f85443SJohannes Berg 				 * Consider ctx1..3, link1..6, each ctx has 2
1118b4f85443SJohannes Berg 				 * links. link1 and link2 from ctx1 request new
11195bcae31dSMichal Kazior 				 * different chandefs starting 2 in-place
11205bcae31dSMichal Kazior 				 * reserations with ctx4 and ctx5 replacing
1121b4f85443SJohannes Berg 				 * ctx1 and ctx2 respectively. Next link5 and
1122b4f85443SJohannes Berg 				 * link6 from ctx3 reserve ctx4. If link3 and
1123b4f85443SJohannes Berg 				 * link4 remain on ctx2 as they are then this
11245bcae31dSMichal Kazior 				 * fails unless `replace_ctx` from ctx5 is
11255bcae31dSMichal Kazior 				 * replaced with ctx3.
11265bcae31dSMichal Kazior 				 */
11275bcae31dSMichal Kazior 				list_for_each_entry(ctx, &local->chanctx_list,
11285bcae31dSMichal Kazior 						    list) {
11295bcae31dSMichal Kazior 					if (ctx->replace_state !=
11305bcae31dSMichal Kazior 					    IEEE80211_CHANCTX_REPLACE_NONE)
11315bcae31dSMichal Kazior 						continue;
11325bcae31dSMichal Kazior 
1133b4f85443SJohannes Berg 					if (!list_empty(&ctx->reserved_links))
11345bcae31dSMichal Kazior 						continue;
11355bcae31dSMichal Kazior 
11365bcae31dSMichal Kazior 					curr_ctx = ctx;
11375bcae31dSMichal Kazior 					break;
11385bcae31dSMichal Kazior 				}
11395bcae31dSMichal Kazior 			}
11405bcae31dSMichal Kazior 
11415bcae31dSMichal Kazior 			/*
11425bcae31dSMichal Kazior 			 * If that's true then all available contexts already
11435bcae31dSMichal Kazior 			 * have reservations and cannot be used.
11445bcae31dSMichal Kazior 			 */
11455bcae31dSMichal Kazior 			if (!curr_ctx ||
11465bcae31dSMichal Kazior 			    (curr_ctx->replace_state ==
11475bcae31dSMichal Kazior 			     IEEE80211_CHANCTX_WILL_BE_REPLACED) ||
1148b4f85443SJohannes Berg 			    !list_empty(&curr_ctx->reserved_links))
11495bcae31dSMichal Kazior 				return -EBUSY;
11505bcae31dSMichal Kazior 
11515bcae31dSMichal Kazior 			new_ctx = ieee80211_alloc_chanctx(local, chandef, mode);
11525bcae31dSMichal Kazior 			if (!new_ctx)
11535bcae31dSMichal Kazior 				return -ENOMEM;
11545bcae31dSMichal Kazior 
11555bcae31dSMichal Kazior 			new_ctx->replace_ctx = curr_ctx;
11565bcae31dSMichal Kazior 			new_ctx->replace_state =
11575bcae31dSMichal Kazior 					IEEE80211_CHANCTX_REPLACES_OTHER;
11585bcae31dSMichal Kazior 
11595bcae31dSMichal Kazior 			curr_ctx->replace_ctx = new_ctx;
11605bcae31dSMichal Kazior 			curr_ctx->replace_state =
11615bcae31dSMichal Kazior 					IEEE80211_CHANCTX_WILL_BE_REPLACED;
11625bcae31dSMichal Kazior 
11635bcae31dSMichal Kazior 			list_add_rcu(&new_ctx->list, &local->chanctx_list);
116411335a55SLuciano Coelho 		}
11655d52ee81SLuciano Coelho 	}
116611335a55SLuciano Coelho 
1167b4f85443SJohannes Berg 	list_add(&link->reserved_chanctx_list, &new_ctx->reserved_links);
1168b4f85443SJohannes Berg 	link->reserved_chanctx = new_ctx;
1169b4f85443SJohannes Berg 	link->reserved_chandef = *chandef;
1170b4f85443SJohannes Berg 	link->reserved_radar_required = radar_required;
1171b4f85443SJohannes Berg 	link->reserved_ready = false;
11725bcae31dSMichal Kazior 
11735bcae31dSMichal Kazior 	return 0;
117411335a55SLuciano Coelho }
117511335a55SLuciano Coelho 
117603078de4SMichal Kazior static void
1177b4f85443SJohannes Berg ieee80211_link_chanctx_reservation_complete(struct ieee80211_link_data *link)
117803078de4SMichal Kazior {
1179b4f85443SJohannes Berg 	struct ieee80211_sub_if_data *sdata = link->sdata;
1180b4f85443SJohannes Berg 
118103078de4SMichal Kazior 	switch (sdata->vif.type) {
118203078de4SMichal Kazior 	case NL80211_IFTYPE_ADHOC:
118303078de4SMichal Kazior 	case NL80211_IFTYPE_AP:
118403078de4SMichal Kazior 	case NL80211_IFTYPE_MESH_POINT:
11856e0bd6c3SRostislav Lisovy 	case NL80211_IFTYPE_OCB:
118603078de4SMichal Kazior 		ieee80211_queue_work(&sdata->local->hw,
1187b4f85443SJohannes Berg 				     &link->csa_finalize_work);
118803078de4SMichal Kazior 		break;
118903078de4SMichal Kazior 	case NL80211_IFTYPE_STATION:
11904c3ebc56SMichal Kazior 		ieee80211_queue_work(&sdata->local->hw,
1191*5bd5666dSJohannes Berg 				     &link->u.mgd.chswitch_work);
11924c3ebc56SMichal Kazior 		break;
11934c3ebc56SMichal Kazior 	case NL80211_IFTYPE_UNSPECIFIED:
119403078de4SMichal Kazior 	case NL80211_IFTYPE_AP_VLAN:
119503078de4SMichal Kazior 	case NL80211_IFTYPE_WDS:
119603078de4SMichal Kazior 	case NL80211_IFTYPE_MONITOR:
119703078de4SMichal Kazior 	case NL80211_IFTYPE_P2P_CLIENT:
119803078de4SMichal Kazior 	case NL80211_IFTYPE_P2P_GO:
119903078de4SMichal Kazior 	case NL80211_IFTYPE_P2P_DEVICE:
1200cb3b7d87SAyala Beker 	case NL80211_IFTYPE_NAN:
120103078de4SMichal Kazior 	case NUM_NL80211_IFTYPES:
120203078de4SMichal Kazior 		WARN_ON(1);
120303078de4SMichal Kazior 		break;
120403078de4SMichal Kazior 	}
120503078de4SMichal Kazior }
120603078de4SMichal Kazior 
12072967e031SFelix Fietkau static void
1208b4f85443SJohannes Berg ieee80211_link_update_chandef(struct ieee80211_link_data *link,
12092967e031SFelix Fietkau 			      const struct cfg80211_chan_def *chandef)
12102967e031SFelix Fietkau {
1211b4f85443SJohannes Berg 	struct ieee80211_sub_if_data *sdata = link->sdata;
1212b4f85443SJohannes Berg 	unsigned int link_id = link->link_id;
12132967e031SFelix Fietkau 	struct ieee80211_sub_if_data *vlan;
12142967e031SFelix Fietkau 
1215d8675a63SJohannes Berg 	link->conf->chandef = *chandef;
12162967e031SFelix Fietkau 
12172967e031SFelix Fietkau 	if (sdata->vif.type != NL80211_IFTYPE_AP)
12182967e031SFelix Fietkau 		return;
12192967e031SFelix Fietkau 
1220d8675a63SJohannes Berg 	rcu_read_lock();
1221d8675a63SJohannes Berg 	list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) {
1222d8675a63SJohannes Berg 		struct ieee80211_bss_conf *vlan_conf;
1223d8675a63SJohannes Berg 
1224d8675a63SJohannes Berg 		vlan_conf = rcu_dereference(vlan->vif.link_conf[link_id]);
1225d8675a63SJohannes Berg 		if (WARN_ON(!vlan_conf))
1226d8675a63SJohannes Berg 			continue;
1227d8675a63SJohannes Berg 
1228d8675a63SJohannes Berg 		vlan_conf->chandef = *chandef;
1229d8675a63SJohannes Berg 	}
1230d8675a63SJohannes Berg 	rcu_read_unlock();
12312967e031SFelix Fietkau }
12322967e031SFelix Fietkau 
12335bcae31dSMichal Kazior static int
1234b4f85443SJohannes Berg ieee80211_link_use_reserved_reassign(struct ieee80211_link_data *link)
123511335a55SLuciano Coelho {
1236b4f85443SJohannes Berg 	struct ieee80211_sub_if_data *sdata = link->sdata;
1237d8675a63SJohannes Berg 	struct ieee80211_bss_conf *link_conf = link->conf;
123811335a55SLuciano Coelho 	struct ieee80211_local *local = sdata->local;
12395bcae31dSMichal Kazior 	struct ieee80211_vif_chanctx_switch vif_chsw[1] = {};
12405bcae31dSMichal Kazior 	struct ieee80211_chanctx *old_ctx, *new_ctx;
12415bcae31dSMichal Kazior 	const struct cfg80211_chan_def *chandef;
12425bcae31dSMichal Kazior 	u32 changed = 0;
12435bcae31dSMichal Kazior 	int err;
124411335a55SLuciano Coelho 
124511335a55SLuciano Coelho 	lockdep_assert_held(&local->mtx);
12465bcae31dSMichal Kazior 	lockdep_assert_held(&local->chanctx_mtx);
124711335a55SLuciano Coelho 
1248b4f85443SJohannes Berg 	new_ctx = link->reserved_chanctx;
1249b4f85443SJohannes Berg 	old_ctx = ieee80211_link_get_chanctx(link);
125011335a55SLuciano Coelho 
1251b4f85443SJohannes Berg 	if (WARN_ON(!link->reserved_ready))
12525bcae31dSMichal Kazior 		return -EBUSY;
125311335a55SLuciano Coelho 
12545bcae31dSMichal Kazior 	if (WARN_ON(!new_ctx))
12555bcae31dSMichal Kazior 		return -EINVAL;
125611335a55SLuciano Coelho 
12575bcae31dSMichal Kazior 	if (WARN_ON(!old_ctx))
12585bcae31dSMichal Kazior 		return -EINVAL;
125911335a55SLuciano Coelho 
12605bcae31dSMichal Kazior 	if (WARN_ON(new_ctx->replace_state ==
12615bcae31dSMichal Kazior 		    IEEE80211_CHANCTX_REPLACES_OTHER))
12625bcae31dSMichal Kazior 		return -EINVAL;
126311335a55SLuciano Coelho 
12645bcae31dSMichal Kazior 	chandef = ieee80211_chanctx_non_reserved_chandef(local, new_ctx,
1265b4f85443SJohannes Berg 				&link->reserved_chandef);
12665bcae31dSMichal Kazior 	if (WARN_ON(!chandef))
12675bcae31dSMichal Kazior 		return -EINVAL;
126811335a55SLuciano Coelho 
1269b4f85443SJohannes Berg 	if (link_conf->chandef.width != link->reserved_chandef.width)
1270d6c37509SMordechay Goodstein 		changed = BSS_CHANGED_BANDWIDTH;
127144b72ca8SIlan Peer 
1272b4f85443SJohannes Berg 	ieee80211_link_update_chandef(link, &link->reserved_chandef);
1273bf45a242SAndrei Otcheretianski 
1274d6c37509SMordechay Goodstein 	ieee80211_change_chanctx(local, new_ctx, old_ctx, chandef);
127544b72ca8SIlan Peer 
12765bcae31dSMichal Kazior 	vif_chsw[0].vif = &sdata->vif;
12775bcae31dSMichal Kazior 	vif_chsw[0].old_ctx = &old_ctx->conf;
12785bcae31dSMichal Kazior 	vif_chsw[0].new_ctx = &new_ctx->conf;
1279b4f85443SJohannes Berg 	vif_chsw[0].link_id = link->link_id;
12805bcae31dSMichal Kazior 
1281b4f85443SJohannes Berg 	list_del(&link->reserved_chanctx_list);
1282b4f85443SJohannes Berg 	link->reserved_chanctx = NULL;
128311335a55SLuciano Coelho 
12845bcae31dSMichal Kazior 	err = drv_switch_vif_chanctx(local, vif_chsw, 1,
12855bcae31dSMichal Kazior 				     CHANCTX_SWMODE_REASSIGN_VIF);
12865bcae31dSMichal Kazior 	if (err) {
12875bcae31dSMichal Kazior 		if (ieee80211_chanctx_refcount(local, new_ctx) == 0)
12885bcae31dSMichal Kazior 			ieee80211_free_chanctx(local, new_ctx);
12895bcae31dSMichal Kazior 
129003078de4SMichal Kazior 		goto out;
129111335a55SLuciano Coelho 	}
129211335a55SLuciano Coelho 
1293b4f85443SJohannes Berg 	list_move(&link->assigned_chanctx_list, &new_ctx->assigned_links);
1294b4f85443SJohannes Berg 	rcu_assign_pointer(link_conf->chanctx_conf, &new_ctx->conf);
12955bcae31dSMichal Kazior 
129611335a55SLuciano Coelho 	if (sdata->vif.type == NL80211_IFTYPE_AP)
1297b4f85443SJohannes Berg 		__ieee80211_link_copy_chanctx_to_vlans(link, false);
12985bcae31dSMichal Kazior 
129917c18bf8SJohannes Berg 	ieee80211_check_fast_xmit_iface(sdata);
130017c18bf8SJohannes Berg 
13015bcae31dSMichal Kazior 	if (ieee80211_chanctx_refcount(local, old_ctx) == 0)
13025bcae31dSMichal Kazior 		ieee80211_free_chanctx(local, old_ctx);
13035bcae31dSMichal Kazior 
1304d6c37509SMordechay Goodstein 	ieee80211_recalc_chanctx_min_def(local, new_ctx);
1305722ddb0dSEmmanuel Grumbach 	ieee80211_recalc_smps_chanctx(local, new_ctx);
1306722ddb0dSEmmanuel Grumbach 	ieee80211_recalc_radar_chanctx(local, new_ctx);
1307722ddb0dSEmmanuel Grumbach 
13085bcae31dSMichal Kazior 	if (changed)
1309d8675a63SJohannes Berg 		ieee80211_link_info_change_notify(sdata, link, changed);
13105bcae31dSMichal Kazior 
131103078de4SMichal Kazior out:
1312b4f85443SJohannes Berg 	ieee80211_link_chanctx_reservation_complete(link);
13135bcae31dSMichal Kazior 	return err;
13145d52ee81SLuciano Coelho }
131511335a55SLuciano Coelho 
13165bcae31dSMichal Kazior static int
1317b4f85443SJohannes Berg ieee80211_link_use_reserved_assign(struct ieee80211_link_data *link)
13185bcae31dSMichal Kazior {
1319b4f85443SJohannes Berg 	struct ieee80211_sub_if_data *sdata = link->sdata;
13205bcae31dSMichal Kazior 	struct ieee80211_local *local = sdata->local;
13215bcae31dSMichal Kazior 	struct ieee80211_chanctx *old_ctx, *new_ctx;
13225bcae31dSMichal Kazior 	const struct cfg80211_chan_def *chandef;
13235bcae31dSMichal Kazior 	int err;
13245bcae31dSMichal Kazior 
1325d8675a63SJohannes Berg 	old_ctx = ieee80211_link_get_chanctx(link);
1326b4f85443SJohannes Berg 	new_ctx = link->reserved_chanctx;
13275bcae31dSMichal Kazior 
1328b4f85443SJohannes Berg 	if (WARN_ON(!link->reserved_ready))
13295bcae31dSMichal Kazior 		return -EINVAL;
13305bcae31dSMichal Kazior 
13315bcae31dSMichal Kazior 	if (WARN_ON(old_ctx))
13325bcae31dSMichal Kazior 		return -EINVAL;
13335bcae31dSMichal Kazior 
13345bcae31dSMichal Kazior 	if (WARN_ON(!new_ctx))
13355bcae31dSMichal Kazior 		return -EINVAL;
13365bcae31dSMichal Kazior 
13375bcae31dSMichal Kazior 	if (WARN_ON(new_ctx->replace_state ==
13385bcae31dSMichal Kazior 		    IEEE80211_CHANCTX_REPLACES_OTHER))
13395bcae31dSMichal Kazior 		return -EINVAL;
13405bcae31dSMichal Kazior 
13415bcae31dSMichal Kazior 	chandef = ieee80211_chanctx_non_reserved_chandef(local, new_ctx,
1342b4f85443SJohannes Berg 				&link->reserved_chandef);
13435bcae31dSMichal Kazior 	if (WARN_ON(!chandef))
13445bcae31dSMichal Kazior 		return -EINVAL;
13455bcae31dSMichal Kazior 
1346d6c37509SMordechay Goodstein 	ieee80211_change_chanctx(local, new_ctx, new_ctx, chandef);
1347bf45a242SAndrei Otcheretianski 
1348b4f85443SJohannes Berg 	list_del(&link->reserved_chanctx_list);
1349b4f85443SJohannes Berg 	link->reserved_chanctx = NULL;
13505bcae31dSMichal Kazior 
1351b4f85443SJohannes Berg 	err = ieee80211_assign_link_chanctx(link, new_ctx);
13525bcae31dSMichal Kazior 	if (err) {
13535bcae31dSMichal Kazior 		if (ieee80211_chanctx_refcount(local, new_ctx) == 0)
13545bcae31dSMichal Kazior 			ieee80211_free_chanctx(local, new_ctx);
13555bcae31dSMichal Kazior 
13565bcae31dSMichal Kazior 		goto out;
13575bcae31dSMichal Kazior 	}
13585bcae31dSMichal Kazior 
13595bcae31dSMichal Kazior out:
1360b4f85443SJohannes Berg 	ieee80211_link_chanctx_reservation_complete(link);
13615bcae31dSMichal Kazior 	return err;
13625bcae31dSMichal Kazior }
13635bcae31dSMichal Kazior 
13645bcae31dSMichal Kazior static bool
1365b4f85443SJohannes Berg ieee80211_link_has_in_place_reservation(struct ieee80211_link_data *link)
13665bcae31dSMichal Kazior {
1367b4f85443SJohannes Berg 	struct ieee80211_sub_if_data *sdata = link->sdata;
13685bcae31dSMichal Kazior 	struct ieee80211_chanctx *old_ctx, *new_ctx;
13695bcae31dSMichal Kazior 
13705bcae31dSMichal Kazior 	lockdep_assert_held(&sdata->local->chanctx_mtx);
13715bcae31dSMichal Kazior 
1372b4f85443SJohannes Berg 	new_ctx = link->reserved_chanctx;
1373b4f85443SJohannes Berg 	old_ctx = ieee80211_link_get_chanctx(link);
13745bcae31dSMichal Kazior 
13755bcae31dSMichal Kazior 	if (!old_ctx)
13765bcae31dSMichal Kazior 		return false;
13775bcae31dSMichal Kazior 
13785bcae31dSMichal Kazior 	if (WARN_ON(!new_ctx))
13795bcae31dSMichal Kazior 		return false;
13805bcae31dSMichal Kazior 
13815bcae31dSMichal Kazior 	if (old_ctx->replace_state != IEEE80211_CHANCTX_WILL_BE_REPLACED)
13825bcae31dSMichal Kazior 		return false;
13835bcae31dSMichal Kazior 
13845bcae31dSMichal Kazior 	if (new_ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
13855bcae31dSMichal Kazior 		return false;
13865bcae31dSMichal Kazior 
13875bcae31dSMichal Kazior 	return true;
13885bcae31dSMichal Kazior }
13895bcae31dSMichal Kazior 
13905bcae31dSMichal Kazior static int ieee80211_chsw_switch_hwconf(struct ieee80211_local *local,
13915bcae31dSMichal Kazior 					struct ieee80211_chanctx *new_ctx)
13925bcae31dSMichal Kazior {
13935bcae31dSMichal Kazior 	const struct cfg80211_chan_def *chandef;
13945bcae31dSMichal Kazior 
13955bcae31dSMichal Kazior 	lockdep_assert_held(&local->mtx);
13965bcae31dSMichal Kazior 	lockdep_assert_held(&local->chanctx_mtx);
13975bcae31dSMichal Kazior 
13985bcae31dSMichal Kazior 	chandef = ieee80211_chanctx_reserved_chandef(local, new_ctx, NULL);
13995bcae31dSMichal Kazior 	if (WARN_ON(!chandef))
14005bcae31dSMichal Kazior 		return -EINVAL;
14015bcae31dSMichal Kazior 
14025bcae31dSMichal Kazior 	local->hw.conf.radar_enabled = new_ctx->conf.radar_enabled;
14035bcae31dSMichal Kazior 	local->_oper_chandef = *chandef;
14045bcae31dSMichal Kazior 	ieee80211_hw_config(local, 0);
14055bcae31dSMichal Kazior 
14065bcae31dSMichal Kazior 	return 0;
14075bcae31dSMichal Kazior }
14085bcae31dSMichal Kazior 
14095bcae31dSMichal Kazior static int ieee80211_chsw_switch_vifs(struct ieee80211_local *local,
14105bcae31dSMichal Kazior 				      int n_vifs)
14115bcae31dSMichal Kazior {
14125bcae31dSMichal Kazior 	struct ieee80211_vif_chanctx_switch *vif_chsw;
1413b4f85443SJohannes Berg 	struct ieee80211_link_data *link;
14145bcae31dSMichal Kazior 	struct ieee80211_chanctx *ctx, *old_ctx;
14155bcae31dSMichal Kazior 	int i, err;
14165bcae31dSMichal Kazior 
14175bcae31dSMichal Kazior 	lockdep_assert_held(&local->mtx);
14185bcae31dSMichal Kazior 	lockdep_assert_held(&local->chanctx_mtx);
14195bcae31dSMichal Kazior 
14206396bb22SKees Cook 	vif_chsw = kcalloc(n_vifs, sizeof(vif_chsw[0]), GFP_KERNEL);
14215bcae31dSMichal Kazior 	if (!vif_chsw)
14225bcae31dSMichal Kazior 		return -ENOMEM;
14235bcae31dSMichal Kazior 
14245bcae31dSMichal Kazior 	i = 0;
14255bcae31dSMichal Kazior 	list_for_each_entry(ctx, &local->chanctx_list, list) {
14265bcae31dSMichal Kazior 		if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
14275bcae31dSMichal Kazior 			continue;
14285bcae31dSMichal Kazior 
14295bcae31dSMichal Kazior 		if (WARN_ON(!ctx->replace_ctx)) {
14305bcae31dSMichal Kazior 			err = -EINVAL;
14315bcae31dSMichal Kazior 			goto out;
14325bcae31dSMichal Kazior 		}
14335bcae31dSMichal Kazior 
1434b4f85443SJohannes Berg 		list_for_each_entry(link, &ctx->reserved_links,
14355bcae31dSMichal Kazior 				    reserved_chanctx_list) {
1436b4f85443SJohannes Berg 			if (!ieee80211_link_has_in_place_reservation(link))
14375bcae31dSMichal Kazior 				continue;
14385bcae31dSMichal Kazior 
1439b4f85443SJohannes Berg 			old_ctx = ieee80211_link_get_chanctx(link);
1440b4f85443SJohannes Berg 			vif_chsw[i].vif = &link->sdata->vif;
14415bcae31dSMichal Kazior 			vif_chsw[i].old_ctx = &old_ctx->conf;
14425bcae31dSMichal Kazior 			vif_chsw[i].new_ctx = &ctx->conf;
1443b4f85443SJohannes Berg 			vif_chsw[i].link_id = link->link_id;
14445bcae31dSMichal Kazior 
14455bcae31dSMichal Kazior 			i++;
14465bcae31dSMichal Kazior 		}
14475bcae31dSMichal Kazior 	}
14485bcae31dSMichal Kazior 
14495bcae31dSMichal Kazior 	err = drv_switch_vif_chanctx(local, vif_chsw, n_vifs,
14505bcae31dSMichal Kazior 				     CHANCTX_SWMODE_SWAP_CONTEXTS);
14515bcae31dSMichal Kazior 
14525bcae31dSMichal Kazior out:
14535bcae31dSMichal Kazior 	kfree(vif_chsw);
14545bcae31dSMichal Kazior 	return err;
14555bcae31dSMichal Kazior }
14565bcae31dSMichal Kazior 
14575bcae31dSMichal Kazior static int ieee80211_chsw_switch_ctxs(struct ieee80211_local *local)
14585bcae31dSMichal Kazior {
14595bcae31dSMichal Kazior 	struct ieee80211_chanctx *ctx;
14605bcae31dSMichal Kazior 	int err;
14615bcae31dSMichal Kazior 
14625bcae31dSMichal Kazior 	lockdep_assert_held(&local->mtx);
14635bcae31dSMichal Kazior 	lockdep_assert_held(&local->chanctx_mtx);
14645bcae31dSMichal Kazior 
14655bcae31dSMichal Kazior 	list_for_each_entry(ctx, &local->chanctx_list, list) {
14665bcae31dSMichal Kazior 		if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
14675bcae31dSMichal Kazior 			continue;
14685bcae31dSMichal Kazior 
1469b4f85443SJohannes Berg 		if (!list_empty(&ctx->replace_ctx->assigned_links))
14705bcae31dSMichal Kazior 			continue;
14715bcae31dSMichal Kazior 
14725bcae31dSMichal Kazior 		ieee80211_del_chanctx(local, ctx->replace_ctx);
14735bcae31dSMichal Kazior 		err = ieee80211_add_chanctx(local, ctx);
14745bcae31dSMichal Kazior 		if (err)
14755bcae31dSMichal Kazior 			goto err;
14765bcae31dSMichal Kazior 	}
14775bcae31dSMichal Kazior 
14785bcae31dSMichal Kazior 	return 0;
14795bcae31dSMichal Kazior 
14805bcae31dSMichal Kazior err:
14815bcae31dSMichal Kazior 	WARN_ON(ieee80211_add_chanctx(local, ctx));
14825bcae31dSMichal Kazior 	list_for_each_entry_continue_reverse(ctx, &local->chanctx_list, list) {
14835bcae31dSMichal Kazior 		if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
14845bcae31dSMichal Kazior 			continue;
14855bcae31dSMichal Kazior 
1486b4f85443SJohannes Berg 		if (!list_empty(&ctx->replace_ctx->assigned_links))
14875bcae31dSMichal Kazior 			continue;
14885bcae31dSMichal Kazior 
14895bcae31dSMichal Kazior 		ieee80211_del_chanctx(local, ctx);
14905bcae31dSMichal Kazior 		WARN_ON(ieee80211_add_chanctx(local, ctx->replace_ctx));
14915bcae31dSMichal Kazior 	}
14925bcae31dSMichal Kazior 
14935bcae31dSMichal Kazior 	return err;
14945bcae31dSMichal Kazior }
14955bcae31dSMichal Kazior 
1496649b2a4dSJohannes Berg static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local)
14975bcae31dSMichal Kazior {
14985bcae31dSMichal Kazior 	struct ieee80211_chanctx *ctx, *ctx_tmp, *old_ctx;
14995bcae31dSMichal Kazior 	struct ieee80211_chanctx *new_ctx = NULL;
1500b7f2405cSKirtika Ruchandani 	int err, n_assigned, n_reserved, n_ready;
15015bcae31dSMichal Kazior 	int n_ctx = 0, n_vifs_switch = 0, n_vifs_assign = 0, n_vifs_ctxless = 0;
15025bcae31dSMichal Kazior 
15035bcae31dSMichal Kazior 	lockdep_assert_held(&local->mtx);
15045bcae31dSMichal Kazior 	lockdep_assert_held(&local->chanctx_mtx);
15055bcae31dSMichal Kazior 
15065bcae31dSMichal Kazior 	/*
15075bcae31dSMichal Kazior 	 * If there are 2 independent pairs of channel contexts performing
15085bcae31dSMichal Kazior 	 * cross-switch of their vifs this code will still wait until both are
15095bcae31dSMichal Kazior 	 * ready even though it could be possible to switch one before the
15105bcae31dSMichal Kazior 	 * other is ready.
15115bcae31dSMichal Kazior 	 *
15125bcae31dSMichal Kazior 	 * For practical reasons and code simplicity just do a single huge
15135bcae31dSMichal Kazior 	 * switch.
15145bcae31dSMichal Kazior 	 */
15155bcae31dSMichal Kazior 
15165bcae31dSMichal Kazior 	/*
15175bcae31dSMichal Kazior 	 * Verify if the reservation is still feasible.
15185bcae31dSMichal Kazior 	 *  - if it's not then disconnect
15195bcae31dSMichal Kazior 	 *  - if it is but not all vifs necessary are ready then defer
15205bcae31dSMichal Kazior 	 */
15215bcae31dSMichal Kazior 
15225bcae31dSMichal Kazior 	list_for_each_entry(ctx, &local->chanctx_list, list) {
1523b4f85443SJohannes Berg 		struct ieee80211_link_data *link;
1524b4f85443SJohannes Berg 
15255bcae31dSMichal Kazior 		if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
15265bcae31dSMichal Kazior 			continue;
15275bcae31dSMichal Kazior 
15285bcae31dSMichal Kazior 		if (WARN_ON(!ctx->replace_ctx)) {
15295bcae31dSMichal Kazior 			err = -EINVAL;
15305bcae31dSMichal Kazior 			goto err;
15315bcae31dSMichal Kazior 		}
15325bcae31dSMichal Kazior 
15335bcae31dSMichal Kazior 		if (!local->use_chanctx)
15345bcae31dSMichal Kazior 			new_ctx = ctx;
15355bcae31dSMichal Kazior 
15365bcae31dSMichal Kazior 		n_ctx++;
15375bcae31dSMichal Kazior 
15385bcae31dSMichal Kazior 		n_assigned = 0;
15395bcae31dSMichal Kazior 		n_reserved = 0;
15405bcae31dSMichal Kazior 		n_ready = 0;
15415bcae31dSMichal Kazior 
1542b4f85443SJohannes Berg 		list_for_each_entry(link, &ctx->replace_ctx->assigned_links,
15435bcae31dSMichal Kazior 				    assigned_chanctx_list) {
15445bcae31dSMichal Kazior 			n_assigned++;
1545b4f85443SJohannes Berg 			if (link->reserved_chanctx) {
15465bcae31dSMichal Kazior 				n_reserved++;
1547b4f85443SJohannes Berg 				if (link->reserved_ready)
15485bcae31dSMichal Kazior 					n_ready++;
15495bcae31dSMichal Kazior 			}
15505bcae31dSMichal Kazior 		}
15515bcae31dSMichal Kazior 
15525bcae31dSMichal Kazior 		if (n_assigned != n_reserved) {
15535bcae31dSMichal Kazior 			if (n_ready == n_reserved) {
15545bcae31dSMichal Kazior 				wiphy_info(local->hw.wiphy,
15555bcae31dSMichal Kazior 					   "channel context reservation cannot be finalized because some interfaces aren't switching\n");
15565bcae31dSMichal Kazior 				err = -EBUSY;
15575bcae31dSMichal Kazior 				goto err;
15585bcae31dSMichal Kazior 			}
15595bcae31dSMichal Kazior 
15605bcae31dSMichal Kazior 			return -EAGAIN;
15615bcae31dSMichal Kazior 		}
15625bcae31dSMichal Kazior 
15635bcae31dSMichal Kazior 		ctx->conf.radar_enabled = false;
1564b4f85443SJohannes Berg 		list_for_each_entry(link, &ctx->reserved_links,
15655bcae31dSMichal Kazior 				    reserved_chanctx_list) {
1566b4f85443SJohannes Berg 			if (ieee80211_link_has_in_place_reservation(link) &&
1567b4f85443SJohannes Berg 			    !link->reserved_ready)
15685bcae31dSMichal Kazior 				return -EAGAIN;
15695bcae31dSMichal Kazior 
1570b4f85443SJohannes Berg 			old_ctx = ieee80211_link_get_chanctx(link);
15715bcae31dSMichal Kazior 			if (old_ctx) {
15725bcae31dSMichal Kazior 				if (old_ctx->replace_state ==
15735bcae31dSMichal Kazior 				    IEEE80211_CHANCTX_WILL_BE_REPLACED)
15745bcae31dSMichal Kazior 					n_vifs_switch++;
15755bcae31dSMichal Kazior 				else
15765bcae31dSMichal Kazior 					n_vifs_assign++;
15775bcae31dSMichal Kazior 			} else {
15785bcae31dSMichal Kazior 				n_vifs_ctxless++;
15795bcae31dSMichal Kazior 			}
15805bcae31dSMichal Kazior 
1581b4f85443SJohannes Berg 			if (link->reserved_radar_required)
15825bcae31dSMichal Kazior 				ctx->conf.radar_enabled = true;
15835bcae31dSMichal Kazior 		}
15845bcae31dSMichal Kazior 	}
15855bcae31dSMichal Kazior 
15865bcae31dSMichal Kazior 	if (WARN_ON(n_ctx == 0) ||
15875bcae31dSMichal Kazior 	    WARN_ON(n_vifs_switch == 0 &&
15885bcae31dSMichal Kazior 		    n_vifs_assign == 0 &&
15895bcae31dSMichal Kazior 		    n_vifs_ctxless == 0) ||
15905bcae31dSMichal Kazior 	    WARN_ON(n_ctx > 1 && !local->use_chanctx) ||
15915bcae31dSMichal Kazior 	    WARN_ON(!new_ctx && !local->use_chanctx)) {
15925bcae31dSMichal Kazior 		err = -EINVAL;
15935bcae31dSMichal Kazior 		goto err;
15945bcae31dSMichal Kazior 	}
15955bcae31dSMichal Kazior 
15965bcae31dSMichal Kazior 	/*
15975bcae31dSMichal Kazior 	 * All necessary vifs are ready. Perform the switch now depending on
15985bcae31dSMichal Kazior 	 * reservations and driver capabilities.
15995bcae31dSMichal Kazior 	 */
16005bcae31dSMichal Kazior 
16015bcae31dSMichal Kazior 	if (local->use_chanctx) {
16025bcae31dSMichal Kazior 		if (n_vifs_switch > 0) {
16035bcae31dSMichal Kazior 			err = ieee80211_chsw_switch_vifs(local, n_vifs_switch);
16045bcae31dSMichal Kazior 			if (err)
16055bcae31dSMichal Kazior 				goto err;
16065bcae31dSMichal Kazior 		}
16075bcae31dSMichal Kazior 
16085bcae31dSMichal Kazior 		if (n_vifs_assign > 0 || n_vifs_ctxless > 0) {
16095bcae31dSMichal Kazior 			err = ieee80211_chsw_switch_ctxs(local);
16105bcae31dSMichal Kazior 			if (err)
16115bcae31dSMichal Kazior 				goto err;
16125bcae31dSMichal Kazior 		}
16135bcae31dSMichal Kazior 	} else {
16145bcae31dSMichal Kazior 		err = ieee80211_chsw_switch_hwconf(local, new_ctx);
16155bcae31dSMichal Kazior 		if (err)
16165bcae31dSMichal Kazior 			goto err;
16175bcae31dSMichal Kazior 	}
16185bcae31dSMichal Kazior 
16195bcae31dSMichal Kazior 	/*
16205bcae31dSMichal Kazior 	 * Update all structures, values and pointers to point to new channel
16215bcae31dSMichal Kazior 	 * context(s).
16225bcae31dSMichal Kazior 	 */
16235bcae31dSMichal Kazior 	list_for_each_entry(ctx, &local->chanctx_list, list) {
1624b4f85443SJohannes Berg 		struct ieee80211_link_data *link, *link_tmp;
1625b4f85443SJohannes Berg 
16265bcae31dSMichal Kazior 		if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
16275bcae31dSMichal Kazior 			continue;
16285bcae31dSMichal Kazior 
16295bcae31dSMichal Kazior 		if (WARN_ON(!ctx->replace_ctx)) {
16305bcae31dSMichal Kazior 			err = -EINVAL;
16315bcae31dSMichal Kazior 			goto err;
16325bcae31dSMichal Kazior 		}
16335bcae31dSMichal Kazior 
1634b4f85443SJohannes Berg 		list_for_each_entry(link, &ctx->reserved_links,
16355bcae31dSMichal Kazior 				    reserved_chanctx_list) {
1636b4f85443SJohannes Berg 			struct ieee80211_sub_if_data *sdata = link->sdata;
1637d8675a63SJohannes Berg 			struct ieee80211_bss_conf *link_conf = link->conf;
16385bcae31dSMichal Kazior 			u32 changed = 0;
16395bcae31dSMichal Kazior 
1640b4f85443SJohannes Berg 			if (!ieee80211_link_has_in_place_reservation(link))
16415bcae31dSMichal Kazior 				continue;
16425bcae31dSMichal Kazior 
1643b4f85443SJohannes Berg 			rcu_assign_pointer(link_conf->chanctx_conf,
1644d0a9123eSJohannes Berg 					   &ctx->conf);
16455bcae31dSMichal Kazior 
16465bcae31dSMichal Kazior 			if (sdata->vif.type == NL80211_IFTYPE_AP)
1647b4f85443SJohannes Berg 				__ieee80211_link_copy_chanctx_to_vlans(link,
16485bcae31dSMichal Kazior 								       false);
16495bcae31dSMichal Kazior 
165017c18bf8SJohannes Berg 			ieee80211_check_fast_xmit_iface(sdata);
165117c18bf8SJohannes Berg 
1652b4f85443SJohannes Berg 			link->radar_required = link->reserved_radar_required;
16535bcae31dSMichal Kazior 
1654b4f85443SJohannes Berg 			if (link_conf->chandef.width != link->reserved_chandef.width)
16555bcae31dSMichal Kazior 				changed = BSS_CHANGED_BANDWIDTH;
16565bcae31dSMichal Kazior 
1657b4f85443SJohannes Berg 			ieee80211_link_update_chandef(link, &link->reserved_chandef);
16585bcae31dSMichal Kazior 			if (changed)
1659b4f85443SJohannes Berg 				ieee80211_link_info_change_notify(sdata,
1660d8675a63SJohannes Berg 								  link,
16615bcae31dSMichal Kazior 								  changed);
16625bcae31dSMichal Kazior 
1663db82d8a9SLorenzo Bianconi 			ieee80211_recalc_txpower(sdata, false);
16645bcae31dSMichal Kazior 		}
166511335a55SLuciano Coelho 
166611335a55SLuciano Coelho 		ieee80211_recalc_chanctx_chantype(local, ctx);
166711335a55SLuciano Coelho 		ieee80211_recalc_smps_chanctx(local, ctx);
166811335a55SLuciano Coelho 		ieee80211_recalc_radar_chanctx(local, ctx);
166911335a55SLuciano Coelho 		ieee80211_recalc_chanctx_min_def(local, ctx);
16705bcae31dSMichal Kazior 
1671b4f85443SJohannes Berg 		list_for_each_entry_safe(link, link_tmp, &ctx->reserved_links,
16725bcae31dSMichal Kazior 					 reserved_chanctx_list) {
1673b4f85443SJohannes Berg 			if (ieee80211_link_get_chanctx(link) != ctx)
16745bcae31dSMichal Kazior 				continue;
16755bcae31dSMichal Kazior 
1676b4f85443SJohannes Berg 			list_del(&link->reserved_chanctx_list);
1677b4f85443SJohannes Berg 			list_move(&link->assigned_chanctx_list,
1678b4f85443SJohannes Berg 				  &ctx->assigned_links);
1679b4f85443SJohannes Berg 			link->reserved_chanctx = NULL;
168003078de4SMichal Kazior 
1681b4f85443SJohannes Berg 			ieee80211_link_chanctx_reservation_complete(link);
16825bcae31dSMichal Kazior 		}
16835bcae31dSMichal Kazior 
16845bcae31dSMichal Kazior 		/*
16855bcae31dSMichal Kazior 		 * This context might have been a dependency for an already
16865bcae31dSMichal Kazior 		 * ready re-assign reservation interface that was deferred. Do
16875bcae31dSMichal Kazior 		 * not propagate error to the caller though. The in-place
16885bcae31dSMichal Kazior 		 * reservation for originally requested interface has already
16895bcae31dSMichal Kazior 		 * succeeded at this point.
16905bcae31dSMichal Kazior 		 */
1691b4f85443SJohannes Berg 		list_for_each_entry_safe(link, link_tmp, &ctx->reserved_links,
16925bcae31dSMichal Kazior 					 reserved_chanctx_list) {
1693b4f85443SJohannes Berg 			if (WARN_ON(ieee80211_link_has_in_place_reservation(link)))
16945bcae31dSMichal Kazior 				continue;
16955bcae31dSMichal Kazior 
1696b4f85443SJohannes Berg 			if (WARN_ON(link->reserved_chanctx != ctx))
16975bcae31dSMichal Kazior 				continue;
16985bcae31dSMichal Kazior 
1699b4f85443SJohannes Berg 			if (!link->reserved_ready)
17005bcae31dSMichal Kazior 				continue;
17015bcae31dSMichal Kazior 
1702b4f85443SJohannes Berg 			if (ieee80211_link_get_chanctx(link))
1703b4f85443SJohannes Berg 				err = ieee80211_link_use_reserved_reassign(link);
17045bcae31dSMichal Kazior 			else
1705b4f85443SJohannes Berg 				err = ieee80211_link_use_reserved_assign(link);
17065bcae31dSMichal Kazior 
17075bcae31dSMichal Kazior 			if (err) {
1708b4f85443SJohannes Berg 				link_info(link,
17095bcae31dSMichal Kazior 					  "failed to finalize (re-)assign reservation (err=%d)\n",
17105bcae31dSMichal Kazior 					  err);
1711b4f85443SJohannes Berg 				ieee80211_link_unreserve_chanctx(link);
17125bcae31dSMichal Kazior 				cfg80211_stop_iface(local->hw.wiphy,
1713b4f85443SJohannes Berg 						    &link->sdata->wdev,
17145bcae31dSMichal Kazior 						    GFP_KERNEL);
17155bcae31dSMichal Kazior 			}
17165bcae31dSMichal Kazior 		}
17175bcae31dSMichal Kazior 	}
17185bcae31dSMichal Kazior 
17195bcae31dSMichal Kazior 	/*
17205bcae31dSMichal Kazior 	 * Finally free old contexts
17215bcae31dSMichal Kazior 	 */
17225bcae31dSMichal Kazior 
17235bcae31dSMichal Kazior 	list_for_each_entry_safe(ctx, ctx_tmp, &local->chanctx_list, list) {
17245bcae31dSMichal Kazior 		if (ctx->replace_state != IEEE80211_CHANCTX_WILL_BE_REPLACED)
17255bcae31dSMichal Kazior 			continue;
17265bcae31dSMichal Kazior 
17275bcae31dSMichal Kazior 		ctx->replace_ctx->replace_ctx = NULL;
17285bcae31dSMichal Kazior 		ctx->replace_ctx->replace_state =
17295bcae31dSMichal Kazior 				IEEE80211_CHANCTX_REPLACE_NONE;
17305bcae31dSMichal Kazior 
17315bcae31dSMichal Kazior 		list_del_rcu(&ctx->list);
17325bcae31dSMichal Kazior 		kfree_rcu(ctx, rcu_head);
17335bcae31dSMichal Kazior 	}
17345bcae31dSMichal Kazior 
17355bcae31dSMichal Kazior 	return 0;
17365bcae31dSMichal Kazior 
17375bcae31dSMichal Kazior err:
17385bcae31dSMichal Kazior 	list_for_each_entry(ctx, &local->chanctx_list, list) {
1739b4f85443SJohannes Berg 		struct ieee80211_link_data *link, *link_tmp;
1740b4f85443SJohannes Berg 
17415bcae31dSMichal Kazior 		if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
17425bcae31dSMichal Kazior 			continue;
17435bcae31dSMichal Kazior 
1744b4f85443SJohannes Berg 		list_for_each_entry_safe(link, link_tmp, &ctx->reserved_links,
174503078de4SMichal Kazior 					 reserved_chanctx_list) {
1746b4f85443SJohannes Berg 			ieee80211_link_unreserve_chanctx(link);
1747b4f85443SJohannes Berg 			ieee80211_link_chanctx_reservation_complete(link);
174803078de4SMichal Kazior 		}
17495bcae31dSMichal Kazior 	}
17505bcae31dSMichal Kazior 
17515bcae31dSMichal Kazior 	return err;
17525bcae31dSMichal Kazior }
17535bcae31dSMichal Kazior 
1754b4f85443SJohannes Berg static void __ieee80211_link_release_channel(struct ieee80211_link_data *link)
1755649b2a4dSJohannes Berg {
1756b4f85443SJohannes Berg 	struct ieee80211_sub_if_data *sdata = link->sdata;
1757d8675a63SJohannes Berg 	struct ieee80211_bss_conf *link_conf = link->conf;
1758649b2a4dSJohannes Berg 	struct ieee80211_local *local = sdata->local;
1759649b2a4dSJohannes Berg 	struct ieee80211_chanctx_conf *conf;
1760649b2a4dSJohannes Berg 	struct ieee80211_chanctx *ctx;
1761649b2a4dSJohannes Berg 	bool use_reserved_switch = false;
1762649b2a4dSJohannes Berg 
1763649b2a4dSJohannes Berg 	lockdep_assert_held(&local->chanctx_mtx);
1764649b2a4dSJohannes Berg 
1765b4f85443SJohannes Berg 	conf = rcu_dereference_protected(link_conf->chanctx_conf,
1766649b2a4dSJohannes Berg 					 lockdep_is_held(&local->chanctx_mtx));
1767649b2a4dSJohannes Berg 	if (!conf)
1768649b2a4dSJohannes Berg 		return;
1769649b2a4dSJohannes Berg 
1770649b2a4dSJohannes Berg 	ctx = container_of(conf, struct ieee80211_chanctx, conf);
1771649b2a4dSJohannes Berg 
1772b4f85443SJohannes Berg 	if (link->reserved_chanctx) {
1773b4f85443SJohannes Berg 		if (link->reserved_chanctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER &&
1774b4f85443SJohannes Berg 		    ieee80211_chanctx_num_reserved(local, link->reserved_chanctx) > 1)
1775649b2a4dSJohannes Berg 			use_reserved_switch = true;
1776649b2a4dSJohannes Berg 
1777b4f85443SJohannes Berg 		ieee80211_link_unreserve_chanctx(link);
1778649b2a4dSJohannes Berg 	}
1779649b2a4dSJohannes Berg 
1780b4f85443SJohannes Berg 	ieee80211_assign_link_chanctx(link, NULL);
1781649b2a4dSJohannes Berg 	if (ieee80211_chanctx_refcount(local, ctx) == 0)
1782649b2a4dSJohannes Berg 		ieee80211_free_chanctx(local, ctx);
1783649b2a4dSJohannes Berg 
1784b4f85443SJohannes Berg 	link->radar_required = false;
1785104f5a62SEliad Peller 
1786649b2a4dSJohannes Berg 	/* Unreserving may ready an in-place reservation. */
1787649b2a4dSJohannes Berg 	if (use_reserved_switch)
1788649b2a4dSJohannes Berg 		ieee80211_vif_use_reserved_switch(local);
1789649b2a4dSJohannes Berg }
1790649b2a4dSJohannes Berg 
1791b4f85443SJohannes Berg int ieee80211_link_use_channel(struct ieee80211_link_data *link,
1792649b2a4dSJohannes Berg 			       const struct cfg80211_chan_def *chandef,
1793649b2a4dSJohannes Berg 			       enum ieee80211_chanctx_mode mode)
1794649b2a4dSJohannes Berg {
1795b4f85443SJohannes Berg 	struct ieee80211_sub_if_data *sdata = link->sdata;
1796649b2a4dSJohannes Berg 	struct ieee80211_local *local = sdata->local;
1797649b2a4dSJohannes Berg 	struct ieee80211_chanctx *ctx;
1798649b2a4dSJohannes Berg 	u8 radar_detect_width = 0;
1799649b2a4dSJohannes Berg 	int ret;
1800649b2a4dSJohannes Berg 
1801649b2a4dSJohannes Berg 	lockdep_assert_held(&local->mtx);
1802649b2a4dSJohannes Berg 
1803649b2a4dSJohannes Berg 	WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev));
1804649b2a4dSJohannes Berg 
1805649b2a4dSJohannes Berg 	mutex_lock(&local->chanctx_mtx);
1806649b2a4dSJohannes Berg 
1807649b2a4dSJohannes Berg 	ret = cfg80211_chandef_dfs_required(local->hw.wiphy,
1808649b2a4dSJohannes Berg 					    chandef,
1809649b2a4dSJohannes Berg 					    sdata->wdev.iftype);
1810649b2a4dSJohannes Berg 	if (ret < 0)
1811649b2a4dSJohannes Berg 		goto out;
1812649b2a4dSJohannes Berg 	if (ret > 0)
1813649b2a4dSJohannes Berg 		radar_detect_width = BIT(chandef->width);
1814649b2a4dSJohannes Berg 
1815d8675a63SJohannes Berg 	link->radar_required = ret;
1816649b2a4dSJohannes Berg 
1817649b2a4dSJohannes Berg 	ret = ieee80211_check_combinations(sdata, chandef, mode,
1818649b2a4dSJohannes Berg 					   radar_detect_width);
1819649b2a4dSJohannes Berg 	if (ret < 0)
1820649b2a4dSJohannes Berg 		goto out;
1821649b2a4dSJohannes Berg 
1822b4f85443SJohannes Berg 	__ieee80211_link_release_channel(link);
1823649b2a4dSJohannes Berg 
1824649b2a4dSJohannes Berg 	ctx = ieee80211_find_chanctx(local, chandef, mode);
1825649b2a4dSJohannes Berg 	if (!ctx)
1826649b2a4dSJohannes Berg 		ctx = ieee80211_new_chanctx(local, chandef, mode);
1827649b2a4dSJohannes Berg 	if (IS_ERR(ctx)) {
1828649b2a4dSJohannes Berg 		ret = PTR_ERR(ctx);
1829649b2a4dSJohannes Berg 		goto out;
1830649b2a4dSJohannes Berg 	}
1831649b2a4dSJohannes Berg 
1832b4f85443SJohannes Berg 	ieee80211_link_update_chandef(link, chandef);
1833649b2a4dSJohannes Berg 
1834b4f85443SJohannes Berg 	ret = ieee80211_assign_link_chanctx(link, ctx);
1835649b2a4dSJohannes Berg 	if (ret) {
1836649b2a4dSJohannes Berg 		/* if assign fails refcount stays the same */
1837649b2a4dSJohannes Berg 		if (ieee80211_chanctx_refcount(local, ctx) == 0)
1838649b2a4dSJohannes Berg 			ieee80211_free_chanctx(local, ctx);
1839649b2a4dSJohannes Berg 		goto out;
1840649b2a4dSJohannes Berg 	}
1841649b2a4dSJohannes Berg 
1842649b2a4dSJohannes Berg 	ieee80211_recalc_smps_chanctx(local, ctx);
1843649b2a4dSJohannes Berg 	ieee80211_recalc_radar_chanctx(local, ctx);
1844649b2a4dSJohannes Berg  out:
1845104f5a62SEliad Peller 	if (ret)
1846b4f85443SJohannes Berg 		link->radar_required = false;
1847104f5a62SEliad Peller 
1848649b2a4dSJohannes Berg 	mutex_unlock(&local->chanctx_mtx);
1849649b2a4dSJohannes Berg 	return ret;
1850649b2a4dSJohannes Berg }
1851649b2a4dSJohannes Berg 
1852b4f85443SJohannes Berg int ieee80211_link_use_reserved_context(struct ieee80211_link_data *link)
18535bcae31dSMichal Kazior {
1854b4f85443SJohannes Berg 	struct ieee80211_sub_if_data *sdata = link->sdata;
18555bcae31dSMichal Kazior 	struct ieee80211_local *local = sdata->local;
18565bcae31dSMichal Kazior 	struct ieee80211_chanctx *new_ctx;
18575bcae31dSMichal Kazior 	struct ieee80211_chanctx *old_ctx;
18585bcae31dSMichal Kazior 	int err;
18595bcae31dSMichal Kazior 
18605bcae31dSMichal Kazior 	lockdep_assert_held(&local->mtx);
18615bcae31dSMichal Kazior 	lockdep_assert_held(&local->chanctx_mtx);
18625bcae31dSMichal Kazior 
1863b4f85443SJohannes Berg 	new_ctx = link->reserved_chanctx;
1864b4f85443SJohannes Berg 	old_ctx = ieee80211_link_get_chanctx(link);
18655bcae31dSMichal Kazior 
18665bcae31dSMichal Kazior 	if (WARN_ON(!new_ctx))
18675bcae31dSMichal Kazior 		return -EINVAL;
18685bcae31dSMichal Kazior 
18695bcae31dSMichal Kazior 	if (WARN_ON(new_ctx->replace_state ==
18705bcae31dSMichal Kazior 		    IEEE80211_CHANCTX_WILL_BE_REPLACED))
18715bcae31dSMichal Kazior 		return -EINVAL;
18725bcae31dSMichal Kazior 
1873b4f85443SJohannes Berg 	if (WARN_ON(link->reserved_ready))
18745bcae31dSMichal Kazior 		return -EINVAL;
18755bcae31dSMichal Kazior 
1876b4f85443SJohannes Berg 	link->reserved_ready = true;
18775bcae31dSMichal Kazior 
18785bcae31dSMichal Kazior 	if (new_ctx->replace_state == IEEE80211_CHANCTX_REPLACE_NONE) {
18795bcae31dSMichal Kazior 		if (old_ctx)
1880b4f85443SJohannes Berg 			return ieee80211_link_use_reserved_reassign(link);
18815bcae31dSMichal Kazior 
1882b4f85443SJohannes Berg 		return ieee80211_link_use_reserved_assign(link);
18835bcae31dSMichal Kazior 	}
18845bcae31dSMichal Kazior 
18855bcae31dSMichal Kazior 	/*
18865bcae31dSMichal Kazior 	 * In-place reservation may need to be finalized now either if:
18875bcae31dSMichal Kazior 	 *  a) sdata is taking part in the swapping itself and is the last one
18885bcae31dSMichal Kazior 	 *  b) sdata has switched with a re-assign reservation to an existing
18895bcae31dSMichal Kazior 	 *     context readying in-place switching of old_ctx
18905bcae31dSMichal Kazior 	 *
18915bcae31dSMichal Kazior 	 * In case of (b) do not propagate the error up because the requested
18925bcae31dSMichal Kazior 	 * sdata already switched successfully. Just spill an extra warning.
18935bcae31dSMichal Kazior 	 * The ieee80211_vif_use_reserved_switch() already stops all necessary
18945bcae31dSMichal Kazior 	 * interfaces upon failure.
18955bcae31dSMichal Kazior 	 */
18965bcae31dSMichal Kazior 	if ((old_ctx &&
18975bcae31dSMichal Kazior 	     old_ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED) ||
18985bcae31dSMichal Kazior 	    new_ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER) {
18995bcae31dSMichal Kazior 		err = ieee80211_vif_use_reserved_switch(local);
19005bcae31dSMichal Kazior 		if (err && err != -EAGAIN) {
19015bcae31dSMichal Kazior 			if (new_ctx->replace_state ==
19025bcae31dSMichal Kazior 			    IEEE80211_CHANCTX_REPLACES_OTHER)
19035bcae31dSMichal Kazior 				return err;
19045bcae31dSMichal Kazior 
19055bcae31dSMichal Kazior 			wiphy_info(local->hw.wiphy,
19065bcae31dSMichal Kazior 				   "depending in-place reservation failed (err=%d)\n",
19075bcae31dSMichal Kazior 				   err);
19085bcae31dSMichal Kazior 		}
19095bcae31dSMichal Kazior 	}
19105bcae31dSMichal Kazior 
19115bcae31dSMichal Kazior 	return 0;
191273da7d5bSSimon Wunderlich }
191373da7d5bSSimon Wunderlich 
1914b4f85443SJohannes Berg int ieee80211_link_change_bandwidth(struct ieee80211_link_data *link,
19152c9b7359SJohannes Berg 				    const struct cfg80211_chan_def *chandef,
19162c9b7359SJohannes Berg 				    u32 *changed)
19172c9b7359SJohannes Berg {
1918b4f85443SJohannes Berg 	struct ieee80211_sub_if_data *sdata = link->sdata;
1919d8675a63SJohannes Berg 	struct ieee80211_bss_conf *link_conf = link->conf;
19202c9b7359SJohannes Berg 	struct ieee80211_local *local = sdata->local;
19212c9b7359SJohannes Berg 	struct ieee80211_chanctx_conf *conf;
19222c9b7359SJohannes Berg 	struct ieee80211_chanctx *ctx;
19235bcae31dSMichal Kazior 	const struct cfg80211_chan_def *compat;
19242c9b7359SJohannes Berg 	int ret;
19252c9b7359SJohannes Berg 
19262c9b7359SJohannes Berg 	if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef,
19272c9b7359SJohannes Berg 				     IEEE80211_CHAN_DISABLED))
19282c9b7359SJohannes Berg 		return -EINVAL;
19292c9b7359SJohannes Berg 
19302c9b7359SJohannes Berg 	mutex_lock(&local->chanctx_mtx);
1931b4f85443SJohannes Berg 	if (cfg80211_chandef_identical(chandef, &link_conf->chandef)) {
19322c9b7359SJohannes Berg 		ret = 0;
19332c9b7359SJohannes Berg 		goto out;
19342c9b7359SJohannes Berg 	}
19352c9b7359SJohannes Berg 
19362c9b7359SJohannes Berg 	if (chandef->width == NL80211_CHAN_WIDTH_20_NOHT ||
1937b4f85443SJohannes Berg 	    link_conf->chandef.width == NL80211_CHAN_WIDTH_20_NOHT) {
19382c9b7359SJohannes Berg 		ret = -EINVAL;
19392c9b7359SJohannes Berg 		goto out;
19402c9b7359SJohannes Berg 	}
19412c9b7359SJohannes Berg 
1942b4f85443SJohannes Berg 	conf = rcu_dereference_protected(link_conf->chanctx_conf,
19432c9b7359SJohannes Berg 					 lockdep_is_held(&local->chanctx_mtx));
19442c9b7359SJohannes Berg 	if (!conf) {
19452c9b7359SJohannes Berg 		ret = -EINVAL;
19462c9b7359SJohannes Berg 		goto out;
19472c9b7359SJohannes Berg 	}
19482c9b7359SJohannes Berg 
19492c9b7359SJohannes Berg 	ctx = container_of(conf, struct ieee80211_chanctx, conf);
19505bcae31dSMichal Kazior 
19515bcae31dSMichal Kazior 	compat = cfg80211_chandef_compatible(&conf->def, chandef);
19525bcae31dSMichal Kazior 	if (!compat) {
19532c9b7359SJohannes Berg 		ret = -EINVAL;
19542c9b7359SJohannes Berg 		goto out;
19552c9b7359SJohannes Berg 	}
19562c9b7359SJohannes Berg 
19575bcae31dSMichal Kazior 	switch (ctx->replace_state) {
19585bcae31dSMichal Kazior 	case IEEE80211_CHANCTX_REPLACE_NONE:
19595bcae31dSMichal Kazior 		if (!ieee80211_chanctx_reserved_chandef(local, ctx, compat)) {
19605bcae31dSMichal Kazior 			ret = -EBUSY;
19615bcae31dSMichal Kazior 			goto out;
19625bcae31dSMichal Kazior 		}
19635bcae31dSMichal Kazior 		break;
19645bcae31dSMichal Kazior 	case IEEE80211_CHANCTX_WILL_BE_REPLACED:
1965d070f913SStephen Hemminger 		/* TODO: Perhaps the bandwidth change could be treated as a
19665bcae31dSMichal Kazior 		 * reservation itself? */
19675bcae31dSMichal Kazior 		ret = -EBUSY;
19685bcae31dSMichal Kazior 		goto out;
19695bcae31dSMichal Kazior 	case IEEE80211_CHANCTX_REPLACES_OTHER:
19705bcae31dSMichal Kazior 		/* channel context that is going to replace another channel
19715bcae31dSMichal Kazior 		 * context doesn't really exist and shouldn't be assigned
19725bcae31dSMichal Kazior 		 * anywhere yet */
19735bcae31dSMichal Kazior 		WARN_ON(1);
19745bcae31dSMichal Kazior 		break;
19755bcae31dSMichal Kazior 	}
19765bcae31dSMichal Kazior 
1977b4f85443SJohannes Berg 	ieee80211_link_update_chandef(link, chandef);
19782c9b7359SJohannes Berg 
19792c9b7359SJohannes Berg 	ieee80211_recalc_chanctx_chantype(local, ctx);
19802c9b7359SJohannes Berg 
19812c9b7359SJohannes Berg 	*changed |= BSS_CHANGED_BANDWIDTH;
19822c9b7359SJohannes Berg 	ret = 0;
19832c9b7359SJohannes Berg  out:
19842c9b7359SJohannes Berg 	mutex_unlock(&local->chanctx_mtx);
19852c9b7359SJohannes Berg 	return ret;
19862c9b7359SJohannes Berg }
19872c9b7359SJohannes Berg 
1988b4f85443SJohannes Berg void ieee80211_link_release_channel(struct ieee80211_link_data *link)
1989d01a1e65SMichal Kazior {
1990b4f85443SJohannes Berg 	struct ieee80211_sub_if_data *sdata = link->sdata;
1991b4f85443SJohannes Berg 
199255de908aSJohannes Berg 	WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev));
199355de908aSJohannes Berg 
199434a3740dSJohannes Berg 	lockdep_assert_held(&sdata->local->mtx);
199534a3740dSJohannes Berg 
1996d01a1e65SMichal Kazior 	mutex_lock(&sdata->local->chanctx_mtx);
1997b4f85443SJohannes Berg 	__ieee80211_link_release_channel(link);
1998d01a1e65SMichal Kazior 	mutex_unlock(&sdata->local->chanctx_mtx);
1999d01a1e65SMichal Kazior }
20003448c005SJohannes Berg 
2001b4f85443SJohannes Berg void ieee80211_link_vlan_copy_chanctx(struct ieee80211_link_data *link)
20024d76d21bSJohannes Berg {
2003b4f85443SJohannes Berg 	struct ieee80211_sub_if_data *sdata = link->sdata;
2004b4f85443SJohannes Berg 	unsigned int link_id = link->link_id;
2005d8675a63SJohannes Berg 	struct ieee80211_bss_conf *link_conf = link->conf;
2006d8675a63SJohannes Berg 	struct ieee80211_bss_conf *ap_conf;
20074d76d21bSJohannes Berg 	struct ieee80211_local *local = sdata->local;
20084d76d21bSJohannes Berg 	struct ieee80211_sub_if_data *ap;
20094d76d21bSJohannes Berg 	struct ieee80211_chanctx_conf *conf;
20104d76d21bSJohannes Berg 
20114d76d21bSJohannes Berg 	if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP_VLAN || !sdata->bss))
20124d76d21bSJohannes Berg 		return;
20134d76d21bSJohannes Berg 
20144d76d21bSJohannes Berg 	ap = container_of(sdata->bss, struct ieee80211_sub_if_data, u.ap);
20154d76d21bSJohannes Berg 
20164d76d21bSJohannes Berg 	mutex_lock(&local->chanctx_mtx);
20174d76d21bSJohannes Berg 
2018d8675a63SJohannes Berg 	rcu_read_lock();
2019d8675a63SJohannes Berg 	ap_conf = rcu_dereference(ap->vif.link_conf[link_id]);
2020d8675a63SJohannes Berg 	conf = rcu_dereference_protected(ap_conf->chanctx_conf,
20214d76d21bSJohannes Berg 					 lockdep_is_held(&local->chanctx_mtx));
2022b4f85443SJohannes Berg 	rcu_assign_pointer(link_conf->chanctx_conf, conf);
2023d8675a63SJohannes Berg 	rcu_read_unlock();
20244d76d21bSJohannes Berg 	mutex_unlock(&local->chanctx_mtx);
20254d76d21bSJohannes Berg }
20264d76d21bSJohannes Berg 
20273448c005SJohannes Berg void ieee80211_iter_chan_contexts_atomic(
20283448c005SJohannes Berg 	struct ieee80211_hw *hw,
20293448c005SJohannes Berg 	void (*iter)(struct ieee80211_hw *hw,
20303448c005SJohannes Berg 		     struct ieee80211_chanctx_conf *chanctx_conf,
20313448c005SJohannes Berg 		     void *data),
20323448c005SJohannes Berg 	void *iter_data)
20333448c005SJohannes Berg {
20343448c005SJohannes Berg 	struct ieee80211_local *local = hw_to_local(hw);
20353448c005SJohannes Berg 	struct ieee80211_chanctx *ctx;
20363448c005SJohannes Berg 
20373448c005SJohannes Berg 	rcu_read_lock();
20383448c005SJohannes Berg 	list_for_each_entry_rcu(ctx, &local->chanctx_list, list)
20398a61af65SJohannes Berg 		if (ctx->driver_present)
20403448c005SJohannes Berg 			iter(hw, &ctx->conf, iter_data);
20413448c005SJohannes Berg 	rcu_read_unlock();
20423448c005SJohannes Berg }
20433448c005SJohannes Berg EXPORT_SYMBOL_GPL(ieee80211_iter_chan_contexts_atomic);
2044