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