~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/net/mac80211/chan.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

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

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php