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

source code of linux/net/mac80211/chan.c