1457c8996SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2f444de05SJohannes Berg /* 3f444de05SJohannes Berg * mac80211 - channel management 4d0a9123eSJohannes Berg * Copyright 2020 - 2022 Intel Corporation 5f444de05SJohannes Berg */ 6f444de05SJohannes Berg 70aaffa9bSJohannes Berg #include <linux/nl80211.h> 83448c005SJohannes Berg #include <linux/export.h> 94d76d21bSJohannes Berg #include <linux/rtnetlink.h> 103117bbdbSPaul Stewart #include <net/cfg80211.h> 11f444de05SJohannes Berg #include "ieee80211_i.h" 1235f2fce9SMichal Kazior #include "driver-ops.h" 1344b72ca8SIlan Peer #include "rate.h" 14f444de05SJohannes Berg 15c0166da9SMichal Kazior static int ieee80211_chanctx_num_assigned(struct ieee80211_local *local, 16c0166da9SMichal Kazior struct ieee80211_chanctx *ctx) 17c0166da9SMichal Kazior { 18b4f85443SJohannes Berg struct ieee80211_link_data *link; 19c0166da9SMichal Kazior int num = 0; 20c0166da9SMichal Kazior 21c0166da9SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 22c0166da9SMichal Kazior 23b4f85443SJohannes Berg list_for_each_entry(link, &ctx->assigned_links, assigned_chanctx_list) 24c0166da9SMichal Kazior num++; 25c0166da9SMichal Kazior 26c0166da9SMichal Kazior return num; 27c0166da9SMichal Kazior } 28c0166da9SMichal Kazior 29c0166da9SMichal Kazior static int ieee80211_chanctx_num_reserved(struct ieee80211_local *local, 30c0166da9SMichal Kazior struct ieee80211_chanctx *ctx) 31c0166da9SMichal Kazior { 32b4f85443SJohannes Berg struct ieee80211_link_data *link; 33c0166da9SMichal Kazior int num = 0; 34c0166da9SMichal Kazior 35c0166da9SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 36c0166da9SMichal Kazior 37b4f85443SJohannes Berg list_for_each_entry(link, &ctx->reserved_links, reserved_chanctx_list) 38c0166da9SMichal Kazior num++; 39c0166da9SMichal Kazior 40c0166da9SMichal Kazior return num; 41c0166da9SMichal Kazior } 42c0166da9SMichal Kazior 43c0166da9SMichal Kazior int ieee80211_chanctx_refcount(struct ieee80211_local *local, 44c0166da9SMichal Kazior struct ieee80211_chanctx *ctx) 45c0166da9SMichal Kazior { 46c0166da9SMichal Kazior return ieee80211_chanctx_num_assigned(local, ctx) + 47c0166da9SMichal Kazior ieee80211_chanctx_num_reserved(local, ctx); 48c0166da9SMichal Kazior } 49c0166da9SMichal Kazior 50c2b90ad8SMichal Kazior static int ieee80211_num_chanctx(struct ieee80211_local *local) 51c2b90ad8SMichal Kazior { 52c2b90ad8SMichal Kazior struct ieee80211_chanctx *ctx; 53c2b90ad8SMichal Kazior int num = 0; 54c2b90ad8SMichal Kazior 55c2b90ad8SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 56c2b90ad8SMichal Kazior 57c2b90ad8SMichal Kazior list_for_each_entry(ctx, &local->chanctx_list, list) 58c2b90ad8SMichal Kazior num++; 59c2b90ad8SMichal Kazior 60c2b90ad8SMichal Kazior return num; 61c2b90ad8SMichal Kazior } 62c2b90ad8SMichal Kazior 63c2b90ad8SMichal Kazior static bool ieee80211_can_create_new_chanctx(struct ieee80211_local *local) 64c2b90ad8SMichal Kazior { 65c2b90ad8SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 66c2b90ad8SMichal Kazior return ieee80211_num_chanctx(local) < ieee80211_max_num_channels(local); 67c2b90ad8SMichal Kazior } 68c2b90ad8SMichal Kazior 695bcae31dSMichal Kazior static struct ieee80211_chanctx * 70d8675a63SJohannes Berg ieee80211_link_get_chanctx(struct ieee80211_link_data *link) 715bcae31dSMichal Kazior { 72d8675a63SJohannes Berg struct ieee80211_local *local __maybe_unused = link->sdata->local; 735bcae31dSMichal Kazior struct ieee80211_chanctx_conf *conf; 745bcae31dSMichal Kazior 75d8675a63SJohannes Berg conf = rcu_dereference_protected(link->conf->chanctx_conf, 765bcae31dSMichal Kazior lockdep_is_held(&local->chanctx_mtx)); 775bcae31dSMichal Kazior if (!conf) 785bcae31dSMichal Kazior return NULL; 795bcae31dSMichal Kazior 805bcae31dSMichal Kazior return container_of(conf, struct ieee80211_chanctx, conf); 815bcae31dSMichal Kazior } 825bcae31dSMichal Kazior 830288157bSMichal Kazior static const struct cfg80211_chan_def * 840288157bSMichal Kazior ieee80211_chanctx_reserved_chandef(struct ieee80211_local *local, 850288157bSMichal Kazior struct ieee80211_chanctx *ctx, 860288157bSMichal Kazior const struct cfg80211_chan_def *compat) 870288157bSMichal Kazior { 88b4f85443SJohannes Berg struct ieee80211_link_data *link; 890288157bSMichal Kazior 900288157bSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 910288157bSMichal Kazior 92b4f85443SJohannes Berg list_for_each_entry(link, &ctx->reserved_links, 930288157bSMichal Kazior reserved_chanctx_list) { 940288157bSMichal Kazior if (!compat) 95b4f85443SJohannes Berg compat = &link->reserved_chandef; 960288157bSMichal Kazior 97b4f85443SJohannes Berg compat = cfg80211_chandef_compatible(&link->reserved_chandef, 980288157bSMichal Kazior compat); 990288157bSMichal Kazior if (!compat) 1000288157bSMichal Kazior break; 1010288157bSMichal Kazior } 1020288157bSMichal Kazior 1030288157bSMichal Kazior return compat; 1040288157bSMichal Kazior } 1050288157bSMichal Kazior 10613f348a8SMichal Kazior static const struct cfg80211_chan_def * 10713f348a8SMichal Kazior ieee80211_chanctx_non_reserved_chandef(struct ieee80211_local *local, 10813f348a8SMichal Kazior struct ieee80211_chanctx *ctx, 10913f348a8SMichal Kazior const struct cfg80211_chan_def *compat) 11013f348a8SMichal Kazior { 111b4f85443SJohannes Berg struct ieee80211_link_data *link; 11213f348a8SMichal Kazior 11313f348a8SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 11413f348a8SMichal Kazior 115b4f85443SJohannes Berg list_for_each_entry(link, &ctx->assigned_links, 11613f348a8SMichal Kazior assigned_chanctx_list) { 117d8675a63SJohannes Berg struct ieee80211_bss_conf *link_conf = link->conf; 118b4f85443SJohannes Berg 119b4f85443SJohannes Berg if (link->reserved_chanctx) 12013f348a8SMichal Kazior continue; 12113f348a8SMichal Kazior 12213f348a8SMichal Kazior if (!compat) 123b4f85443SJohannes Berg compat = &link_conf->chandef; 12413f348a8SMichal Kazior 12513f348a8SMichal Kazior compat = cfg80211_chandef_compatible( 126b4f85443SJohannes Berg &link_conf->chandef, compat); 12713f348a8SMichal Kazior if (!compat) 12813f348a8SMichal Kazior break; 12913f348a8SMichal Kazior } 13013f348a8SMichal Kazior 13113f348a8SMichal Kazior return compat; 13213f348a8SMichal Kazior } 13313f348a8SMichal Kazior 13413f348a8SMichal Kazior static const struct cfg80211_chan_def * 13513f348a8SMichal Kazior ieee80211_chanctx_combined_chandef(struct ieee80211_local *local, 13613f348a8SMichal Kazior struct ieee80211_chanctx *ctx, 13713f348a8SMichal Kazior const struct cfg80211_chan_def *compat) 13813f348a8SMichal Kazior { 13913f348a8SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 14013f348a8SMichal Kazior 14113f348a8SMichal Kazior compat = ieee80211_chanctx_reserved_chandef(local, ctx, compat); 14213f348a8SMichal Kazior if (!compat) 14313f348a8SMichal Kazior return NULL; 14413f348a8SMichal Kazior 14513f348a8SMichal Kazior compat = ieee80211_chanctx_non_reserved_chandef(local, ctx, compat); 14613f348a8SMichal Kazior if (!compat) 14713f348a8SMichal Kazior return NULL; 14813f348a8SMichal Kazior 14913f348a8SMichal Kazior return compat; 15013f348a8SMichal Kazior } 15113f348a8SMichal Kazior 15213f348a8SMichal Kazior static bool 15313f348a8SMichal Kazior ieee80211_chanctx_can_reserve_chandef(struct ieee80211_local *local, 15413f348a8SMichal Kazior struct ieee80211_chanctx *ctx, 15513f348a8SMichal Kazior const struct cfg80211_chan_def *def) 15613f348a8SMichal Kazior { 15713f348a8SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 15813f348a8SMichal Kazior 15913f348a8SMichal Kazior if (ieee80211_chanctx_combined_chandef(local, ctx, def)) 16013f348a8SMichal Kazior return true; 16113f348a8SMichal Kazior 162b4f85443SJohannes Berg if (!list_empty(&ctx->reserved_links) && 16313f348a8SMichal Kazior ieee80211_chanctx_reserved_chandef(local, ctx, def)) 16413f348a8SMichal Kazior return true; 16513f348a8SMichal Kazior 16613f348a8SMichal Kazior return false; 16713f348a8SMichal Kazior } 16813f348a8SMichal Kazior 16913f348a8SMichal Kazior static struct ieee80211_chanctx * 17013f348a8SMichal Kazior ieee80211_find_reservation_chanctx(struct ieee80211_local *local, 17113f348a8SMichal Kazior const struct cfg80211_chan_def *chandef, 17213f348a8SMichal Kazior enum ieee80211_chanctx_mode mode) 17313f348a8SMichal Kazior { 17413f348a8SMichal Kazior struct ieee80211_chanctx *ctx; 17513f348a8SMichal Kazior 17613f348a8SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 17713f348a8SMichal Kazior 17813f348a8SMichal Kazior if (mode == IEEE80211_CHANCTX_EXCLUSIVE) 17913f348a8SMichal Kazior return NULL; 18013f348a8SMichal Kazior 18113f348a8SMichal Kazior list_for_each_entry(ctx, &local->chanctx_list, list) { 1825bcae31dSMichal Kazior if (ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED) 1835bcae31dSMichal Kazior continue; 1845bcae31dSMichal Kazior 18513f348a8SMichal Kazior if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE) 18613f348a8SMichal Kazior continue; 18713f348a8SMichal Kazior 18813f348a8SMichal Kazior if (!ieee80211_chanctx_can_reserve_chandef(local, ctx, 18913f348a8SMichal Kazior chandef)) 19013f348a8SMichal Kazior continue; 19113f348a8SMichal Kazior 19213f348a8SMichal Kazior return ctx; 19313f348a8SMichal Kazior } 19413f348a8SMichal Kazior 19513f348a8SMichal Kazior return NULL; 19613f348a8SMichal Kazior } 19713f348a8SMichal Kazior 198b4f85443SJohannes Berg static enum nl80211_chan_width ieee80211_get_sta_bw(struct sta_info *sta, 199b4f85443SJohannes Berg unsigned int link_id) 20021f659bfSEliad Peller { 201c71420dbSJohannes Berg enum ieee80211_sta_rx_bandwidth width; 202c71420dbSJohannes Berg struct link_sta_info *link_sta; 203c71420dbSJohannes Berg 204c71420dbSJohannes Berg link_sta = rcu_dereference(sta->link[link_id]); 205b4f85443SJohannes Berg 206b4f85443SJohannes Berg /* no effect if this STA has no presence on this link */ 207c71420dbSJohannes Berg if (!link_sta) 208b4f85443SJohannes Berg return NL80211_CHAN_WIDTH_20_NOHT; 209bbf31e88SIlan Peer 210c71420dbSJohannes Berg width = ieee80211_sta_cap_rx_bw(link_sta); 211c71420dbSJohannes Berg 212bbf31e88SIlan Peer switch (width) { 21321f659bfSEliad Peller case IEEE80211_STA_RX_BW_20: 214c71420dbSJohannes Berg if (link_sta->pub->ht_cap.ht_supported) 21521f659bfSEliad Peller return NL80211_CHAN_WIDTH_20; 21621f659bfSEliad Peller else 21721f659bfSEliad Peller return NL80211_CHAN_WIDTH_20_NOHT; 21821f659bfSEliad Peller case IEEE80211_STA_RX_BW_40: 21921f659bfSEliad Peller return NL80211_CHAN_WIDTH_40; 22021f659bfSEliad Peller case IEEE80211_STA_RX_BW_80: 22121f659bfSEliad Peller return NL80211_CHAN_WIDTH_80; 22221f659bfSEliad Peller case IEEE80211_STA_RX_BW_160: 22321f659bfSEliad Peller /* 22421f659bfSEliad Peller * This applied for both 160 and 80+80. since we use 22521f659bfSEliad Peller * the returned value to consider degradation of 22621f659bfSEliad Peller * ctx->conf.min_def, we have to make sure to take 22721f659bfSEliad Peller * the bigger one (NL80211_CHAN_WIDTH_160). 22821f659bfSEliad Peller * Otherwise we might try degrading even when not 22921f659bfSEliad Peller * needed, as the max required sta_bw returned (80+80) 23021f659bfSEliad Peller * might be smaller than the configured bw (160). 23121f659bfSEliad Peller */ 23221f659bfSEliad Peller return NL80211_CHAN_WIDTH_160; 2335dca295dSIlan Peer case IEEE80211_STA_RX_BW_320: 2345dca295dSIlan Peer return NL80211_CHAN_WIDTH_320; 23521f659bfSEliad Peller default: 23621f659bfSEliad Peller WARN_ON(1); 23721f659bfSEliad Peller return NL80211_CHAN_WIDTH_20; 23821f659bfSEliad Peller } 23921f659bfSEliad Peller } 24021f659bfSEliad Peller 24121f659bfSEliad Peller static enum nl80211_chan_width 242b4f85443SJohannes Berg ieee80211_get_max_required_bw(struct ieee80211_sub_if_data *sdata, 243b4f85443SJohannes Berg unsigned int link_id) 24421f659bfSEliad Peller { 24521f659bfSEliad Peller enum nl80211_chan_width max_bw = NL80211_CHAN_WIDTH_20_NOHT; 24621f659bfSEliad Peller struct sta_info *sta; 24721f659bfSEliad Peller 24821f659bfSEliad Peller list_for_each_entry_rcu(sta, &sdata->local->sta_list, list) { 24921f659bfSEliad Peller if (sdata != sta->sdata && 25021f659bfSEliad Peller !(sta->sdata->bss && sta->sdata->bss == sdata->bss)) 25121f659bfSEliad Peller continue; 25221f659bfSEliad Peller 253b4f85443SJohannes Berg max_bw = max(max_bw, ieee80211_get_sta_bw(sta, link_id)); 25421f659bfSEliad Peller } 25521f659bfSEliad Peller 25621f659bfSEliad Peller return max_bw; 25721f659bfSEliad Peller } 25821f659bfSEliad Peller 25921f659bfSEliad Peller static enum nl80211_chan_width 260b4f85443SJohannes Berg ieee80211_get_chanctx_vif_max_required_bw(struct ieee80211_sub_if_data *sdata, 261b4f85443SJohannes Berg struct ieee80211_chanctx_conf *conf) 262b4f85443SJohannes Berg { 263b4f85443SJohannes Berg enum nl80211_chan_width max_bw = NL80211_CHAN_WIDTH_20_NOHT; 264b4f85443SJohannes Berg struct ieee80211_vif *vif = &sdata->vif; 265b4f85443SJohannes Berg int link_id; 266b4f85443SJohannes Berg 267d8675a63SJohannes Berg rcu_read_lock(); 268b4f85443SJohannes Berg for (link_id = 0; link_id < ARRAY_SIZE(sdata->link); link_id++) { 269b4f85443SJohannes Berg enum nl80211_chan_width width = NL80211_CHAN_WIDTH_20_NOHT; 270b4f85443SJohannes Berg struct ieee80211_bss_conf *link_conf = 271d8675a63SJohannes Berg rcu_dereference(sdata->vif.link_conf[link_id]); 272b4f85443SJohannes Berg 273b4f85443SJohannes Berg if (!link_conf) 274b4f85443SJohannes Berg continue; 275b4f85443SJohannes Berg 276b4f85443SJohannes Berg if (rcu_access_pointer(link_conf->chanctx_conf) != conf) 277b4f85443SJohannes Berg continue; 278b4f85443SJohannes Berg 279b4f85443SJohannes Berg switch (vif->type) { 280b4f85443SJohannes Berg case NL80211_IFTYPE_AP: 281b4f85443SJohannes Berg case NL80211_IFTYPE_AP_VLAN: 282b4f85443SJohannes Berg width = ieee80211_get_max_required_bw(sdata, link_id); 283b4f85443SJohannes Berg break; 284b4f85443SJohannes Berg case NL80211_IFTYPE_STATION: 285b4f85443SJohannes Berg /* 286b4f85443SJohannes Berg * The ap's sta->bandwidth is not set yet at this 287b4f85443SJohannes Berg * point, so take the width from the chandef, but 288b4f85443SJohannes Berg * account also for TDLS peers 289b4f85443SJohannes Berg */ 290b4f85443SJohannes Berg width = max(link_conf->chandef.width, 291b4f85443SJohannes Berg ieee80211_get_max_required_bw(sdata, link_id)); 292b4f85443SJohannes Berg break; 293b4f85443SJohannes Berg case NL80211_IFTYPE_P2P_DEVICE: 294b4f85443SJohannes Berg case NL80211_IFTYPE_NAN: 295b4f85443SJohannes Berg continue; 296b4f85443SJohannes Berg case NL80211_IFTYPE_ADHOC: 297b4f85443SJohannes Berg case NL80211_IFTYPE_MESH_POINT: 298b4f85443SJohannes Berg case NL80211_IFTYPE_OCB: 299b4f85443SJohannes Berg width = link_conf->chandef.width; 300b4f85443SJohannes Berg break; 301b4f85443SJohannes Berg case NL80211_IFTYPE_WDS: 302b4f85443SJohannes Berg case NL80211_IFTYPE_UNSPECIFIED: 303b4f85443SJohannes Berg case NUM_NL80211_IFTYPES: 304b4f85443SJohannes Berg case NL80211_IFTYPE_MONITOR: 305b4f85443SJohannes Berg case NL80211_IFTYPE_P2P_CLIENT: 306b4f85443SJohannes Berg case NL80211_IFTYPE_P2P_GO: 307b4f85443SJohannes Berg WARN_ON_ONCE(1); 308b4f85443SJohannes Berg } 309b4f85443SJohannes Berg 310b4f85443SJohannes Berg max_bw = max(max_bw, width); 311b4f85443SJohannes Berg } 312d8675a63SJohannes Berg rcu_read_unlock(); 313b4f85443SJohannes Berg 314b4f85443SJohannes Berg return max_bw; 315b4f85443SJohannes Berg } 316b4f85443SJohannes Berg 317b4f85443SJohannes Berg static enum nl80211_chan_width 31821f659bfSEliad Peller ieee80211_get_chanctx_max_required_bw(struct ieee80211_local *local, 31921f659bfSEliad Peller struct ieee80211_chanctx_conf *conf) 32021f659bfSEliad Peller { 32121f659bfSEliad Peller struct ieee80211_sub_if_data *sdata; 32221f659bfSEliad Peller enum nl80211_chan_width max_bw = NL80211_CHAN_WIDTH_20_NOHT; 32321f659bfSEliad Peller 32421f659bfSEliad Peller rcu_read_lock(); 32521f659bfSEliad Peller list_for_each_entry_rcu(sdata, &local->interfaces, list) { 326b4f85443SJohannes Berg enum nl80211_chan_width width; 32721f659bfSEliad Peller 32821f659bfSEliad Peller if (!ieee80211_sdata_running(sdata)) 32921f659bfSEliad Peller continue; 33021f659bfSEliad Peller 331b4f85443SJohannes Berg width = ieee80211_get_chanctx_vif_max_required_bw(sdata, conf); 33221f659bfSEliad Peller 33321f659bfSEliad Peller max_bw = max(max_bw, width); 33421f659bfSEliad Peller } 3351c37a72cSEliad Peller 3361c37a72cSEliad Peller /* use the configured bandwidth in case of monitor interface */ 3371c37a72cSEliad Peller sdata = rcu_dereference(local->monitor_sdata); 338b4f85443SJohannes Berg if (sdata && 339d8675a63SJohannes Berg rcu_access_pointer(sdata->vif.bss_conf.chanctx_conf) == conf) 3401c37a72cSEliad Peller max_bw = max(max_bw, conf->def.width); 3411c37a72cSEliad Peller 34221f659bfSEliad Peller rcu_read_unlock(); 34321f659bfSEliad Peller 34421f659bfSEliad Peller return max_bw; 34521f659bfSEliad Peller } 34621f659bfSEliad Peller 34721f659bfSEliad Peller /* 34821f659bfSEliad Peller * recalc the min required chan width of the channel context, which is 34921f659bfSEliad Peller * the max of min required widths of all the interfaces bound to this 35021f659bfSEliad Peller * channel context. 35121f659bfSEliad Peller */ 352d6c37509SMordechay Goodstein static u32 _ieee80211_recalc_chanctx_min_def(struct ieee80211_local *local, 35321f659bfSEliad Peller struct ieee80211_chanctx *ctx) 35421f659bfSEliad Peller { 35521f659bfSEliad Peller enum nl80211_chan_width max_bw; 35621f659bfSEliad Peller struct cfg80211_chan_def min_def; 35721f659bfSEliad Peller 35821f659bfSEliad Peller lockdep_assert_held(&local->chanctx_mtx); 35921f659bfSEliad Peller 360df78a0c0SThomas Pedersen /* don't optimize non-20MHz based and radar_enabled confs */ 36121f659bfSEliad Peller if (ctx->conf.def.width == NL80211_CHAN_WIDTH_5 || 36221f659bfSEliad Peller ctx->conf.def.width == NL80211_CHAN_WIDTH_10 || 363df78a0c0SThomas Pedersen ctx->conf.def.width == NL80211_CHAN_WIDTH_1 || 364df78a0c0SThomas Pedersen ctx->conf.def.width == NL80211_CHAN_WIDTH_2 || 365df78a0c0SThomas Pedersen ctx->conf.def.width == NL80211_CHAN_WIDTH_4 || 366df78a0c0SThomas Pedersen ctx->conf.def.width == NL80211_CHAN_WIDTH_8 || 367df78a0c0SThomas Pedersen ctx->conf.def.width == NL80211_CHAN_WIDTH_16 || 36821f659bfSEliad Peller ctx->conf.radar_enabled) { 36921f659bfSEliad Peller ctx->conf.min_def = ctx->conf.def; 370d6c37509SMordechay Goodstein return 0; 37121f659bfSEliad Peller } 37221f659bfSEliad Peller 37321f659bfSEliad Peller max_bw = ieee80211_get_chanctx_max_required_bw(local, &ctx->conf); 37421f659bfSEliad Peller 37521f659bfSEliad Peller /* downgrade chandef up to max_bw */ 37621f659bfSEliad Peller min_def = ctx->conf.def; 37721f659bfSEliad Peller while (min_def.width > max_bw) 37821f659bfSEliad Peller ieee80211_chandef_downgrade(&min_def); 37921f659bfSEliad Peller 38021f659bfSEliad Peller if (cfg80211_chandef_identical(&ctx->conf.min_def, &min_def)) 381d6c37509SMordechay Goodstein return 0; 38221f659bfSEliad Peller 38321f659bfSEliad Peller ctx->conf.min_def = min_def; 38421f659bfSEliad Peller if (!ctx->driver_present) 385d6c37509SMordechay Goodstein return 0; 38621f659bfSEliad Peller 387d6c37509SMordechay Goodstein return IEEE80211_CHANCTX_CHANGE_MIN_WIDTH; 38821f659bfSEliad Peller } 38921f659bfSEliad Peller 390d6c37509SMordechay Goodstein /* calling this function is assuming that station vif is updated to 391b4f85443SJohannes Berg * lates changes by calling ieee80211_link_update_chandef 392d6c37509SMordechay Goodstein */ 39344b72ca8SIlan Peer static void ieee80211_chan_bw_change(struct ieee80211_local *local, 394d6c37509SMordechay Goodstein struct ieee80211_chanctx *ctx, 395d6c37509SMordechay Goodstein bool narrowed) 39644b72ca8SIlan Peer { 39744b72ca8SIlan Peer struct sta_info *sta; 39844b72ca8SIlan Peer struct ieee80211_supported_band *sband = 39944b72ca8SIlan Peer local->hw.wiphy->bands[ctx->conf.def.chan->band]; 40044b72ca8SIlan Peer 40144b72ca8SIlan Peer rcu_read_lock(); 40244b72ca8SIlan Peer list_for_each_entry_rcu(sta, &local->sta_list, 40344b72ca8SIlan Peer list) { 404b4f85443SJohannes Berg struct ieee80211_sub_if_data *sdata = sta->sdata; 40544b72ca8SIlan Peer enum ieee80211_sta_rx_bandwidth new_sta_bw; 406b4f85443SJohannes Berg unsigned int link_id; 40744b72ca8SIlan Peer 40844b72ca8SIlan Peer if (!ieee80211_sdata_running(sta->sdata)) 40944b72ca8SIlan Peer continue; 41044b72ca8SIlan Peer 411b4f85443SJohannes Berg for (link_id = 0; link_id < ARRAY_SIZE(sta->sdata->link); link_id++) { 412b4f85443SJohannes Berg struct ieee80211_bss_conf *link_conf = 413d8675a63SJohannes Berg rcu_dereference(sdata->vif.link_conf[link_id]); 414c71420dbSJohannes Berg struct link_sta_info *link_sta; 415b4f85443SJohannes Berg 416b4f85443SJohannes Berg if (!link_conf) 41744b72ca8SIlan Peer continue; 41844b72ca8SIlan Peer 419b4f85443SJohannes Berg if (rcu_access_pointer(link_conf->chanctx_conf) != &ctx->conf) 420b4f85443SJohannes Berg continue; 421b4f85443SJohannes Berg 422c71420dbSJohannes Berg link_sta = rcu_dereference(sta->link[link_id]); 423c71420dbSJohannes Berg if (!link_sta) 424c71420dbSJohannes Berg continue; 425c71420dbSJohannes Berg 426c71420dbSJohannes Berg new_sta_bw = ieee80211_sta_cur_vht_bw(link_sta); 427d6c37509SMordechay Goodstein 428d6c37509SMordechay Goodstein /* nothing change */ 429c71420dbSJohannes Berg if (new_sta_bw == link_sta->pub->bandwidth) 43044b72ca8SIlan Peer continue; 43144b72ca8SIlan Peer 432d6c37509SMordechay Goodstein /* vif changed to narrow BW and narrow BW for station wasn't 433d6c37509SMordechay Goodstein * requested or vise versa */ 434c71420dbSJohannes Berg if ((new_sta_bw < link_sta->pub->bandwidth) == !narrowed) 435d6c37509SMordechay Goodstein continue; 436d6c37509SMordechay Goodstein 437c71420dbSJohannes Berg link_sta->pub->bandwidth = new_sta_bw; 438b4f85443SJohannes Berg rate_control_rate_update(local, sband, sta, link_id, 43944b72ca8SIlan Peer IEEE80211_RC_BW_CHANGED); 44044b72ca8SIlan Peer } 441b4f85443SJohannes Berg } 44244b72ca8SIlan Peer rcu_read_unlock(); 44344b72ca8SIlan Peer } 44444b72ca8SIlan Peer 445d6c37509SMordechay Goodstein /* 446d6c37509SMordechay Goodstein * recalc the min required chan width of the channel context, which is 447d6c37509SMordechay Goodstein * the max of min required widths of all the interfaces bound to this 448d6c37509SMordechay Goodstein * channel context. 449d6c37509SMordechay Goodstein */ 450d6c37509SMordechay Goodstein void ieee80211_recalc_chanctx_min_def(struct ieee80211_local *local, 451d6c37509SMordechay Goodstein struct ieee80211_chanctx *ctx) 452e89a96f5SMichal Kazior { 453d6c37509SMordechay Goodstein u32 changed = _ieee80211_recalc_chanctx_min_def(local, ctx); 45444b72ca8SIlan Peer 455d6c37509SMordechay Goodstein if (!changed) 456e89a96f5SMichal Kazior return; 457d6c37509SMordechay Goodstein 458d6c37509SMordechay Goodstein /* check is BW narrowed */ 459d6c37509SMordechay Goodstein ieee80211_chan_bw_change(local, ctx, true); 460d6c37509SMordechay Goodstein 461d6c37509SMordechay Goodstein drv_change_chanctx(local, ctx, changed); 462d6c37509SMordechay Goodstein 463d6c37509SMordechay Goodstein /* check is BW wider */ 464d6c37509SMordechay Goodstein ieee80211_chan_bw_change(local, ctx, false); 465aa507a7bSArik Nemtsov } 466e89a96f5SMichal Kazior 467d6c37509SMordechay Goodstein static void ieee80211_change_chanctx(struct ieee80211_local *local, 468d6c37509SMordechay Goodstein struct ieee80211_chanctx *ctx, 469d6c37509SMordechay Goodstein struct ieee80211_chanctx *old_ctx, 470d6c37509SMordechay Goodstein const struct cfg80211_chan_def *chandef) 471d6c37509SMordechay Goodstein { 472d6c37509SMordechay Goodstein u32 changed; 47344b72ca8SIlan Peer 4745dca295dSIlan Peer /* expected to handle only 20/40/80/160/320 channel widths */ 47544b72ca8SIlan Peer switch (chandef->width) { 47644b72ca8SIlan Peer case NL80211_CHAN_WIDTH_20_NOHT: 47744b72ca8SIlan Peer case NL80211_CHAN_WIDTH_20: 47844b72ca8SIlan Peer case NL80211_CHAN_WIDTH_40: 47944b72ca8SIlan Peer case NL80211_CHAN_WIDTH_80: 48044b72ca8SIlan Peer case NL80211_CHAN_WIDTH_80P80: 48144b72ca8SIlan Peer case NL80211_CHAN_WIDTH_160: 4825dca295dSIlan Peer case NL80211_CHAN_WIDTH_320: 48344b72ca8SIlan Peer break; 48444b72ca8SIlan Peer default: 48544b72ca8SIlan Peer WARN_ON(1); 48644b72ca8SIlan Peer } 48744b72ca8SIlan Peer 488d6c37509SMordechay Goodstein /* Check maybe BW narrowed - we do this _before_ calling recalc_chanctx_min_def 489d6c37509SMordechay Goodstein * due to maybe not returning from it, e.g in case new context was added 490d6c37509SMordechay Goodstein * first time with all parameters up to date. 491d6c37509SMordechay Goodstein */ 492d6c37509SMordechay Goodstein ieee80211_chan_bw_change(local, old_ctx, true); 49344b72ca8SIlan Peer 494d6c37509SMordechay Goodstein if (cfg80211_chandef_identical(&ctx->conf.def, chandef)) { 49521f659bfSEliad Peller ieee80211_recalc_chanctx_min_def(local, ctx); 496d6c37509SMordechay Goodstein return; 497d6c37509SMordechay Goodstein } 498d6c37509SMordechay Goodstein 499d6c37509SMordechay Goodstein WARN_ON(!cfg80211_chandef_compatible(&ctx->conf.def, chandef)); 500d6c37509SMordechay Goodstein 501d6c37509SMordechay Goodstein ctx->conf.def = *chandef; 502d6c37509SMordechay Goodstein 503d6c37509SMordechay Goodstein /* check if min chanctx also changed */ 504d6c37509SMordechay Goodstein changed = IEEE80211_CHANCTX_CHANGE_WIDTH | 505d6c37509SMordechay Goodstein _ieee80211_recalc_chanctx_min_def(local, ctx); 506d6c37509SMordechay Goodstein drv_change_chanctx(local, ctx, changed); 50755de908aSJohannes Berg 50855de908aSJohannes Berg if (!local->use_chanctx) { 509675a0b04SKarl Beldan local->_oper_chandef = *chandef; 51055de908aSJohannes Berg ieee80211_hw_config(local, 0); 51155de908aSJohannes Berg } 51244b72ca8SIlan Peer 513d6c37509SMordechay Goodstein /* check is BW wider */ 514d6c37509SMordechay Goodstein ieee80211_chan_bw_change(local, old_ctx, false); 5150aaffa9bSJohannes Berg } 516d01a1e65SMichal Kazior 517d01a1e65SMichal Kazior static struct ieee80211_chanctx * 518d01a1e65SMichal Kazior ieee80211_find_chanctx(struct ieee80211_local *local, 5194bf88530SJohannes Berg const struct cfg80211_chan_def *chandef, 520d01a1e65SMichal Kazior enum ieee80211_chanctx_mode mode) 521d01a1e65SMichal Kazior { 522d01a1e65SMichal Kazior struct ieee80211_chanctx *ctx; 523d01a1e65SMichal Kazior 524d01a1e65SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 525d01a1e65SMichal Kazior 526d01a1e65SMichal Kazior if (mode == IEEE80211_CHANCTX_EXCLUSIVE) 527d01a1e65SMichal Kazior return NULL; 528d01a1e65SMichal Kazior 529d01a1e65SMichal Kazior list_for_each_entry(ctx, &local->chanctx_list, list) { 5304bf88530SJohannes Berg const struct cfg80211_chan_def *compat; 531e89a96f5SMichal Kazior 5325bcae31dSMichal Kazior if (ctx->replace_state != IEEE80211_CHANCTX_REPLACE_NONE) 5335bcae31dSMichal Kazior continue; 5345bcae31dSMichal Kazior 535d01a1e65SMichal Kazior if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE) 536d01a1e65SMichal Kazior continue; 5374bf88530SJohannes Berg 5384bf88530SJohannes Berg compat = cfg80211_chandef_compatible(&ctx->conf.def, chandef); 5394bf88530SJohannes Berg if (!compat) 540d01a1e65SMichal Kazior continue; 541d01a1e65SMichal Kazior 5420288157bSMichal Kazior compat = ieee80211_chanctx_reserved_chandef(local, ctx, 5430288157bSMichal Kazior compat); 5440288157bSMichal Kazior if (!compat) 5450288157bSMichal Kazior continue; 5460288157bSMichal Kazior 547d6c37509SMordechay Goodstein ieee80211_change_chanctx(local, ctx, ctx, compat); 548e89a96f5SMichal Kazior 549d01a1e65SMichal Kazior return ctx; 550d01a1e65SMichal Kazior } 551d01a1e65SMichal Kazior 552d01a1e65SMichal Kazior return NULL; 553d01a1e65SMichal Kazior } 554d01a1e65SMichal Kazior 5555cbc95a7SEliad Peller bool ieee80211_is_radar_required(struct ieee80211_local *local) 556e4746851SSimon Wunderlich { 557e4746851SSimon Wunderlich struct ieee80211_sub_if_data *sdata; 558e4746851SSimon Wunderlich 559cc901de1SMichal Kazior lockdep_assert_held(&local->mtx); 560cc901de1SMichal Kazior 561e4746851SSimon Wunderlich rcu_read_lock(); 562e4746851SSimon Wunderlich list_for_each_entry_rcu(sdata, &local->interfaces, list) { 563b4f85443SJohannes Berg unsigned int link_id; 564b4f85443SJohannes Berg 565b4f85443SJohannes Berg for (link_id = 0; link_id < ARRAY_SIZE(sdata->link); link_id++) { 566d8675a63SJohannes Berg struct ieee80211_link_data *link; 567d8675a63SJohannes Berg 568d8675a63SJohannes Berg link = rcu_dereference(sdata->link[link_id]); 569d8675a63SJohannes Berg 570d8675a63SJohannes Berg if (link && link->radar_required) { 571e4746851SSimon Wunderlich rcu_read_unlock(); 572e4746851SSimon Wunderlich return true; 573e4746851SSimon Wunderlich } 574e4746851SSimon Wunderlich } 575b4f85443SJohannes Berg } 576e4746851SSimon Wunderlich rcu_read_unlock(); 577e4746851SSimon Wunderlich 578e4746851SSimon Wunderlich return false; 579e4746851SSimon Wunderlich } 580e4746851SSimon Wunderlich 581e7f2337aSEliad Peller static bool 582e7f2337aSEliad Peller ieee80211_chanctx_radar_required(struct ieee80211_local *local, 583e7f2337aSEliad Peller struct ieee80211_chanctx *ctx) 584e7f2337aSEliad Peller { 585e7f2337aSEliad Peller struct ieee80211_chanctx_conf *conf = &ctx->conf; 586e7f2337aSEliad Peller struct ieee80211_sub_if_data *sdata; 587e7f2337aSEliad Peller bool required = false; 588e7f2337aSEliad Peller 589e7f2337aSEliad Peller lockdep_assert_held(&local->chanctx_mtx); 590e7f2337aSEliad Peller lockdep_assert_held(&local->mtx); 591e7f2337aSEliad Peller 592e7f2337aSEliad Peller rcu_read_lock(); 593e7f2337aSEliad Peller list_for_each_entry_rcu(sdata, &local->interfaces, list) { 594b4f85443SJohannes Berg unsigned int link_id; 595b4f85443SJohannes Berg 596e7f2337aSEliad Peller if (!ieee80211_sdata_running(sdata)) 597e7f2337aSEliad Peller continue; 598b4f85443SJohannes Berg for (link_id = 0; link_id < ARRAY_SIZE(sdata->link); link_id++) { 599d8675a63SJohannes Berg struct ieee80211_link_data *link; 600b4f85443SJohannes Berg 601d8675a63SJohannes Berg link = rcu_dereference(sdata->link[link_id]); 602d8675a63SJohannes Berg if (!link) 603e7f2337aSEliad Peller continue; 604e7f2337aSEliad Peller 605d8675a63SJohannes Berg if (rcu_access_pointer(link->conf->chanctx_conf) != conf) 606b4f85443SJohannes Berg continue; 607d8675a63SJohannes Berg if (!link->radar_required) 608b4f85443SJohannes Berg continue; 609e7f2337aSEliad Peller required = true; 610e7f2337aSEliad Peller break; 611e7f2337aSEliad Peller } 612b4f85443SJohannes Berg 613b4f85443SJohannes Berg if (required) 614b4f85443SJohannes Berg break; 615b4f85443SJohannes Berg } 616e7f2337aSEliad Peller rcu_read_unlock(); 617e7f2337aSEliad Peller 618e7f2337aSEliad Peller return required; 619e7f2337aSEliad Peller } 620e7f2337aSEliad Peller 621d01a1e65SMichal Kazior static struct ieee80211_chanctx * 622ed68ebcaSMichal Kazior ieee80211_alloc_chanctx(struct ieee80211_local *local, 6234bf88530SJohannes Berg const struct cfg80211_chan_def *chandef, 624d01a1e65SMichal Kazior enum ieee80211_chanctx_mode mode) 625d01a1e65SMichal Kazior { 626d01a1e65SMichal Kazior struct ieee80211_chanctx *ctx; 627d01a1e65SMichal Kazior 628d01a1e65SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 629d01a1e65SMichal Kazior 630d01a1e65SMichal Kazior ctx = kzalloc(sizeof(*ctx) + local->hw.chanctx_data_size, GFP_KERNEL); 631d01a1e65SMichal Kazior if (!ctx) 632ed68ebcaSMichal Kazior return NULL; 633d01a1e65SMichal Kazior 634b4f85443SJohannes Berg INIT_LIST_HEAD(&ctx->assigned_links); 635b4f85443SJohannes Berg INIT_LIST_HEAD(&ctx->reserved_links); 6364bf88530SJohannes Berg ctx->conf.def = *chandef; 63704ecd257SJohannes Berg ctx->conf.rx_chains_static = 1; 63804ecd257SJohannes Berg ctx->conf.rx_chains_dynamic = 1; 639d01a1e65SMichal Kazior ctx->mode = mode; 640e7f2337aSEliad Peller ctx->conf.radar_enabled = false; 64121f659bfSEliad Peller ieee80211_recalc_chanctx_min_def(local, ctx); 642ed68ebcaSMichal Kazior 643ed68ebcaSMichal Kazior return ctx; 644ed68ebcaSMichal Kazior } 645ed68ebcaSMichal Kazior 646ed68ebcaSMichal Kazior static int ieee80211_add_chanctx(struct ieee80211_local *local, 647ed68ebcaSMichal Kazior struct ieee80211_chanctx *ctx) 648ed68ebcaSMichal Kazior { 649ed68ebcaSMichal Kazior u32 changed; 650ed68ebcaSMichal Kazior int err; 651ed68ebcaSMichal Kazior 652ed68ebcaSMichal Kazior lockdep_assert_held(&local->mtx); 653ed68ebcaSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 654ed68ebcaSMichal Kazior 655e4746851SSimon Wunderlich if (!local->use_chanctx) 656e4746851SSimon Wunderlich local->hw.conf.radar_enabled = ctx->conf.radar_enabled; 657d01a1e65SMichal Kazior 658382a103bSJohannes Berg /* turn idle off *before* setting channel -- some drivers need that */ 659382a103bSJohannes Berg changed = ieee80211_idle_off(local); 660382a103bSJohannes Berg if (changed) 661382a103bSJohannes Berg ieee80211_hw_config(local, changed); 662382a103bSJohannes Berg 66355de908aSJohannes Berg if (!local->use_chanctx) { 664ed68ebcaSMichal Kazior local->_oper_chandef = ctx->conf.def; 6659b4816f5SMichal Kazior ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); 66655de908aSJohannes Berg } else { 66735f2fce9SMichal Kazior err = drv_add_chanctx(local, ctx); 66835f2fce9SMichal Kazior if (err) { 669382a103bSJohannes Berg ieee80211_recalc_idle(local); 670ed68ebcaSMichal Kazior return err; 671ed68ebcaSMichal Kazior } 672ed68ebcaSMichal Kazior } 673ed68ebcaSMichal Kazior 674ed68ebcaSMichal Kazior return 0; 675ed68ebcaSMichal Kazior } 676ed68ebcaSMichal Kazior 677ed68ebcaSMichal Kazior static struct ieee80211_chanctx * 678ed68ebcaSMichal Kazior ieee80211_new_chanctx(struct ieee80211_local *local, 679ed68ebcaSMichal Kazior const struct cfg80211_chan_def *chandef, 680ed68ebcaSMichal Kazior enum ieee80211_chanctx_mode mode) 681ed68ebcaSMichal Kazior { 682ed68ebcaSMichal Kazior struct ieee80211_chanctx *ctx; 683ed68ebcaSMichal Kazior int err; 684ed68ebcaSMichal Kazior 685ed68ebcaSMichal Kazior lockdep_assert_held(&local->mtx); 686ed68ebcaSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 687ed68ebcaSMichal Kazior 688ed68ebcaSMichal Kazior ctx = ieee80211_alloc_chanctx(local, chandef, mode); 689ed68ebcaSMichal Kazior if (!ctx) 690ed68ebcaSMichal Kazior return ERR_PTR(-ENOMEM); 691ed68ebcaSMichal Kazior 692ed68ebcaSMichal Kazior err = ieee80211_add_chanctx(local, ctx); 693ed68ebcaSMichal Kazior if (err) { 694ed68ebcaSMichal Kazior kfree(ctx); 69534a3740dSJohannes Berg return ERR_PTR(err); 69635f2fce9SMichal Kazior } 69735f2fce9SMichal Kazior 6983448c005SJohannes Berg list_add_rcu(&ctx->list, &local->chanctx_list); 699d01a1e65SMichal Kazior return ctx; 700d01a1e65SMichal Kazior } 701d01a1e65SMichal Kazior 7021f0d54cdSMichal Kazior static void ieee80211_del_chanctx(struct ieee80211_local *local, 703d01a1e65SMichal Kazior struct ieee80211_chanctx *ctx) 704d01a1e65SMichal Kazior { 705d01a1e65SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 706d01a1e65SMichal Kazior 70755de908aSJohannes Berg if (!local->use_chanctx) { 708675a0b04SKarl Beldan struct cfg80211_chan_def *chandef = &local->_oper_chandef; 7095e480774SThomas Pedersen /* S1G doesn't have 20MHz, so get the correct width for the 7105e480774SThomas Pedersen * current channel. 7115e480774SThomas Pedersen */ 7125e480774SThomas Pedersen if (chandef->chan->band == NL80211_BAND_S1GHZ) 7135e480774SThomas Pedersen chandef->width = 7145e480774SThomas Pedersen ieee80211_s1g_channel_width(chandef->chan); 7155e480774SThomas Pedersen else 716675a0b04SKarl Beldan chandef->width = NL80211_CHAN_WIDTH_20_NOHT; 717675a0b04SKarl Beldan chandef->center_freq1 = chandef->chan->center_freq; 718b6011960SThomas Pedersen chandef->freq1_offset = chandef->chan->freq_offset; 719675a0b04SKarl Beldan chandef->center_freq2 = 0; 720e4746851SSimon Wunderlich 721e4746851SSimon Wunderlich /* NOTE: Disabling radar is only valid here for 722e4746851SSimon Wunderlich * single channel context. To be sure, check it ... 723e4746851SSimon Wunderlich */ 7241f0d54cdSMichal Kazior WARN_ON(local->hw.conf.radar_enabled && 7251f0d54cdSMichal Kazior !list_empty(&local->chanctx_list)); 7261f0d54cdSMichal Kazior 727e4746851SSimon Wunderlich local->hw.conf.radar_enabled = false; 728e4746851SSimon Wunderlich 7299b4816f5SMichal Kazior ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); 73055de908aSJohannes Berg } else { 73135f2fce9SMichal Kazior drv_remove_chanctx(local, ctx); 73255de908aSJohannes Berg } 73335f2fce9SMichal Kazior 734fd0f979aSJohannes Berg ieee80211_recalc_idle(local); 735d01a1e65SMichal Kazior } 736d01a1e65SMichal Kazior 7371f0d54cdSMichal Kazior static void ieee80211_free_chanctx(struct ieee80211_local *local, 738d01a1e65SMichal Kazior struct ieee80211_chanctx *ctx) 739d01a1e65SMichal Kazior { 740d01a1e65SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 741d01a1e65SMichal Kazior 742c0166da9SMichal Kazior WARN_ON_ONCE(ieee80211_chanctx_refcount(local, ctx) != 0); 74335f2fce9SMichal Kazior 7441f0d54cdSMichal Kazior list_del_rcu(&ctx->list); 7451f0d54cdSMichal Kazior ieee80211_del_chanctx(local, ctx); 7461f0d54cdSMichal Kazior kfree_rcu(ctx, rcu_head); 747d01a1e65SMichal Kazior } 748d01a1e65SMichal Kazior 7490fabfaafSArik Nemtsov void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local, 750e89a96f5SMichal Kazior struct ieee80211_chanctx *ctx) 751e89a96f5SMichal Kazior { 752e89a96f5SMichal Kazior struct ieee80211_chanctx_conf *conf = &ctx->conf; 753e89a96f5SMichal Kazior struct ieee80211_sub_if_data *sdata; 7544bf88530SJohannes Berg const struct cfg80211_chan_def *compat = NULL; 7550fabfaafSArik Nemtsov struct sta_info *sta; 756e89a96f5SMichal Kazior 757e89a96f5SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 758e89a96f5SMichal Kazior 759e89a96f5SMichal Kazior rcu_read_lock(); 760e89a96f5SMichal Kazior list_for_each_entry_rcu(sdata, &local->interfaces, list) { 761b4f85443SJohannes Berg int link_id; 7624bf88530SJohannes Berg 763e89a96f5SMichal Kazior if (!ieee80211_sdata_running(sdata)) 764e89a96f5SMichal Kazior continue; 765b4f85443SJohannes Berg 7660e67c136SFelix Fietkau if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) 7670e67c136SFelix Fietkau continue; 768e89a96f5SMichal Kazior 769b4f85443SJohannes Berg for (link_id = 0; link_id < ARRAY_SIZE(sdata->link); link_id++) { 770b4f85443SJohannes Berg struct ieee80211_bss_conf *link_conf = 771d8675a63SJohannes Berg rcu_dereference(sdata->vif.link_conf[link_id]); 7724bf88530SJohannes Berg 773b4f85443SJohannes Berg if (!link_conf) 774b4f85443SJohannes Berg continue; 775b4f85443SJohannes Berg 776b4f85443SJohannes Berg if (rcu_access_pointer(link_conf->chanctx_conf) != conf) 777b4f85443SJohannes Berg continue; 778b4f85443SJohannes Berg 779b4f85443SJohannes Berg if (!compat) 780b4f85443SJohannes Berg compat = &link_conf->chandef; 781b4f85443SJohannes Berg 782b4f85443SJohannes Berg compat = cfg80211_chandef_compatible(&link_conf->chandef, 783b4f85443SJohannes Berg compat); 784a00f4f6eSMichal Kazior if (WARN_ON_ONCE(!compat)) 7854bf88530SJohannes Berg break; 786e89a96f5SMichal Kazior } 787b4f85443SJohannes Berg } 7880fabfaafSArik Nemtsov 7890fabfaafSArik Nemtsov /* TDLS peers can sometimes affect the chandef width */ 7900fabfaafSArik Nemtsov list_for_each_entry_rcu(sta, &local->sta_list, list) { 7910fabfaafSArik Nemtsov if (!sta->uploaded || 7920fabfaafSArik Nemtsov !test_sta_flag(sta, WLAN_STA_TDLS_WIDER_BW) || 7930fabfaafSArik Nemtsov !test_sta_flag(sta, WLAN_STA_AUTHORIZED) || 7940fabfaafSArik Nemtsov !sta->tdls_chandef.chan) 7950fabfaafSArik Nemtsov continue; 7960fabfaafSArik Nemtsov 7970fabfaafSArik Nemtsov compat = cfg80211_chandef_compatible(&sta->tdls_chandef, 7980fabfaafSArik Nemtsov compat); 7990fabfaafSArik Nemtsov if (WARN_ON_ONCE(!compat)) 8000fabfaafSArik Nemtsov break; 8010fabfaafSArik Nemtsov } 802e89a96f5SMichal Kazior rcu_read_unlock(); 803e89a96f5SMichal Kazior 804a00f4f6eSMichal Kazior if (!compat) 8054bf88530SJohannes Berg return; 806e89a96f5SMichal Kazior 807d6c37509SMordechay Goodstein ieee80211_change_chanctx(local, ctx, ctx, compat); 808e89a96f5SMichal Kazior } 809e89a96f5SMichal Kazior 810367bbd10SJohannes Berg static void ieee80211_recalc_radar_chanctx(struct ieee80211_local *local, 811367bbd10SJohannes Berg struct ieee80211_chanctx *chanctx) 812367bbd10SJohannes Berg { 813367bbd10SJohannes Berg bool radar_enabled; 814367bbd10SJohannes Berg 815367bbd10SJohannes Berg lockdep_assert_held(&local->chanctx_mtx); 8165cbc95a7SEliad Peller /* for ieee80211_is_radar_required */ 81734a3740dSJohannes Berg lockdep_assert_held(&local->mtx); 818367bbd10SJohannes Berg 819e7f2337aSEliad Peller radar_enabled = ieee80211_chanctx_radar_required(local, chanctx); 820367bbd10SJohannes Berg 821367bbd10SJohannes Berg if (radar_enabled == chanctx->conf.radar_enabled) 822367bbd10SJohannes Berg return; 823367bbd10SJohannes Berg 824367bbd10SJohannes Berg chanctx->conf.radar_enabled = radar_enabled; 825367bbd10SJohannes Berg 826367bbd10SJohannes Berg if (!local->use_chanctx) { 827367bbd10SJohannes Berg local->hw.conf.radar_enabled = chanctx->conf.radar_enabled; 828367bbd10SJohannes Berg ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); 829367bbd10SJohannes Berg } 830367bbd10SJohannes Berg 831367bbd10SJohannes Berg drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RADAR); 832367bbd10SJohannes Berg } 833367bbd10SJohannes Berg 834b4f85443SJohannes Berg static int ieee80211_assign_link_chanctx(struct ieee80211_link_data *link, 83577eeba97SLuciano Coelho struct ieee80211_chanctx *new_ctx) 836d01a1e65SMichal Kazior { 837b4f85443SJohannes Berg struct ieee80211_sub_if_data *sdata = link->sdata; 838b4f85443SJohannes Berg unsigned int link_id = link->link_id; 83935f2fce9SMichal Kazior struct ieee80211_local *local = sdata->local; 84077eeba97SLuciano Coelho struct ieee80211_chanctx_conf *conf; 84177eeba97SLuciano Coelho struct ieee80211_chanctx *curr_ctx = NULL; 84277eeba97SLuciano Coelho int ret = 0; 843d01a1e65SMichal Kazior 844708d50edSAyala Beker if (WARN_ON(sdata->vif.type == NL80211_IFTYPE_NAN)) 845708d50edSAyala Beker return -ENOTSUPP; 846708d50edSAyala Beker 847d8675a63SJohannes Berg conf = rcu_dereference_protected(link->conf->chanctx_conf, 84877eeba97SLuciano Coelho lockdep_is_held(&local->chanctx_mtx)); 849d01a1e65SMichal Kazior 85077eeba97SLuciano Coelho if (conf) { 85177eeba97SLuciano Coelho curr_ctx = container_of(conf, struct ieee80211_chanctx, conf); 85235f2fce9SMichal Kazior 853b4f85443SJohannes Berg drv_unassign_vif_chanctx(local, sdata, link_id, curr_ctx); 85477eeba97SLuciano Coelho conf = NULL; 855b4f85443SJohannes Berg list_del(&link->assigned_chanctx_list); 85677eeba97SLuciano Coelho } 85777eeba97SLuciano Coelho 85877eeba97SLuciano Coelho if (new_ctx) { 859b4f85443SJohannes Berg ret = drv_assign_vif_chanctx(local, sdata, link_id, new_ctx); 86077eeba97SLuciano Coelho if (ret) 86177eeba97SLuciano Coelho goto out; 86277eeba97SLuciano Coelho 86377eeba97SLuciano Coelho conf = &new_ctx->conf; 864b4f85443SJohannes Berg list_add(&link->assigned_chanctx_list, 865b4f85443SJohannes Berg &new_ctx->assigned_links); 86677eeba97SLuciano Coelho } 86777eeba97SLuciano Coelho 86877eeba97SLuciano Coelho out: 869d8675a63SJohannes Berg rcu_assign_pointer(link->conf->chanctx_conf, conf); 87077eeba97SLuciano Coelho 871f276e20bSJohannes Berg sdata->vif.cfg.idle = !conf; 87277eeba97SLuciano Coelho 873c0166da9SMichal Kazior if (curr_ctx && ieee80211_chanctx_num_assigned(local, curr_ctx) > 0) { 87477eeba97SLuciano Coelho ieee80211_recalc_chanctx_chantype(local, curr_ctx); 87577eeba97SLuciano Coelho ieee80211_recalc_smps_chanctx(local, curr_ctx); 87677eeba97SLuciano Coelho ieee80211_recalc_radar_chanctx(local, curr_ctx); 87777eeba97SLuciano Coelho ieee80211_recalc_chanctx_min_def(local, curr_ctx); 87877eeba97SLuciano Coelho } 87977eeba97SLuciano Coelho 880c0166da9SMichal Kazior if (new_ctx && ieee80211_chanctx_num_assigned(local, new_ctx) > 0) { 881db82d8a9SLorenzo Bianconi ieee80211_recalc_txpower(sdata, false); 88277eeba97SLuciano Coelho ieee80211_recalc_chanctx_min_def(local, new_ctx); 88377eeba97SLuciano Coelho } 8845bbe754dSJohannes Berg 8855bbe754dSJohannes Berg if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE && 8865bbe754dSJohannes Berg sdata->vif.type != NL80211_IFTYPE_MONITOR) 8877b7090b4SJohannes Berg ieee80211_vif_cfg_change_notify(sdata, BSS_CHANGED_IDLE); 888fd0f979aSJohannes Berg 88917c18bf8SJohannes Berg ieee80211_check_fast_xmit_iface(sdata); 89017c18bf8SJohannes Berg 89177eeba97SLuciano Coelho return ret; 892d01a1e65SMichal Kazior } 893d01a1e65SMichal Kazior 89404ecd257SJohannes Berg void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local, 89504ecd257SJohannes Berg struct ieee80211_chanctx *chanctx) 89604ecd257SJohannes Berg { 89704ecd257SJohannes Berg struct ieee80211_sub_if_data *sdata; 89804ecd257SJohannes Berg u8 rx_chains_static, rx_chains_dynamic; 89904ecd257SJohannes Berg 90004ecd257SJohannes Berg lockdep_assert_held(&local->chanctx_mtx); 90104ecd257SJohannes Berg 90204ecd257SJohannes Berg rx_chains_static = 1; 90304ecd257SJohannes Berg rx_chains_dynamic = 1; 90404ecd257SJohannes Berg 90504ecd257SJohannes Berg rcu_read_lock(); 90604ecd257SJohannes Berg list_for_each_entry_rcu(sdata, &local->interfaces, list) { 90704ecd257SJohannes Berg u8 needed_static, needed_dynamic; 908b4f85443SJohannes Berg unsigned int link_id; 90904ecd257SJohannes Berg 91004ecd257SJohannes Berg if (!ieee80211_sdata_running(sdata)) 91104ecd257SJohannes Berg continue; 91204ecd257SJohannes Berg 91304ecd257SJohannes Berg switch (sdata->vif.type) { 91404ecd257SJohannes Berg case NL80211_IFTYPE_STATION: 91504ecd257SJohannes Berg if (!sdata->u.mgd.associated) 91604ecd257SJohannes Berg continue; 91704ecd257SJohannes Berg break; 91804ecd257SJohannes Berg case NL80211_IFTYPE_AP: 91904ecd257SJohannes Berg case NL80211_IFTYPE_ADHOC: 92004ecd257SJohannes Berg case NL80211_IFTYPE_MESH_POINT: 921239281f8SRostislav Lisovy case NL80211_IFTYPE_OCB: 92204ecd257SJohannes Berg break; 92304ecd257SJohannes Berg default: 924b4f85443SJohannes Berg continue; 92504ecd257SJohannes Berg } 92604ecd257SJohannes Berg 927b4f85443SJohannes Berg for (link_id = 0; link_id < ARRAY_SIZE(sdata->link); link_id++) { 928d8675a63SJohannes Berg struct ieee80211_link_data *link; 929b4f85443SJohannes Berg 930d8675a63SJohannes Berg link = rcu_dereference(sdata->link[link_id]); 931d8675a63SJohannes Berg 932d8675a63SJohannes Berg if (!link) 933b4f85443SJohannes Berg continue; 934b4f85443SJohannes Berg 935d8675a63SJohannes Berg if (rcu_access_pointer(link->conf->chanctx_conf) != &chanctx->conf) 936b4f85443SJohannes Berg continue; 937b4f85443SJohannes Berg 938b4f85443SJohannes Berg switch (link->smps_mode) { 93904ecd257SJohannes Berg default: 94004ecd257SJohannes Berg WARN_ONCE(1, "Invalid SMPS mode %d\n", 941b4f85443SJohannes Berg link->smps_mode); 942fc0561dcSGustavo A. R. Silva fallthrough; 94304ecd257SJohannes Berg case IEEE80211_SMPS_OFF: 944b4f85443SJohannes Berg needed_static = link->needed_rx_chains; 945b4f85443SJohannes Berg needed_dynamic = link->needed_rx_chains; 94604ecd257SJohannes Berg break; 94704ecd257SJohannes Berg case IEEE80211_SMPS_DYNAMIC: 94804ecd257SJohannes Berg needed_static = 1; 949b4f85443SJohannes Berg needed_dynamic = link->needed_rx_chains; 95004ecd257SJohannes Berg break; 95104ecd257SJohannes Berg case IEEE80211_SMPS_STATIC: 95204ecd257SJohannes Berg needed_static = 1; 95304ecd257SJohannes Berg needed_dynamic = 1; 95404ecd257SJohannes Berg break; 95504ecd257SJohannes Berg } 95604ecd257SJohannes Berg 95704ecd257SJohannes Berg rx_chains_static = max(rx_chains_static, needed_static); 95804ecd257SJohannes Berg rx_chains_dynamic = max(rx_chains_dynamic, needed_dynamic); 95904ecd257SJohannes Berg } 960b4f85443SJohannes Berg } 9617b8a9cddSIdo Yariv 9627b8a9cddSIdo Yariv /* Disable SMPS for the monitor interface */ 9637b8a9cddSIdo Yariv sdata = rcu_dereference(local->monitor_sdata); 9647b8a9cddSIdo Yariv if (sdata && 965d8675a63SJohannes Berg rcu_access_pointer(sdata->vif.bss_conf.chanctx_conf) == &chanctx->conf) 9667b8a9cddSIdo Yariv rx_chains_dynamic = rx_chains_static = local->rx_chains; 9677b8a9cddSIdo Yariv 96804ecd257SJohannes Berg rcu_read_unlock(); 96904ecd257SJohannes Berg 97004ecd257SJohannes Berg if (!local->use_chanctx) { 97104ecd257SJohannes Berg if (rx_chains_static > 1) 97204ecd257SJohannes Berg local->smps_mode = IEEE80211_SMPS_OFF; 97304ecd257SJohannes Berg else if (rx_chains_dynamic > 1) 97404ecd257SJohannes Berg local->smps_mode = IEEE80211_SMPS_DYNAMIC; 97504ecd257SJohannes Berg else 97604ecd257SJohannes Berg local->smps_mode = IEEE80211_SMPS_STATIC; 97704ecd257SJohannes Berg ieee80211_hw_config(local, 0); 97804ecd257SJohannes Berg } 97904ecd257SJohannes Berg 98004ecd257SJohannes Berg if (rx_chains_static == chanctx->conf.rx_chains_static && 98104ecd257SJohannes Berg rx_chains_dynamic == chanctx->conf.rx_chains_dynamic) 98204ecd257SJohannes Berg return; 98304ecd257SJohannes Berg 98404ecd257SJohannes Berg chanctx->conf.rx_chains_static = rx_chains_static; 98504ecd257SJohannes Berg chanctx->conf.rx_chains_dynamic = rx_chains_dynamic; 98604ecd257SJohannes Berg drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RX_CHAINS); 98704ecd257SJohannes Berg } 98804ecd257SJohannes Berg 98911335a55SLuciano Coelho static void 990b4f85443SJohannes Berg __ieee80211_link_copy_chanctx_to_vlans(struct ieee80211_link_data *link, 99111335a55SLuciano Coelho bool clear) 99211335a55SLuciano Coelho { 993b4f85443SJohannes Berg struct ieee80211_sub_if_data *sdata = link->sdata; 994b4f85443SJohannes Berg unsigned int link_id = link->link_id; 995d8675a63SJohannes Berg struct ieee80211_bss_conf *link_conf = link->conf; 99633926eb7SJohannes Berg struct ieee80211_local *local __maybe_unused = sdata->local; 99711335a55SLuciano Coelho struct ieee80211_sub_if_data *vlan; 99811335a55SLuciano Coelho struct ieee80211_chanctx_conf *conf; 99911335a55SLuciano Coelho 100011335a55SLuciano Coelho if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP)) 100111335a55SLuciano Coelho return; 100211335a55SLuciano Coelho 100311335a55SLuciano Coelho lockdep_assert_held(&local->mtx); 100411335a55SLuciano Coelho 100511335a55SLuciano Coelho /* Check that conf exists, even when clearing this function 100611335a55SLuciano Coelho * must be called with the AP's channel context still there 100711335a55SLuciano Coelho * as it would otherwise cause VLANs to have an invalid 100811335a55SLuciano Coelho * channel context pointer for a while, possibly pointing 100911335a55SLuciano Coelho * to a channel context that has already been freed. 101011335a55SLuciano Coelho */ 1011b4f85443SJohannes Berg conf = rcu_dereference_protected(link_conf->chanctx_conf, 101211335a55SLuciano Coelho lockdep_is_held(&local->chanctx_mtx)); 101311335a55SLuciano Coelho WARN_ON(!conf); 101411335a55SLuciano Coelho 101511335a55SLuciano Coelho if (clear) 101611335a55SLuciano Coelho conf = NULL; 101711335a55SLuciano Coelho 1018d8675a63SJohannes Berg rcu_read_lock(); 1019d8675a63SJohannes Berg list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) { 1020d8675a63SJohannes Berg struct ieee80211_bss_conf *vlan_conf; 1021d8675a63SJohannes Berg 1022d8675a63SJohannes Berg vlan_conf = rcu_dereference(vlan->vif.link_conf[link_id]); 1023d8675a63SJohannes Berg if (WARN_ON(!vlan_conf)) 1024d8675a63SJohannes Berg continue; 1025d8675a63SJohannes Berg 1026d8675a63SJohannes Berg rcu_assign_pointer(vlan_conf->chanctx_conf, conf); 1027d8675a63SJohannes Berg } 1028d8675a63SJohannes Berg rcu_read_unlock(); 102911335a55SLuciano Coelho } 103011335a55SLuciano Coelho 1031b4f85443SJohannes Berg void ieee80211_link_copy_chanctx_to_vlans(struct ieee80211_link_data *link, 103211335a55SLuciano Coelho bool clear) 103311335a55SLuciano Coelho { 1034b4f85443SJohannes Berg struct ieee80211_local *local = link->sdata->local; 103511335a55SLuciano Coelho 103611335a55SLuciano Coelho mutex_lock(&local->chanctx_mtx); 103711335a55SLuciano Coelho 1038b4f85443SJohannes Berg __ieee80211_link_copy_chanctx_to_vlans(link, clear); 103911335a55SLuciano Coelho 104011335a55SLuciano Coelho mutex_unlock(&local->chanctx_mtx); 104111335a55SLuciano Coelho } 104211335a55SLuciano Coelho 1043b4f85443SJohannes Berg int ieee80211_link_unreserve_chanctx(struct ieee80211_link_data *link) 104411335a55SLuciano Coelho { 1045b4f85443SJohannes Berg struct ieee80211_sub_if_data *sdata = link->sdata; 1046b4f85443SJohannes Berg struct ieee80211_chanctx *ctx = link->reserved_chanctx; 1047e3afb920SMichal Kazior 104811335a55SLuciano Coelho lockdep_assert_held(&sdata->local->chanctx_mtx); 104911335a55SLuciano Coelho 1050e3afb920SMichal Kazior if (WARN_ON(!ctx)) 105111335a55SLuciano Coelho return -EINVAL; 105211335a55SLuciano Coelho 1053b4f85443SJohannes Berg list_del(&link->reserved_chanctx_list); 1054b4f85443SJohannes Berg link->reserved_chanctx = NULL; 105511335a55SLuciano Coelho 10565bcae31dSMichal Kazior if (ieee80211_chanctx_refcount(sdata->local, ctx) == 0) { 10575bcae31dSMichal Kazior if (ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER) { 10585bcae31dSMichal Kazior if (WARN_ON(!ctx->replace_ctx)) 10595bcae31dSMichal Kazior return -EINVAL; 10605bcae31dSMichal Kazior 10615bcae31dSMichal Kazior WARN_ON(ctx->replace_ctx->replace_state != 10625bcae31dSMichal Kazior IEEE80211_CHANCTX_WILL_BE_REPLACED); 10635bcae31dSMichal Kazior WARN_ON(ctx->replace_ctx->replace_ctx != ctx); 10645bcae31dSMichal Kazior 10655bcae31dSMichal Kazior ctx->replace_ctx->replace_ctx = NULL; 10665bcae31dSMichal Kazior ctx->replace_ctx->replace_state = 10675bcae31dSMichal Kazior IEEE80211_CHANCTX_REPLACE_NONE; 10685bcae31dSMichal Kazior 10695bcae31dSMichal Kazior list_del_rcu(&ctx->list); 10705bcae31dSMichal Kazior kfree_rcu(ctx, rcu_head); 10715bcae31dSMichal Kazior } else { 1072e3afb920SMichal Kazior ieee80211_free_chanctx(sdata->local, ctx); 10735bcae31dSMichal Kazior } 10745bcae31dSMichal Kazior } 1075e3afb920SMichal Kazior 107611335a55SLuciano Coelho return 0; 107711335a55SLuciano Coelho } 107811335a55SLuciano Coelho 1079b4f85443SJohannes Berg int ieee80211_link_reserve_chanctx(struct ieee80211_link_data *link, 108011335a55SLuciano Coelho const struct cfg80211_chan_def *chandef, 108109332481SMichal Kazior enum ieee80211_chanctx_mode mode, 108209332481SMichal Kazior bool radar_required) 108311335a55SLuciano Coelho { 1084b4f85443SJohannes Berg struct ieee80211_sub_if_data *sdata = link->sdata; 108511335a55SLuciano Coelho struct ieee80211_local *local = sdata->local; 10865bcae31dSMichal Kazior struct ieee80211_chanctx *new_ctx, *curr_ctx, *ctx; 108711335a55SLuciano Coelho 10885bcae31dSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 108911335a55SLuciano Coelho 1090b4f85443SJohannes Berg curr_ctx = ieee80211_link_get_chanctx(link); 10915bcae31dSMichal Kazior if (curr_ctx && local->use_chanctx && !local->ops->switch_vif_chanctx) 10925bcae31dSMichal Kazior return -ENOTSUPP; 109311335a55SLuciano Coelho 109413f348a8SMichal Kazior new_ctx = ieee80211_find_reservation_chanctx(local, chandef, mode); 109511335a55SLuciano Coelho if (!new_ctx) { 10965bcae31dSMichal Kazior if (ieee80211_can_create_new_chanctx(local)) { 109711335a55SLuciano Coelho new_ctx = ieee80211_new_chanctx(local, chandef, mode); 10985bcae31dSMichal Kazior if (IS_ERR(new_ctx)) 10995bcae31dSMichal Kazior return PTR_ERR(new_ctx); 1100c2b90ad8SMichal Kazior } else { 11015bcae31dSMichal Kazior if (!curr_ctx || 11025bcae31dSMichal Kazior (curr_ctx->replace_state == 11035bcae31dSMichal Kazior IEEE80211_CHANCTX_WILL_BE_REPLACED) || 1104b4f85443SJohannes Berg !list_empty(&curr_ctx->reserved_links)) { 11055bcae31dSMichal Kazior /* 1106b4f85443SJohannes Berg * Another link already requested this context 11075bcae31dSMichal Kazior * for a reservation. Find another one hoping 1108b4f85443SJohannes Berg * all links assigned to it will also switch 11095bcae31dSMichal Kazior * soon enough. 11105bcae31dSMichal Kazior * 11115bcae31dSMichal Kazior * TODO: This needs a little more work as some 11125bcae31dSMichal Kazior * cases (more than 2 chanctx capable devices) 11135bcae31dSMichal Kazior * may fail which could otherwise succeed 11145bcae31dSMichal Kazior * provided some channel context juggling was 11155bcae31dSMichal Kazior * performed. 11165bcae31dSMichal Kazior * 1117b4f85443SJohannes Berg * Consider ctx1..3, link1..6, each ctx has 2 1118b4f85443SJohannes Berg * links. link1 and link2 from ctx1 request new 11195bcae31dSMichal Kazior * different chandefs starting 2 in-place 11205bcae31dSMichal Kazior * reserations with ctx4 and ctx5 replacing 1121b4f85443SJohannes Berg * ctx1 and ctx2 respectively. Next link5 and 1122b4f85443SJohannes Berg * link6 from ctx3 reserve ctx4. If link3 and 1123b4f85443SJohannes Berg * link4 remain on ctx2 as they are then this 11245bcae31dSMichal Kazior * fails unless `replace_ctx` from ctx5 is 11255bcae31dSMichal Kazior * replaced with ctx3. 11265bcae31dSMichal Kazior */ 11275bcae31dSMichal Kazior list_for_each_entry(ctx, &local->chanctx_list, 11285bcae31dSMichal Kazior list) { 11295bcae31dSMichal Kazior if (ctx->replace_state != 11305bcae31dSMichal Kazior IEEE80211_CHANCTX_REPLACE_NONE) 11315bcae31dSMichal Kazior continue; 11325bcae31dSMichal Kazior 1133b4f85443SJohannes Berg if (!list_empty(&ctx->reserved_links)) 11345bcae31dSMichal Kazior continue; 11355bcae31dSMichal Kazior 11365bcae31dSMichal Kazior curr_ctx = ctx; 11375bcae31dSMichal Kazior break; 11385bcae31dSMichal Kazior } 11395bcae31dSMichal Kazior } 11405bcae31dSMichal Kazior 11415bcae31dSMichal Kazior /* 11425bcae31dSMichal Kazior * If that's true then all available contexts already 11435bcae31dSMichal Kazior * have reservations and cannot be used. 11445bcae31dSMichal Kazior */ 11455bcae31dSMichal Kazior if (!curr_ctx || 11465bcae31dSMichal Kazior (curr_ctx->replace_state == 11475bcae31dSMichal Kazior IEEE80211_CHANCTX_WILL_BE_REPLACED) || 1148b4f85443SJohannes Berg !list_empty(&curr_ctx->reserved_links)) 11495bcae31dSMichal Kazior return -EBUSY; 11505bcae31dSMichal Kazior 11515bcae31dSMichal Kazior new_ctx = ieee80211_alloc_chanctx(local, chandef, mode); 11525bcae31dSMichal Kazior if (!new_ctx) 11535bcae31dSMichal Kazior return -ENOMEM; 11545bcae31dSMichal Kazior 11555bcae31dSMichal Kazior new_ctx->replace_ctx = curr_ctx; 11565bcae31dSMichal Kazior new_ctx->replace_state = 11575bcae31dSMichal Kazior IEEE80211_CHANCTX_REPLACES_OTHER; 11585bcae31dSMichal Kazior 11595bcae31dSMichal Kazior curr_ctx->replace_ctx = new_ctx; 11605bcae31dSMichal Kazior curr_ctx->replace_state = 11615bcae31dSMichal Kazior IEEE80211_CHANCTX_WILL_BE_REPLACED; 11625bcae31dSMichal Kazior 11635bcae31dSMichal Kazior list_add_rcu(&new_ctx->list, &local->chanctx_list); 116411335a55SLuciano Coelho } 11655d52ee81SLuciano Coelho } 116611335a55SLuciano Coelho 1167b4f85443SJohannes Berg list_add(&link->reserved_chanctx_list, &new_ctx->reserved_links); 1168b4f85443SJohannes Berg link->reserved_chanctx = new_ctx; 1169b4f85443SJohannes Berg link->reserved_chandef = *chandef; 1170b4f85443SJohannes Berg link->reserved_radar_required = radar_required; 1171b4f85443SJohannes Berg link->reserved_ready = false; 11725bcae31dSMichal Kazior 11735bcae31dSMichal Kazior return 0; 117411335a55SLuciano Coelho } 117511335a55SLuciano Coelho 117603078de4SMichal Kazior static void 1177b4f85443SJohannes Berg ieee80211_link_chanctx_reservation_complete(struct ieee80211_link_data *link) 117803078de4SMichal Kazior { 1179b4f85443SJohannes Berg struct ieee80211_sub_if_data *sdata = link->sdata; 1180b4f85443SJohannes Berg 118103078de4SMichal Kazior switch (sdata->vif.type) { 118203078de4SMichal Kazior case NL80211_IFTYPE_ADHOC: 118303078de4SMichal Kazior case NL80211_IFTYPE_AP: 118403078de4SMichal Kazior case NL80211_IFTYPE_MESH_POINT: 11856e0bd6c3SRostislav Lisovy case NL80211_IFTYPE_OCB: 118603078de4SMichal Kazior ieee80211_queue_work(&sdata->local->hw, 1187b4f85443SJohannes Berg &link->csa_finalize_work); 118803078de4SMichal Kazior break; 118903078de4SMichal Kazior case NL80211_IFTYPE_STATION: 11904c3ebc56SMichal Kazior ieee80211_queue_work(&sdata->local->hw, 1191*5bd5666dSJohannes Berg &link->u.mgd.chswitch_work); 11924c3ebc56SMichal Kazior break; 11934c3ebc56SMichal Kazior case NL80211_IFTYPE_UNSPECIFIED: 119403078de4SMichal Kazior case NL80211_IFTYPE_AP_VLAN: 119503078de4SMichal Kazior case NL80211_IFTYPE_WDS: 119603078de4SMichal Kazior case NL80211_IFTYPE_MONITOR: 119703078de4SMichal Kazior case NL80211_IFTYPE_P2P_CLIENT: 119803078de4SMichal Kazior case NL80211_IFTYPE_P2P_GO: 119903078de4SMichal Kazior case NL80211_IFTYPE_P2P_DEVICE: 1200cb3b7d87SAyala Beker case NL80211_IFTYPE_NAN: 120103078de4SMichal Kazior case NUM_NL80211_IFTYPES: 120203078de4SMichal Kazior WARN_ON(1); 120303078de4SMichal Kazior break; 120403078de4SMichal Kazior } 120503078de4SMichal Kazior } 120603078de4SMichal Kazior 12072967e031SFelix Fietkau static void 1208b4f85443SJohannes Berg ieee80211_link_update_chandef(struct ieee80211_link_data *link, 12092967e031SFelix Fietkau const struct cfg80211_chan_def *chandef) 12102967e031SFelix Fietkau { 1211b4f85443SJohannes Berg struct ieee80211_sub_if_data *sdata = link->sdata; 1212b4f85443SJohannes Berg unsigned int link_id = link->link_id; 12132967e031SFelix Fietkau struct ieee80211_sub_if_data *vlan; 12142967e031SFelix Fietkau 1215d8675a63SJohannes Berg link->conf->chandef = *chandef; 12162967e031SFelix Fietkau 12172967e031SFelix Fietkau if (sdata->vif.type != NL80211_IFTYPE_AP) 12182967e031SFelix Fietkau return; 12192967e031SFelix Fietkau 1220d8675a63SJohannes Berg rcu_read_lock(); 1221d8675a63SJohannes Berg list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) { 1222d8675a63SJohannes Berg struct ieee80211_bss_conf *vlan_conf; 1223d8675a63SJohannes Berg 1224d8675a63SJohannes Berg vlan_conf = rcu_dereference(vlan->vif.link_conf[link_id]); 1225d8675a63SJohannes Berg if (WARN_ON(!vlan_conf)) 1226d8675a63SJohannes Berg continue; 1227d8675a63SJohannes Berg 1228d8675a63SJohannes Berg vlan_conf->chandef = *chandef; 1229d8675a63SJohannes Berg } 1230d8675a63SJohannes Berg rcu_read_unlock(); 12312967e031SFelix Fietkau } 12322967e031SFelix Fietkau 12335bcae31dSMichal Kazior static int 1234b4f85443SJohannes Berg ieee80211_link_use_reserved_reassign(struct ieee80211_link_data *link) 123511335a55SLuciano Coelho { 1236b4f85443SJohannes Berg struct ieee80211_sub_if_data *sdata = link->sdata; 1237d8675a63SJohannes Berg struct ieee80211_bss_conf *link_conf = link->conf; 123811335a55SLuciano Coelho struct ieee80211_local *local = sdata->local; 12395bcae31dSMichal Kazior struct ieee80211_vif_chanctx_switch vif_chsw[1] = {}; 12405bcae31dSMichal Kazior struct ieee80211_chanctx *old_ctx, *new_ctx; 12415bcae31dSMichal Kazior const struct cfg80211_chan_def *chandef; 12425bcae31dSMichal Kazior u32 changed = 0; 12435bcae31dSMichal Kazior int err; 124411335a55SLuciano Coelho 124511335a55SLuciano Coelho lockdep_assert_held(&local->mtx); 12465bcae31dSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 124711335a55SLuciano Coelho 1248b4f85443SJohannes Berg new_ctx = link->reserved_chanctx; 1249b4f85443SJohannes Berg old_ctx = ieee80211_link_get_chanctx(link); 125011335a55SLuciano Coelho 1251b4f85443SJohannes Berg if (WARN_ON(!link->reserved_ready)) 12525bcae31dSMichal Kazior return -EBUSY; 125311335a55SLuciano Coelho 12545bcae31dSMichal Kazior if (WARN_ON(!new_ctx)) 12555bcae31dSMichal Kazior return -EINVAL; 125611335a55SLuciano Coelho 12575bcae31dSMichal Kazior if (WARN_ON(!old_ctx)) 12585bcae31dSMichal Kazior return -EINVAL; 125911335a55SLuciano Coelho 12605bcae31dSMichal Kazior if (WARN_ON(new_ctx->replace_state == 12615bcae31dSMichal Kazior IEEE80211_CHANCTX_REPLACES_OTHER)) 12625bcae31dSMichal Kazior return -EINVAL; 126311335a55SLuciano Coelho 12645bcae31dSMichal Kazior chandef = ieee80211_chanctx_non_reserved_chandef(local, new_ctx, 1265b4f85443SJohannes Berg &link->reserved_chandef); 12665bcae31dSMichal Kazior if (WARN_ON(!chandef)) 12675bcae31dSMichal Kazior return -EINVAL; 126811335a55SLuciano Coelho 1269b4f85443SJohannes Berg if (link_conf->chandef.width != link->reserved_chandef.width) 1270d6c37509SMordechay Goodstein changed = BSS_CHANGED_BANDWIDTH; 127144b72ca8SIlan Peer 1272b4f85443SJohannes Berg ieee80211_link_update_chandef(link, &link->reserved_chandef); 1273bf45a242SAndrei Otcheretianski 1274d6c37509SMordechay Goodstein ieee80211_change_chanctx(local, new_ctx, old_ctx, chandef); 127544b72ca8SIlan Peer 12765bcae31dSMichal Kazior vif_chsw[0].vif = &sdata->vif; 12775bcae31dSMichal Kazior vif_chsw[0].old_ctx = &old_ctx->conf; 12785bcae31dSMichal Kazior vif_chsw[0].new_ctx = &new_ctx->conf; 1279b4f85443SJohannes Berg vif_chsw[0].link_id = link->link_id; 12805bcae31dSMichal Kazior 1281b4f85443SJohannes Berg list_del(&link->reserved_chanctx_list); 1282b4f85443SJohannes Berg link->reserved_chanctx = NULL; 128311335a55SLuciano Coelho 12845bcae31dSMichal Kazior err = drv_switch_vif_chanctx(local, vif_chsw, 1, 12855bcae31dSMichal Kazior CHANCTX_SWMODE_REASSIGN_VIF); 12865bcae31dSMichal Kazior if (err) { 12875bcae31dSMichal Kazior if (ieee80211_chanctx_refcount(local, new_ctx) == 0) 12885bcae31dSMichal Kazior ieee80211_free_chanctx(local, new_ctx); 12895bcae31dSMichal Kazior 129003078de4SMichal Kazior goto out; 129111335a55SLuciano Coelho } 129211335a55SLuciano Coelho 1293b4f85443SJohannes Berg list_move(&link->assigned_chanctx_list, &new_ctx->assigned_links); 1294b4f85443SJohannes Berg rcu_assign_pointer(link_conf->chanctx_conf, &new_ctx->conf); 12955bcae31dSMichal Kazior 129611335a55SLuciano Coelho if (sdata->vif.type == NL80211_IFTYPE_AP) 1297b4f85443SJohannes Berg __ieee80211_link_copy_chanctx_to_vlans(link, false); 12985bcae31dSMichal Kazior 129917c18bf8SJohannes Berg ieee80211_check_fast_xmit_iface(sdata); 130017c18bf8SJohannes Berg 13015bcae31dSMichal Kazior if (ieee80211_chanctx_refcount(local, old_ctx) == 0) 13025bcae31dSMichal Kazior ieee80211_free_chanctx(local, old_ctx); 13035bcae31dSMichal Kazior 1304d6c37509SMordechay Goodstein ieee80211_recalc_chanctx_min_def(local, new_ctx); 1305722ddb0dSEmmanuel Grumbach ieee80211_recalc_smps_chanctx(local, new_ctx); 1306722ddb0dSEmmanuel Grumbach ieee80211_recalc_radar_chanctx(local, new_ctx); 1307722ddb0dSEmmanuel Grumbach 13085bcae31dSMichal Kazior if (changed) 1309d8675a63SJohannes Berg ieee80211_link_info_change_notify(sdata, link, changed); 13105bcae31dSMichal Kazior 131103078de4SMichal Kazior out: 1312b4f85443SJohannes Berg ieee80211_link_chanctx_reservation_complete(link); 13135bcae31dSMichal Kazior return err; 13145d52ee81SLuciano Coelho } 131511335a55SLuciano Coelho 13165bcae31dSMichal Kazior static int 1317b4f85443SJohannes Berg ieee80211_link_use_reserved_assign(struct ieee80211_link_data *link) 13185bcae31dSMichal Kazior { 1319b4f85443SJohannes Berg struct ieee80211_sub_if_data *sdata = link->sdata; 13205bcae31dSMichal Kazior struct ieee80211_local *local = sdata->local; 13215bcae31dSMichal Kazior struct ieee80211_chanctx *old_ctx, *new_ctx; 13225bcae31dSMichal Kazior const struct cfg80211_chan_def *chandef; 13235bcae31dSMichal Kazior int err; 13245bcae31dSMichal Kazior 1325d8675a63SJohannes Berg old_ctx = ieee80211_link_get_chanctx(link); 1326b4f85443SJohannes Berg new_ctx = link->reserved_chanctx; 13275bcae31dSMichal Kazior 1328b4f85443SJohannes Berg if (WARN_ON(!link->reserved_ready)) 13295bcae31dSMichal Kazior return -EINVAL; 13305bcae31dSMichal Kazior 13315bcae31dSMichal Kazior if (WARN_ON(old_ctx)) 13325bcae31dSMichal Kazior return -EINVAL; 13335bcae31dSMichal Kazior 13345bcae31dSMichal Kazior if (WARN_ON(!new_ctx)) 13355bcae31dSMichal Kazior return -EINVAL; 13365bcae31dSMichal Kazior 13375bcae31dSMichal Kazior if (WARN_ON(new_ctx->replace_state == 13385bcae31dSMichal Kazior IEEE80211_CHANCTX_REPLACES_OTHER)) 13395bcae31dSMichal Kazior return -EINVAL; 13405bcae31dSMichal Kazior 13415bcae31dSMichal Kazior chandef = ieee80211_chanctx_non_reserved_chandef(local, new_ctx, 1342b4f85443SJohannes Berg &link->reserved_chandef); 13435bcae31dSMichal Kazior if (WARN_ON(!chandef)) 13445bcae31dSMichal Kazior return -EINVAL; 13455bcae31dSMichal Kazior 1346d6c37509SMordechay Goodstein ieee80211_change_chanctx(local, new_ctx, new_ctx, chandef); 1347bf45a242SAndrei Otcheretianski 1348b4f85443SJohannes Berg list_del(&link->reserved_chanctx_list); 1349b4f85443SJohannes Berg link->reserved_chanctx = NULL; 13505bcae31dSMichal Kazior 1351b4f85443SJohannes Berg err = ieee80211_assign_link_chanctx(link, new_ctx); 13525bcae31dSMichal Kazior if (err) { 13535bcae31dSMichal Kazior if (ieee80211_chanctx_refcount(local, new_ctx) == 0) 13545bcae31dSMichal Kazior ieee80211_free_chanctx(local, new_ctx); 13555bcae31dSMichal Kazior 13565bcae31dSMichal Kazior goto out; 13575bcae31dSMichal Kazior } 13585bcae31dSMichal Kazior 13595bcae31dSMichal Kazior out: 1360b4f85443SJohannes Berg ieee80211_link_chanctx_reservation_complete(link); 13615bcae31dSMichal Kazior return err; 13625bcae31dSMichal Kazior } 13635bcae31dSMichal Kazior 13645bcae31dSMichal Kazior static bool 1365b4f85443SJohannes Berg ieee80211_link_has_in_place_reservation(struct ieee80211_link_data *link) 13665bcae31dSMichal Kazior { 1367b4f85443SJohannes Berg struct ieee80211_sub_if_data *sdata = link->sdata; 13685bcae31dSMichal Kazior struct ieee80211_chanctx *old_ctx, *new_ctx; 13695bcae31dSMichal Kazior 13705bcae31dSMichal Kazior lockdep_assert_held(&sdata->local->chanctx_mtx); 13715bcae31dSMichal Kazior 1372b4f85443SJohannes Berg new_ctx = link->reserved_chanctx; 1373b4f85443SJohannes Berg old_ctx = ieee80211_link_get_chanctx(link); 13745bcae31dSMichal Kazior 13755bcae31dSMichal Kazior if (!old_ctx) 13765bcae31dSMichal Kazior return false; 13775bcae31dSMichal Kazior 13785bcae31dSMichal Kazior if (WARN_ON(!new_ctx)) 13795bcae31dSMichal Kazior return false; 13805bcae31dSMichal Kazior 13815bcae31dSMichal Kazior if (old_ctx->replace_state != IEEE80211_CHANCTX_WILL_BE_REPLACED) 13825bcae31dSMichal Kazior return false; 13835bcae31dSMichal Kazior 13845bcae31dSMichal Kazior if (new_ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) 13855bcae31dSMichal Kazior return false; 13865bcae31dSMichal Kazior 13875bcae31dSMichal Kazior return true; 13885bcae31dSMichal Kazior } 13895bcae31dSMichal Kazior 13905bcae31dSMichal Kazior static int ieee80211_chsw_switch_hwconf(struct ieee80211_local *local, 13915bcae31dSMichal Kazior struct ieee80211_chanctx *new_ctx) 13925bcae31dSMichal Kazior { 13935bcae31dSMichal Kazior const struct cfg80211_chan_def *chandef; 13945bcae31dSMichal Kazior 13955bcae31dSMichal Kazior lockdep_assert_held(&local->mtx); 13965bcae31dSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 13975bcae31dSMichal Kazior 13985bcae31dSMichal Kazior chandef = ieee80211_chanctx_reserved_chandef(local, new_ctx, NULL); 13995bcae31dSMichal Kazior if (WARN_ON(!chandef)) 14005bcae31dSMichal Kazior return -EINVAL; 14015bcae31dSMichal Kazior 14025bcae31dSMichal Kazior local->hw.conf.radar_enabled = new_ctx->conf.radar_enabled; 14035bcae31dSMichal Kazior local->_oper_chandef = *chandef; 14045bcae31dSMichal Kazior ieee80211_hw_config(local, 0); 14055bcae31dSMichal Kazior 14065bcae31dSMichal Kazior return 0; 14075bcae31dSMichal Kazior } 14085bcae31dSMichal Kazior 14095bcae31dSMichal Kazior static int ieee80211_chsw_switch_vifs(struct ieee80211_local *local, 14105bcae31dSMichal Kazior int n_vifs) 14115bcae31dSMichal Kazior { 14125bcae31dSMichal Kazior struct ieee80211_vif_chanctx_switch *vif_chsw; 1413b4f85443SJohannes Berg struct ieee80211_link_data *link; 14145bcae31dSMichal Kazior struct ieee80211_chanctx *ctx, *old_ctx; 14155bcae31dSMichal Kazior int i, err; 14165bcae31dSMichal Kazior 14175bcae31dSMichal Kazior lockdep_assert_held(&local->mtx); 14185bcae31dSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 14195bcae31dSMichal Kazior 14206396bb22SKees Cook vif_chsw = kcalloc(n_vifs, sizeof(vif_chsw[0]), GFP_KERNEL); 14215bcae31dSMichal Kazior if (!vif_chsw) 14225bcae31dSMichal Kazior return -ENOMEM; 14235bcae31dSMichal Kazior 14245bcae31dSMichal Kazior i = 0; 14255bcae31dSMichal Kazior list_for_each_entry(ctx, &local->chanctx_list, list) { 14265bcae31dSMichal Kazior if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) 14275bcae31dSMichal Kazior continue; 14285bcae31dSMichal Kazior 14295bcae31dSMichal Kazior if (WARN_ON(!ctx->replace_ctx)) { 14305bcae31dSMichal Kazior err = -EINVAL; 14315bcae31dSMichal Kazior goto out; 14325bcae31dSMichal Kazior } 14335bcae31dSMichal Kazior 1434b4f85443SJohannes Berg list_for_each_entry(link, &ctx->reserved_links, 14355bcae31dSMichal Kazior reserved_chanctx_list) { 1436b4f85443SJohannes Berg if (!ieee80211_link_has_in_place_reservation(link)) 14375bcae31dSMichal Kazior continue; 14385bcae31dSMichal Kazior 1439b4f85443SJohannes Berg old_ctx = ieee80211_link_get_chanctx(link); 1440b4f85443SJohannes Berg vif_chsw[i].vif = &link->sdata->vif; 14415bcae31dSMichal Kazior vif_chsw[i].old_ctx = &old_ctx->conf; 14425bcae31dSMichal Kazior vif_chsw[i].new_ctx = &ctx->conf; 1443b4f85443SJohannes Berg vif_chsw[i].link_id = link->link_id; 14445bcae31dSMichal Kazior 14455bcae31dSMichal Kazior i++; 14465bcae31dSMichal Kazior } 14475bcae31dSMichal Kazior } 14485bcae31dSMichal Kazior 14495bcae31dSMichal Kazior err = drv_switch_vif_chanctx(local, vif_chsw, n_vifs, 14505bcae31dSMichal Kazior CHANCTX_SWMODE_SWAP_CONTEXTS); 14515bcae31dSMichal Kazior 14525bcae31dSMichal Kazior out: 14535bcae31dSMichal Kazior kfree(vif_chsw); 14545bcae31dSMichal Kazior return err; 14555bcae31dSMichal Kazior } 14565bcae31dSMichal Kazior 14575bcae31dSMichal Kazior static int ieee80211_chsw_switch_ctxs(struct ieee80211_local *local) 14585bcae31dSMichal Kazior { 14595bcae31dSMichal Kazior struct ieee80211_chanctx *ctx; 14605bcae31dSMichal Kazior int err; 14615bcae31dSMichal Kazior 14625bcae31dSMichal Kazior lockdep_assert_held(&local->mtx); 14635bcae31dSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 14645bcae31dSMichal Kazior 14655bcae31dSMichal Kazior list_for_each_entry(ctx, &local->chanctx_list, list) { 14665bcae31dSMichal Kazior if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) 14675bcae31dSMichal Kazior continue; 14685bcae31dSMichal Kazior 1469b4f85443SJohannes Berg if (!list_empty(&ctx->replace_ctx->assigned_links)) 14705bcae31dSMichal Kazior continue; 14715bcae31dSMichal Kazior 14725bcae31dSMichal Kazior ieee80211_del_chanctx(local, ctx->replace_ctx); 14735bcae31dSMichal Kazior err = ieee80211_add_chanctx(local, ctx); 14745bcae31dSMichal Kazior if (err) 14755bcae31dSMichal Kazior goto err; 14765bcae31dSMichal Kazior } 14775bcae31dSMichal Kazior 14785bcae31dSMichal Kazior return 0; 14795bcae31dSMichal Kazior 14805bcae31dSMichal Kazior err: 14815bcae31dSMichal Kazior WARN_ON(ieee80211_add_chanctx(local, ctx)); 14825bcae31dSMichal Kazior list_for_each_entry_continue_reverse(ctx, &local->chanctx_list, list) { 14835bcae31dSMichal Kazior if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) 14845bcae31dSMichal Kazior continue; 14855bcae31dSMichal Kazior 1486b4f85443SJohannes Berg if (!list_empty(&ctx->replace_ctx->assigned_links)) 14875bcae31dSMichal Kazior continue; 14885bcae31dSMichal Kazior 14895bcae31dSMichal Kazior ieee80211_del_chanctx(local, ctx); 14905bcae31dSMichal Kazior WARN_ON(ieee80211_add_chanctx(local, ctx->replace_ctx)); 14915bcae31dSMichal Kazior } 14925bcae31dSMichal Kazior 14935bcae31dSMichal Kazior return err; 14945bcae31dSMichal Kazior } 14955bcae31dSMichal Kazior 1496649b2a4dSJohannes Berg static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local) 14975bcae31dSMichal Kazior { 14985bcae31dSMichal Kazior struct ieee80211_chanctx *ctx, *ctx_tmp, *old_ctx; 14995bcae31dSMichal Kazior struct ieee80211_chanctx *new_ctx = NULL; 1500b7f2405cSKirtika Ruchandani int err, n_assigned, n_reserved, n_ready; 15015bcae31dSMichal Kazior int n_ctx = 0, n_vifs_switch = 0, n_vifs_assign = 0, n_vifs_ctxless = 0; 15025bcae31dSMichal Kazior 15035bcae31dSMichal Kazior lockdep_assert_held(&local->mtx); 15045bcae31dSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 15055bcae31dSMichal Kazior 15065bcae31dSMichal Kazior /* 15075bcae31dSMichal Kazior * If there are 2 independent pairs of channel contexts performing 15085bcae31dSMichal Kazior * cross-switch of their vifs this code will still wait until both are 15095bcae31dSMichal Kazior * ready even though it could be possible to switch one before the 15105bcae31dSMichal Kazior * other is ready. 15115bcae31dSMichal Kazior * 15125bcae31dSMichal Kazior * For practical reasons and code simplicity just do a single huge 15135bcae31dSMichal Kazior * switch. 15145bcae31dSMichal Kazior */ 15155bcae31dSMichal Kazior 15165bcae31dSMichal Kazior /* 15175bcae31dSMichal Kazior * Verify if the reservation is still feasible. 15185bcae31dSMichal Kazior * - if it's not then disconnect 15195bcae31dSMichal Kazior * - if it is but not all vifs necessary are ready then defer 15205bcae31dSMichal Kazior */ 15215bcae31dSMichal Kazior 15225bcae31dSMichal Kazior list_for_each_entry(ctx, &local->chanctx_list, list) { 1523b4f85443SJohannes Berg struct ieee80211_link_data *link; 1524b4f85443SJohannes Berg 15255bcae31dSMichal Kazior if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) 15265bcae31dSMichal Kazior continue; 15275bcae31dSMichal Kazior 15285bcae31dSMichal Kazior if (WARN_ON(!ctx->replace_ctx)) { 15295bcae31dSMichal Kazior err = -EINVAL; 15305bcae31dSMichal Kazior goto err; 15315bcae31dSMichal Kazior } 15325bcae31dSMichal Kazior 15335bcae31dSMichal Kazior if (!local->use_chanctx) 15345bcae31dSMichal Kazior new_ctx = ctx; 15355bcae31dSMichal Kazior 15365bcae31dSMichal Kazior n_ctx++; 15375bcae31dSMichal Kazior 15385bcae31dSMichal Kazior n_assigned = 0; 15395bcae31dSMichal Kazior n_reserved = 0; 15405bcae31dSMichal Kazior n_ready = 0; 15415bcae31dSMichal Kazior 1542b4f85443SJohannes Berg list_for_each_entry(link, &ctx->replace_ctx->assigned_links, 15435bcae31dSMichal Kazior assigned_chanctx_list) { 15445bcae31dSMichal Kazior n_assigned++; 1545b4f85443SJohannes Berg if (link->reserved_chanctx) { 15465bcae31dSMichal Kazior n_reserved++; 1547b4f85443SJohannes Berg if (link->reserved_ready) 15485bcae31dSMichal Kazior n_ready++; 15495bcae31dSMichal Kazior } 15505bcae31dSMichal Kazior } 15515bcae31dSMichal Kazior 15525bcae31dSMichal Kazior if (n_assigned != n_reserved) { 15535bcae31dSMichal Kazior if (n_ready == n_reserved) { 15545bcae31dSMichal Kazior wiphy_info(local->hw.wiphy, 15555bcae31dSMichal Kazior "channel context reservation cannot be finalized because some interfaces aren't switching\n"); 15565bcae31dSMichal Kazior err = -EBUSY; 15575bcae31dSMichal Kazior goto err; 15585bcae31dSMichal Kazior } 15595bcae31dSMichal Kazior 15605bcae31dSMichal Kazior return -EAGAIN; 15615bcae31dSMichal Kazior } 15625bcae31dSMichal Kazior 15635bcae31dSMichal Kazior ctx->conf.radar_enabled = false; 1564b4f85443SJohannes Berg list_for_each_entry(link, &ctx->reserved_links, 15655bcae31dSMichal Kazior reserved_chanctx_list) { 1566b4f85443SJohannes Berg if (ieee80211_link_has_in_place_reservation(link) && 1567b4f85443SJohannes Berg !link->reserved_ready) 15685bcae31dSMichal Kazior return -EAGAIN; 15695bcae31dSMichal Kazior 1570b4f85443SJohannes Berg old_ctx = ieee80211_link_get_chanctx(link); 15715bcae31dSMichal Kazior if (old_ctx) { 15725bcae31dSMichal Kazior if (old_ctx->replace_state == 15735bcae31dSMichal Kazior IEEE80211_CHANCTX_WILL_BE_REPLACED) 15745bcae31dSMichal Kazior n_vifs_switch++; 15755bcae31dSMichal Kazior else 15765bcae31dSMichal Kazior n_vifs_assign++; 15775bcae31dSMichal Kazior } else { 15785bcae31dSMichal Kazior n_vifs_ctxless++; 15795bcae31dSMichal Kazior } 15805bcae31dSMichal Kazior 1581b4f85443SJohannes Berg if (link->reserved_radar_required) 15825bcae31dSMichal Kazior ctx->conf.radar_enabled = true; 15835bcae31dSMichal Kazior } 15845bcae31dSMichal Kazior } 15855bcae31dSMichal Kazior 15865bcae31dSMichal Kazior if (WARN_ON(n_ctx == 0) || 15875bcae31dSMichal Kazior WARN_ON(n_vifs_switch == 0 && 15885bcae31dSMichal Kazior n_vifs_assign == 0 && 15895bcae31dSMichal Kazior n_vifs_ctxless == 0) || 15905bcae31dSMichal Kazior WARN_ON(n_ctx > 1 && !local->use_chanctx) || 15915bcae31dSMichal Kazior WARN_ON(!new_ctx && !local->use_chanctx)) { 15925bcae31dSMichal Kazior err = -EINVAL; 15935bcae31dSMichal Kazior goto err; 15945bcae31dSMichal Kazior } 15955bcae31dSMichal Kazior 15965bcae31dSMichal Kazior /* 15975bcae31dSMichal Kazior * All necessary vifs are ready. Perform the switch now depending on 15985bcae31dSMichal Kazior * reservations and driver capabilities. 15995bcae31dSMichal Kazior */ 16005bcae31dSMichal Kazior 16015bcae31dSMichal Kazior if (local->use_chanctx) { 16025bcae31dSMichal Kazior if (n_vifs_switch > 0) { 16035bcae31dSMichal Kazior err = ieee80211_chsw_switch_vifs(local, n_vifs_switch); 16045bcae31dSMichal Kazior if (err) 16055bcae31dSMichal Kazior goto err; 16065bcae31dSMichal Kazior } 16075bcae31dSMichal Kazior 16085bcae31dSMichal Kazior if (n_vifs_assign > 0 || n_vifs_ctxless > 0) { 16095bcae31dSMichal Kazior err = ieee80211_chsw_switch_ctxs(local); 16105bcae31dSMichal Kazior if (err) 16115bcae31dSMichal Kazior goto err; 16125bcae31dSMichal Kazior } 16135bcae31dSMichal Kazior } else { 16145bcae31dSMichal Kazior err = ieee80211_chsw_switch_hwconf(local, new_ctx); 16155bcae31dSMichal Kazior if (err) 16165bcae31dSMichal Kazior goto err; 16175bcae31dSMichal Kazior } 16185bcae31dSMichal Kazior 16195bcae31dSMichal Kazior /* 16205bcae31dSMichal Kazior * Update all structures, values and pointers to point to new channel 16215bcae31dSMichal Kazior * context(s). 16225bcae31dSMichal Kazior */ 16235bcae31dSMichal Kazior list_for_each_entry(ctx, &local->chanctx_list, list) { 1624b4f85443SJohannes Berg struct ieee80211_link_data *link, *link_tmp; 1625b4f85443SJohannes Berg 16265bcae31dSMichal Kazior if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) 16275bcae31dSMichal Kazior continue; 16285bcae31dSMichal Kazior 16295bcae31dSMichal Kazior if (WARN_ON(!ctx->replace_ctx)) { 16305bcae31dSMichal Kazior err = -EINVAL; 16315bcae31dSMichal Kazior goto err; 16325bcae31dSMichal Kazior } 16335bcae31dSMichal Kazior 1634b4f85443SJohannes Berg list_for_each_entry(link, &ctx->reserved_links, 16355bcae31dSMichal Kazior reserved_chanctx_list) { 1636b4f85443SJohannes Berg struct ieee80211_sub_if_data *sdata = link->sdata; 1637d8675a63SJohannes Berg struct ieee80211_bss_conf *link_conf = link->conf; 16385bcae31dSMichal Kazior u32 changed = 0; 16395bcae31dSMichal Kazior 1640b4f85443SJohannes Berg if (!ieee80211_link_has_in_place_reservation(link)) 16415bcae31dSMichal Kazior continue; 16425bcae31dSMichal Kazior 1643b4f85443SJohannes Berg rcu_assign_pointer(link_conf->chanctx_conf, 1644d0a9123eSJohannes Berg &ctx->conf); 16455bcae31dSMichal Kazior 16465bcae31dSMichal Kazior if (sdata->vif.type == NL80211_IFTYPE_AP) 1647b4f85443SJohannes Berg __ieee80211_link_copy_chanctx_to_vlans(link, 16485bcae31dSMichal Kazior false); 16495bcae31dSMichal Kazior 165017c18bf8SJohannes Berg ieee80211_check_fast_xmit_iface(sdata); 165117c18bf8SJohannes Berg 1652b4f85443SJohannes Berg link->radar_required = link->reserved_radar_required; 16535bcae31dSMichal Kazior 1654b4f85443SJohannes Berg if (link_conf->chandef.width != link->reserved_chandef.width) 16555bcae31dSMichal Kazior changed = BSS_CHANGED_BANDWIDTH; 16565bcae31dSMichal Kazior 1657b4f85443SJohannes Berg ieee80211_link_update_chandef(link, &link->reserved_chandef); 16585bcae31dSMichal Kazior if (changed) 1659b4f85443SJohannes Berg ieee80211_link_info_change_notify(sdata, 1660d8675a63SJohannes Berg link, 16615bcae31dSMichal Kazior changed); 16625bcae31dSMichal Kazior 1663db82d8a9SLorenzo Bianconi ieee80211_recalc_txpower(sdata, false); 16645bcae31dSMichal Kazior } 166511335a55SLuciano Coelho 166611335a55SLuciano Coelho ieee80211_recalc_chanctx_chantype(local, ctx); 166711335a55SLuciano Coelho ieee80211_recalc_smps_chanctx(local, ctx); 166811335a55SLuciano Coelho ieee80211_recalc_radar_chanctx(local, ctx); 166911335a55SLuciano Coelho ieee80211_recalc_chanctx_min_def(local, ctx); 16705bcae31dSMichal Kazior 1671b4f85443SJohannes Berg list_for_each_entry_safe(link, link_tmp, &ctx->reserved_links, 16725bcae31dSMichal Kazior reserved_chanctx_list) { 1673b4f85443SJohannes Berg if (ieee80211_link_get_chanctx(link) != ctx) 16745bcae31dSMichal Kazior continue; 16755bcae31dSMichal Kazior 1676b4f85443SJohannes Berg list_del(&link->reserved_chanctx_list); 1677b4f85443SJohannes Berg list_move(&link->assigned_chanctx_list, 1678b4f85443SJohannes Berg &ctx->assigned_links); 1679b4f85443SJohannes Berg link->reserved_chanctx = NULL; 168003078de4SMichal Kazior 1681b4f85443SJohannes Berg ieee80211_link_chanctx_reservation_complete(link); 16825bcae31dSMichal Kazior } 16835bcae31dSMichal Kazior 16845bcae31dSMichal Kazior /* 16855bcae31dSMichal Kazior * This context might have been a dependency for an already 16865bcae31dSMichal Kazior * ready re-assign reservation interface that was deferred. Do 16875bcae31dSMichal Kazior * not propagate error to the caller though. The in-place 16885bcae31dSMichal Kazior * reservation for originally requested interface has already 16895bcae31dSMichal Kazior * succeeded at this point. 16905bcae31dSMichal Kazior */ 1691b4f85443SJohannes Berg list_for_each_entry_safe(link, link_tmp, &ctx->reserved_links, 16925bcae31dSMichal Kazior reserved_chanctx_list) { 1693b4f85443SJohannes Berg if (WARN_ON(ieee80211_link_has_in_place_reservation(link))) 16945bcae31dSMichal Kazior continue; 16955bcae31dSMichal Kazior 1696b4f85443SJohannes Berg if (WARN_ON(link->reserved_chanctx != ctx)) 16975bcae31dSMichal Kazior continue; 16985bcae31dSMichal Kazior 1699b4f85443SJohannes Berg if (!link->reserved_ready) 17005bcae31dSMichal Kazior continue; 17015bcae31dSMichal Kazior 1702b4f85443SJohannes Berg if (ieee80211_link_get_chanctx(link)) 1703b4f85443SJohannes Berg err = ieee80211_link_use_reserved_reassign(link); 17045bcae31dSMichal Kazior else 1705b4f85443SJohannes Berg err = ieee80211_link_use_reserved_assign(link); 17065bcae31dSMichal Kazior 17075bcae31dSMichal Kazior if (err) { 1708b4f85443SJohannes Berg link_info(link, 17095bcae31dSMichal Kazior "failed to finalize (re-)assign reservation (err=%d)\n", 17105bcae31dSMichal Kazior err); 1711b4f85443SJohannes Berg ieee80211_link_unreserve_chanctx(link); 17125bcae31dSMichal Kazior cfg80211_stop_iface(local->hw.wiphy, 1713b4f85443SJohannes Berg &link->sdata->wdev, 17145bcae31dSMichal Kazior GFP_KERNEL); 17155bcae31dSMichal Kazior } 17165bcae31dSMichal Kazior } 17175bcae31dSMichal Kazior } 17185bcae31dSMichal Kazior 17195bcae31dSMichal Kazior /* 17205bcae31dSMichal Kazior * Finally free old contexts 17215bcae31dSMichal Kazior */ 17225bcae31dSMichal Kazior 17235bcae31dSMichal Kazior list_for_each_entry_safe(ctx, ctx_tmp, &local->chanctx_list, list) { 17245bcae31dSMichal Kazior if (ctx->replace_state != IEEE80211_CHANCTX_WILL_BE_REPLACED) 17255bcae31dSMichal Kazior continue; 17265bcae31dSMichal Kazior 17275bcae31dSMichal Kazior ctx->replace_ctx->replace_ctx = NULL; 17285bcae31dSMichal Kazior ctx->replace_ctx->replace_state = 17295bcae31dSMichal Kazior IEEE80211_CHANCTX_REPLACE_NONE; 17305bcae31dSMichal Kazior 17315bcae31dSMichal Kazior list_del_rcu(&ctx->list); 17325bcae31dSMichal Kazior kfree_rcu(ctx, rcu_head); 17335bcae31dSMichal Kazior } 17345bcae31dSMichal Kazior 17355bcae31dSMichal Kazior return 0; 17365bcae31dSMichal Kazior 17375bcae31dSMichal Kazior err: 17385bcae31dSMichal Kazior list_for_each_entry(ctx, &local->chanctx_list, list) { 1739b4f85443SJohannes Berg struct ieee80211_link_data *link, *link_tmp; 1740b4f85443SJohannes Berg 17415bcae31dSMichal Kazior if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) 17425bcae31dSMichal Kazior continue; 17435bcae31dSMichal Kazior 1744b4f85443SJohannes Berg list_for_each_entry_safe(link, link_tmp, &ctx->reserved_links, 174503078de4SMichal Kazior reserved_chanctx_list) { 1746b4f85443SJohannes Berg ieee80211_link_unreserve_chanctx(link); 1747b4f85443SJohannes Berg ieee80211_link_chanctx_reservation_complete(link); 174803078de4SMichal Kazior } 17495bcae31dSMichal Kazior } 17505bcae31dSMichal Kazior 17515bcae31dSMichal Kazior return err; 17525bcae31dSMichal Kazior } 17535bcae31dSMichal Kazior 1754b4f85443SJohannes Berg static void __ieee80211_link_release_channel(struct ieee80211_link_data *link) 1755649b2a4dSJohannes Berg { 1756b4f85443SJohannes Berg struct ieee80211_sub_if_data *sdata = link->sdata; 1757d8675a63SJohannes Berg struct ieee80211_bss_conf *link_conf = link->conf; 1758649b2a4dSJohannes Berg struct ieee80211_local *local = sdata->local; 1759649b2a4dSJohannes Berg struct ieee80211_chanctx_conf *conf; 1760649b2a4dSJohannes Berg struct ieee80211_chanctx *ctx; 1761649b2a4dSJohannes Berg bool use_reserved_switch = false; 1762649b2a4dSJohannes Berg 1763649b2a4dSJohannes Berg lockdep_assert_held(&local->chanctx_mtx); 1764649b2a4dSJohannes Berg 1765b4f85443SJohannes Berg conf = rcu_dereference_protected(link_conf->chanctx_conf, 1766649b2a4dSJohannes Berg lockdep_is_held(&local->chanctx_mtx)); 1767649b2a4dSJohannes Berg if (!conf) 1768649b2a4dSJohannes Berg return; 1769649b2a4dSJohannes Berg 1770649b2a4dSJohannes Berg ctx = container_of(conf, struct ieee80211_chanctx, conf); 1771649b2a4dSJohannes Berg 1772b4f85443SJohannes Berg if (link->reserved_chanctx) { 1773b4f85443SJohannes Berg if (link->reserved_chanctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER && 1774b4f85443SJohannes Berg ieee80211_chanctx_num_reserved(local, link->reserved_chanctx) > 1) 1775649b2a4dSJohannes Berg use_reserved_switch = true; 1776649b2a4dSJohannes Berg 1777b4f85443SJohannes Berg ieee80211_link_unreserve_chanctx(link); 1778649b2a4dSJohannes Berg } 1779649b2a4dSJohannes Berg 1780b4f85443SJohannes Berg ieee80211_assign_link_chanctx(link, NULL); 1781649b2a4dSJohannes Berg if (ieee80211_chanctx_refcount(local, ctx) == 0) 1782649b2a4dSJohannes Berg ieee80211_free_chanctx(local, ctx); 1783649b2a4dSJohannes Berg 1784b4f85443SJohannes Berg link->radar_required = false; 1785104f5a62SEliad Peller 1786649b2a4dSJohannes Berg /* Unreserving may ready an in-place reservation. */ 1787649b2a4dSJohannes Berg if (use_reserved_switch) 1788649b2a4dSJohannes Berg ieee80211_vif_use_reserved_switch(local); 1789649b2a4dSJohannes Berg } 1790649b2a4dSJohannes Berg 1791b4f85443SJohannes Berg int ieee80211_link_use_channel(struct ieee80211_link_data *link, 1792649b2a4dSJohannes Berg const struct cfg80211_chan_def *chandef, 1793649b2a4dSJohannes Berg enum ieee80211_chanctx_mode mode) 1794649b2a4dSJohannes Berg { 1795b4f85443SJohannes Berg struct ieee80211_sub_if_data *sdata = link->sdata; 1796649b2a4dSJohannes Berg struct ieee80211_local *local = sdata->local; 1797649b2a4dSJohannes Berg struct ieee80211_chanctx *ctx; 1798649b2a4dSJohannes Berg u8 radar_detect_width = 0; 1799649b2a4dSJohannes Berg int ret; 1800649b2a4dSJohannes Berg 1801649b2a4dSJohannes Berg lockdep_assert_held(&local->mtx); 1802649b2a4dSJohannes Berg 1803649b2a4dSJohannes Berg WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev)); 1804649b2a4dSJohannes Berg 1805649b2a4dSJohannes Berg mutex_lock(&local->chanctx_mtx); 1806649b2a4dSJohannes Berg 1807649b2a4dSJohannes Berg ret = cfg80211_chandef_dfs_required(local->hw.wiphy, 1808649b2a4dSJohannes Berg chandef, 1809649b2a4dSJohannes Berg sdata->wdev.iftype); 1810649b2a4dSJohannes Berg if (ret < 0) 1811649b2a4dSJohannes Berg goto out; 1812649b2a4dSJohannes Berg if (ret > 0) 1813649b2a4dSJohannes Berg radar_detect_width = BIT(chandef->width); 1814649b2a4dSJohannes Berg 1815d8675a63SJohannes Berg link->radar_required = ret; 1816649b2a4dSJohannes Berg 1817649b2a4dSJohannes Berg ret = ieee80211_check_combinations(sdata, chandef, mode, 1818649b2a4dSJohannes Berg radar_detect_width); 1819649b2a4dSJohannes Berg if (ret < 0) 1820649b2a4dSJohannes Berg goto out; 1821649b2a4dSJohannes Berg 1822b4f85443SJohannes Berg __ieee80211_link_release_channel(link); 1823649b2a4dSJohannes Berg 1824649b2a4dSJohannes Berg ctx = ieee80211_find_chanctx(local, chandef, mode); 1825649b2a4dSJohannes Berg if (!ctx) 1826649b2a4dSJohannes Berg ctx = ieee80211_new_chanctx(local, chandef, mode); 1827649b2a4dSJohannes Berg if (IS_ERR(ctx)) { 1828649b2a4dSJohannes Berg ret = PTR_ERR(ctx); 1829649b2a4dSJohannes Berg goto out; 1830649b2a4dSJohannes Berg } 1831649b2a4dSJohannes Berg 1832b4f85443SJohannes Berg ieee80211_link_update_chandef(link, chandef); 1833649b2a4dSJohannes Berg 1834b4f85443SJohannes Berg ret = ieee80211_assign_link_chanctx(link, ctx); 1835649b2a4dSJohannes Berg if (ret) { 1836649b2a4dSJohannes Berg /* if assign fails refcount stays the same */ 1837649b2a4dSJohannes Berg if (ieee80211_chanctx_refcount(local, ctx) == 0) 1838649b2a4dSJohannes Berg ieee80211_free_chanctx(local, ctx); 1839649b2a4dSJohannes Berg goto out; 1840649b2a4dSJohannes Berg } 1841649b2a4dSJohannes Berg 1842649b2a4dSJohannes Berg ieee80211_recalc_smps_chanctx(local, ctx); 1843649b2a4dSJohannes Berg ieee80211_recalc_radar_chanctx(local, ctx); 1844649b2a4dSJohannes Berg out: 1845104f5a62SEliad Peller if (ret) 1846b4f85443SJohannes Berg link->radar_required = false; 1847104f5a62SEliad Peller 1848649b2a4dSJohannes Berg mutex_unlock(&local->chanctx_mtx); 1849649b2a4dSJohannes Berg return ret; 1850649b2a4dSJohannes Berg } 1851649b2a4dSJohannes Berg 1852b4f85443SJohannes Berg int ieee80211_link_use_reserved_context(struct ieee80211_link_data *link) 18535bcae31dSMichal Kazior { 1854b4f85443SJohannes Berg struct ieee80211_sub_if_data *sdata = link->sdata; 18555bcae31dSMichal Kazior struct ieee80211_local *local = sdata->local; 18565bcae31dSMichal Kazior struct ieee80211_chanctx *new_ctx; 18575bcae31dSMichal Kazior struct ieee80211_chanctx *old_ctx; 18585bcae31dSMichal Kazior int err; 18595bcae31dSMichal Kazior 18605bcae31dSMichal Kazior lockdep_assert_held(&local->mtx); 18615bcae31dSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 18625bcae31dSMichal Kazior 1863b4f85443SJohannes Berg new_ctx = link->reserved_chanctx; 1864b4f85443SJohannes Berg old_ctx = ieee80211_link_get_chanctx(link); 18655bcae31dSMichal Kazior 18665bcae31dSMichal Kazior if (WARN_ON(!new_ctx)) 18675bcae31dSMichal Kazior return -EINVAL; 18685bcae31dSMichal Kazior 18695bcae31dSMichal Kazior if (WARN_ON(new_ctx->replace_state == 18705bcae31dSMichal Kazior IEEE80211_CHANCTX_WILL_BE_REPLACED)) 18715bcae31dSMichal Kazior return -EINVAL; 18725bcae31dSMichal Kazior 1873b4f85443SJohannes Berg if (WARN_ON(link->reserved_ready)) 18745bcae31dSMichal Kazior return -EINVAL; 18755bcae31dSMichal Kazior 1876b4f85443SJohannes Berg link->reserved_ready = true; 18775bcae31dSMichal Kazior 18785bcae31dSMichal Kazior if (new_ctx->replace_state == IEEE80211_CHANCTX_REPLACE_NONE) { 18795bcae31dSMichal Kazior if (old_ctx) 1880b4f85443SJohannes Berg return ieee80211_link_use_reserved_reassign(link); 18815bcae31dSMichal Kazior 1882b4f85443SJohannes Berg return ieee80211_link_use_reserved_assign(link); 18835bcae31dSMichal Kazior } 18845bcae31dSMichal Kazior 18855bcae31dSMichal Kazior /* 18865bcae31dSMichal Kazior * In-place reservation may need to be finalized now either if: 18875bcae31dSMichal Kazior * a) sdata is taking part in the swapping itself and is the last one 18885bcae31dSMichal Kazior * b) sdata has switched with a re-assign reservation to an existing 18895bcae31dSMichal Kazior * context readying in-place switching of old_ctx 18905bcae31dSMichal Kazior * 18915bcae31dSMichal Kazior * In case of (b) do not propagate the error up because the requested 18925bcae31dSMichal Kazior * sdata already switched successfully. Just spill an extra warning. 18935bcae31dSMichal Kazior * The ieee80211_vif_use_reserved_switch() already stops all necessary 18945bcae31dSMichal Kazior * interfaces upon failure. 18955bcae31dSMichal Kazior */ 18965bcae31dSMichal Kazior if ((old_ctx && 18975bcae31dSMichal Kazior old_ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED) || 18985bcae31dSMichal Kazior new_ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER) { 18995bcae31dSMichal Kazior err = ieee80211_vif_use_reserved_switch(local); 19005bcae31dSMichal Kazior if (err && err != -EAGAIN) { 19015bcae31dSMichal Kazior if (new_ctx->replace_state == 19025bcae31dSMichal Kazior IEEE80211_CHANCTX_REPLACES_OTHER) 19035bcae31dSMichal Kazior return err; 19045bcae31dSMichal Kazior 19055bcae31dSMichal Kazior wiphy_info(local->hw.wiphy, 19065bcae31dSMichal Kazior "depending in-place reservation failed (err=%d)\n", 19075bcae31dSMichal Kazior err); 19085bcae31dSMichal Kazior } 19095bcae31dSMichal Kazior } 19105bcae31dSMichal Kazior 19115bcae31dSMichal Kazior return 0; 191273da7d5bSSimon Wunderlich } 191373da7d5bSSimon Wunderlich 1914b4f85443SJohannes Berg int ieee80211_link_change_bandwidth(struct ieee80211_link_data *link, 19152c9b7359SJohannes Berg const struct cfg80211_chan_def *chandef, 19162c9b7359SJohannes Berg u32 *changed) 19172c9b7359SJohannes Berg { 1918b4f85443SJohannes Berg struct ieee80211_sub_if_data *sdata = link->sdata; 1919d8675a63SJohannes Berg struct ieee80211_bss_conf *link_conf = link->conf; 19202c9b7359SJohannes Berg struct ieee80211_local *local = sdata->local; 19212c9b7359SJohannes Berg struct ieee80211_chanctx_conf *conf; 19222c9b7359SJohannes Berg struct ieee80211_chanctx *ctx; 19235bcae31dSMichal Kazior const struct cfg80211_chan_def *compat; 19242c9b7359SJohannes Berg int ret; 19252c9b7359SJohannes Berg 19262c9b7359SJohannes Berg if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef, 19272c9b7359SJohannes Berg IEEE80211_CHAN_DISABLED)) 19282c9b7359SJohannes Berg return -EINVAL; 19292c9b7359SJohannes Berg 19302c9b7359SJohannes Berg mutex_lock(&local->chanctx_mtx); 1931b4f85443SJohannes Berg if (cfg80211_chandef_identical(chandef, &link_conf->chandef)) { 19322c9b7359SJohannes Berg ret = 0; 19332c9b7359SJohannes Berg goto out; 19342c9b7359SJohannes Berg } 19352c9b7359SJohannes Berg 19362c9b7359SJohannes Berg if (chandef->width == NL80211_CHAN_WIDTH_20_NOHT || 1937b4f85443SJohannes Berg link_conf->chandef.width == NL80211_CHAN_WIDTH_20_NOHT) { 19382c9b7359SJohannes Berg ret = -EINVAL; 19392c9b7359SJohannes Berg goto out; 19402c9b7359SJohannes Berg } 19412c9b7359SJohannes Berg 1942b4f85443SJohannes Berg conf = rcu_dereference_protected(link_conf->chanctx_conf, 19432c9b7359SJohannes Berg lockdep_is_held(&local->chanctx_mtx)); 19442c9b7359SJohannes Berg if (!conf) { 19452c9b7359SJohannes Berg ret = -EINVAL; 19462c9b7359SJohannes Berg goto out; 19472c9b7359SJohannes Berg } 19482c9b7359SJohannes Berg 19492c9b7359SJohannes Berg ctx = container_of(conf, struct ieee80211_chanctx, conf); 19505bcae31dSMichal Kazior 19515bcae31dSMichal Kazior compat = cfg80211_chandef_compatible(&conf->def, chandef); 19525bcae31dSMichal Kazior if (!compat) { 19532c9b7359SJohannes Berg ret = -EINVAL; 19542c9b7359SJohannes Berg goto out; 19552c9b7359SJohannes Berg } 19562c9b7359SJohannes Berg 19575bcae31dSMichal Kazior switch (ctx->replace_state) { 19585bcae31dSMichal Kazior case IEEE80211_CHANCTX_REPLACE_NONE: 19595bcae31dSMichal Kazior if (!ieee80211_chanctx_reserved_chandef(local, ctx, compat)) { 19605bcae31dSMichal Kazior ret = -EBUSY; 19615bcae31dSMichal Kazior goto out; 19625bcae31dSMichal Kazior } 19635bcae31dSMichal Kazior break; 19645bcae31dSMichal Kazior case IEEE80211_CHANCTX_WILL_BE_REPLACED: 1965d070f913SStephen Hemminger /* TODO: Perhaps the bandwidth change could be treated as a 19665bcae31dSMichal Kazior * reservation itself? */ 19675bcae31dSMichal Kazior ret = -EBUSY; 19685bcae31dSMichal Kazior goto out; 19695bcae31dSMichal Kazior case IEEE80211_CHANCTX_REPLACES_OTHER: 19705bcae31dSMichal Kazior /* channel context that is going to replace another channel 19715bcae31dSMichal Kazior * context doesn't really exist and shouldn't be assigned 19725bcae31dSMichal Kazior * anywhere yet */ 19735bcae31dSMichal Kazior WARN_ON(1); 19745bcae31dSMichal Kazior break; 19755bcae31dSMichal Kazior } 19765bcae31dSMichal Kazior 1977b4f85443SJohannes Berg ieee80211_link_update_chandef(link, chandef); 19782c9b7359SJohannes Berg 19792c9b7359SJohannes Berg ieee80211_recalc_chanctx_chantype(local, ctx); 19802c9b7359SJohannes Berg 19812c9b7359SJohannes Berg *changed |= BSS_CHANGED_BANDWIDTH; 19822c9b7359SJohannes Berg ret = 0; 19832c9b7359SJohannes Berg out: 19842c9b7359SJohannes Berg mutex_unlock(&local->chanctx_mtx); 19852c9b7359SJohannes Berg return ret; 19862c9b7359SJohannes Berg } 19872c9b7359SJohannes Berg 1988b4f85443SJohannes Berg void ieee80211_link_release_channel(struct ieee80211_link_data *link) 1989d01a1e65SMichal Kazior { 1990b4f85443SJohannes Berg struct ieee80211_sub_if_data *sdata = link->sdata; 1991b4f85443SJohannes Berg 199255de908aSJohannes Berg WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev)); 199355de908aSJohannes Berg 199434a3740dSJohannes Berg lockdep_assert_held(&sdata->local->mtx); 199534a3740dSJohannes Berg 1996d01a1e65SMichal Kazior mutex_lock(&sdata->local->chanctx_mtx); 1997b4f85443SJohannes Berg __ieee80211_link_release_channel(link); 1998d01a1e65SMichal Kazior mutex_unlock(&sdata->local->chanctx_mtx); 1999d01a1e65SMichal Kazior } 20003448c005SJohannes Berg 2001b4f85443SJohannes Berg void ieee80211_link_vlan_copy_chanctx(struct ieee80211_link_data *link) 20024d76d21bSJohannes Berg { 2003b4f85443SJohannes Berg struct ieee80211_sub_if_data *sdata = link->sdata; 2004b4f85443SJohannes Berg unsigned int link_id = link->link_id; 2005d8675a63SJohannes Berg struct ieee80211_bss_conf *link_conf = link->conf; 2006d8675a63SJohannes Berg struct ieee80211_bss_conf *ap_conf; 20074d76d21bSJohannes Berg struct ieee80211_local *local = sdata->local; 20084d76d21bSJohannes Berg struct ieee80211_sub_if_data *ap; 20094d76d21bSJohannes Berg struct ieee80211_chanctx_conf *conf; 20104d76d21bSJohannes Berg 20114d76d21bSJohannes Berg if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP_VLAN || !sdata->bss)) 20124d76d21bSJohannes Berg return; 20134d76d21bSJohannes Berg 20144d76d21bSJohannes Berg ap = container_of(sdata->bss, struct ieee80211_sub_if_data, u.ap); 20154d76d21bSJohannes Berg 20164d76d21bSJohannes Berg mutex_lock(&local->chanctx_mtx); 20174d76d21bSJohannes Berg 2018d8675a63SJohannes Berg rcu_read_lock(); 2019d8675a63SJohannes Berg ap_conf = rcu_dereference(ap->vif.link_conf[link_id]); 2020d8675a63SJohannes Berg conf = rcu_dereference_protected(ap_conf->chanctx_conf, 20214d76d21bSJohannes Berg lockdep_is_held(&local->chanctx_mtx)); 2022b4f85443SJohannes Berg rcu_assign_pointer(link_conf->chanctx_conf, conf); 2023d8675a63SJohannes Berg rcu_read_unlock(); 20244d76d21bSJohannes Berg mutex_unlock(&local->chanctx_mtx); 20254d76d21bSJohannes Berg } 20264d76d21bSJohannes Berg 20273448c005SJohannes Berg void ieee80211_iter_chan_contexts_atomic( 20283448c005SJohannes Berg struct ieee80211_hw *hw, 20293448c005SJohannes Berg void (*iter)(struct ieee80211_hw *hw, 20303448c005SJohannes Berg struct ieee80211_chanctx_conf *chanctx_conf, 20313448c005SJohannes Berg void *data), 20323448c005SJohannes Berg void *iter_data) 20333448c005SJohannes Berg { 20343448c005SJohannes Berg struct ieee80211_local *local = hw_to_local(hw); 20353448c005SJohannes Berg struct ieee80211_chanctx *ctx; 20363448c005SJohannes Berg 20373448c005SJohannes Berg rcu_read_lock(); 20383448c005SJohannes Berg list_for_each_entry_rcu(ctx, &local->chanctx_list, list) 20398a61af65SJohannes Berg if (ctx->driver_present) 20403448c005SJohannes Berg iter(hw, &ctx->conf, iter_data); 20413448c005SJohannes Berg rcu_read_unlock(); 20423448c005SJohannes Berg } 20433448c005SJohannes Berg EXPORT_SYMBOL_GPL(ieee80211_iter_chan_contexts_atomic); 2044