1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * mac80211 - channel management
4*4882a593Smuzhiyun */
5*4882a593Smuzhiyun
6*4882a593Smuzhiyun #include <linux/nl80211.h>
7*4882a593Smuzhiyun #include <linux/export.h>
8*4882a593Smuzhiyun #include <linux/rtnetlink.h>
9*4882a593Smuzhiyun #include <net/cfg80211.h>
10*4882a593Smuzhiyun #include "ieee80211_i.h"
11*4882a593Smuzhiyun #include "driver-ops.h"
12*4882a593Smuzhiyun
ieee80211_chanctx_num_assigned(struct ieee80211_local * local,struct ieee80211_chanctx * ctx)13*4882a593Smuzhiyun static int ieee80211_chanctx_num_assigned(struct ieee80211_local *local,
14*4882a593Smuzhiyun struct ieee80211_chanctx *ctx)
15*4882a593Smuzhiyun {
16*4882a593Smuzhiyun struct ieee80211_sub_if_data *sdata;
17*4882a593Smuzhiyun int num = 0;
18*4882a593Smuzhiyun
19*4882a593Smuzhiyun lockdep_assert_held(&local->chanctx_mtx);
20*4882a593Smuzhiyun
21*4882a593Smuzhiyun list_for_each_entry(sdata, &ctx->assigned_vifs, assigned_chanctx_list)
22*4882a593Smuzhiyun num++;
23*4882a593Smuzhiyun
24*4882a593Smuzhiyun return num;
25*4882a593Smuzhiyun }
26*4882a593Smuzhiyun
ieee80211_chanctx_num_reserved(struct ieee80211_local * local,struct ieee80211_chanctx * ctx)27*4882a593Smuzhiyun static int ieee80211_chanctx_num_reserved(struct ieee80211_local *local,
28*4882a593Smuzhiyun struct ieee80211_chanctx *ctx)
29*4882a593Smuzhiyun {
30*4882a593Smuzhiyun struct ieee80211_sub_if_data *sdata;
31*4882a593Smuzhiyun int num = 0;
32*4882a593Smuzhiyun
33*4882a593Smuzhiyun lockdep_assert_held(&local->chanctx_mtx);
34*4882a593Smuzhiyun
35*4882a593Smuzhiyun list_for_each_entry(sdata, &ctx->reserved_vifs, reserved_chanctx_list)
36*4882a593Smuzhiyun num++;
37*4882a593Smuzhiyun
38*4882a593Smuzhiyun return num;
39*4882a593Smuzhiyun }
40*4882a593Smuzhiyun
ieee80211_chanctx_refcount(struct ieee80211_local * local,struct ieee80211_chanctx * ctx)41*4882a593Smuzhiyun int ieee80211_chanctx_refcount(struct ieee80211_local *local,
42*4882a593Smuzhiyun struct ieee80211_chanctx *ctx)
43*4882a593Smuzhiyun {
44*4882a593Smuzhiyun return ieee80211_chanctx_num_assigned(local, ctx) +
45*4882a593Smuzhiyun ieee80211_chanctx_num_reserved(local, ctx);
46*4882a593Smuzhiyun }
47*4882a593Smuzhiyun
ieee80211_num_chanctx(struct ieee80211_local * local)48*4882a593Smuzhiyun static int ieee80211_num_chanctx(struct ieee80211_local *local)
49*4882a593Smuzhiyun {
50*4882a593Smuzhiyun struct ieee80211_chanctx *ctx;
51*4882a593Smuzhiyun int num = 0;
52*4882a593Smuzhiyun
53*4882a593Smuzhiyun lockdep_assert_held(&local->chanctx_mtx);
54*4882a593Smuzhiyun
55*4882a593Smuzhiyun list_for_each_entry(ctx, &local->chanctx_list, list)
56*4882a593Smuzhiyun num++;
57*4882a593Smuzhiyun
58*4882a593Smuzhiyun return num;
59*4882a593Smuzhiyun }
60*4882a593Smuzhiyun
ieee80211_can_create_new_chanctx(struct ieee80211_local * local)61*4882a593Smuzhiyun static bool ieee80211_can_create_new_chanctx(struct ieee80211_local *local)
62*4882a593Smuzhiyun {
63*4882a593Smuzhiyun lockdep_assert_held(&local->chanctx_mtx);
64*4882a593Smuzhiyun return ieee80211_num_chanctx(local) < ieee80211_max_num_channels(local);
65*4882a593Smuzhiyun }
66*4882a593Smuzhiyun
67*4882a593Smuzhiyun static struct ieee80211_chanctx *
ieee80211_vif_get_chanctx(struct ieee80211_sub_if_data * sdata)68*4882a593Smuzhiyun ieee80211_vif_get_chanctx(struct ieee80211_sub_if_data *sdata)
69*4882a593Smuzhiyun {
70*4882a593Smuzhiyun struct ieee80211_local *local __maybe_unused = sdata->local;
71*4882a593Smuzhiyun struct ieee80211_chanctx_conf *conf;
72*4882a593Smuzhiyun
73*4882a593Smuzhiyun conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
74*4882a593Smuzhiyun lockdep_is_held(&local->chanctx_mtx));
75*4882a593Smuzhiyun if (!conf)
76*4882a593Smuzhiyun return NULL;
77*4882a593Smuzhiyun
78*4882a593Smuzhiyun return container_of(conf, struct ieee80211_chanctx, conf);
79*4882a593Smuzhiyun }
80*4882a593Smuzhiyun
81*4882a593Smuzhiyun static const struct cfg80211_chan_def *
ieee80211_chanctx_reserved_chandef(struct ieee80211_local * local,struct ieee80211_chanctx * ctx,const struct cfg80211_chan_def * compat)82*4882a593Smuzhiyun ieee80211_chanctx_reserved_chandef(struct ieee80211_local *local,
83*4882a593Smuzhiyun struct ieee80211_chanctx *ctx,
84*4882a593Smuzhiyun const struct cfg80211_chan_def *compat)
85*4882a593Smuzhiyun {
86*4882a593Smuzhiyun struct ieee80211_sub_if_data *sdata;
87*4882a593Smuzhiyun
88*4882a593Smuzhiyun lockdep_assert_held(&local->chanctx_mtx);
89*4882a593Smuzhiyun
90*4882a593Smuzhiyun list_for_each_entry(sdata, &ctx->reserved_vifs,
91*4882a593Smuzhiyun reserved_chanctx_list) {
92*4882a593Smuzhiyun if (!compat)
93*4882a593Smuzhiyun compat = &sdata->reserved_chandef;
94*4882a593Smuzhiyun
95*4882a593Smuzhiyun compat = cfg80211_chandef_compatible(&sdata->reserved_chandef,
96*4882a593Smuzhiyun compat);
97*4882a593Smuzhiyun if (!compat)
98*4882a593Smuzhiyun break;
99*4882a593Smuzhiyun }
100*4882a593Smuzhiyun
101*4882a593Smuzhiyun return compat;
102*4882a593Smuzhiyun }
103*4882a593Smuzhiyun
104*4882a593Smuzhiyun static const struct cfg80211_chan_def *
ieee80211_chanctx_non_reserved_chandef(struct ieee80211_local * local,struct ieee80211_chanctx * ctx,const struct cfg80211_chan_def * compat)105*4882a593Smuzhiyun ieee80211_chanctx_non_reserved_chandef(struct ieee80211_local *local,
106*4882a593Smuzhiyun struct ieee80211_chanctx *ctx,
107*4882a593Smuzhiyun const struct cfg80211_chan_def *compat)
108*4882a593Smuzhiyun {
109*4882a593Smuzhiyun struct ieee80211_sub_if_data *sdata;
110*4882a593Smuzhiyun
111*4882a593Smuzhiyun lockdep_assert_held(&local->chanctx_mtx);
112*4882a593Smuzhiyun
113*4882a593Smuzhiyun list_for_each_entry(sdata, &ctx->assigned_vifs,
114*4882a593Smuzhiyun assigned_chanctx_list) {
115*4882a593Smuzhiyun if (sdata->reserved_chanctx != NULL)
116*4882a593Smuzhiyun continue;
117*4882a593Smuzhiyun
118*4882a593Smuzhiyun if (!compat)
119*4882a593Smuzhiyun compat = &sdata->vif.bss_conf.chandef;
120*4882a593Smuzhiyun
121*4882a593Smuzhiyun compat = cfg80211_chandef_compatible(
122*4882a593Smuzhiyun &sdata->vif.bss_conf.chandef, compat);
123*4882a593Smuzhiyun if (!compat)
124*4882a593Smuzhiyun break;
125*4882a593Smuzhiyun }
126*4882a593Smuzhiyun
127*4882a593Smuzhiyun return compat;
128*4882a593Smuzhiyun }
129*4882a593Smuzhiyun
130*4882a593Smuzhiyun static const struct cfg80211_chan_def *
ieee80211_chanctx_combined_chandef(struct ieee80211_local * local,struct ieee80211_chanctx * ctx,const struct cfg80211_chan_def * compat)131*4882a593Smuzhiyun ieee80211_chanctx_combined_chandef(struct ieee80211_local *local,
132*4882a593Smuzhiyun struct ieee80211_chanctx *ctx,
133*4882a593Smuzhiyun const struct cfg80211_chan_def *compat)
134*4882a593Smuzhiyun {
135*4882a593Smuzhiyun lockdep_assert_held(&local->chanctx_mtx);
136*4882a593Smuzhiyun
137*4882a593Smuzhiyun compat = ieee80211_chanctx_reserved_chandef(local, ctx, compat);
138*4882a593Smuzhiyun if (!compat)
139*4882a593Smuzhiyun return NULL;
140*4882a593Smuzhiyun
141*4882a593Smuzhiyun compat = ieee80211_chanctx_non_reserved_chandef(local, ctx, compat);
142*4882a593Smuzhiyun if (!compat)
143*4882a593Smuzhiyun return NULL;
144*4882a593Smuzhiyun
145*4882a593Smuzhiyun return compat;
146*4882a593Smuzhiyun }
147*4882a593Smuzhiyun
148*4882a593Smuzhiyun static bool
ieee80211_chanctx_can_reserve_chandef(struct ieee80211_local * local,struct ieee80211_chanctx * ctx,const struct cfg80211_chan_def * def)149*4882a593Smuzhiyun ieee80211_chanctx_can_reserve_chandef(struct ieee80211_local *local,
150*4882a593Smuzhiyun struct ieee80211_chanctx *ctx,
151*4882a593Smuzhiyun const struct cfg80211_chan_def *def)
152*4882a593Smuzhiyun {
153*4882a593Smuzhiyun lockdep_assert_held(&local->chanctx_mtx);
154*4882a593Smuzhiyun
155*4882a593Smuzhiyun if (ieee80211_chanctx_combined_chandef(local, ctx, def))
156*4882a593Smuzhiyun return true;
157*4882a593Smuzhiyun
158*4882a593Smuzhiyun if (!list_empty(&ctx->reserved_vifs) &&
159*4882a593Smuzhiyun ieee80211_chanctx_reserved_chandef(local, ctx, def))
160*4882a593Smuzhiyun return true;
161*4882a593Smuzhiyun
162*4882a593Smuzhiyun return false;
163*4882a593Smuzhiyun }
164*4882a593Smuzhiyun
165*4882a593Smuzhiyun static struct ieee80211_chanctx *
ieee80211_find_reservation_chanctx(struct ieee80211_local * local,const struct cfg80211_chan_def * chandef,enum ieee80211_chanctx_mode mode)166*4882a593Smuzhiyun ieee80211_find_reservation_chanctx(struct ieee80211_local *local,
167*4882a593Smuzhiyun const struct cfg80211_chan_def *chandef,
168*4882a593Smuzhiyun enum ieee80211_chanctx_mode mode)
169*4882a593Smuzhiyun {
170*4882a593Smuzhiyun struct ieee80211_chanctx *ctx;
171*4882a593Smuzhiyun
172*4882a593Smuzhiyun lockdep_assert_held(&local->chanctx_mtx);
173*4882a593Smuzhiyun
174*4882a593Smuzhiyun if (mode == IEEE80211_CHANCTX_EXCLUSIVE)
175*4882a593Smuzhiyun return NULL;
176*4882a593Smuzhiyun
177*4882a593Smuzhiyun list_for_each_entry(ctx, &local->chanctx_list, list) {
178*4882a593Smuzhiyun if (ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED)
179*4882a593Smuzhiyun continue;
180*4882a593Smuzhiyun
181*4882a593Smuzhiyun if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE)
182*4882a593Smuzhiyun continue;
183*4882a593Smuzhiyun
184*4882a593Smuzhiyun if (!ieee80211_chanctx_can_reserve_chandef(local, ctx,
185*4882a593Smuzhiyun chandef))
186*4882a593Smuzhiyun continue;
187*4882a593Smuzhiyun
188*4882a593Smuzhiyun return ctx;
189*4882a593Smuzhiyun }
190*4882a593Smuzhiyun
191*4882a593Smuzhiyun return NULL;
192*4882a593Smuzhiyun }
193*4882a593Smuzhiyun
ieee80211_get_sta_bw(struct ieee80211_sta * sta)194*4882a593Smuzhiyun enum nl80211_chan_width ieee80211_get_sta_bw(struct ieee80211_sta *sta)
195*4882a593Smuzhiyun {
196*4882a593Smuzhiyun switch (sta->bandwidth) {
197*4882a593Smuzhiyun case IEEE80211_STA_RX_BW_20:
198*4882a593Smuzhiyun if (sta->ht_cap.ht_supported)
199*4882a593Smuzhiyun return NL80211_CHAN_WIDTH_20;
200*4882a593Smuzhiyun else
201*4882a593Smuzhiyun return NL80211_CHAN_WIDTH_20_NOHT;
202*4882a593Smuzhiyun case IEEE80211_STA_RX_BW_40:
203*4882a593Smuzhiyun return NL80211_CHAN_WIDTH_40;
204*4882a593Smuzhiyun case IEEE80211_STA_RX_BW_80:
205*4882a593Smuzhiyun return NL80211_CHAN_WIDTH_80;
206*4882a593Smuzhiyun case IEEE80211_STA_RX_BW_160:
207*4882a593Smuzhiyun /*
208*4882a593Smuzhiyun * This applied for both 160 and 80+80. since we use
209*4882a593Smuzhiyun * the returned value to consider degradation of
210*4882a593Smuzhiyun * ctx->conf.min_def, we have to make sure to take
211*4882a593Smuzhiyun * the bigger one (NL80211_CHAN_WIDTH_160).
212*4882a593Smuzhiyun * Otherwise we might try degrading even when not
213*4882a593Smuzhiyun * needed, as the max required sta_bw returned (80+80)
214*4882a593Smuzhiyun * might be smaller than the configured bw (160).
215*4882a593Smuzhiyun */
216*4882a593Smuzhiyun return NL80211_CHAN_WIDTH_160;
217*4882a593Smuzhiyun default:
218*4882a593Smuzhiyun WARN_ON(1);
219*4882a593Smuzhiyun return NL80211_CHAN_WIDTH_20;
220*4882a593Smuzhiyun }
221*4882a593Smuzhiyun }
222*4882a593Smuzhiyun
223*4882a593Smuzhiyun static enum nl80211_chan_width
ieee80211_get_max_required_bw(struct ieee80211_sub_if_data * sdata)224*4882a593Smuzhiyun ieee80211_get_max_required_bw(struct ieee80211_sub_if_data *sdata)
225*4882a593Smuzhiyun {
226*4882a593Smuzhiyun enum nl80211_chan_width max_bw = NL80211_CHAN_WIDTH_20_NOHT;
227*4882a593Smuzhiyun struct sta_info *sta;
228*4882a593Smuzhiyun
229*4882a593Smuzhiyun rcu_read_lock();
230*4882a593Smuzhiyun list_for_each_entry_rcu(sta, &sdata->local->sta_list, list) {
231*4882a593Smuzhiyun if (sdata != sta->sdata &&
232*4882a593Smuzhiyun !(sta->sdata->bss && sta->sdata->bss == sdata->bss))
233*4882a593Smuzhiyun continue;
234*4882a593Smuzhiyun
235*4882a593Smuzhiyun max_bw = max(max_bw, ieee80211_get_sta_bw(&sta->sta));
236*4882a593Smuzhiyun }
237*4882a593Smuzhiyun rcu_read_unlock();
238*4882a593Smuzhiyun
239*4882a593Smuzhiyun return max_bw;
240*4882a593Smuzhiyun }
241*4882a593Smuzhiyun
242*4882a593Smuzhiyun static enum nl80211_chan_width
ieee80211_get_chanctx_max_required_bw(struct ieee80211_local * local,struct ieee80211_chanctx_conf * conf)243*4882a593Smuzhiyun ieee80211_get_chanctx_max_required_bw(struct ieee80211_local *local,
244*4882a593Smuzhiyun struct ieee80211_chanctx_conf *conf)
245*4882a593Smuzhiyun {
246*4882a593Smuzhiyun struct ieee80211_sub_if_data *sdata;
247*4882a593Smuzhiyun enum nl80211_chan_width max_bw = NL80211_CHAN_WIDTH_20_NOHT;
248*4882a593Smuzhiyun
249*4882a593Smuzhiyun rcu_read_lock();
250*4882a593Smuzhiyun list_for_each_entry_rcu(sdata, &local->interfaces, list) {
251*4882a593Smuzhiyun struct ieee80211_vif *vif = &sdata->vif;
252*4882a593Smuzhiyun enum nl80211_chan_width width = NL80211_CHAN_WIDTH_20_NOHT;
253*4882a593Smuzhiyun
254*4882a593Smuzhiyun if (!ieee80211_sdata_running(sdata))
255*4882a593Smuzhiyun continue;
256*4882a593Smuzhiyun
257*4882a593Smuzhiyun if (rcu_access_pointer(sdata->vif.chanctx_conf) != conf)
258*4882a593Smuzhiyun continue;
259*4882a593Smuzhiyun
260*4882a593Smuzhiyun switch (vif->type) {
261*4882a593Smuzhiyun case NL80211_IFTYPE_AP:
262*4882a593Smuzhiyun case NL80211_IFTYPE_AP_VLAN:
263*4882a593Smuzhiyun width = ieee80211_get_max_required_bw(sdata);
264*4882a593Smuzhiyun break;
265*4882a593Smuzhiyun case NL80211_IFTYPE_STATION:
266*4882a593Smuzhiyun /*
267*4882a593Smuzhiyun * The ap's sta->bandwidth is not set yet at this
268*4882a593Smuzhiyun * point, so take the width from the chandef, but
269*4882a593Smuzhiyun * account also for TDLS peers
270*4882a593Smuzhiyun */
271*4882a593Smuzhiyun width = max(vif->bss_conf.chandef.width,
272*4882a593Smuzhiyun ieee80211_get_max_required_bw(sdata));
273*4882a593Smuzhiyun break;
274*4882a593Smuzhiyun case NL80211_IFTYPE_P2P_DEVICE:
275*4882a593Smuzhiyun case NL80211_IFTYPE_NAN:
276*4882a593Smuzhiyun continue;
277*4882a593Smuzhiyun case NL80211_IFTYPE_ADHOC:
278*4882a593Smuzhiyun case NL80211_IFTYPE_WDS:
279*4882a593Smuzhiyun case NL80211_IFTYPE_MESH_POINT:
280*4882a593Smuzhiyun case NL80211_IFTYPE_OCB:
281*4882a593Smuzhiyun width = vif->bss_conf.chandef.width;
282*4882a593Smuzhiyun break;
283*4882a593Smuzhiyun case NL80211_IFTYPE_UNSPECIFIED:
284*4882a593Smuzhiyun case NUM_NL80211_IFTYPES:
285*4882a593Smuzhiyun case NL80211_IFTYPE_MONITOR:
286*4882a593Smuzhiyun case NL80211_IFTYPE_P2P_CLIENT:
287*4882a593Smuzhiyun case NL80211_IFTYPE_P2P_GO:
288*4882a593Smuzhiyun WARN_ON_ONCE(1);
289*4882a593Smuzhiyun }
290*4882a593Smuzhiyun max_bw = max(max_bw, width);
291*4882a593Smuzhiyun }
292*4882a593Smuzhiyun
293*4882a593Smuzhiyun /* use the configured bandwidth in case of monitor interface */
294*4882a593Smuzhiyun sdata = rcu_dereference(local->monitor_sdata);
295*4882a593Smuzhiyun if (sdata && rcu_access_pointer(sdata->vif.chanctx_conf) == conf)
296*4882a593Smuzhiyun max_bw = max(max_bw, conf->def.width);
297*4882a593Smuzhiyun
298*4882a593Smuzhiyun rcu_read_unlock();
299*4882a593Smuzhiyun
300*4882a593Smuzhiyun return max_bw;
301*4882a593Smuzhiyun }
302*4882a593Smuzhiyun
303*4882a593Smuzhiyun /*
304*4882a593Smuzhiyun * recalc the min required chan width of the channel context, which is
305*4882a593Smuzhiyun * the max of min required widths of all the interfaces bound to this
306*4882a593Smuzhiyun * channel context.
307*4882a593Smuzhiyun */
ieee80211_recalc_chanctx_min_def(struct ieee80211_local * local,struct ieee80211_chanctx * ctx)308*4882a593Smuzhiyun void ieee80211_recalc_chanctx_min_def(struct ieee80211_local *local,
309*4882a593Smuzhiyun struct ieee80211_chanctx *ctx)
310*4882a593Smuzhiyun {
311*4882a593Smuzhiyun enum nl80211_chan_width max_bw;
312*4882a593Smuzhiyun struct cfg80211_chan_def min_def;
313*4882a593Smuzhiyun
314*4882a593Smuzhiyun lockdep_assert_held(&local->chanctx_mtx);
315*4882a593Smuzhiyun
316*4882a593Smuzhiyun /* don't optimize non-20MHz based and radar_enabled confs */
317*4882a593Smuzhiyun if (ctx->conf.def.width == NL80211_CHAN_WIDTH_5 ||
318*4882a593Smuzhiyun ctx->conf.def.width == NL80211_CHAN_WIDTH_10 ||
319*4882a593Smuzhiyun ctx->conf.def.width == NL80211_CHAN_WIDTH_1 ||
320*4882a593Smuzhiyun ctx->conf.def.width == NL80211_CHAN_WIDTH_2 ||
321*4882a593Smuzhiyun ctx->conf.def.width == NL80211_CHAN_WIDTH_4 ||
322*4882a593Smuzhiyun ctx->conf.def.width == NL80211_CHAN_WIDTH_8 ||
323*4882a593Smuzhiyun ctx->conf.def.width == NL80211_CHAN_WIDTH_16 ||
324*4882a593Smuzhiyun ctx->conf.radar_enabled) {
325*4882a593Smuzhiyun ctx->conf.min_def = ctx->conf.def;
326*4882a593Smuzhiyun return;
327*4882a593Smuzhiyun }
328*4882a593Smuzhiyun
329*4882a593Smuzhiyun max_bw = ieee80211_get_chanctx_max_required_bw(local, &ctx->conf);
330*4882a593Smuzhiyun
331*4882a593Smuzhiyun /* downgrade chandef up to max_bw */
332*4882a593Smuzhiyun min_def = ctx->conf.def;
333*4882a593Smuzhiyun while (min_def.width > max_bw)
334*4882a593Smuzhiyun ieee80211_chandef_downgrade(&min_def);
335*4882a593Smuzhiyun
336*4882a593Smuzhiyun if (cfg80211_chandef_identical(&ctx->conf.min_def, &min_def))
337*4882a593Smuzhiyun return;
338*4882a593Smuzhiyun
339*4882a593Smuzhiyun ctx->conf.min_def = min_def;
340*4882a593Smuzhiyun if (!ctx->driver_present)
341*4882a593Smuzhiyun return;
342*4882a593Smuzhiyun
343*4882a593Smuzhiyun drv_change_chanctx(local, ctx, IEEE80211_CHANCTX_CHANGE_MIN_WIDTH);
344*4882a593Smuzhiyun }
345*4882a593Smuzhiyun
ieee80211_change_chanctx(struct ieee80211_local * local,struct ieee80211_chanctx * ctx,const struct cfg80211_chan_def * chandef)346*4882a593Smuzhiyun static void ieee80211_change_chanctx(struct ieee80211_local *local,
347*4882a593Smuzhiyun struct ieee80211_chanctx *ctx,
348*4882a593Smuzhiyun const struct cfg80211_chan_def *chandef)
349*4882a593Smuzhiyun {
350*4882a593Smuzhiyun if (cfg80211_chandef_identical(&ctx->conf.def, chandef)) {
351*4882a593Smuzhiyun ieee80211_recalc_chanctx_min_def(local, ctx);
352*4882a593Smuzhiyun return;
353*4882a593Smuzhiyun }
354*4882a593Smuzhiyun
355*4882a593Smuzhiyun WARN_ON(!cfg80211_chandef_compatible(&ctx->conf.def, chandef));
356*4882a593Smuzhiyun
357*4882a593Smuzhiyun ctx->conf.def = *chandef;
358*4882a593Smuzhiyun drv_change_chanctx(local, ctx, IEEE80211_CHANCTX_CHANGE_WIDTH);
359*4882a593Smuzhiyun ieee80211_recalc_chanctx_min_def(local, ctx);
360*4882a593Smuzhiyun
361*4882a593Smuzhiyun if (!local->use_chanctx) {
362*4882a593Smuzhiyun local->_oper_chandef = *chandef;
363*4882a593Smuzhiyun ieee80211_hw_config(local, 0);
364*4882a593Smuzhiyun }
365*4882a593Smuzhiyun }
366*4882a593Smuzhiyun
367*4882a593Smuzhiyun static struct ieee80211_chanctx *
ieee80211_find_chanctx(struct ieee80211_local * local,const struct cfg80211_chan_def * chandef,enum ieee80211_chanctx_mode mode)368*4882a593Smuzhiyun ieee80211_find_chanctx(struct ieee80211_local *local,
369*4882a593Smuzhiyun const struct cfg80211_chan_def *chandef,
370*4882a593Smuzhiyun enum ieee80211_chanctx_mode mode)
371*4882a593Smuzhiyun {
372*4882a593Smuzhiyun struct ieee80211_chanctx *ctx;
373*4882a593Smuzhiyun
374*4882a593Smuzhiyun lockdep_assert_held(&local->chanctx_mtx);
375*4882a593Smuzhiyun
376*4882a593Smuzhiyun if (mode == IEEE80211_CHANCTX_EXCLUSIVE)
377*4882a593Smuzhiyun return NULL;
378*4882a593Smuzhiyun
379*4882a593Smuzhiyun list_for_each_entry(ctx, &local->chanctx_list, list) {
380*4882a593Smuzhiyun const struct cfg80211_chan_def *compat;
381*4882a593Smuzhiyun
382*4882a593Smuzhiyun if (ctx->replace_state != IEEE80211_CHANCTX_REPLACE_NONE)
383*4882a593Smuzhiyun continue;
384*4882a593Smuzhiyun
385*4882a593Smuzhiyun if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE)
386*4882a593Smuzhiyun continue;
387*4882a593Smuzhiyun
388*4882a593Smuzhiyun compat = cfg80211_chandef_compatible(&ctx->conf.def, chandef);
389*4882a593Smuzhiyun if (!compat)
390*4882a593Smuzhiyun continue;
391*4882a593Smuzhiyun
392*4882a593Smuzhiyun compat = ieee80211_chanctx_reserved_chandef(local, ctx,
393*4882a593Smuzhiyun compat);
394*4882a593Smuzhiyun if (!compat)
395*4882a593Smuzhiyun continue;
396*4882a593Smuzhiyun
397*4882a593Smuzhiyun ieee80211_change_chanctx(local, ctx, compat);
398*4882a593Smuzhiyun
399*4882a593Smuzhiyun return ctx;
400*4882a593Smuzhiyun }
401*4882a593Smuzhiyun
402*4882a593Smuzhiyun return NULL;
403*4882a593Smuzhiyun }
404*4882a593Smuzhiyun
ieee80211_is_radar_required(struct ieee80211_local * local)405*4882a593Smuzhiyun bool ieee80211_is_radar_required(struct ieee80211_local *local)
406*4882a593Smuzhiyun {
407*4882a593Smuzhiyun struct ieee80211_sub_if_data *sdata;
408*4882a593Smuzhiyun
409*4882a593Smuzhiyun lockdep_assert_held(&local->mtx);
410*4882a593Smuzhiyun
411*4882a593Smuzhiyun rcu_read_lock();
412*4882a593Smuzhiyun list_for_each_entry_rcu(sdata, &local->interfaces, list) {
413*4882a593Smuzhiyun if (sdata->radar_required) {
414*4882a593Smuzhiyun rcu_read_unlock();
415*4882a593Smuzhiyun return true;
416*4882a593Smuzhiyun }
417*4882a593Smuzhiyun }
418*4882a593Smuzhiyun rcu_read_unlock();
419*4882a593Smuzhiyun
420*4882a593Smuzhiyun return false;
421*4882a593Smuzhiyun }
422*4882a593Smuzhiyun
423*4882a593Smuzhiyun static bool
ieee80211_chanctx_radar_required(struct ieee80211_local * local,struct ieee80211_chanctx * ctx)424*4882a593Smuzhiyun ieee80211_chanctx_radar_required(struct ieee80211_local *local,
425*4882a593Smuzhiyun struct ieee80211_chanctx *ctx)
426*4882a593Smuzhiyun {
427*4882a593Smuzhiyun struct ieee80211_chanctx_conf *conf = &ctx->conf;
428*4882a593Smuzhiyun struct ieee80211_sub_if_data *sdata;
429*4882a593Smuzhiyun bool required = false;
430*4882a593Smuzhiyun
431*4882a593Smuzhiyun lockdep_assert_held(&local->chanctx_mtx);
432*4882a593Smuzhiyun lockdep_assert_held(&local->mtx);
433*4882a593Smuzhiyun
434*4882a593Smuzhiyun rcu_read_lock();
435*4882a593Smuzhiyun list_for_each_entry_rcu(sdata, &local->interfaces, list) {
436*4882a593Smuzhiyun if (!ieee80211_sdata_running(sdata))
437*4882a593Smuzhiyun continue;
438*4882a593Smuzhiyun if (rcu_access_pointer(sdata->vif.chanctx_conf) != conf)
439*4882a593Smuzhiyun continue;
440*4882a593Smuzhiyun if (!sdata->radar_required)
441*4882a593Smuzhiyun continue;
442*4882a593Smuzhiyun
443*4882a593Smuzhiyun required = true;
444*4882a593Smuzhiyun break;
445*4882a593Smuzhiyun }
446*4882a593Smuzhiyun rcu_read_unlock();
447*4882a593Smuzhiyun
448*4882a593Smuzhiyun return required;
449*4882a593Smuzhiyun }
450*4882a593Smuzhiyun
451*4882a593Smuzhiyun static struct ieee80211_chanctx *
ieee80211_alloc_chanctx(struct ieee80211_local * local,const struct cfg80211_chan_def * chandef,enum ieee80211_chanctx_mode mode)452*4882a593Smuzhiyun ieee80211_alloc_chanctx(struct ieee80211_local *local,
453*4882a593Smuzhiyun const struct cfg80211_chan_def *chandef,
454*4882a593Smuzhiyun enum ieee80211_chanctx_mode mode)
455*4882a593Smuzhiyun {
456*4882a593Smuzhiyun struct ieee80211_chanctx *ctx;
457*4882a593Smuzhiyun
458*4882a593Smuzhiyun lockdep_assert_held(&local->chanctx_mtx);
459*4882a593Smuzhiyun
460*4882a593Smuzhiyun ctx = kzalloc(sizeof(*ctx) + local->hw.chanctx_data_size, GFP_KERNEL);
461*4882a593Smuzhiyun if (!ctx)
462*4882a593Smuzhiyun return NULL;
463*4882a593Smuzhiyun
464*4882a593Smuzhiyun INIT_LIST_HEAD(&ctx->assigned_vifs);
465*4882a593Smuzhiyun INIT_LIST_HEAD(&ctx->reserved_vifs);
466*4882a593Smuzhiyun ctx->conf.def = *chandef;
467*4882a593Smuzhiyun ctx->conf.rx_chains_static = 1;
468*4882a593Smuzhiyun ctx->conf.rx_chains_dynamic = 1;
469*4882a593Smuzhiyun ctx->mode = mode;
470*4882a593Smuzhiyun ctx->conf.radar_enabled = false;
471*4882a593Smuzhiyun ieee80211_recalc_chanctx_min_def(local, ctx);
472*4882a593Smuzhiyun
473*4882a593Smuzhiyun return ctx;
474*4882a593Smuzhiyun }
475*4882a593Smuzhiyun
ieee80211_add_chanctx(struct ieee80211_local * local,struct ieee80211_chanctx * ctx)476*4882a593Smuzhiyun static int ieee80211_add_chanctx(struct ieee80211_local *local,
477*4882a593Smuzhiyun struct ieee80211_chanctx *ctx)
478*4882a593Smuzhiyun {
479*4882a593Smuzhiyun u32 changed;
480*4882a593Smuzhiyun int err;
481*4882a593Smuzhiyun
482*4882a593Smuzhiyun lockdep_assert_held(&local->mtx);
483*4882a593Smuzhiyun lockdep_assert_held(&local->chanctx_mtx);
484*4882a593Smuzhiyun
485*4882a593Smuzhiyun if (!local->use_chanctx)
486*4882a593Smuzhiyun local->hw.conf.radar_enabled = ctx->conf.radar_enabled;
487*4882a593Smuzhiyun
488*4882a593Smuzhiyun /* turn idle off *before* setting channel -- some drivers need that */
489*4882a593Smuzhiyun changed = ieee80211_idle_off(local);
490*4882a593Smuzhiyun if (changed)
491*4882a593Smuzhiyun ieee80211_hw_config(local, changed);
492*4882a593Smuzhiyun
493*4882a593Smuzhiyun if (!local->use_chanctx) {
494*4882a593Smuzhiyun local->_oper_chandef = ctx->conf.def;
495*4882a593Smuzhiyun ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
496*4882a593Smuzhiyun } else {
497*4882a593Smuzhiyun err = drv_add_chanctx(local, ctx);
498*4882a593Smuzhiyun if (err) {
499*4882a593Smuzhiyun ieee80211_recalc_idle(local);
500*4882a593Smuzhiyun return err;
501*4882a593Smuzhiyun }
502*4882a593Smuzhiyun }
503*4882a593Smuzhiyun
504*4882a593Smuzhiyun return 0;
505*4882a593Smuzhiyun }
506*4882a593Smuzhiyun
507*4882a593Smuzhiyun static struct ieee80211_chanctx *
ieee80211_new_chanctx(struct ieee80211_local * local,const struct cfg80211_chan_def * chandef,enum ieee80211_chanctx_mode mode)508*4882a593Smuzhiyun ieee80211_new_chanctx(struct ieee80211_local *local,
509*4882a593Smuzhiyun const struct cfg80211_chan_def *chandef,
510*4882a593Smuzhiyun enum ieee80211_chanctx_mode mode)
511*4882a593Smuzhiyun {
512*4882a593Smuzhiyun struct ieee80211_chanctx *ctx;
513*4882a593Smuzhiyun int err;
514*4882a593Smuzhiyun
515*4882a593Smuzhiyun lockdep_assert_held(&local->mtx);
516*4882a593Smuzhiyun lockdep_assert_held(&local->chanctx_mtx);
517*4882a593Smuzhiyun
518*4882a593Smuzhiyun ctx = ieee80211_alloc_chanctx(local, chandef, mode);
519*4882a593Smuzhiyun if (!ctx)
520*4882a593Smuzhiyun return ERR_PTR(-ENOMEM);
521*4882a593Smuzhiyun
522*4882a593Smuzhiyun err = ieee80211_add_chanctx(local, ctx);
523*4882a593Smuzhiyun if (err) {
524*4882a593Smuzhiyun kfree(ctx);
525*4882a593Smuzhiyun return ERR_PTR(err);
526*4882a593Smuzhiyun }
527*4882a593Smuzhiyun
528*4882a593Smuzhiyun list_add_rcu(&ctx->list, &local->chanctx_list);
529*4882a593Smuzhiyun return ctx;
530*4882a593Smuzhiyun }
531*4882a593Smuzhiyun
ieee80211_del_chanctx(struct ieee80211_local * local,struct ieee80211_chanctx * ctx)532*4882a593Smuzhiyun static void ieee80211_del_chanctx(struct ieee80211_local *local,
533*4882a593Smuzhiyun struct ieee80211_chanctx *ctx)
534*4882a593Smuzhiyun {
535*4882a593Smuzhiyun lockdep_assert_held(&local->chanctx_mtx);
536*4882a593Smuzhiyun
537*4882a593Smuzhiyun if (!local->use_chanctx) {
538*4882a593Smuzhiyun struct cfg80211_chan_def *chandef = &local->_oper_chandef;
539*4882a593Smuzhiyun /* S1G doesn't have 20MHz, so get the correct width for the
540*4882a593Smuzhiyun * current channel.
541*4882a593Smuzhiyun */
542*4882a593Smuzhiyun if (chandef->chan->band == NL80211_BAND_S1GHZ)
543*4882a593Smuzhiyun chandef->width =
544*4882a593Smuzhiyun ieee80211_s1g_channel_width(chandef->chan);
545*4882a593Smuzhiyun else
546*4882a593Smuzhiyun chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
547*4882a593Smuzhiyun chandef->center_freq1 = chandef->chan->center_freq;
548*4882a593Smuzhiyun chandef->freq1_offset = chandef->chan->freq_offset;
549*4882a593Smuzhiyun chandef->center_freq2 = 0;
550*4882a593Smuzhiyun
551*4882a593Smuzhiyun /* NOTE: Disabling radar is only valid here for
552*4882a593Smuzhiyun * single channel context. To be sure, check it ...
553*4882a593Smuzhiyun */
554*4882a593Smuzhiyun WARN_ON(local->hw.conf.radar_enabled &&
555*4882a593Smuzhiyun !list_empty(&local->chanctx_list));
556*4882a593Smuzhiyun
557*4882a593Smuzhiyun local->hw.conf.radar_enabled = false;
558*4882a593Smuzhiyun
559*4882a593Smuzhiyun ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
560*4882a593Smuzhiyun } else {
561*4882a593Smuzhiyun drv_remove_chanctx(local, ctx);
562*4882a593Smuzhiyun }
563*4882a593Smuzhiyun
564*4882a593Smuzhiyun ieee80211_recalc_idle(local);
565*4882a593Smuzhiyun }
566*4882a593Smuzhiyun
ieee80211_free_chanctx(struct ieee80211_local * local,struct ieee80211_chanctx * ctx)567*4882a593Smuzhiyun static void ieee80211_free_chanctx(struct ieee80211_local *local,
568*4882a593Smuzhiyun struct ieee80211_chanctx *ctx)
569*4882a593Smuzhiyun {
570*4882a593Smuzhiyun lockdep_assert_held(&local->chanctx_mtx);
571*4882a593Smuzhiyun
572*4882a593Smuzhiyun WARN_ON_ONCE(ieee80211_chanctx_refcount(local, ctx) != 0);
573*4882a593Smuzhiyun
574*4882a593Smuzhiyun list_del_rcu(&ctx->list);
575*4882a593Smuzhiyun ieee80211_del_chanctx(local, ctx);
576*4882a593Smuzhiyun kfree_rcu(ctx, rcu_head);
577*4882a593Smuzhiyun }
578*4882a593Smuzhiyun
ieee80211_recalc_chanctx_chantype(struct ieee80211_local * local,struct ieee80211_chanctx * ctx)579*4882a593Smuzhiyun void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local,
580*4882a593Smuzhiyun struct ieee80211_chanctx *ctx)
581*4882a593Smuzhiyun {
582*4882a593Smuzhiyun struct ieee80211_chanctx_conf *conf = &ctx->conf;
583*4882a593Smuzhiyun struct ieee80211_sub_if_data *sdata;
584*4882a593Smuzhiyun const struct cfg80211_chan_def *compat = NULL;
585*4882a593Smuzhiyun struct sta_info *sta;
586*4882a593Smuzhiyun
587*4882a593Smuzhiyun lockdep_assert_held(&local->chanctx_mtx);
588*4882a593Smuzhiyun
589*4882a593Smuzhiyun rcu_read_lock();
590*4882a593Smuzhiyun list_for_each_entry_rcu(sdata, &local->interfaces, list) {
591*4882a593Smuzhiyun
592*4882a593Smuzhiyun if (!ieee80211_sdata_running(sdata))
593*4882a593Smuzhiyun continue;
594*4882a593Smuzhiyun if (rcu_access_pointer(sdata->vif.chanctx_conf) != conf)
595*4882a593Smuzhiyun continue;
596*4882a593Smuzhiyun if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
597*4882a593Smuzhiyun continue;
598*4882a593Smuzhiyun
599*4882a593Smuzhiyun if (!compat)
600*4882a593Smuzhiyun compat = &sdata->vif.bss_conf.chandef;
601*4882a593Smuzhiyun
602*4882a593Smuzhiyun compat = cfg80211_chandef_compatible(
603*4882a593Smuzhiyun &sdata->vif.bss_conf.chandef, compat);
604*4882a593Smuzhiyun if (WARN_ON_ONCE(!compat))
605*4882a593Smuzhiyun break;
606*4882a593Smuzhiyun }
607*4882a593Smuzhiyun
608*4882a593Smuzhiyun /* TDLS peers can sometimes affect the chandef width */
609*4882a593Smuzhiyun list_for_each_entry_rcu(sta, &local->sta_list, list) {
610*4882a593Smuzhiyun if (!sta->uploaded ||
611*4882a593Smuzhiyun !test_sta_flag(sta, WLAN_STA_TDLS_WIDER_BW) ||
612*4882a593Smuzhiyun !test_sta_flag(sta, WLAN_STA_AUTHORIZED) ||
613*4882a593Smuzhiyun !sta->tdls_chandef.chan)
614*4882a593Smuzhiyun continue;
615*4882a593Smuzhiyun
616*4882a593Smuzhiyun compat = cfg80211_chandef_compatible(&sta->tdls_chandef,
617*4882a593Smuzhiyun compat);
618*4882a593Smuzhiyun if (WARN_ON_ONCE(!compat))
619*4882a593Smuzhiyun break;
620*4882a593Smuzhiyun }
621*4882a593Smuzhiyun rcu_read_unlock();
622*4882a593Smuzhiyun
623*4882a593Smuzhiyun if (!compat)
624*4882a593Smuzhiyun return;
625*4882a593Smuzhiyun
626*4882a593Smuzhiyun ieee80211_change_chanctx(local, ctx, compat);
627*4882a593Smuzhiyun }
628*4882a593Smuzhiyun
ieee80211_recalc_radar_chanctx(struct ieee80211_local * local,struct ieee80211_chanctx * chanctx)629*4882a593Smuzhiyun static void ieee80211_recalc_radar_chanctx(struct ieee80211_local *local,
630*4882a593Smuzhiyun struct ieee80211_chanctx *chanctx)
631*4882a593Smuzhiyun {
632*4882a593Smuzhiyun bool radar_enabled;
633*4882a593Smuzhiyun
634*4882a593Smuzhiyun lockdep_assert_held(&local->chanctx_mtx);
635*4882a593Smuzhiyun /* for ieee80211_is_radar_required */
636*4882a593Smuzhiyun lockdep_assert_held(&local->mtx);
637*4882a593Smuzhiyun
638*4882a593Smuzhiyun radar_enabled = ieee80211_chanctx_radar_required(local, chanctx);
639*4882a593Smuzhiyun
640*4882a593Smuzhiyun if (radar_enabled == chanctx->conf.radar_enabled)
641*4882a593Smuzhiyun return;
642*4882a593Smuzhiyun
643*4882a593Smuzhiyun chanctx->conf.radar_enabled = radar_enabled;
644*4882a593Smuzhiyun
645*4882a593Smuzhiyun if (!local->use_chanctx) {
646*4882a593Smuzhiyun local->hw.conf.radar_enabled = chanctx->conf.radar_enabled;
647*4882a593Smuzhiyun ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
648*4882a593Smuzhiyun }
649*4882a593Smuzhiyun
650*4882a593Smuzhiyun drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RADAR);
651*4882a593Smuzhiyun }
652*4882a593Smuzhiyun
ieee80211_assign_vif_chanctx(struct ieee80211_sub_if_data * sdata,struct ieee80211_chanctx * new_ctx)653*4882a593Smuzhiyun static int ieee80211_assign_vif_chanctx(struct ieee80211_sub_if_data *sdata,
654*4882a593Smuzhiyun struct ieee80211_chanctx *new_ctx)
655*4882a593Smuzhiyun {
656*4882a593Smuzhiyun struct ieee80211_local *local = sdata->local;
657*4882a593Smuzhiyun struct ieee80211_chanctx_conf *conf;
658*4882a593Smuzhiyun struct ieee80211_chanctx *curr_ctx = NULL;
659*4882a593Smuzhiyun int ret = 0;
660*4882a593Smuzhiyun
661*4882a593Smuzhiyun if (WARN_ON(sdata->vif.type == NL80211_IFTYPE_NAN))
662*4882a593Smuzhiyun return -ENOTSUPP;
663*4882a593Smuzhiyun
664*4882a593Smuzhiyun conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
665*4882a593Smuzhiyun lockdep_is_held(&local->chanctx_mtx));
666*4882a593Smuzhiyun
667*4882a593Smuzhiyun if (conf) {
668*4882a593Smuzhiyun curr_ctx = container_of(conf, struct ieee80211_chanctx, conf);
669*4882a593Smuzhiyun
670*4882a593Smuzhiyun drv_unassign_vif_chanctx(local, sdata, curr_ctx);
671*4882a593Smuzhiyun conf = NULL;
672*4882a593Smuzhiyun list_del(&sdata->assigned_chanctx_list);
673*4882a593Smuzhiyun }
674*4882a593Smuzhiyun
675*4882a593Smuzhiyun if (new_ctx) {
676*4882a593Smuzhiyun ret = drv_assign_vif_chanctx(local, sdata, new_ctx);
677*4882a593Smuzhiyun if (ret)
678*4882a593Smuzhiyun goto out;
679*4882a593Smuzhiyun
680*4882a593Smuzhiyun conf = &new_ctx->conf;
681*4882a593Smuzhiyun list_add(&sdata->assigned_chanctx_list,
682*4882a593Smuzhiyun &new_ctx->assigned_vifs);
683*4882a593Smuzhiyun }
684*4882a593Smuzhiyun
685*4882a593Smuzhiyun out:
686*4882a593Smuzhiyun rcu_assign_pointer(sdata->vif.chanctx_conf, conf);
687*4882a593Smuzhiyun
688*4882a593Smuzhiyun sdata->vif.bss_conf.idle = !conf;
689*4882a593Smuzhiyun
690*4882a593Smuzhiyun if (curr_ctx && ieee80211_chanctx_num_assigned(local, curr_ctx) > 0) {
691*4882a593Smuzhiyun ieee80211_recalc_chanctx_chantype(local, curr_ctx);
692*4882a593Smuzhiyun ieee80211_recalc_smps_chanctx(local, curr_ctx);
693*4882a593Smuzhiyun ieee80211_recalc_radar_chanctx(local, curr_ctx);
694*4882a593Smuzhiyun ieee80211_recalc_chanctx_min_def(local, curr_ctx);
695*4882a593Smuzhiyun }
696*4882a593Smuzhiyun
697*4882a593Smuzhiyun if (new_ctx && ieee80211_chanctx_num_assigned(local, new_ctx) > 0) {
698*4882a593Smuzhiyun ieee80211_recalc_txpower(sdata, false);
699*4882a593Smuzhiyun ieee80211_recalc_chanctx_min_def(local, new_ctx);
700*4882a593Smuzhiyun }
701*4882a593Smuzhiyun
702*4882a593Smuzhiyun if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE &&
703*4882a593Smuzhiyun sdata->vif.type != NL80211_IFTYPE_MONITOR)
704*4882a593Smuzhiyun ieee80211_bss_info_change_notify(sdata,
705*4882a593Smuzhiyun BSS_CHANGED_IDLE);
706*4882a593Smuzhiyun
707*4882a593Smuzhiyun ieee80211_check_fast_xmit_iface(sdata);
708*4882a593Smuzhiyun
709*4882a593Smuzhiyun return ret;
710*4882a593Smuzhiyun }
711*4882a593Smuzhiyun
ieee80211_recalc_smps_chanctx(struct ieee80211_local * local,struct ieee80211_chanctx * chanctx)712*4882a593Smuzhiyun void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local,
713*4882a593Smuzhiyun struct ieee80211_chanctx *chanctx)
714*4882a593Smuzhiyun {
715*4882a593Smuzhiyun struct ieee80211_sub_if_data *sdata;
716*4882a593Smuzhiyun u8 rx_chains_static, rx_chains_dynamic;
717*4882a593Smuzhiyun
718*4882a593Smuzhiyun lockdep_assert_held(&local->chanctx_mtx);
719*4882a593Smuzhiyun
720*4882a593Smuzhiyun rx_chains_static = 1;
721*4882a593Smuzhiyun rx_chains_dynamic = 1;
722*4882a593Smuzhiyun
723*4882a593Smuzhiyun rcu_read_lock();
724*4882a593Smuzhiyun list_for_each_entry_rcu(sdata, &local->interfaces, list) {
725*4882a593Smuzhiyun u8 needed_static, needed_dynamic;
726*4882a593Smuzhiyun
727*4882a593Smuzhiyun if (!ieee80211_sdata_running(sdata))
728*4882a593Smuzhiyun continue;
729*4882a593Smuzhiyun
730*4882a593Smuzhiyun if (rcu_access_pointer(sdata->vif.chanctx_conf) !=
731*4882a593Smuzhiyun &chanctx->conf)
732*4882a593Smuzhiyun continue;
733*4882a593Smuzhiyun
734*4882a593Smuzhiyun switch (sdata->vif.type) {
735*4882a593Smuzhiyun case NL80211_IFTYPE_P2P_DEVICE:
736*4882a593Smuzhiyun case NL80211_IFTYPE_NAN:
737*4882a593Smuzhiyun continue;
738*4882a593Smuzhiyun case NL80211_IFTYPE_STATION:
739*4882a593Smuzhiyun if (!sdata->u.mgd.associated)
740*4882a593Smuzhiyun continue;
741*4882a593Smuzhiyun break;
742*4882a593Smuzhiyun case NL80211_IFTYPE_AP_VLAN:
743*4882a593Smuzhiyun continue;
744*4882a593Smuzhiyun case NL80211_IFTYPE_AP:
745*4882a593Smuzhiyun case NL80211_IFTYPE_ADHOC:
746*4882a593Smuzhiyun case NL80211_IFTYPE_WDS:
747*4882a593Smuzhiyun case NL80211_IFTYPE_MESH_POINT:
748*4882a593Smuzhiyun case NL80211_IFTYPE_OCB:
749*4882a593Smuzhiyun break;
750*4882a593Smuzhiyun default:
751*4882a593Smuzhiyun WARN_ON_ONCE(1);
752*4882a593Smuzhiyun }
753*4882a593Smuzhiyun
754*4882a593Smuzhiyun switch (sdata->smps_mode) {
755*4882a593Smuzhiyun default:
756*4882a593Smuzhiyun WARN_ONCE(1, "Invalid SMPS mode %d\n",
757*4882a593Smuzhiyun sdata->smps_mode);
758*4882a593Smuzhiyun fallthrough;
759*4882a593Smuzhiyun case IEEE80211_SMPS_OFF:
760*4882a593Smuzhiyun needed_static = sdata->needed_rx_chains;
761*4882a593Smuzhiyun needed_dynamic = sdata->needed_rx_chains;
762*4882a593Smuzhiyun break;
763*4882a593Smuzhiyun case IEEE80211_SMPS_DYNAMIC:
764*4882a593Smuzhiyun needed_static = 1;
765*4882a593Smuzhiyun needed_dynamic = sdata->needed_rx_chains;
766*4882a593Smuzhiyun break;
767*4882a593Smuzhiyun case IEEE80211_SMPS_STATIC:
768*4882a593Smuzhiyun needed_static = 1;
769*4882a593Smuzhiyun needed_dynamic = 1;
770*4882a593Smuzhiyun break;
771*4882a593Smuzhiyun }
772*4882a593Smuzhiyun
773*4882a593Smuzhiyun rx_chains_static = max(rx_chains_static, needed_static);
774*4882a593Smuzhiyun rx_chains_dynamic = max(rx_chains_dynamic, needed_dynamic);
775*4882a593Smuzhiyun }
776*4882a593Smuzhiyun
777*4882a593Smuzhiyun /* Disable SMPS for the monitor interface */
778*4882a593Smuzhiyun sdata = rcu_dereference(local->monitor_sdata);
779*4882a593Smuzhiyun if (sdata &&
780*4882a593Smuzhiyun rcu_access_pointer(sdata->vif.chanctx_conf) == &chanctx->conf)
781*4882a593Smuzhiyun rx_chains_dynamic = rx_chains_static = local->rx_chains;
782*4882a593Smuzhiyun
783*4882a593Smuzhiyun rcu_read_unlock();
784*4882a593Smuzhiyun
785*4882a593Smuzhiyun if (!local->use_chanctx) {
786*4882a593Smuzhiyun if (rx_chains_static > 1)
787*4882a593Smuzhiyun local->smps_mode = IEEE80211_SMPS_OFF;
788*4882a593Smuzhiyun else if (rx_chains_dynamic > 1)
789*4882a593Smuzhiyun local->smps_mode = IEEE80211_SMPS_DYNAMIC;
790*4882a593Smuzhiyun else
791*4882a593Smuzhiyun local->smps_mode = IEEE80211_SMPS_STATIC;
792*4882a593Smuzhiyun ieee80211_hw_config(local, 0);
793*4882a593Smuzhiyun }
794*4882a593Smuzhiyun
795*4882a593Smuzhiyun if (rx_chains_static == chanctx->conf.rx_chains_static &&
796*4882a593Smuzhiyun rx_chains_dynamic == chanctx->conf.rx_chains_dynamic)
797*4882a593Smuzhiyun return;
798*4882a593Smuzhiyun
799*4882a593Smuzhiyun chanctx->conf.rx_chains_static = rx_chains_static;
800*4882a593Smuzhiyun chanctx->conf.rx_chains_dynamic = rx_chains_dynamic;
801*4882a593Smuzhiyun drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RX_CHAINS);
802*4882a593Smuzhiyun }
803*4882a593Smuzhiyun
804*4882a593Smuzhiyun static void
__ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data * sdata,bool clear)805*4882a593Smuzhiyun __ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata,
806*4882a593Smuzhiyun bool clear)
807*4882a593Smuzhiyun {
808*4882a593Smuzhiyun struct ieee80211_local *local __maybe_unused = sdata->local;
809*4882a593Smuzhiyun struct ieee80211_sub_if_data *vlan;
810*4882a593Smuzhiyun struct ieee80211_chanctx_conf *conf;
811*4882a593Smuzhiyun
812*4882a593Smuzhiyun if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP))
813*4882a593Smuzhiyun return;
814*4882a593Smuzhiyun
815*4882a593Smuzhiyun lockdep_assert_held(&local->mtx);
816*4882a593Smuzhiyun
817*4882a593Smuzhiyun /* Check that conf exists, even when clearing this function
818*4882a593Smuzhiyun * must be called with the AP's channel context still there
819*4882a593Smuzhiyun * as it would otherwise cause VLANs to have an invalid
820*4882a593Smuzhiyun * channel context pointer for a while, possibly pointing
821*4882a593Smuzhiyun * to a channel context that has already been freed.
822*4882a593Smuzhiyun */
823*4882a593Smuzhiyun conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
824*4882a593Smuzhiyun lockdep_is_held(&local->chanctx_mtx));
825*4882a593Smuzhiyun WARN_ON(!conf);
826*4882a593Smuzhiyun
827*4882a593Smuzhiyun if (clear)
828*4882a593Smuzhiyun conf = NULL;
829*4882a593Smuzhiyun
830*4882a593Smuzhiyun list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
831*4882a593Smuzhiyun rcu_assign_pointer(vlan->vif.chanctx_conf, conf);
832*4882a593Smuzhiyun }
833*4882a593Smuzhiyun
ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data * sdata,bool clear)834*4882a593Smuzhiyun void ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata,
835*4882a593Smuzhiyun bool clear)
836*4882a593Smuzhiyun {
837*4882a593Smuzhiyun struct ieee80211_local *local = sdata->local;
838*4882a593Smuzhiyun
839*4882a593Smuzhiyun mutex_lock(&local->chanctx_mtx);
840*4882a593Smuzhiyun
841*4882a593Smuzhiyun __ieee80211_vif_copy_chanctx_to_vlans(sdata, clear);
842*4882a593Smuzhiyun
843*4882a593Smuzhiyun mutex_unlock(&local->chanctx_mtx);
844*4882a593Smuzhiyun }
845*4882a593Smuzhiyun
ieee80211_vif_unreserve_chanctx(struct ieee80211_sub_if_data * sdata)846*4882a593Smuzhiyun int ieee80211_vif_unreserve_chanctx(struct ieee80211_sub_if_data *sdata)
847*4882a593Smuzhiyun {
848*4882a593Smuzhiyun struct ieee80211_chanctx *ctx = sdata->reserved_chanctx;
849*4882a593Smuzhiyun
850*4882a593Smuzhiyun lockdep_assert_held(&sdata->local->chanctx_mtx);
851*4882a593Smuzhiyun
852*4882a593Smuzhiyun if (WARN_ON(!ctx))
853*4882a593Smuzhiyun return -EINVAL;
854*4882a593Smuzhiyun
855*4882a593Smuzhiyun list_del(&sdata->reserved_chanctx_list);
856*4882a593Smuzhiyun sdata->reserved_chanctx = NULL;
857*4882a593Smuzhiyun
858*4882a593Smuzhiyun if (ieee80211_chanctx_refcount(sdata->local, ctx) == 0) {
859*4882a593Smuzhiyun if (ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER) {
860*4882a593Smuzhiyun if (WARN_ON(!ctx->replace_ctx))
861*4882a593Smuzhiyun return -EINVAL;
862*4882a593Smuzhiyun
863*4882a593Smuzhiyun WARN_ON(ctx->replace_ctx->replace_state !=
864*4882a593Smuzhiyun IEEE80211_CHANCTX_WILL_BE_REPLACED);
865*4882a593Smuzhiyun WARN_ON(ctx->replace_ctx->replace_ctx != ctx);
866*4882a593Smuzhiyun
867*4882a593Smuzhiyun ctx->replace_ctx->replace_ctx = NULL;
868*4882a593Smuzhiyun ctx->replace_ctx->replace_state =
869*4882a593Smuzhiyun IEEE80211_CHANCTX_REPLACE_NONE;
870*4882a593Smuzhiyun
871*4882a593Smuzhiyun list_del_rcu(&ctx->list);
872*4882a593Smuzhiyun kfree_rcu(ctx, rcu_head);
873*4882a593Smuzhiyun } else {
874*4882a593Smuzhiyun ieee80211_free_chanctx(sdata->local, ctx);
875*4882a593Smuzhiyun }
876*4882a593Smuzhiyun }
877*4882a593Smuzhiyun
878*4882a593Smuzhiyun return 0;
879*4882a593Smuzhiyun }
880*4882a593Smuzhiyun
ieee80211_vif_reserve_chanctx(struct ieee80211_sub_if_data * sdata,const struct cfg80211_chan_def * chandef,enum ieee80211_chanctx_mode mode,bool radar_required)881*4882a593Smuzhiyun int ieee80211_vif_reserve_chanctx(struct ieee80211_sub_if_data *sdata,
882*4882a593Smuzhiyun const struct cfg80211_chan_def *chandef,
883*4882a593Smuzhiyun enum ieee80211_chanctx_mode mode,
884*4882a593Smuzhiyun bool radar_required)
885*4882a593Smuzhiyun {
886*4882a593Smuzhiyun struct ieee80211_local *local = sdata->local;
887*4882a593Smuzhiyun struct ieee80211_chanctx *new_ctx, *curr_ctx, *ctx;
888*4882a593Smuzhiyun
889*4882a593Smuzhiyun lockdep_assert_held(&local->chanctx_mtx);
890*4882a593Smuzhiyun
891*4882a593Smuzhiyun curr_ctx = ieee80211_vif_get_chanctx(sdata);
892*4882a593Smuzhiyun if (curr_ctx && local->use_chanctx && !local->ops->switch_vif_chanctx)
893*4882a593Smuzhiyun return -ENOTSUPP;
894*4882a593Smuzhiyun
895*4882a593Smuzhiyun new_ctx = ieee80211_find_reservation_chanctx(local, chandef, mode);
896*4882a593Smuzhiyun if (!new_ctx) {
897*4882a593Smuzhiyun if (ieee80211_can_create_new_chanctx(local)) {
898*4882a593Smuzhiyun new_ctx = ieee80211_new_chanctx(local, chandef, mode);
899*4882a593Smuzhiyun if (IS_ERR(new_ctx))
900*4882a593Smuzhiyun return PTR_ERR(new_ctx);
901*4882a593Smuzhiyun } else {
902*4882a593Smuzhiyun if (!curr_ctx ||
903*4882a593Smuzhiyun (curr_ctx->replace_state ==
904*4882a593Smuzhiyun IEEE80211_CHANCTX_WILL_BE_REPLACED) ||
905*4882a593Smuzhiyun !list_empty(&curr_ctx->reserved_vifs)) {
906*4882a593Smuzhiyun /*
907*4882a593Smuzhiyun * Another vif already requested this context
908*4882a593Smuzhiyun * for a reservation. Find another one hoping
909*4882a593Smuzhiyun * all vifs assigned to it will also switch
910*4882a593Smuzhiyun * soon enough.
911*4882a593Smuzhiyun *
912*4882a593Smuzhiyun * TODO: This needs a little more work as some
913*4882a593Smuzhiyun * cases (more than 2 chanctx capable devices)
914*4882a593Smuzhiyun * may fail which could otherwise succeed
915*4882a593Smuzhiyun * provided some channel context juggling was
916*4882a593Smuzhiyun * performed.
917*4882a593Smuzhiyun *
918*4882a593Smuzhiyun * Consider ctx1..3, vif1..6, each ctx has 2
919*4882a593Smuzhiyun * vifs. vif1 and vif2 from ctx1 request new
920*4882a593Smuzhiyun * different chandefs starting 2 in-place
921*4882a593Smuzhiyun * reserations with ctx4 and ctx5 replacing
922*4882a593Smuzhiyun * ctx1 and ctx2 respectively. Next vif5 and
923*4882a593Smuzhiyun * vif6 from ctx3 reserve ctx4. If vif3 and
924*4882a593Smuzhiyun * vif4 remain on ctx2 as they are then this
925*4882a593Smuzhiyun * fails unless `replace_ctx` from ctx5 is
926*4882a593Smuzhiyun * replaced with ctx3.
927*4882a593Smuzhiyun */
928*4882a593Smuzhiyun list_for_each_entry(ctx, &local->chanctx_list,
929*4882a593Smuzhiyun list) {
930*4882a593Smuzhiyun if (ctx->replace_state !=
931*4882a593Smuzhiyun IEEE80211_CHANCTX_REPLACE_NONE)
932*4882a593Smuzhiyun continue;
933*4882a593Smuzhiyun
934*4882a593Smuzhiyun if (!list_empty(&ctx->reserved_vifs))
935*4882a593Smuzhiyun continue;
936*4882a593Smuzhiyun
937*4882a593Smuzhiyun curr_ctx = ctx;
938*4882a593Smuzhiyun break;
939*4882a593Smuzhiyun }
940*4882a593Smuzhiyun }
941*4882a593Smuzhiyun
942*4882a593Smuzhiyun /*
943*4882a593Smuzhiyun * If that's true then all available contexts already
944*4882a593Smuzhiyun * have reservations and cannot be used.
945*4882a593Smuzhiyun */
946*4882a593Smuzhiyun if (!curr_ctx ||
947*4882a593Smuzhiyun (curr_ctx->replace_state ==
948*4882a593Smuzhiyun IEEE80211_CHANCTX_WILL_BE_REPLACED) ||
949*4882a593Smuzhiyun !list_empty(&curr_ctx->reserved_vifs))
950*4882a593Smuzhiyun return -EBUSY;
951*4882a593Smuzhiyun
952*4882a593Smuzhiyun new_ctx = ieee80211_alloc_chanctx(local, chandef, mode);
953*4882a593Smuzhiyun if (!new_ctx)
954*4882a593Smuzhiyun return -ENOMEM;
955*4882a593Smuzhiyun
956*4882a593Smuzhiyun new_ctx->replace_ctx = curr_ctx;
957*4882a593Smuzhiyun new_ctx->replace_state =
958*4882a593Smuzhiyun IEEE80211_CHANCTX_REPLACES_OTHER;
959*4882a593Smuzhiyun
960*4882a593Smuzhiyun curr_ctx->replace_ctx = new_ctx;
961*4882a593Smuzhiyun curr_ctx->replace_state =
962*4882a593Smuzhiyun IEEE80211_CHANCTX_WILL_BE_REPLACED;
963*4882a593Smuzhiyun
964*4882a593Smuzhiyun list_add_rcu(&new_ctx->list, &local->chanctx_list);
965*4882a593Smuzhiyun }
966*4882a593Smuzhiyun }
967*4882a593Smuzhiyun
968*4882a593Smuzhiyun list_add(&sdata->reserved_chanctx_list, &new_ctx->reserved_vifs);
969*4882a593Smuzhiyun sdata->reserved_chanctx = new_ctx;
970*4882a593Smuzhiyun sdata->reserved_chandef = *chandef;
971*4882a593Smuzhiyun sdata->reserved_radar_required = radar_required;
972*4882a593Smuzhiyun sdata->reserved_ready = false;
973*4882a593Smuzhiyun
974*4882a593Smuzhiyun return 0;
975*4882a593Smuzhiyun }
976*4882a593Smuzhiyun
977*4882a593Smuzhiyun static void
ieee80211_vif_chanctx_reservation_complete(struct ieee80211_sub_if_data * sdata)978*4882a593Smuzhiyun ieee80211_vif_chanctx_reservation_complete(struct ieee80211_sub_if_data *sdata)
979*4882a593Smuzhiyun {
980*4882a593Smuzhiyun switch (sdata->vif.type) {
981*4882a593Smuzhiyun case NL80211_IFTYPE_ADHOC:
982*4882a593Smuzhiyun case NL80211_IFTYPE_AP:
983*4882a593Smuzhiyun case NL80211_IFTYPE_MESH_POINT:
984*4882a593Smuzhiyun case NL80211_IFTYPE_OCB:
985*4882a593Smuzhiyun ieee80211_queue_work(&sdata->local->hw,
986*4882a593Smuzhiyun &sdata->csa_finalize_work);
987*4882a593Smuzhiyun break;
988*4882a593Smuzhiyun case NL80211_IFTYPE_STATION:
989*4882a593Smuzhiyun ieee80211_queue_work(&sdata->local->hw,
990*4882a593Smuzhiyun &sdata->u.mgd.chswitch_work);
991*4882a593Smuzhiyun break;
992*4882a593Smuzhiyun case NL80211_IFTYPE_UNSPECIFIED:
993*4882a593Smuzhiyun case NL80211_IFTYPE_AP_VLAN:
994*4882a593Smuzhiyun case NL80211_IFTYPE_WDS:
995*4882a593Smuzhiyun case NL80211_IFTYPE_MONITOR:
996*4882a593Smuzhiyun case NL80211_IFTYPE_P2P_CLIENT:
997*4882a593Smuzhiyun case NL80211_IFTYPE_P2P_GO:
998*4882a593Smuzhiyun case NL80211_IFTYPE_P2P_DEVICE:
999*4882a593Smuzhiyun case NL80211_IFTYPE_NAN:
1000*4882a593Smuzhiyun case NUM_NL80211_IFTYPES:
1001*4882a593Smuzhiyun WARN_ON(1);
1002*4882a593Smuzhiyun break;
1003*4882a593Smuzhiyun }
1004*4882a593Smuzhiyun }
1005*4882a593Smuzhiyun
1006*4882a593Smuzhiyun static void
ieee80211_vif_update_chandef(struct ieee80211_sub_if_data * sdata,const struct cfg80211_chan_def * chandef)1007*4882a593Smuzhiyun ieee80211_vif_update_chandef(struct ieee80211_sub_if_data *sdata,
1008*4882a593Smuzhiyun const struct cfg80211_chan_def *chandef)
1009*4882a593Smuzhiyun {
1010*4882a593Smuzhiyun struct ieee80211_sub_if_data *vlan;
1011*4882a593Smuzhiyun
1012*4882a593Smuzhiyun sdata->vif.bss_conf.chandef = *chandef;
1013*4882a593Smuzhiyun
1014*4882a593Smuzhiyun if (sdata->vif.type != NL80211_IFTYPE_AP)
1015*4882a593Smuzhiyun return;
1016*4882a593Smuzhiyun
1017*4882a593Smuzhiyun list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
1018*4882a593Smuzhiyun vlan->vif.bss_conf.chandef = *chandef;
1019*4882a593Smuzhiyun }
1020*4882a593Smuzhiyun
1021*4882a593Smuzhiyun static int
ieee80211_vif_use_reserved_reassign(struct ieee80211_sub_if_data * sdata)1022*4882a593Smuzhiyun ieee80211_vif_use_reserved_reassign(struct ieee80211_sub_if_data *sdata)
1023*4882a593Smuzhiyun {
1024*4882a593Smuzhiyun struct ieee80211_local *local = sdata->local;
1025*4882a593Smuzhiyun struct ieee80211_vif_chanctx_switch vif_chsw[1] = {};
1026*4882a593Smuzhiyun struct ieee80211_chanctx *old_ctx, *new_ctx;
1027*4882a593Smuzhiyun const struct cfg80211_chan_def *chandef;
1028*4882a593Smuzhiyun u32 changed = 0;
1029*4882a593Smuzhiyun int err;
1030*4882a593Smuzhiyun
1031*4882a593Smuzhiyun lockdep_assert_held(&local->mtx);
1032*4882a593Smuzhiyun lockdep_assert_held(&local->chanctx_mtx);
1033*4882a593Smuzhiyun
1034*4882a593Smuzhiyun new_ctx = sdata->reserved_chanctx;
1035*4882a593Smuzhiyun old_ctx = ieee80211_vif_get_chanctx(sdata);
1036*4882a593Smuzhiyun
1037*4882a593Smuzhiyun if (WARN_ON(!sdata->reserved_ready))
1038*4882a593Smuzhiyun return -EBUSY;
1039*4882a593Smuzhiyun
1040*4882a593Smuzhiyun if (WARN_ON(!new_ctx))
1041*4882a593Smuzhiyun return -EINVAL;
1042*4882a593Smuzhiyun
1043*4882a593Smuzhiyun if (WARN_ON(!old_ctx))
1044*4882a593Smuzhiyun return -EINVAL;
1045*4882a593Smuzhiyun
1046*4882a593Smuzhiyun if (WARN_ON(new_ctx->replace_state ==
1047*4882a593Smuzhiyun IEEE80211_CHANCTX_REPLACES_OTHER))
1048*4882a593Smuzhiyun return -EINVAL;
1049*4882a593Smuzhiyun
1050*4882a593Smuzhiyun chandef = ieee80211_chanctx_non_reserved_chandef(local, new_ctx,
1051*4882a593Smuzhiyun &sdata->reserved_chandef);
1052*4882a593Smuzhiyun if (WARN_ON(!chandef))
1053*4882a593Smuzhiyun return -EINVAL;
1054*4882a593Smuzhiyun
1055*4882a593Smuzhiyun ieee80211_change_chanctx(local, new_ctx, chandef);
1056*4882a593Smuzhiyun
1057*4882a593Smuzhiyun vif_chsw[0].vif = &sdata->vif;
1058*4882a593Smuzhiyun vif_chsw[0].old_ctx = &old_ctx->conf;
1059*4882a593Smuzhiyun vif_chsw[0].new_ctx = &new_ctx->conf;
1060*4882a593Smuzhiyun
1061*4882a593Smuzhiyun list_del(&sdata->reserved_chanctx_list);
1062*4882a593Smuzhiyun sdata->reserved_chanctx = NULL;
1063*4882a593Smuzhiyun
1064*4882a593Smuzhiyun err = drv_switch_vif_chanctx(local, vif_chsw, 1,
1065*4882a593Smuzhiyun CHANCTX_SWMODE_REASSIGN_VIF);
1066*4882a593Smuzhiyun if (err) {
1067*4882a593Smuzhiyun if (ieee80211_chanctx_refcount(local, new_ctx) == 0)
1068*4882a593Smuzhiyun ieee80211_free_chanctx(local, new_ctx);
1069*4882a593Smuzhiyun
1070*4882a593Smuzhiyun goto out;
1071*4882a593Smuzhiyun }
1072*4882a593Smuzhiyun
1073*4882a593Smuzhiyun list_move(&sdata->assigned_chanctx_list, &new_ctx->assigned_vifs);
1074*4882a593Smuzhiyun rcu_assign_pointer(sdata->vif.chanctx_conf, &new_ctx->conf);
1075*4882a593Smuzhiyun
1076*4882a593Smuzhiyun if (sdata->vif.type == NL80211_IFTYPE_AP)
1077*4882a593Smuzhiyun __ieee80211_vif_copy_chanctx_to_vlans(sdata, false);
1078*4882a593Smuzhiyun
1079*4882a593Smuzhiyun ieee80211_check_fast_xmit_iface(sdata);
1080*4882a593Smuzhiyun
1081*4882a593Smuzhiyun if (ieee80211_chanctx_refcount(local, old_ctx) == 0)
1082*4882a593Smuzhiyun ieee80211_free_chanctx(local, old_ctx);
1083*4882a593Smuzhiyun
1084*4882a593Smuzhiyun if (sdata->vif.bss_conf.chandef.width != sdata->reserved_chandef.width)
1085*4882a593Smuzhiyun changed = BSS_CHANGED_BANDWIDTH;
1086*4882a593Smuzhiyun
1087*4882a593Smuzhiyun ieee80211_vif_update_chandef(sdata, &sdata->reserved_chandef);
1088*4882a593Smuzhiyun
1089*4882a593Smuzhiyun ieee80211_recalc_smps_chanctx(local, new_ctx);
1090*4882a593Smuzhiyun ieee80211_recalc_radar_chanctx(local, new_ctx);
1091*4882a593Smuzhiyun ieee80211_recalc_chanctx_min_def(local, new_ctx);
1092*4882a593Smuzhiyun
1093*4882a593Smuzhiyun if (changed)
1094*4882a593Smuzhiyun ieee80211_bss_info_change_notify(sdata, changed);
1095*4882a593Smuzhiyun
1096*4882a593Smuzhiyun out:
1097*4882a593Smuzhiyun ieee80211_vif_chanctx_reservation_complete(sdata);
1098*4882a593Smuzhiyun return err;
1099*4882a593Smuzhiyun }
1100*4882a593Smuzhiyun
1101*4882a593Smuzhiyun static int
ieee80211_vif_use_reserved_assign(struct ieee80211_sub_if_data * sdata)1102*4882a593Smuzhiyun ieee80211_vif_use_reserved_assign(struct ieee80211_sub_if_data *sdata)
1103*4882a593Smuzhiyun {
1104*4882a593Smuzhiyun struct ieee80211_local *local = sdata->local;
1105*4882a593Smuzhiyun struct ieee80211_chanctx *old_ctx, *new_ctx;
1106*4882a593Smuzhiyun const struct cfg80211_chan_def *chandef;
1107*4882a593Smuzhiyun int err;
1108*4882a593Smuzhiyun
1109*4882a593Smuzhiyun old_ctx = ieee80211_vif_get_chanctx(sdata);
1110*4882a593Smuzhiyun new_ctx = sdata->reserved_chanctx;
1111*4882a593Smuzhiyun
1112*4882a593Smuzhiyun if (WARN_ON(!sdata->reserved_ready))
1113*4882a593Smuzhiyun return -EINVAL;
1114*4882a593Smuzhiyun
1115*4882a593Smuzhiyun if (WARN_ON(old_ctx))
1116*4882a593Smuzhiyun return -EINVAL;
1117*4882a593Smuzhiyun
1118*4882a593Smuzhiyun if (WARN_ON(!new_ctx))
1119*4882a593Smuzhiyun return -EINVAL;
1120*4882a593Smuzhiyun
1121*4882a593Smuzhiyun if (WARN_ON(new_ctx->replace_state ==
1122*4882a593Smuzhiyun IEEE80211_CHANCTX_REPLACES_OTHER))
1123*4882a593Smuzhiyun return -EINVAL;
1124*4882a593Smuzhiyun
1125*4882a593Smuzhiyun chandef = ieee80211_chanctx_non_reserved_chandef(local, new_ctx,
1126*4882a593Smuzhiyun &sdata->reserved_chandef);
1127*4882a593Smuzhiyun if (WARN_ON(!chandef))
1128*4882a593Smuzhiyun return -EINVAL;
1129*4882a593Smuzhiyun
1130*4882a593Smuzhiyun ieee80211_change_chanctx(local, new_ctx, chandef);
1131*4882a593Smuzhiyun
1132*4882a593Smuzhiyun list_del(&sdata->reserved_chanctx_list);
1133*4882a593Smuzhiyun sdata->reserved_chanctx = NULL;
1134*4882a593Smuzhiyun
1135*4882a593Smuzhiyun err = ieee80211_assign_vif_chanctx(sdata, new_ctx);
1136*4882a593Smuzhiyun if (err) {
1137*4882a593Smuzhiyun if (ieee80211_chanctx_refcount(local, new_ctx) == 0)
1138*4882a593Smuzhiyun ieee80211_free_chanctx(local, new_ctx);
1139*4882a593Smuzhiyun
1140*4882a593Smuzhiyun goto out;
1141*4882a593Smuzhiyun }
1142*4882a593Smuzhiyun
1143*4882a593Smuzhiyun out:
1144*4882a593Smuzhiyun ieee80211_vif_chanctx_reservation_complete(sdata);
1145*4882a593Smuzhiyun return err;
1146*4882a593Smuzhiyun }
1147*4882a593Smuzhiyun
1148*4882a593Smuzhiyun static bool
ieee80211_vif_has_in_place_reservation(struct ieee80211_sub_if_data * sdata)1149*4882a593Smuzhiyun ieee80211_vif_has_in_place_reservation(struct ieee80211_sub_if_data *sdata)
1150*4882a593Smuzhiyun {
1151*4882a593Smuzhiyun struct ieee80211_chanctx *old_ctx, *new_ctx;
1152*4882a593Smuzhiyun
1153*4882a593Smuzhiyun lockdep_assert_held(&sdata->local->chanctx_mtx);
1154*4882a593Smuzhiyun
1155*4882a593Smuzhiyun new_ctx = sdata->reserved_chanctx;
1156*4882a593Smuzhiyun old_ctx = ieee80211_vif_get_chanctx(sdata);
1157*4882a593Smuzhiyun
1158*4882a593Smuzhiyun if (!old_ctx)
1159*4882a593Smuzhiyun return false;
1160*4882a593Smuzhiyun
1161*4882a593Smuzhiyun if (WARN_ON(!new_ctx))
1162*4882a593Smuzhiyun return false;
1163*4882a593Smuzhiyun
1164*4882a593Smuzhiyun if (old_ctx->replace_state != IEEE80211_CHANCTX_WILL_BE_REPLACED)
1165*4882a593Smuzhiyun return false;
1166*4882a593Smuzhiyun
1167*4882a593Smuzhiyun if (new_ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
1168*4882a593Smuzhiyun return false;
1169*4882a593Smuzhiyun
1170*4882a593Smuzhiyun return true;
1171*4882a593Smuzhiyun }
1172*4882a593Smuzhiyun
ieee80211_chsw_switch_hwconf(struct ieee80211_local * local,struct ieee80211_chanctx * new_ctx)1173*4882a593Smuzhiyun static int ieee80211_chsw_switch_hwconf(struct ieee80211_local *local,
1174*4882a593Smuzhiyun struct ieee80211_chanctx *new_ctx)
1175*4882a593Smuzhiyun {
1176*4882a593Smuzhiyun const struct cfg80211_chan_def *chandef;
1177*4882a593Smuzhiyun
1178*4882a593Smuzhiyun lockdep_assert_held(&local->mtx);
1179*4882a593Smuzhiyun lockdep_assert_held(&local->chanctx_mtx);
1180*4882a593Smuzhiyun
1181*4882a593Smuzhiyun chandef = ieee80211_chanctx_reserved_chandef(local, new_ctx, NULL);
1182*4882a593Smuzhiyun if (WARN_ON(!chandef))
1183*4882a593Smuzhiyun return -EINVAL;
1184*4882a593Smuzhiyun
1185*4882a593Smuzhiyun local->hw.conf.radar_enabled = new_ctx->conf.radar_enabled;
1186*4882a593Smuzhiyun local->_oper_chandef = *chandef;
1187*4882a593Smuzhiyun ieee80211_hw_config(local, 0);
1188*4882a593Smuzhiyun
1189*4882a593Smuzhiyun return 0;
1190*4882a593Smuzhiyun }
1191*4882a593Smuzhiyun
ieee80211_chsw_switch_vifs(struct ieee80211_local * local,int n_vifs)1192*4882a593Smuzhiyun static int ieee80211_chsw_switch_vifs(struct ieee80211_local *local,
1193*4882a593Smuzhiyun int n_vifs)
1194*4882a593Smuzhiyun {
1195*4882a593Smuzhiyun struct ieee80211_vif_chanctx_switch *vif_chsw;
1196*4882a593Smuzhiyun struct ieee80211_sub_if_data *sdata;
1197*4882a593Smuzhiyun struct ieee80211_chanctx *ctx, *old_ctx;
1198*4882a593Smuzhiyun int i, err;
1199*4882a593Smuzhiyun
1200*4882a593Smuzhiyun lockdep_assert_held(&local->mtx);
1201*4882a593Smuzhiyun lockdep_assert_held(&local->chanctx_mtx);
1202*4882a593Smuzhiyun
1203*4882a593Smuzhiyun vif_chsw = kcalloc(n_vifs, sizeof(vif_chsw[0]), GFP_KERNEL);
1204*4882a593Smuzhiyun if (!vif_chsw)
1205*4882a593Smuzhiyun return -ENOMEM;
1206*4882a593Smuzhiyun
1207*4882a593Smuzhiyun i = 0;
1208*4882a593Smuzhiyun list_for_each_entry(ctx, &local->chanctx_list, list) {
1209*4882a593Smuzhiyun if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
1210*4882a593Smuzhiyun continue;
1211*4882a593Smuzhiyun
1212*4882a593Smuzhiyun if (WARN_ON(!ctx->replace_ctx)) {
1213*4882a593Smuzhiyun err = -EINVAL;
1214*4882a593Smuzhiyun goto out;
1215*4882a593Smuzhiyun }
1216*4882a593Smuzhiyun
1217*4882a593Smuzhiyun list_for_each_entry(sdata, &ctx->reserved_vifs,
1218*4882a593Smuzhiyun reserved_chanctx_list) {
1219*4882a593Smuzhiyun if (!ieee80211_vif_has_in_place_reservation(
1220*4882a593Smuzhiyun sdata))
1221*4882a593Smuzhiyun continue;
1222*4882a593Smuzhiyun
1223*4882a593Smuzhiyun old_ctx = ieee80211_vif_get_chanctx(sdata);
1224*4882a593Smuzhiyun vif_chsw[i].vif = &sdata->vif;
1225*4882a593Smuzhiyun vif_chsw[i].old_ctx = &old_ctx->conf;
1226*4882a593Smuzhiyun vif_chsw[i].new_ctx = &ctx->conf;
1227*4882a593Smuzhiyun
1228*4882a593Smuzhiyun i++;
1229*4882a593Smuzhiyun }
1230*4882a593Smuzhiyun }
1231*4882a593Smuzhiyun
1232*4882a593Smuzhiyun err = drv_switch_vif_chanctx(local, vif_chsw, n_vifs,
1233*4882a593Smuzhiyun CHANCTX_SWMODE_SWAP_CONTEXTS);
1234*4882a593Smuzhiyun
1235*4882a593Smuzhiyun out:
1236*4882a593Smuzhiyun kfree(vif_chsw);
1237*4882a593Smuzhiyun return err;
1238*4882a593Smuzhiyun }
1239*4882a593Smuzhiyun
ieee80211_chsw_switch_ctxs(struct ieee80211_local * local)1240*4882a593Smuzhiyun static int ieee80211_chsw_switch_ctxs(struct ieee80211_local *local)
1241*4882a593Smuzhiyun {
1242*4882a593Smuzhiyun struct ieee80211_chanctx *ctx;
1243*4882a593Smuzhiyun int err;
1244*4882a593Smuzhiyun
1245*4882a593Smuzhiyun lockdep_assert_held(&local->mtx);
1246*4882a593Smuzhiyun lockdep_assert_held(&local->chanctx_mtx);
1247*4882a593Smuzhiyun
1248*4882a593Smuzhiyun list_for_each_entry(ctx, &local->chanctx_list, list) {
1249*4882a593Smuzhiyun if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
1250*4882a593Smuzhiyun continue;
1251*4882a593Smuzhiyun
1252*4882a593Smuzhiyun if (!list_empty(&ctx->replace_ctx->assigned_vifs))
1253*4882a593Smuzhiyun continue;
1254*4882a593Smuzhiyun
1255*4882a593Smuzhiyun ieee80211_del_chanctx(local, ctx->replace_ctx);
1256*4882a593Smuzhiyun err = ieee80211_add_chanctx(local, ctx);
1257*4882a593Smuzhiyun if (err)
1258*4882a593Smuzhiyun goto err;
1259*4882a593Smuzhiyun }
1260*4882a593Smuzhiyun
1261*4882a593Smuzhiyun return 0;
1262*4882a593Smuzhiyun
1263*4882a593Smuzhiyun err:
1264*4882a593Smuzhiyun WARN_ON(ieee80211_add_chanctx(local, ctx));
1265*4882a593Smuzhiyun list_for_each_entry_continue_reverse(ctx, &local->chanctx_list, list) {
1266*4882a593Smuzhiyun if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
1267*4882a593Smuzhiyun continue;
1268*4882a593Smuzhiyun
1269*4882a593Smuzhiyun if (!list_empty(&ctx->replace_ctx->assigned_vifs))
1270*4882a593Smuzhiyun continue;
1271*4882a593Smuzhiyun
1272*4882a593Smuzhiyun ieee80211_del_chanctx(local, ctx);
1273*4882a593Smuzhiyun WARN_ON(ieee80211_add_chanctx(local, ctx->replace_ctx));
1274*4882a593Smuzhiyun }
1275*4882a593Smuzhiyun
1276*4882a593Smuzhiyun return err;
1277*4882a593Smuzhiyun }
1278*4882a593Smuzhiyun
ieee80211_vif_use_reserved_switch(struct ieee80211_local * local)1279*4882a593Smuzhiyun static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local)
1280*4882a593Smuzhiyun {
1281*4882a593Smuzhiyun struct ieee80211_sub_if_data *sdata, *sdata_tmp;
1282*4882a593Smuzhiyun struct ieee80211_chanctx *ctx, *ctx_tmp, *old_ctx;
1283*4882a593Smuzhiyun struct ieee80211_chanctx *new_ctx = NULL;
1284*4882a593Smuzhiyun int err, n_assigned, n_reserved, n_ready;
1285*4882a593Smuzhiyun int n_ctx = 0, n_vifs_switch = 0, n_vifs_assign = 0, n_vifs_ctxless = 0;
1286*4882a593Smuzhiyun
1287*4882a593Smuzhiyun lockdep_assert_held(&local->mtx);
1288*4882a593Smuzhiyun lockdep_assert_held(&local->chanctx_mtx);
1289*4882a593Smuzhiyun
1290*4882a593Smuzhiyun /*
1291*4882a593Smuzhiyun * If there are 2 independent pairs of channel contexts performing
1292*4882a593Smuzhiyun * cross-switch of their vifs this code will still wait until both are
1293*4882a593Smuzhiyun * ready even though it could be possible to switch one before the
1294*4882a593Smuzhiyun * other is ready.
1295*4882a593Smuzhiyun *
1296*4882a593Smuzhiyun * For practical reasons and code simplicity just do a single huge
1297*4882a593Smuzhiyun * switch.
1298*4882a593Smuzhiyun */
1299*4882a593Smuzhiyun
1300*4882a593Smuzhiyun /*
1301*4882a593Smuzhiyun * Verify if the reservation is still feasible.
1302*4882a593Smuzhiyun * - if it's not then disconnect
1303*4882a593Smuzhiyun * - if it is but not all vifs necessary are ready then defer
1304*4882a593Smuzhiyun */
1305*4882a593Smuzhiyun
1306*4882a593Smuzhiyun list_for_each_entry(ctx, &local->chanctx_list, list) {
1307*4882a593Smuzhiyun if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
1308*4882a593Smuzhiyun continue;
1309*4882a593Smuzhiyun
1310*4882a593Smuzhiyun if (WARN_ON(!ctx->replace_ctx)) {
1311*4882a593Smuzhiyun err = -EINVAL;
1312*4882a593Smuzhiyun goto err;
1313*4882a593Smuzhiyun }
1314*4882a593Smuzhiyun
1315*4882a593Smuzhiyun if (!local->use_chanctx)
1316*4882a593Smuzhiyun new_ctx = ctx;
1317*4882a593Smuzhiyun
1318*4882a593Smuzhiyun n_ctx++;
1319*4882a593Smuzhiyun
1320*4882a593Smuzhiyun n_assigned = 0;
1321*4882a593Smuzhiyun n_reserved = 0;
1322*4882a593Smuzhiyun n_ready = 0;
1323*4882a593Smuzhiyun
1324*4882a593Smuzhiyun list_for_each_entry(sdata, &ctx->replace_ctx->assigned_vifs,
1325*4882a593Smuzhiyun assigned_chanctx_list) {
1326*4882a593Smuzhiyun n_assigned++;
1327*4882a593Smuzhiyun if (sdata->reserved_chanctx) {
1328*4882a593Smuzhiyun n_reserved++;
1329*4882a593Smuzhiyun if (sdata->reserved_ready)
1330*4882a593Smuzhiyun n_ready++;
1331*4882a593Smuzhiyun }
1332*4882a593Smuzhiyun }
1333*4882a593Smuzhiyun
1334*4882a593Smuzhiyun if (n_assigned != n_reserved) {
1335*4882a593Smuzhiyun if (n_ready == n_reserved) {
1336*4882a593Smuzhiyun wiphy_info(local->hw.wiphy,
1337*4882a593Smuzhiyun "channel context reservation cannot be finalized because some interfaces aren't switching\n");
1338*4882a593Smuzhiyun err = -EBUSY;
1339*4882a593Smuzhiyun goto err;
1340*4882a593Smuzhiyun }
1341*4882a593Smuzhiyun
1342*4882a593Smuzhiyun return -EAGAIN;
1343*4882a593Smuzhiyun }
1344*4882a593Smuzhiyun
1345*4882a593Smuzhiyun ctx->conf.radar_enabled = false;
1346*4882a593Smuzhiyun list_for_each_entry(sdata, &ctx->reserved_vifs,
1347*4882a593Smuzhiyun reserved_chanctx_list) {
1348*4882a593Smuzhiyun if (ieee80211_vif_has_in_place_reservation(sdata) &&
1349*4882a593Smuzhiyun !sdata->reserved_ready)
1350*4882a593Smuzhiyun return -EAGAIN;
1351*4882a593Smuzhiyun
1352*4882a593Smuzhiyun old_ctx = ieee80211_vif_get_chanctx(sdata);
1353*4882a593Smuzhiyun if (old_ctx) {
1354*4882a593Smuzhiyun if (old_ctx->replace_state ==
1355*4882a593Smuzhiyun IEEE80211_CHANCTX_WILL_BE_REPLACED)
1356*4882a593Smuzhiyun n_vifs_switch++;
1357*4882a593Smuzhiyun else
1358*4882a593Smuzhiyun n_vifs_assign++;
1359*4882a593Smuzhiyun } else {
1360*4882a593Smuzhiyun n_vifs_ctxless++;
1361*4882a593Smuzhiyun }
1362*4882a593Smuzhiyun
1363*4882a593Smuzhiyun if (sdata->reserved_radar_required)
1364*4882a593Smuzhiyun ctx->conf.radar_enabled = true;
1365*4882a593Smuzhiyun }
1366*4882a593Smuzhiyun }
1367*4882a593Smuzhiyun
1368*4882a593Smuzhiyun if (WARN_ON(n_ctx == 0) ||
1369*4882a593Smuzhiyun WARN_ON(n_vifs_switch == 0 &&
1370*4882a593Smuzhiyun n_vifs_assign == 0 &&
1371*4882a593Smuzhiyun n_vifs_ctxless == 0) ||
1372*4882a593Smuzhiyun WARN_ON(n_ctx > 1 && !local->use_chanctx) ||
1373*4882a593Smuzhiyun WARN_ON(!new_ctx && !local->use_chanctx)) {
1374*4882a593Smuzhiyun err = -EINVAL;
1375*4882a593Smuzhiyun goto err;
1376*4882a593Smuzhiyun }
1377*4882a593Smuzhiyun
1378*4882a593Smuzhiyun /*
1379*4882a593Smuzhiyun * All necessary vifs are ready. Perform the switch now depending on
1380*4882a593Smuzhiyun * reservations and driver capabilities.
1381*4882a593Smuzhiyun */
1382*4882a593Smuzhiyun
1383*4882a593Smuzhiyun if (local->use_chanctx) {
1384*4882a593Smuzhiyun if (n_vifs_switch > 0) {
1385*4882a593Smuzhiyun err = ieee80211_chsw_switch_vifs(local, n_vifs_switch);
1386*4882a593Smuzhiyun if (err)
1387*4882a593Smuzhiyun goto err;
1388*4882a593Smuzhiyun }
1389*4882a593Smuzhiyun
1390*4882a593Smuzhiyun if (n_vifs_assign > 0 || n_vifs_ctxless > 0) {
1391*4882a593Smuzhiyun err = ieee80211_chsw_switch_ctxs(local);
1392*4882a593Smuzhiyun if (err)
1393*4882a593Smuzhiyun goto err;
1394*4882a593Smuzhiyun }
1395*4882a593Smuzhiyun } else {
1396*4882a593Smuzhiyun err = ieee80211_chsw_switch_hwconf(local, new_ctx);
1397*4882a593Smuzhiyun if (err)
1398*4882a593Smuzhiyun goto err;
1399*4882a593Smuzhiyun }
1400*4882a593Smuzhiyun
1401*4882a593Smuzhiyun /*
1402*4882a593Smuzhiyun * Update all structures, values and pointers to point to new channel
1403*4882a593Smuzhiyun * context(s).
1404*4882a593Smuzhiyun */
1405*4882a593Smuzhiyun list_for_each_entry(ctx, &local->chanctx_list, list) {
1406*4882a593Smuzhiyun if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
1407*4882a593Smuzhiyun continue;
1408*4882a593Smuzhiyun
1409*4882a593Smuzhiyun if (WARN_ON(!ctx->replace_ctx)) {
1410*4882a593Smuzhiyun err = -EINVAL;
1411*4882a593Smuzhiyun goto err;
1412*4882a593Smuzhiyun }
1413*4882a593Smuzhiyun
1414*4882a593Smuzhiyun list_for_each_entry(sdata, &ctx->reserved_vifs,
1415*4882a593Smuzhiyun reserved_chanctx_list) {
1416*4882a593Smuzhiyun u32 changed = 0;
1417*4882a593Smuzhiyun
1418*4882a593Smuzhiyun if (!ieee80211_vif_has_in_place_reservation(sdata))
1419*4882a593Smuzhiyun continue;
1420*4882a593Smuzhiyun
1421*4882a593Smuzhiyun rcu_assign_pointer(sdata->vif.chanctx_conf, &ctx->conf);
1422*4882a593Smuzhiyun
1423*4882a593Smuzhiyun if (sdata->vif.type == NL80211_IFTYPE_AP)
1424*4882a593Smuzhiyun __ieee80211_vif_copy_chanctx_to_vlans(sdata,
1425*4882a593Smuzhiyun false);
1426*4882a593Smuzhiyun
1427*4882a593Smuzhiyun ieee80211_check_fast_xmit_iface(sdata);
1428*4882a593Smuzhiyun
1429*4882a593Smuzhiyun sdata->radar_required = sdata->reserved_radar_required;
1430*4882a593Smuzhiyun
1431*4882a593Smuzhiyun if (sdata->vif.bss_conf.chandef.width !=
1432*4882a593Smuzhiyun sdata->reserved_chandef.width)
1433*4882a593Smuzhiyun changed = BSS_CHANGED_BANDWIDTH;
1434*4882a593Smuzhiyun
1435*4882a593Smuzhiyun ieee80211_vif_update_chandef(sdata, &sdata->reserved_chandef);
1436*4882a593Smuzhiyun if (changed)
1437*4882a593Smuzhiyun ieee80211_bss_info_change_notify(sdata,
1438*4882a593Smuzhiyun changed);
1439*4882a593Smuzhiyun
1440*4882a593Smuzhiyun ieee80211_recalc_txpower(sdata, false);
1441*4882a593Smuzhiyun }
1442*4882a593Smuzhiyun
1443*4882a593Smuzhiyun ieee80211_recalc_chanctx_chantype(local, ctx);
1444*4882a593Smuzhiyun ieee80211_recalc_smps_chanctx(local, ctx);
1445*4882a593Smuzhiyun ieee80211_recalc_radar_chanctx(local, ctx);
1446*4882a593Smuzhiyun ieee80211_recalc_chanctx_min_def(local, ctx);
1447*4882a593Smuzhiyun
1448*4882a593Smuzhiyun list_for_each_entry_safe(sdata, sdata_tmp, &ctx->reserved_vifs,
1449*4882a593Smuzhiyun reserved_chanctx_list) {
1450*4882a593Smuzhiyun if (ieee80211_vif_get_chanctx(sdata) != ctx)
1451*4882a593Smuzhiyun continue;
1452*4882a593Smuzhiyun
1453*4882a593Smuzhiyun list_del(&sdata->reserved_chanctx_list);
1454*4882a593Smuzhiyun list_move(&sdata->assigned_chanctx_list,
1455*4882a593Smuzhiyun &ctx->assigned_vifs);
1456*4882a593Smuzhiyun sdata->reserved_chanctx = NULL;
1457*4882a593Smuzhiyun
1458*4882a593Smuzhiyun ieee80211_vif_chanctx_reservation_complete(sdata);
1459*4882a593Smuzhiyun }
1460*4882a593Smuzhiyun
1461*4882a593Smuzhiyun /*
1462*4882a593Smuzhiyun * This context might have been a dependency for an already
1463*4882a593Smuzhiyun * ready re-assign reservation interface that was deferred. Do
1464*4882a593Smuzhiyun * not propagate error to the caller though. The in-place
1465*4882a593Smuzhiyun * reservation for originally requested interface has already
1466*4882a593Smuzhiyun * succeeded at this point.
1467*4882a593Smuzhiyun */
1468*4882a593Smuzhiyun list_for_each_entry_safe(sdata, sdata_tmp, &ctx->reserved_vifs,
1469*4882a593Smuzhiyun reserved_chanctx_list) {
1470*4882a593Smuzhiyun if (WARN_ON(ieee80211_vif_has_in_place_reservation(
1471*4882a593Smuzhiyun sdata)))
1472*4882a593Smuzhiyun continue;
1473*4882a593Smuzhiyun
1474*4882a593Smuzhiyun if (WARN_ON(sdata->reserved_chanctx != ctx))
1475*4882a593Smuzhiyun continue;
1476*4882a593Smuzhiyun
1477*4882a593Smuzhiyun if (!sdata->reserved_ready)
1478*4882a593Smuzhiyun continue;
1479*4882a593Smuzhiyun
1480*4882a593Smuzhiyun if (ieee80211_vif_get_chanctx(sdata))
1481*4882a593Smuzhiyun err = ieee80211_vif_use_reserved_reassign(
1482*4882a593Smuzhiyun sdata);
1483*4882a593Smuzhiyun else
1484*4882a593Smuzhiyun err = ieee80211_vif_use_reserved_assign(sdata);
1485*4882a593Smuzhiyun
1486*4882a593Smuzhiyun if (err) {
1487*4882a593Smuzhiyun sdata_info(sdata,
1488*4882a593Smuzhiyun "failed to finalize (re-)assign reservation (err=%d)\n",
1489*4882a593Smuzhiyun err);
1490*4882a593Smuzhiyun ieee80211_vif_unreserve_chanctx(sdata);
1491*4882a593Smuzhiyun cfg80211_stop_iface(local->hw.wiphy,
1492*4882a593Smuzhiyun &sdata->wdev,
1493*4882a593Smuzhiyun GFP_KERNEL);
1494*4882a593Smuzhiyun }
1495*4882a593Smuzhiyun }
1496*4882a593Smuzhiyun }
1497*4882a593Smuzhiyun
1498*4882a593Smuzhiyun /*
1499*4882a593Smuzhiyun * Finally free old contexts
1500*4882a593Smuzhiyun */
1501*4882a593Smuzhiyun
1502*4882a593Smuzhiyun list_for_each_entry_safe(ctx, ctx_tmp, &local->chanctx_list, list) {
1503*4882a593Smuzhiyun if (ctx->replace_state != IEEE80211_CHANCTX_WILL_BE_REPLACED)
1504*4882a593Smuzhiyun continue;
1505*4882a593Smuzhiyun
1506*4882a593Smuzhiyun ctx->replace_ctx->replace_ctx = NULL;
1507*4882a593Smuzhiyun ctx->replace_ctx->replace_state =
1508*4882a593Smuzhiyun IEEE80211_CHANCTX_REPLACE_NONE;
1509*4882a593Smuzhiyun
1510*4882a593Smuzhiyun list_del_rcu(&ctx->list);
1511*4882a593Smuzhiyun kfree_rcu(ctx, rcu_head);
1512*4882a593Smuzhiyun }
1513*4882a593Smuzhiyun
1514*4882a593Smuzhiyun return 0;
1515*4882a593Smuzhiyun
1516*4882a593Smuzhiyun err:
1517*4882a593Smuzhiyun list_for_each_entry(ctx, &local->chanctx_list, list) {
1518*4882a593Smuzhiyun if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
1519*4882a593Smuzhiyun continue;
1520*4882a593Smuzhiyun
1521*4882a593Smuzhiyun list_for_each_entry_safe(sdata, sdata_tmp, &ctx->reserved_vifs,
1522*4882a593Smuzhiyun reserved_chanctx_list) {
1523*4882a593Smuzhiyun ieee80211_vif_unreserve_chanctx(sdata);
1524*4882a593Smuzhiyun ieee80211_vif_chanctx_reservation_complete(sdata);
1525*4882a593Smuzhiyun }
1526*4882a593Smuzhiyun }
1527*4882a593Smuzhiyun
1528*4882a593Smuzhiyun return err;
1529*4882a593Smuzhiyun }
1530*4882a593Smuzhiyun
__ieee80211_vif_release_channel(struct ieee80211_sub_if_data * sdata)1531*4882a593Smuzhiyun static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)
1532*4882a593Smuzhiyun {
1533*4882a593Smuzhiyun struct ieee80211_local *local = sdata->local;
1534*4882a593Smuzhiyun struct ieee80211_chanctx_conf *conf;
1535*4882a593Smuzhiyun struct ieee80211_chanctx *ctx;
1536*4882a593Smuzhiyun bool use_reserved_switch = false;
1537*4882a593Smuzhiyun
1538*4882a593Smuzhiyun lockdep_assert_held(&local->chanctx_mtx);
1539*4882a593Smuzhiyun
1540*4882a593Smuzhiyun conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
1541*4882a593Smuzhiyun lockdep_is_held(&local->chanctx_mtx));
1542*4882a593Smuzhiyun if (!conf)
1543*4882a593Smuzhiyun return;
1544*4882a593Smuzhiyun
1545*4882a593Smuzhiyun ctx = container_of(conf, struct ieee80211_chanctx, conf);
1546*4882a593Smuzhiyun
1547*4882a593Smuzhiyun if (sdata->reserved_chanctx) {
1548*4882a593Smuzhiyun if (sdata->reserved_chanctx->replace_state ==
1549*4882a593Smuzhiyun IEEE80211_CHANCTX_REPLACES_OTHER &&
1550*4882a593Smuzhiyun ieee80211_chanctx_num_reserved(local,
1551*4882a593Smuzhiyun sdata->reserved_chanctx) > 1)
1552*4882a593Smuzhiyun use_reserved_switch = true;
1553*4882a593Smuzhiyun
1554*4882a593Smuzhiyun ieee80211_vif_unreserve_chanctx(sdata);
1555*4882a593Smuzhiyun }
1556*4882a593Smuzhiyun
1557*4882a593Smuzhiyun ieee80211_assign_vif_chanctx(sdata, NULL);
1558*4882a593Smuzhiyun if (ieee80211_chanctx_refcount(local, ctx) == 0)
1559*4882a593Smuzhiyun ieee80211_free_chanctx(local, ctx);
1560*4882a593Smuzhiyun
1561*4882a593Smuzhiyun sdata->radar_required = false;
1562*4882a593Smuzhiyun
1563*4882a593Smuzhiyun /* Unreserving may ready an in-place reservation. */
1564*4882a593Smuzhiyun if (use_reserved_switch)
1565*4882a593Smuzhiyun ieee80211_vif_use_reserved_switch(local);
1566*4882a593Smuzhiyun }
1567*4882a593Smuzhiyun
ieee80211_vif_use_channel(struct ieee80211_sub_if_data * sdata,const struct cfg80211_chan_def * chandef,enum ieee80211_chanctx_mode mode)1568*4882a593Smuzhiyun int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,
1569*4882a593Smuzhiyun const struct cfg80211_chan_def *chandef,
1570*4882a593Smuzhiyun enum ieee80211_chanctx_mode mode)
1571*4882a593Smuzhiyun {
1572*4882a593Smuzhiyun struct ieee80211_local *local = sdata->local;
1573*4882a593Smuzhiyun struct ieee80211_chanctx *ctx;
1574*4882a593Smuzhiyun u8 radar_detect_width = 0;
1575*4882a593Smuzhiyun int ret;
1576*4882a593Smuzhiyun
1577*4882a593Smuzhiyun lockdep_assert_held(&local->mtx);
1578*4882a593Smuzhiyun
1579*4882a593Smuzhiyun WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev));
1580*4882a593Smuzhiyun
1581*4882a593Smuzhiyun mutex_lock(&local->chanctx_mtx);
1582*4882a593Smuzhiyun
1583*4882a593Smuzhiyun ret = cfg80211_chandef_dfs_required(local->hw.wiphy,
1584*4882a593Smuzhiyun chandef,
1585*4882a593Smuzhiyun sdata->wdev.iftype);
1586*4882a593Smuzhiyun if (ret < 0)
1587*4882a593Smuzhiyun goto out;
1588*4882a593Smuzhiyun if (ret > 0)
1589*4882a593Smuzhiyun radar_detect_width = BIT(chandef->width);
1590*4882a593Smuzhiyun
1591*4882a593Smuzhiyun sdata->radar_required = ret;
1592*4882a593Smuzhiyun
1593*4882a593Smuzhiyun ret = ieee80211_check_combinations(sdata, chandef, mode,
1594*4882a593Smuzhiyun radar_detect_width);
1595*4882a593Smuzhiyun if (ret < 0)
1596*4882a593Smuzhiyun goto out;
1597*4882a593Smuzhiyun
1598*4882a593Smuzhiyun __ieee80211_vif_release_channel(sdata);
1599*4882a593Smuzhiyun
1600*4882a593Smuzhiyun ctx = ieee80211_find_chanctx(local, chandef, mode);
1601*4882a593Smuzhiyun if (!ctx)
1602*4882a593Smuzhiyun ctx = ieee80211_new_chanctx(local, chandef, mode);
1603*4882a593Smuzhiyun if (IS_ERR(ctx)) {
1604*4882a593Smuzhiyun ret = PTR_ERR(ctx);
1605*4882a593Smuzhiyun goto out;
1606*4882a593Smuzhiyun }
1607*4882a593Smuzhiyun
1608*4882a593Smuzhiyun ieee80211_vif_update_chandef(sdata, chandef);
1609*4882a593Smuzhiyun
1610*4882a593Smuzhiyun ret = ieee80211_assign_vif_chanctx(sdata, ctx);
1611*4882a593Smuzhiyun if (ret) {
1612*4882a593Smuzhiyun /* if assign fails refcount stays the same */
1613*4882a593Smuzhiyun if (ieee80211_chanctx_refcount(local, ctx) == 0)
1614*4882a593Smuzhiyun ieee80211_free_chanctx(local, ctx);
1615*4882a593Smuzhiyun goto out;
1616*4882a593Smuzhiyun }
1617*4882a593Smuzhiyun
1618*4882a593Smuzhiyun ieee80211_recalc_smps_chanctx(local, ctx);
1619*4882a593Smuzhiyun ieee80211_recalc_radar_chanctx(local, ctx);
1620*4882a593Smuzhiyun out:
1621*4882a593Smuzhiyun if (ret)
1622*4882a593Smuzhiyun sdata->radar_required = false;
1623*4882a593Smuzhiyun
1624*4882a593Smuzhiyun mutex_unlock(&local->chanctx_mtx);
1625*4882a593Smuzhiyun return ret;
1626*4882a593Smuzhiyun }
1627*4882a593Smuzhiyun
ieee80211_vif_use_reserved_context(struct ieee80211_sub_if_data * sdata)1628*4882a593Smuzhiyun int ieee80211_vif_use_reserved_context(struct ieee80211_sub_if_data *sdata)
1629*4882a593Smuzhiyun {
1630*4882a593Smuzhiyun struct ieee80211_local *local = sdata->local;
1631*4882a593Smuzhiyun struct ieee80211_chanctx *new_ctx;
1632*4882a593Smuzhiyun struct ieee80211_chanctx *old_ctx;
1633*4882a593Smuzhiyun int err;
1634*4882a593Smuzhiyun
1635*4882a593Smuzhiyun lockdep_assert_held(&local->mtx);
1636*4882a593Smuzhiyun lockdep_assert_held(&local->chanctx_mtx);
1637*4882a593Smuzhiyun
1638*4882a593Smuzhiyun new_ctx = sdata->reserved_chanctx;
1639*4882a593Smuzhiyun old_ctx = ieee80211_vif_get_chanctx(sdata);
1640*4882a593Smuzhiyun
1641*4882a593Smuzhiyun if (WARN_ON(!new_ctx))
1642*4882a593Smuzhiyun return -EINVAL;
1643*4882a593Smuzhiyun
1644*4882a593Smuzhiyun if (WARN_ON(new_ctx->replace_state ==
1645*4882a593Smuzhiyun IEEE80211_CHANCTX_WILL_BE_REPLACED))
1646*4882a593Smuzhiyun return -EINVAL;
1647*4882a593Smuzhiyun
1648*4882a593Smuzhiyun if (WARN_ON(sdata->reserved_ready))
1649*4882a593Smuzhiyun return -EINVAL;
1650*4882a593Smuzhiyun
1651*4882a593Smuzhiyun sdata->reserved_ready = true;
1652*4882a593Smuzhiyun
1653*4882a593Smuzhiyun if (new_ctx->replace_state == IEEE80211_CHANCTX_REPLACE_NONE) {
1654*4882a593Smuzhiyun if (old_ctx)
1655*4882a593Smuzhiyun return ieee80211_vif_use_reserved_reassign(sdata);
1656*4882a593Smuzhiyun
1657*4882a593Smuzhiyun return ieee80211_vif_use_reserved_assign(sdata);
1658*4882a593Smuzhiyun }
1659*4882a593Smuzhiyun
1660*4882a593Smuzhiyun /*
1661*4882a593Smuzhiyun * In-place reservation may need to be finalized now either if:
1662*4882a593Smuzhiyun * a) sdata is taking part in the swapping itself and is the last one
1663*4882a593Smuzhiyun * b) sdata has switched with a re-assign reservation to an existing
1664*4882a593Smuzhiyun * context readying in-place switching of old_ctx
1665*4882a593Smuzhiyun *
1666*4882a593Smuzhiyun * In case of (b) do not propagate the error up because the requested
1667*4882a593Smuzhiyun * sdata already switched successfully. Just spill an extra warning.
1668*4882a593Smuzhiyun * The ieee80211_vif_use_reserved_switch() already stops all necessary
1669*4882a593Smuzhiyun * interfaces upon failure.
1670*4882a593Smuzhiyun */
1671*4882a593Smuzhiyun if ((old_ctx &&
1672*4882a593Smuzhiyun old_ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED) ||
1673*4882a593Smuzhiyun new_ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER) {
1674*4882a593Smuzhiyun err = ieee80211_vif_use_reserved_switch(local);
1675*4882a593Smuzhiyun if (err && err != -EAGAIN) {
1676*4882a593Smuzhiyun if (new_ctx->replace_state ==
1677*4882a593Smuzhiyun IEEE80211_CHANCTX_REPLACES_OTHER)
1678*4882a593Smuzhiyun return err;
1679*4882a593Smuzhiyun
1680*4882a593Smuzhiyun wiphy_info(local->hw.wiphy,
1681*4882a593Smuzhiyun "depending in-place reservation failed (err=%d)\n",
1682*4882a593Smuzhiyun err);
1683*4882a593Smuzhiyun }
1684*4882a593Smuzhiyun }
1685*4882a593Smuzhiyun
1686*4882a593Smuzhiyun return 0;
1687*4882a593Smuzhiyun }
1688*4882a593Smuzhiyun
ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data * sdata,const struct cfg80211_chan_def * chandef,u32 * changed)1689*4882a593Smuzhiyun int ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata,
1690*4882a593Smuzhiyun const struct cfg80211_chan_def *chandef,
1691*4882a593Smuzhiyun u32 *changed)
1692*4882a593Smuzhiyun {
1693*4882a593Smuzhiyun struct ieee80211_local *local = sdata->local;
1694*4882a593Smuzhiyun struct ieee80211_chanctx_conf *conf;
1695*4882a593Smuzhiyun struct ieee80211_chanctx *ctx;
1696*4882a593Smuzhiyun const struct cfg80211_chan_def *compat;
1697*4882a593Smuzhiyun int ret;
1698*4882a593Smuzhiyun
1699*4882a593Smuzhiyun if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef,
1700*4882a593Smuzhiyun IEEE80211_CHAN_DISABLED))
1701*4882a593Smuzhiyun return -EINVAL;
1702*4882a593Smuzhiyun
1703*4882a593Smuzhiyun mutex_lock(&local->chanctx_mtx);
1704*4882a593Smuzhiyun if (cfg80211_chandef_identical(chandef, &sdata->vif.bss_conf.chandef)) {
1705*4882a593Smuzhiyun ret = 0;
1706*4882a593Smuzhiyun goto out;
1707*4882a593Smuzhiyun }
1708*4882a593Smuzhiyun
1709*4882a593Smuzhiyun if (chandef->width == NL80211_CHAN_WIDTH_20_NOHT ||
1710*4882a593Smuzhiyun sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT) {
1711*4882a593Smuzhiyun ret = -EINVAL;
1712*4882a593Smuzhiyun goto out;
1713*4882a593Smuzhiyun }
1714*4882a593Smuzhiyun
1715*4882a593Smuzhiyun conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
1716*4882a593Smuzhiyun lockdep_is_held(&local->chanctx_mtx));
1717*4882a593Smuzhiyun if (!conf) {
1718*4882a593Smuzhiyun ret = -EINVAL;
1719*4882a593Smuzhiyun goto out;
1720*4882a593Smuzhiyun }
1721*4882a593Smuzhiyun
1722*4882a593Smuzhiyun ctx = container_of(conf, struct ieee80211_chanctx, conf);
1723*4882a593Smuzhiyun
1724*4882a593Smuzhiyun compat = cfg80211_chandef_compatible(&conf->def, chandef);
1725*4882a593Smuzhiyun if (!compat) {
1726*4882a593Smuzhiyun ret = -EINVAL;
1727*4882a593Smuzhiyun goto out;
1728*4882a593Smuzhiyun }
1729*4882a593Smuzhiyun
1730*4882a593Smuzhiyun switch (ctx->replace_state) {
1731*4882a593Smuzhiyun case IEEE80211_CHANCTX_REPLACE_NONE:
1732*4882a593Smuzhiyun if (!ieee80211_chanctx_reserved_chandef(local, ctx, compat)) {
1733*4882a593Smuzhiyun ret = -EBUSY;
1734*4882a593Smuzhiyun goto out;
1735*4882a593Smuzhiyun }
1736*4882a593Smuzhiyun break;
1737*4882a593Smuzhiyun case IEEE80211_CHANCTX_WILL_BE_REPLACED:
1738*4882a593Smuzhiyun /* TODO: Perhaps the bandwidth change could be treated as a
1739*4882a593Smuzhiyun * reservation itself? */
1740*4882a593Smuzhiyun ret = -EBUSY;
1741*4882a593Smuzhiyun goto out;
1742*4882a593Smuzhiyun case IEEE80211_CHANCTX_REPLACES_OTHER:
1743*4882a593Smuzhiyun /* channel context that is going to replace another channel
1744*4882a593Smuzhiyun * context doesn't really exist and shouldn't be assigned
1745*4882a593Smuzhiyun * anywhere yet */
1746*4882a593Smuzhiyun WARN_ON(1);
1747*4882a593Smuzhiyun break;
1748*4882a593Smuzhiyun }
1749*4882a593Smuzhiyun
1750*4882a593Smuzhiyun ieee80211_vif_update_chandef(sdata, chandef);
1751*4882a593Smuzhiyun
1752*4882a593Smuzhiyun ieee80211_recalc_chanctx_chantype(local, ctx);
1753*4882a593Smuzhiyun
1754*4882a593Smuzhiyun *changed |= BSS_CHANGED_BANDWIDTH;
1755*4882a593Smuzhiyun ret = 0;
1756*4882a593Smuzhiyun out:
1757*4882a593Smuzhiyun mutex_unlock(&local->chanctx_mtx);
1758*4882a593Smuzhiyun return ret;
1759*4882a593Smuzhiyun }
1760*4882a593Smuzhiyun
ieee80211_vif_release_channel(struct ieee80211_sub_if_data * sdata)1761*4882a593Smuzhiyun void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)
1762*4882a593Smuzhiyun {
1763*4882a593Smuzhiyun WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev));
1764*4882a593Smuzhiyun
1765*4882a593Smuzhiyun lockdep_assert_held(&sdata->local->mtx);
1766*4882a593Smuzhiyun
1767*4882a593Smuzhiyun mutex_lock(&sdata->local->chanctx_mtx);
1768*4882a593Smuzhiyun __ieee80211_vif_release_channel(sdata);
1769*4882a593Smuzhiyun mutex_unlock(&sdata->local->chanctx_mtx);
1770*4882a593Smuzhiyun }
1771*4882a593Smuzhiyun
ieee80211_vif_vlan_copy_chanctx(struct ieee80211_sub_if_data * sdata)1772*4882a593Smuzhiyun void ieee80211_vif_vlan_copy_chanctx(struct ieee80211_sub_if_data *sdata)
1773*4882a593Smuzhiyun {
1774*4882a593Smuzhiyun struct ieee80211_local *local = sdata->local;
1775*4882a593Smuzhiyun struct ieee80211_sub_if_data *ap;
1776*4882a593Smuzhiyun struct ieee80211_chanctx_conf *conf;
1777*4882a593Smuzhiyun
1778*4882a593Smuzhiyun if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP_VLAN || !sdata->bss))
1779*4882a593Smuzhiyun return;
1780*4882a593Smuzhiyun
1781*4882a593Smuzhiyun ap = container_of(sdata->bss, struct ieee80211_sub_if_data, u.ap);
1782*4882a593Smuzhiyun
1783*4882a593Smuzhiyun mutex_lock(&local->chanctx_mtx);
1784*4882a593Smuzhiyun
1785*4882a593Smuzhiyun conf = rcu_dereference_protected(ap->vif.chanctx_conf,
1786*4882a593Smuzhiyun lockdep_is_held(&local->chanctx_mtx));
1787*4882a593Smuzhiyun rcu_assign_pointer(sdata->vif.chanctx_conf, conf);
1788*4882a593Smuzhiyun mutex_unlock(&local->chanctx_mtx);
1789*4882a593Smuzhiyun }
1790*4882a593Smuzhiyun
ieee80211_iter_chan_contexts_atomic(struct ieee80211_hw * hw,void (* iter)(struct ieee80211_hw * hw,struct ieee80211_chanctx_conf * chanctx_conf,void * data),void * iter_data)1791*4882a593Smuzhiyun void ieee80211_iter_chan_contexts_atomic(
1792*4882a593Smuzhiyun struct ieee80211_hw *hw,
1793*4882a593Smuzhiyun void (*iter)(struct ieee80211_hw *hw,
1794*4882a593Smuzhiyun struct ieee80211_chanctx_conf *chanctx_conf,
1795*4882a593Smuzhiyun void *data),
1796*4882a593Smuzhiyun void *iter_data)
1797*4882a593Smuzhiyun {
1798*4882a593Smuzhiyun struct ieee80211_local *local = hw_to_local(hw);
1799*4882a593Smuzhiyun struct ieee80211_chanctx *ctx;
1800*4882a593Smuzhiyun
1801*4882a593Smuzhiyun rcu_read_lock();
1802*4882a593Smuzhiyun list_for_each_entry_rcu(ctx, &local->chanctx_list, list)
1803*4882a593Smuzhiyun if (ctx->driver_present)
1804*4882a593Smuzhiyun iter(hw, &ctx->conf, iter_data);
1805*4882a593Smuzhiyun rcu_read_unlock();
1806*4882a593Smuzhiyun }
1807*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(ieee80211_iter_chan_contexts_atomic);
1808