1f444de05SJohannes Berg /* 2f444de05SJohannes Berg * mac80211 - channel management 3f444de05SJohannes Berg */ 4f444de05SJohannes Berg 50aaffa9bSJohannes Berg #include <linux/nl80211.h> 63448c005SJohannes Berg #include <linux/export.h> 74d76d21bSJohannes Berg #include <linux/rtnetlink.h> 83117bbdbSPaul Stewart #include <net/cfg80211.h> 9f444de05SJohannes Berg #include "ieee80211_i.h" 1035f2fce9SMichal Kazior #include "driver-ops.h" 11f444de05SJohannes Berg 12c0166da9SMichal Kazior static int ieee80211_chanctx_num_assigned(struct ieee80211_local *local, 13c0166da9SMichal Kazior struct ieee80211_chanctx *ctx) 14c0166da9SMichal Kazior { 15c0166da9SMichal Kazior struct ieee80211_sub_if_data *sdata; 16c0166da9SMichal Kazior int num = 0; 17c0166da9SMichal Kazior 18c0166da9SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 19c0166da9SMichal Kazior 20c0166da9SMichal Kazior list_for_each_entry(sdata, &ctx->assigned_vifs, assigned_chanctx_list) 21c0166da9SMichal Kazior num++; 22c0166da9SMichal Kazior 23c0166da9SMichal Kazior return num; 24c0166da9SMichal Kazior } 25c0166da9SMichal Kazior 26c0166da9SMichal Kazior static int ieee80211_chanctx_num_reserved(struct ieee80211_local *local, 27c0166da9SMichal Kazior struct ieee80211_chanctx *ctx) 28c0166da9SMichal Kazior { 29c0166da9SMichal Kazior struct ieee80211_sub_if_data *sdata; 30c0166da9SMichal Kazior int num = 0; 31c0166da9SMichal Kazior 32c0166da9SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 33c0166da9SMichal Kazior 34c0166da9SMichal Kazior list_for_each_entry(sdata, &ctx->reserved_vifs, reserved_chanctx_list) 35c0166da9SMichal Kazior num++; 36c0166da9SMichal Kazior 37c0166da9SMichal Kazior return num; 38c0166da9SMichal Kazior } 39c0166da9SMichal Kazior 40c0166da9SMichal Kazior int ieee80211_chanctx_refcount(struct ieee80211_local *local, 41c0166da9SMichal Kazior struct ieee80211_chanctx *ctx) 42c0166da9SMichal Kazior { 43c0166da9SMichal Kazior return ieee80211_chanctx_num_assigned(local, ctx) + 44c0166da9SMichal Kazior ieee80211_chanctx_num_reserved(local, ctx); 45c0166da9SMichal Kazior } 46c0166da9SMichal Kazior 47c2b90ad8SMichal Kazior static int ieee80211_num_chanctx(struct ieee80211_local *local) 48c2b90ad8SMichal Kazior { 49c2b90ad8SMichal Kazior struct ieee80211_chanctx *ctx; 50c2b90ad8SMichal Kazior int num = 0; 51c2b90ad8SMichal Kazior 52c2b90ad8SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 53c2b90ad8SMichal Kazior 54c2b90ad8SMichal Kazior list_for_each_entry(ctx, &local->chanctx_list, list) 55c2b90ad8SMichal Kazior num++; 56c2b90ad8SMichal Kazior 57c2b90ad8SMichal Kazior return num; 58c2b90ad8SMichal Kazior } 59c2b90ad8SMichal Kazior 60c2b90ad8SMichal Kazior static bool ieee80211_can_create_new_chanctx(struct ieee80211_local *local) 61c2b90ad8SMichal Kazior { 62c2b90ad8SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 63c2b90ad8SMichal Kazior return ieee80211_num_chanctx(local) < ieee80211_max_num_channels(local); 64c2b90ad8SMichal Kazior } 65c2b90ad8SMichal Kazior 665bcae31dSMichal Kazior static struct ieee80211_chanctx * 675bcae31dSMichal Kazior ieee80211_vif_get_chanctx(struct ieee80211_sub_if_data *sdata) 685bcae31dSMichal Kazior { 691d4cc30cSJohannes Berg struct ieee80211_local *local __maybe_unused = sdata->local; 705bcae31dSMichal Kazior struct ieee80211_chanctx_conf *conf; 715bcae31dSMichal Kazior 725bcae31dSMichal Kazior conf = rcu_dereference_protected(sdata->vif.chanctx_conf, 735bcae31dSMichal Kazior lockdep_is_held(&local->chanctx_mtx)); 745bcae31dSMichal Kazior if (!conf) 755bcae31dSMichal Kazior return NULL; 765bcae31dSMichal Kazior 775bcae31dSMichal Kazior return container_of(conf, struct ieee80211_chanctx, conf); 785bcae31dSMichal Kazior } 795bcae31dSMichal Kazior 800288157bSMichal Kazior static const struct cfg80211_chan_def * 810288157bSMichal Kazior ieee80211_chanctx_reserved_chandef(struct ieee80211_local *local, 820288157bSMichal Kazior struct ieee80211_chanctx *ctx, 830288157bSMichal Kazior const struct cfg80211_chan_def *compat) 840288157bSMichal Kazior { 850288157bSMichal Kazior struct ieee80211_sub_if_data *sdata; 860288157bSMichal Kazior 870288157bSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 880288157bSMichal Kazior 890288157bSMichal Kazior list_for_each_entry(sdata, &ctx->reserved_vifs, 900288157bSMichal Kazior reserved_chanctx_list) { 910288157bSMichal Kazior if (!compat) 920288157bSMichal Kazior compat = &sdata->reserved_chandef; 930288157bSMichal Kazior 940288157bSMichal Kazior compat = cfg80211_chandef_compatible(&sdata->reserved_chandef, 950288157bSMichal Kazior compat); 960288157bSMichal Kazior if (!compat) 970288157bSMichal Kazior break; 980288157bSMichal Kazior } 990288157bSMichal Kazior 1000288157bSMichal Kazior return compat; 1010288157bSMichal Kazior } 1020288157bSMichal Kazior 10313f348a8SMichal Kazior static const struct cfg80211_chan_def * 10413f348a8SMichal Kazior ieee80211_chanctx_non_reserved_chandef(struct ieee80211_local *local, 10513f348a8SMichal Kazior struct ieee80211_chanctx *ctx, 10613f348a8SMichal Kazior const struct cfg80211_chan_def *compat) 10713f348a8SMichal Kazior { 10813f348a8SMichal Kazior struct ieee80211_sub_if_data *sdata; 10913f348a8SMichal Kazior 11013f348a8SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 11113f348a8SMichal Kazior 11213f348a8SMichal Kazior list_for_each_entry(sdata, &ctx->assigned_vifs, 11313f348a8SMichal Kazior assigned_chanctx_list) { 11413f348a8SMichal Kazior if (sdata->reserved_chanctx != NULL) 11513f348a8SMichal Kazior continue; 11613f348a8SMichal Kazior 11713f348a8SMichal Kazior if (!compat) 11813f348a8SMichal Kazior compat = &sdata->vif.bss_conf.chandef; 11913f348a8SMichal Kazior 12013f348a8SMichal Kazior compat = cfg80211_chandef_compatible( 12113f348a8SMichal Kazior &sdata->vif.bss_conf.chandef, compat); 12213f348a8SMichal Kazior if (!compat) 12313f348a8SMichal Kazior break; 12413f348a8SMichal Kazior } 12513f348a8SMichal Kazior 12613f348a8SMichal Kazior return compat; 12713f348a8SMichal Kazior } 12813f348a8SMichal Kazior 12913f348a8SMichal Kazior static const struct cfg80211_chan_def * 13013f348a8SMichal Kazior ieee80211_chanctx_combined_chandef(struct ieee80211_local *local, 13113f348a8SMichal Kazior struct ieee80211_chanctx *ctx, 13213f348a8SMichal Kazior const struct cfg80211_chan_def *compat) 13313f348a8SMichal Kazior { 13413f348a8SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 13513f348a8SMichal Kazior 13613f348a8SMichal Kazior compat = ieee80211_chanctx_reserved_chandef(local, ctx, compat); 13713f348a8SMichal Kazior if (!compat) 13813f348a8SMichal Kazior return NULL; 13913f348a8SMichal Kazior 14013f348a8SMichal Kazior compat = ieee80211_chanctx_non_reserved_chandef(local, ctx, compat); 14113f348a8SMichal Kazior if (!compat) 14213f348a8SMichal Kazior return NULL; 14313f348a8SMichal Kazior 14413f348a8SMichal Kazior return compat; 14513f348a8SMichal Kazior } 14613f348a8SMichal Kazior 14713f348a8SMichal Kazior static bool 14813f348a8SMichal Kazior ieee80211_chanctx_can_reserve_chandef(struct ieee80211_local *local, 14913f348a8SMichal Kazior struct ieee80211_chanctx *ctx, 15013f348a8SMichal Kazior const struct cfg80211_chan_def *def) 15113f348a8SMichal Kazior { 15213f348a8SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 15313f348a8SMichal Kazior 15413f348a8SMichal Kazior if (ieee80211_chanctx_combined_chandef(local, ctx, def)) 15513f348a8SMichal Kazior return true; 15613f348a8SMichal Kazior 15713f348a8SMichal Kazior if (!list_empty(&ctx->reserved_vifs) && 15813f348a8SMichal Kazior ieee80211_chanctx_reserved_chandef(local, ctx, def)) 15913f348a8SMichal Kazior return true; 16013f348a8SMichal Kazior 16113f348a8SMichal Kazior return false; 16213f348a8SMichal Kazior } 16313f348a8SMichal Kazior 16413f348a8SMichal Kazior static struct ieee80211_chanctx * 16513f348a8SMichal Kazior ieee80211_find_reservation_chanctx(struct ieee80211_local *local, 16613f348a8SMichal Kazior const struct cfg80211_chan_def *chandef, 16713f348a8SMichal Kazior enum ieee80211_chanctx_mode mode) 16813f348a8SMichal Kazior { 16913f348a8SMichal Kazior struct ieee80211_chanctx *ctx; 17013f348a8SMichal Kazior 17113f348a8SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 17213f348a8SMichal Kazior 17313f348a8SMichal Kazior if (mode == IEEE80211_CHANCTX_EXCLUSIVE) 17413f348a8SMichal Kazior return NULL; 17513f348a8SMichal Kazior 17613f348a8SMichal Kazior list_for_each_entry(ctx, &local->chanctx_list, list) { 1775bcae31dSMichal Kazior if (ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED) 1785bcae31dSMichal Kazior continue; 1795bcae31dSMichal Kazior 18013f348a8SMichal Kazior if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE) 18113f348a8SMichal Kazior continue; 18213f348a8SMichal Kazior 18313f348a8SMichal Kazior if (!ieee80211_chanctx_can_reserve_chandef(local, ctx, 18413f348a8SMichal Kazior chandef)) 18513f348a8SMichal Kazior continue; 18613f348a8SMichal Kazior 18713f348a8SMichal Kazior return ctx; 18813f348a8SMichal Kazior } 18913f348a8SMichal Kazior 19013f348a8SMichal Kazior return NULL; 19113f348a8SMichal Kazior } 19213f348a8SMichal Kazior 19321f659bfSEliad Peller static enum nl80211_chan_width ieee80211_get_sta_bw(struct ieee80211_sta *sta) 19421f659bfSEliad Peller { 19521f659bfSEliad Peller switch (sta->bandwidth) { 19621f659bfSEliad Peller case IEEE80211_STA_RX_BW_20: 19721f659bfSEliad Peller if (sta->ht_cap.ht_supported) 19821f659bfSEliad Peller return NL80211_CHAN_WIDTH_20; 19921f659bfSEliad Peller else 20021f659bfSEliad Peller return NL80211_CHAN_WIDTH_20_NOHT; 20121f659bfSEliad Peller case IEEE80211_STA_RX_BW_40: 20221f659bfSEliad Peller return NL80211_CHAN_WIDTH_40; 20321f659bfSEliad Peller case IEEE80211_STA_RX_BW_80: 20421f659bfSEliad Peller return NL80211_CHAN_WIDTH_80; 20521f659bfSEliad Peller case IEEE80211_STA_RX_BW_160: 20621f659bfSEliad Peller /* 20721f659bfSEliad Peller * This applied for both 160 and 80+80. since we use 20821f659bfSEliad Peller * the returned value to consider degradation of 20921f659bfSEliad Peller * ctx->conf.min_def, we have to make sure to take 21021f659bfSEliad Peller * the bigger one (NL80211_CHAN_WIDTH_160). 21121f659bfSEliad Peller * Otherwise we might try degrading even when not 21221f659bfSEliad Peller * needed, as the max required sta_bw returned (80+80) 21321f659bfSEliad Peller * might be smaller than the configured bw (160). 21421f659bfSEliad Peller */ 21521f659bfSEliad Peller return NL80211_CHAN_WIDTH_160; 21621f659bfSEliad Peller default: 21721f659bfSEliad Peller WARN_ON(1); 21821f659bfSEliad Peller return NL80211_CHAN_WIDTH_20; 21921f659bfSEliad Peller } 22021f659bfSEliad Peller } 22121f659bfSEliad Peller 22221f659bfSEliad Peller static enum nl80211_chan_width 22321f659bfSEliad Peller ieee80211_get_max_required_bw(struct ieee80211_sub_if_data *sdata) 22421f659bfSEliad Peller { 22521f659bfSEliad Peller enum nl80211_chan_width max_bw = NL80211_CHAN_WIDTH_20_NOHT; 22621f659bfSEliad Peller struct sta_info *sta; 22721f659bfSEliad Peller 22821f659bfSEliad Peller rcu_read_lock(); 22921f659bfSEliad Peller list_for_each_entry_rcu(sta, &sdata->local->sta_list, list) { 23021f659bfSEliad Peller if (sdata != sta->sdata && 23121f659bfSEliad Peller !(sta->sdata->bss && sta->sdata->bss == sdata->bss)) 23221f659bfSEliad Peller continue; 23321f659bfSEliad Peller 23421f659bfSEliad Peller if (!sta->uploaded) 23521f659bfSEliad Peller continue; 23621f659bfSEliad Peller 23721f659bfSEliad Peller max_bw = max(max_bw, ieee80211_get_sta_bw(&sta->sta)); 23821f659bfSEliad Peller } 23921f659bfSEliad Peller rcu_read_unlock(); 24021f659bfSEliad Peller 24121f659bfSEliad Peller return max_bw; 24221f659bfSEliad Peller } 24321f659bfSEliad Peller 24421f659bfSEliad Peller static enum nl80211_chan_width 24521f659bfSEliad Peller ieee80211_get_chanctx_max_required_bw(struct ieee80211_local *local, 24621f659bfSEliad Peller struct ieee80211_chanctx_conf *conf) 24721f659bfSEliad Peller { 24821f659bfSEliad Peller struct ieee80211_sub_if_data *sdata; 24921f659bfSEliad Peller enum nl80211_chan_width max_bw = NL80211_CHAN_WIDTH_20_NOHT; 25021f659bfSEliad Peller 25121f659bfSEliad Peller rcu_read_lock(); 25221f659bfSEliad Peller list_for_each_entry_rcu(sdata, &local->interfaces, list) { 25321f659bfSEliad Peller struct ieee80211_vif *vif = &sdata->vif; 25421f659bfSEliad Peller enum nl80211_chan_width width = NL80211_CHAN_WIDTH_20_NOHT; 25521f659bfSEliad Peller 25621f659bfSEliad Peller if (!ieee80211_sdata_running(sdata)) 25721f659bfSEliad Peller continue; 25821f659bfSEliad Peller 25921f659bfSEliad Peller if (rcu_access_pointer(sdata->vif.chanctx_conf) != conf) 26021f659bfSEliad Peller continue; 26121f659bfSEliad Peller 26221f659bfSEliad Peller switch (vif->type) { 26321f659bfSEliad Peller case NL80211_IFTYPE_AP: 26421f659bfSEliad Peller case NL80211_IFTYPE_AP_VLAN: 26521f659bfSEliad Peller width = ieee80211_get_max_required_bw(sdata); 26621f659bfSEliad Peller break; 26721f659bfSEliad Peller case NL80211_IFTYPE_P2P_DEVICE: 26821f659bfSEliad Peller continue; 26921f659bfSEliad Peller case NL80211_IFTYPE_STATION: 27021f659bfSEliad Peller case NL80211_IFTYPE_ADHOC: 27121f659bfSEliad Peller case NL80211_IFTYPE_WDS: 27221f659bfSEliad Peller case NL80211_IFTYPE_MESH_POINT: 2736e0bd6c3SRostislav Lisovy case NL80211_IFTYPE_OCB: 27421f659bfSEliad Peller width = vif->bss_conf.chandef.width; 27521f659bfSEliad Peller break; 27621f659bfSEliad Peller case NL80211_IFTYPE_UNSPECIFIED: 27721f659bfSEliad Peller case NUM_NL80211_IFTYPES: 27821f659bfSEliad Peller case NL80211_IFTYPE_MONITOR: 27921f659bfSEliad Peller case NL80211_IFTYPE_P2P_CLIENT: 28021f659bfSEliad Peller case NL80211_IFTYPE_P2P_GO: 28121f659bfSEliad Peller WARN_ON_ONCE(1); 28221f659bfSEliad Peller } 28321f659bfSEliad Peller max_bw = max(max_bw, width); 28421f659bfSEliad Peller } 2851c37a72cSEliad Peller 2861c37a72cSEliad Peller /* use the configured bandwidth in case of monitor interface */ 2871c37a72cSEliad Peller sdata = rcu_dereference(local->monitor_sdata); 2881c37a72cSEliad Peller if (sdata && rcu_access_pointer(sdata->vif.chanctx_conf) == conf) 2891c37a72cSEliad Peller max_bw = max(max_bw, conf->def.width); 2901c37a72cSEliad Peller 29121f659bfSEliad Peller rcu_read_unlock(); 29221f659bfSEliad Peller 29321f659bfSEliad Peller return max_bw; 29421f659bfSEliad Peller } 29521f659bfSEliad Peller 29621f659bfSEliad Peller /* 29721f659bfSEliad Peller * recalc the min required chan width of the channel context, which is 29821f659bfSEliad Peller * the max of min required widths of all the interfaces bound to this 29921f659bfSEliad Peller * channel context. 30021f659bfSEliad Peller */ 30121f659bfSEliad Peller void ieee80211_recalc_chanctx_min_def(struct ieee80211_local *local, 30221f659bfSEliad Peller struct ieee80211_chanctx *ctx) 30321f659bfSEliad Peller { 30421f659bfSEliad Peller enum nl80211_chan_width max_bw; 30521f659bfSEliad Peller struct cfg80211_chan_def min_def; 30621f659bfSEliad Peller 30721f659bfSEliad Peller lockdep_assert_held(&local->chanctx_mtx); 30821f659bfSEliad Peller 30921f659bfSEliad Peller /* don't optimize 5MHz, 10MHz, and radar_enabled confs */ 31021f659bfSEliad Peller if (ctx->conf.def.width == NL80211_CHAN_WIDTH_5 || 31121f659bfSEliad Peller ctx->conf.def.width == NL80211_CHAN_WIDTH_10 || 31221f659bfSEliad Peller ctx->conf.radar_enabled) { 31321f659bfSEliad Peller ctx->conf.min_def = ctx->conf.def; 31421f659bfSEliad Peller return; 31521f659bfSEliad Peller } 31621f659bfSEliad Peller 31721f659bfSEliad Peller max_bw = ieee80211_get_chanctx_max_required_bw(local, &ctx->conf); 31821f659bfSEliad Peller 31921f659bfSEliad Peller /* downgrade chandef up to max_bw */ 32021f659bfSEliad Peller min_def = ctx->conf.def; 32121f659bfSEliad Peller while (min_def.width > max_bw) 32221f659bfSEliad Peller ieee80211_chandef_downgrade(&min_def); 32321f659bfSEliad Peller 32421f659bfSEliad Peller if (cfg80211_chandef_identical(&ctx->conf.min_def, &min_def)) 32521f659bfSEliad Peller return; 32621f659bfSEliad Peller 32721f659bfSEliad Peller ctx->conf.min_def = min_def; 32821f659bfSEliad Peller if (!ctx->driver_present) 32921f659bfSEliad Peller return; 33021f659bfSEliad Peller 33121f659bfSEliad Peller drv_change_chanctx(local, ctx, IEEE80211_CHANCTX_CHANGE_MIN_WIDTH); 33221f659bfSEliad Peller } 33321f659bfSEliad Peller 33418942d3bSJohannes Berg static void ieee80211_change_chanctx(struct ieee80211_local *local, 335e89a96f5SMichal Kazior struct ieee80211_chanctx *ctx, 3364bf88530SJohannes Berg const struct cfg80211_chan_def *chandef) 337e89a96f5SMichal Kazior { 3384bf88530SJohannes Berg if (cfg80211_chandef_identical(&ctx->conf.def, chandef)) 339e89a96f5SMichal Kazior return; 340e89a96f5SMichal Kazior 3414bf88530SJohannes Berg WARN_ON(!cfg80211_chandef_compatible(&ctx->conf.def, chandef)); 3424bf88530SJohannes Berg 3434bf88530SJohannes Berg ctx->conf.def = *chandef; 3444bf88530SJohannes Berg drv_change_chanctx(local, ctx, IEEE80211_CHANCTX_CHANGE_WIDTH); 34521f659bfSEliad Peller ieee80211_recalc_chanctx_min_def(local, ctx); 34655de908aSJohannes Berg 34755de908aSJohannes Berg if (!local->use_chanctx) { 348675a0b04SKarl Beldan local->_oper_chandef = *chandef; 34955de908aSJohannes Berg ieee80211_hw_config(local, 0); 35055de908aSJohannes Berg } 3510aaffa9bSJohannes Berg } 352d01a1e65SMichal Kazior 353d01a1e65SMichal Kazior static struct ieee80211_chanctx * 354d01a1e65SMichal Kazior ieee80211_find_chanctx(struct ieee80211_local *local, 3554bf88530SJohannes Berg const struct cfg80211_chan_def *chandef, 356d01a1e65SMichal Kazior enum ieee80211_chanctx_mode mode) 357d01a1e65SMichal Kazior { 358d01a1e65SMichal Kazior struct ieee80211_chanctx *ctx; 359d01a1e65SMichal Kazior 360d01a1e65SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 361d01a1e65SMichal Kazior 362d01a1e65SMichal Kazior if (mode == IEEE80211_CHANCTX_EXCLUSIVE) 363d01a1e65SMichal Kazior return NULL; 364d01a1e65SMichal Kazior 365d01a1e65SMichal Kazior list_for_each_entry(ctx, &local->chanctx_list, list) { 3664bf88530SJohannes Berg const struct cfg80211_chan_def *compat; 367e89a96f5SMichal Kazior 3685bcae31dSMichal Kazior if (ctx->replace_state != IEEE80211_CHANCTX_REPLACE_NONE) 3695bcae31dSMichal Kazior continue; 3705bcae31dSMichal Kazior 371d01a1e65SMichal Kazior if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE) 372d01a1e65SMichal Kazior continue; 3734bf88530SJohannes Berg 3744bf88530SJohannes Berg compat = cfg80211_chandef_compatible(&ctx->conf.def, chandef); 3754bf88530SJohannes Berg if (!compat) 376d01a1e65SMichal Kazior continue; 377d01a1e65SMichal Kazior 3780288157bSMichal Kazior compat = ieee80211_chanctx_reserved_chandef(local, ctx, 3790288157bSMichal Kazior compat); 3800288157bSMichal Kazior if (!compat) 3810288157bSMichal Kazior continue; 3820288157bSMichal Kazior 38318942d3bSJohannes Berg ieee80211_change_chanctx(local, ctx, compat); 384e89a96f5SMichal Kazior 385d01a1e65SMichal Kazior return ctx; 386d01a1e65SMichal Kazior } 387d01a1e65SMichal Kazior 388d01a1e65SMichal Kazior return NULL; 389d01a1e65SMichal Kazior } 390d01a1e65SMichal Kazior 3915cbc95a7SEliad Peller bool ieee80211_is_radar_required(struct ieee80211_local *local) 392e4746851SSimon Wunderlich { 393e4746851SSimon Wunderlich struct ieee80211_sub_if_data *sdata; 394e4746851SSimon Wunderlich 395cc901de1SMichal Kazior lockdep_assert_held(&local->mtx); 396cc901de1SMichal Kazior 397e4746851SSimon Wunderlich rcu_read_lock(); 398e4746851SSimon Wunderlich list_for_each_entry_rcu(sdata, &local->interfaces, list) { 399e4746851SSimon Wunderlich if (sdata->radar_required) { 400e4746851SSimon Wunderlich rcu_read_unlock(); 401e4746851SSimon Wunderlich return true; 402e4746851SSimon Wunderlich } 403e4746851SSimon Wunderlich } 404e4746851SSimon Wunderlich rcu_read_unlock(); 405e4746851SSimon Wunderlich 406e4746851SSimon Wunderlich return false; 407e4746851SSimon Wunderlich } 408e4746851SSimon Wunderlich 409e7f2337aSEliad Peller static bool 410e7f2337aSEliad Peller ieee80211_chanctx_radar_required(struct ieee80211_local *local, 411e7f2337aSEliad Peller struct ieee80211_chanctx *ctx) 412e7f2337aSEliad Peller { 413e7f2337aSEliad Peller struct ieee80211_chanctx_conf *conf = &ctx->conf; 414e7f2337aSEliad Peller struct ieee80211_sub_if_data *sdata; 415e7f2337aSEliad Peller bool required = false; 416e7f2337aSEliad Peller 417e7f2337aSEliad Peller lockdep_assert_held(&local->chanctx_mtx); 418e7f2337aSEliad Peller lockdep_assert_held(&local->mtx); 419e7f2337aSEliad Peller 420e7f2337aSEliad Peller rcu_read_lock(); 421e7f2337aSEliad Peller list_for_each_entry_rcu(sdata, &local->interfaces, list) { 422e7f2337aSEliad Peller if (!ieee80211_sdata_running(sdata)) 423e7f2337aSEliad Peller continue; 424e7f2337aSEliad Peller if (rcu_access_pointer(sdata->vif.chanctx_conf) != conf) 425e7f2337aSEliad Peller continue; 426e7f2337aSEliad Peller if (!sdata->radar_required) 427e7f2337aSEliad Peller continue; 428e7f2337aSEliad Peller 429e7f2337aSEliad Peller required = true; 430e7f2337aSEliad Peller break; 431e7f2337aSEliad Peller } 432e7f2337aSEliad Peller rcu_read_unlock(); 433e7f2337aSEliad Peller 434e7f2337aSEliad Peller return required; 435e7f2337aSEliad Peller } 436e7f2337aSEliad Peller 437d01a1e65SMichal Kazior static struct ieee80211_chanctx * 438ed68ebcaSMichal Kazior ieee80211_alloc_chanctx(struct ieee80211_local *local, 4394bf88530SJohannes Berg const struct cfg80211_chan_def *chandef, 440d01a1e65SMichal Kazior enum ieee80211_chanctx_mode mode) 441d01a1e65SMichal Kazior { 442d01a1e65SMichal Kazior struct ieee80211_chanctx *ctx; 443d01a1e65SMichal Kazior 444d01a1e65SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 445d01a1e65SMichal Kazior 446d01a1e65SMichal Kazior ctx = kzalloc(sizeof(*ctx) + local->hw.chanctx_data_size, GFP_KERNEL); 447d01a1e65SMichal Kazior if (!ctx) 448ed68ebcaSMichal Kazior return NULL; 449d01a1e65SMichal Kazior 450484298adSMichal Kazior INIT_LIST_HEAD(&ctx->assigned_vifs); 451e3afb920SMichal Kazior INIT_LIST_HEAD(&ctx->reserved_vifs); 4524bf88530SJohannes Berg ctx->conf.def = *chandef; 45304ecd257SJohannes Berg ctx->conf.rx_chains_static = 1; 45404ecd257SJohannes Berg ctx->conf.rx_chains_dynamic = 1; 455d01a1e65SMichal Kazior ctx->mode = mode; 456e7f2337aSEliad Peller ctx->conf.radar_enabled = false; 45721f659bfSEliad Peller ieee80211_recalc_chanctx_min_def(local, ctx); 458ed68ebcaSMichal Kazior 459ed68ebcaSMichal Kazior return ctx; 460ed68ebcaSMichal Kazior } 461ed68ebcaSMichal Kazior 462ed68ebcaSMichal Kazior static int ieee80211_add_chanctx(struct ieee80211_local *local, 463ed68ebcaSMichal Kazior struct ieee80211_chanctx *ctx) 464ed68ebcaSMichal Kazior { 465ed68ebcaSMichal Kazior u32 changed; 466ed68ebcaSMichal Kazior int err; 467ed68ebcaSMichal Kazior 468ed68ebcaSMichal Kazior lockdep_assert_held(&local->mtx); 469ed68ebcaSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 470ed68ebcaSMichal Kazior 471e4746851SSimon Wunderlich if (!local->use_chanctx) 472e4746851SSimon Wunderlich local->hw.conf.radar_enabled = ctx->conf.radar_enabled; 473d01a1e65SMichal Kazior 474382a103bSJohannes Berg /* turn idle off *before* setting channel -- some drivers need that */ 475382a103bSJohannes Berg changed = ieee80211_idle_off(local); 476382a103bSJohannes Berg if (changed) 477382a103bSJohannes Berg ieee80211_hw_config(local, changed); 478382a103bSJohannes Berg 47955de908aSJohannes Berg if (!local->use_chanctx) { 480ed68ebcaSMichal Kazior local->_oper_chandef = ctx->conf.def; 4819b4816f5SMichal Kazior ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); 48255de908aSJohannes Berg } else { 48335f2fce9SMichal Kazior err = drv_add_chanctx(local, ctx); 48435f2fce9SMichal Kazior if (err) { 485382a103bSJohannes Berg ieee80211_recalc_idle(local); 486ed68ebcaSMichal Kazior return err; 487ed68ebcaSMichal Kazior } 488ed68ebcaSMichal Kazior } 489ed68ebcaSMichal Kazior 490ed68ebcaSMichal Kazior return 0; 491ed68ebcaSMichal Kazior } 492ed68ebcaSMichal Kazior 493ed68ebcaSMichal Kazior static struct ieee80211_chanctx * 494ed68ebcaSMichal Kazior ieee80211_new_chanctx(struct ieee80211_local *local, 495ed68ebcaSMichal Kazior const struct cfg80211_chan_def *chandef, 496ed68ebcaSMichal Kazior enum ieee80211_chanctx_mode mode) 497ed68ebcaSMichal Kazior { 498ed68ebcaSMichal Kazior struct ieee80211_chanctx *ctx; 499ed68ebcaSMichal Kazior int err; 500ed68ebcaSMichal Kazior 501ed68ebcaSMichal Kazior lockdep_assert_held(&local->mtx); 502ed68ebcaSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 503ed68ebcaSMichal Kazior 504ed68ebcaSMichal Kazior ctx = ieee80211_alloc_chanctx(local, chandef, mode); 505ed68ebcaSMichal Kazior if (!ctx) 506ed68ebcaSMichal Kazior return ERR_PTR(-ENOMEM); 507ed68ebcaSMichal Kazior 508ed68ebcaSMichal Kazior err = ieee80211_add_chanctx(local, ctx); 509ed68ebcaSMichal Kazior if (err) { 510ed68ebcaSMichal Kazior kfree(ctx); 51134a3740dSJohannes Berg return ERR_PTR(err); 51235f2fce9SMichal Kazior } 51335f2fce9SMichal Kazior 5143448c005SJohannes Berg list_add_rcu(&ctx->list, &local->chanctx_list); 515d01a1e65SMichal Kazior return ctx; 516d01a1e65SMichal Kazior } 517d01a1e65SMichal Kazior 5181f0d54cdSMichal Kazior static void ieee80211_del_chanctx(struct ieee80211_local *local, 519d01a1e65SMichal Kazior struct ieee80211_chanctx *ctx) 520d01a1e65SMichal Kazior { 521d01a1e65SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 522d01a1e65SMichal Kazior 52355de908aSJohannes Berg if (!local->use_chanctx) { 524675a0b04SKarl Beldan struct cfg80211_chan_def *chandef = &local->_oper_chandef; 525675a0b04SKarl Beldan chandef->width = NL80211_CHAN_WIDTH_20_NOHT; 526675a0b04SKarl Beldan chandef->center_freq1 = chandef->chan->center_freq; 527675a0b04SKarl Beldan chandef->center_freq2 = 0; 528e4746851SSimon Wunderlich 529e4746851SSimon Wunderlich /* NOTE: Disabling radar is only valid here for 530e4746851SSimon Wunderlich * single channel context. To be sure, check it ... 531e4746851SSimon Wunderlich */ 5321f0d54cdSMichal Kazior WARN_ON(local->hw.conf.radar_enabled && 5331f0d54cdSMichal Kazior !list_empty(&local->chanctx_list)); 5341f0d54cdSMichal Kazior 535e4746851SSimon Wunderlich local->hw.conf.radar_enabled = false; 536e4746851SSimon Wunderlich 5379b4816f5SMichal Kazior ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); 53855de908aSJohannes Berg } else { 53935f2fce9SMichal Kazior drv_remove_chanctx(local, ctx); 54055de908aSJohannes Berg } 54135f2fce9SMichal Kazior 542fd0f979aSJohannes Berg ieee80211_recalc_idle(local); 543d01a1e65SMichal Kazior } 544d01a1e65SMichal Kazior 5451f0d54cdSMichal Kazior static void ieee80211_free_chanctx(struct ieee80211_local *local, 546d01a1e65SMichal Kazior struct ieee80211_chanctx *ctx) 547d01a1e65SMichal Kazior { 548d01a1e65SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 549d01a1e65SMichal Kazior 550c0166da9SMichal Kazior WARN_ON_ONCE(ieee80211_chanctx_refcount(local, ctx) != 0); 55135f2fce9SMichal Kazior 5521f0d54cdSMichal Kazior list_del_rcu(&ctx->list); 5531f0d54cdSMichal Kazior ieee80211_del_chanctx(local, ctx); 5541f0d54cdSMichal Kazior kfree_rcu(ctx, rcu_head); 555d01a1e65SMichal Kazior } 556d01a1e65SMichal Kazior 5574bf88530SJohannes Berg static void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local, 558e89a96f5SMichal Kazior struct ieee80211_chanctx *ctx) 559e89a96f5SMichal Kazior { 560e89a96f5SMichal Kazior struct ieee80211_chanctx_conf *conf = &ctx->conf; 561e89a96f5SMichal Kazior struct ieee80211_sub_if_data *sdata; 5624bf88530SJohannes Berg const struct cfg80211_chan_def *compat = NULL; 563e89a96f5SMichal Kazior 564e89a96f5SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 565e89a96f5SMichal Kazior 566e89a96f5SMichal Kazior rcu_read_lock(); 567e89a96f5SMichal Kazior list_for_each_entry_rcu(sdata, &local->interfaces, list) { 5684bf88530SJohannes Berg 569e89a96f5SMichal Kazior if (!ieee80211_sdata_running(sdata)) 570e89a96f5SMichal Kazior continue; 571e89a96f5SMichal Kazior if (rcu_access_pointer(sdata->vif.chanctx_conf) != conf) 572e89a96f5SMichal Kazior continue; 5730e67c136SFelix Fietkau if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) 5740e67c136SFelix Fietkau continue; 575e89a96f5SMichal Kazior 5764bf88530SJohannes Berg if (!compat) 5774bf88530SJohannes Berg compat = &sdata->vif.bss_conf.chandef; 5784bf88530SJohannes Berg 5794bf88530SJohannes Berg compat = cfg80211_chandef_compatible( 5804bf88530SJohannes Berg &sdata->vif.bss_conf.chandef, compat); 581a00f4f6eSMichal Kazior if (WARN_ON_ONCE(!compat)) 5824bf88530SJohannes Berg break; 583e89a96f5SMichal Kazior } 584e89a96f5SMichal Kazior rcu_read_unlock(); 585e89a96f5SMichal Kazior 586a00f4f6eSMichal Kazior if (!compat) 5874bf88530SJohannes Berg return; 588e89a96f5SMichal Kazior 58918942d3bSJohannes Berg ieee80211_change_chanctx(local, ctx, compat); 590e89a96f5SMichal Kazior } 591e89a96f5SMichal Kazior 592367bbd10SJohannes Berg static void ieee80211_recalc_radar_chanctx(struct ieee80211_local *local, 593367bbd10SJohannes Berg struct ieee80211_chanctx *chanctx) 594367bbd10SJohannes Berg { 595367bbd10SJohannes Berg bool radar_enabled; 596367bbd10SJohannes Berg 597367bbd10SJohannes Berg lockdep_assert_held(&local->chanctx_mtx); 5985cbc95a7SEliad Peller /* for ieee80211_is_radar_required */ 59934a3740dSJohannes Berg lockdep_assert_held(&local->mtx); 600367bbd10SJohannes Berg 601e7f2337aSEliad Peller radar_enabled = ieee80211_chanctx_radar_required(local, chanctx); 602367bbd10SJohannes Berg 603367bbd10SJohannes Berg if (radar_enabled == chanctx->conf.radar_enabled) 604367bbd10SJohannes Berg return; 605367bbd10SJohannes Berg 606367bbd10SJohannes Berg chanctx->conf.radar_enabled = radar_enabled; 607367bbd10SJohannes Berg 608367bbd10SJohannes Berg if (!local->use_chanctx) { 609367bbd10SJohannes Berg local->hw.conf.radar_enabled = chanctx->conf.radar_enabled; 610367bbd10SJohannes Berg ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); 611367bbd10SJohannes Berg } 612367bbd10SJohannes Berg 613367bbd10SJohannes Berg drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RADAR); 614367bbd10SJohannes Berg } 615367bbd10SJohannes Berg 61677eeba97SLuciano Coelho static int ieee80211_assign_vif_chanctx(struct ieee80211_sub_if_data *sdata, 61777eeba97SLuciano Coelho struct ieee80211_chanctx *new_ctx) 618d01a1e65SMichal Kazior { 61935f2fce9SMichal Kazior struct ieee80211_local *local = sdata->local; 62077eeba97SLuciano Coelho struct ieee80211_chanctx_conf *conf; 62177eeba97SLuciano Coelho struct ieee80211_chanctx *curr_ctx = NULL; 62277eeba97SLuciano Coelho int ret = 0; 623d01a1e65SMichal Kazior 62477eeba97SLuciano Coelho conf = rcu_dereference_protected(sdata->vif.chanctx_conf, 62577eeba97SLuciano Coelho lockdep_is_held(&local->chanctx_mtx)); 626d01a1e65SMichal Kazior 62777eeba97SLuciano Coelho if (conf) { 62877eeba97SLuciano Coelho curr_ctx = container_of(conf, struct ieee80211_chanctx, conf); 62935f2fce9SMichal Kazior 63077eeba97SLuciano Coelho drv_unassign_vif_chanctx(local, sdata, curr_ctx); 63177eeba97SLuciano Coelho conf = NULL; 632484298adSMichal Kazior list_del(&sdata->assigned_chanctx_list); 63377eeba97SLuciano Coelho } 63477eeba97SLuciano Coelho 63577eeba97SLuciano Coelho if (new_ctx) { 63677eeba97SLuciano Coelho ret = drv_assign_vif_chanctx(local, sdata, new_ctx); 63777eeba97SLuciano Coelho if (ret) 63877eeba97SLuciano Coelho goto out; 63977eeba97SLuciano Coelho 64077eeba97SLuciano Coelho conf = &new_ctx->conf; 641484298adSMichal Kazior list_add(&sdata->assigned_chanctx_list, 642484298adSMichal Kazior &new_ctx->assigned_vifs); 64377eeba97SLuciano Coelho } 64477eeba97SLuciano Coelho 64577eeba97SLuciano Coelho out: 64677eeba97SLuciano Coelho rcu_assign_pointer(sdata->vif.chanctx_conf, conf); 64777eeba97SLuciano Coelho 64877eeba97SLuciano Coelho sdata->vif.bss_conf.idle = !conf; 64977eeba97SLuciano Coelho 650c0166da9SMichal Kazior if (curr_ctx && ieee80211_chanctx_num_assigned(local, curr_ctx) > 0) { 65177eeba97SLuciano Coelho ieee80211_recalc_chanctx_chantype(local, curr_ctx); 65277eeba97SLuciano Coelho ieee80211_recalc_smps_chanctx(local, curr_ctx); 65377eeba97SLuciano Coelho ieee80211_recalc_radar_chanctx(local, curr_ctx); 65477eeba97SLuciano Coelho ieee80211_recalc_chanctx_min_def(local, curr_ctx); 65577eeba97SLuciano Coelho } 65677eeba97SLuciano Coelho 657c0166da9SMichal Kazior if (new_ctx && ieee80211_chanctx_num_assigned(local, new_ctx) > 0) { 658db82d8a9SLorenzo Bianconi ieee80211_recalc_txpower(sdata, false); 65977eeba97SLuciano Coelho ieee80211_recalc_chanctx_min_def(local, new_ctx); 66077eeba97SLuciano Coelho } 6615bbe754dSJohannes Berg 6625bbe754dSJohannes Berg if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE && 6635bbe754dSJohannes Berg sdata->vif.type != NL80211_IFTYPE_MONITOR) 66477eeba97SLuciano Coelho ieee80211_bss_info_change_notify(sdata, 66577eeba97SLuciano Coelho BSS_CHANGED_IDLE); 666fd0f979aSJohannes Berg 66717c18bf8SJohannes Berg ieee80211_check_fast_xmit_iface(sdata); 66817c18bf8SJohannes Berg 66977eeba97SLuciano Coelho return ret; 670d01a1e65SMichal Kazior } 671d01a1e65SMichal Kazior 67204ecd257SJohannes Berg void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local, 67304ecd257SJohannes Berg struct ieee80211_chanctx *chanctx) 67404ecd257SJohannes Berg { 67504ecd257SJohannes Berg struct ieee80211_sub_if_data *sdata; 67604ecd257SJohannes Berg u8 rx_chains_static, rx_chains_dynamic; 67704ecd257SJohannes Berg 67804ecd257SJohannes Berg lockdep_assert_held(&local->chanctx_mtx); 67904ecd257SJohannes Berg 68004ecd257SJohannes Berg rx_chains_static = 1; 68104ecd257SJohannes Berg rx_chains_dynamic = 1; 68204ecd257SJohannes Berg 68304ecd257SJohannes Berg rcu_read_lock(); 68404ecd257SJohannes Berg list_for_each_entry_rcu(sdata, &local->interfaces, list) { 68504ecd257SJohannes Berg u8 needed_static, needed_dynamic; 68604ecd257SJohannes Berg 68704ecd257SJohannes Berg if (!ieee80211_sdata_running(sdata)) 68804ecd257SJohannes Berg continue; 68904ecd257SJohannes Berg 69004ecd257SJohannes Berg if (rcu_access_pointer(sdata->vif.chanctx_conf) != 69104ecd257SJohannes Berg &chanctx->conf) 69204ecd257SJohannes Berg continue; 69304ecd257SJohannes Berg 69404ecd257SJohannes Berg switch (sdata->vif.type) { 69504ecd257SJohannes Berg case NL80211_IFTYPE_P2P_DEVICE: 69604ecd257SJohannes Berg continue; 69704ecd257SJohannes Berg case NL80211_IFTYPE_STATION: 69804ecd257SJohannes Berg if (!sdata->u.mgd.associated) 69904ecd257SJohannes Berg continue; 70004ecd257SJohannes Berg break; 70104ecd257SJohannes Berg case NL80211_IFTYPE_AP_VLAN: 70204ecd257SJohannes Berg continue; 70304ecd257SJohannes Berg case NL80211_IFTYPE_AP: 70404ecd257SJohannes Berg case NL80211_IFTYPE_ADHOC: 70504ecd257SJohannes Berg case NL80211_IFTYPE_WDS: 70604ecd257SJohannes Berg case NL80211_IFTYPE_MESH_POINT: 707239281f8SRostislav Lisovy case NL80211_IFTYPE_OCB: 70804ecd257SJohannes Berg break; 70904ecd257SJohannes Berg default: 71004ecd257SJohannes Berg WARN_ON_ONCE(1); 71104ecd257SJohannes Berg } 71204ecd257SJohannes Berg 71304ecd257SJohannes Berg switch (sdata->smps_mode) { 71404ecd257SJohannes Berg default: 71504ecd257SJohannes Berg WARN_ONCE(1, "Invalid SMPS mode %d\n", 71604ecd257SJohannes Berg sdata->smps_mode); 71704ecd257SJohannes Berg /* fall through */ 71804ecd257SJohannes Berg case IEEE80211_SMPS_OFF: 71904ecd257SJohannes Berg needed_static = sdata->needed_rx_chains; 72004ecd257SJohannes Berg needed_dynamic = sdata->needed_rx_chains; 72104ecd257SJohannes Berg break; 72204ecd257SJohannes Berg case IEEE80211_SMPS_DYNAMIC: 72304ecd257SJohannes Berg needed_static = 1; 72404ecd257SJohannes Berg needed_dynamic = sdata->needed_rx_chains; 72504ecd257SJohannes Berg break; 72604ecd257SJohannes Berg case IEEE80211_SMPS_STATIC: 72704ecd257SJohannes Berg needed_static = 1; 72804ecd257SJohannes Berg needed_dynamic = 1; 72904ecd257SJohannes Berg break; 73004ecd257SJohannes Berg } 73104ecd257SJohannes Berg 73204ecd257SJohannes Berg rx_chains_static = max(rx_chains_static, needed_static); 73304ecd257SJohannes Berg rx_chains_dynamic = max(rx_chains_dynamic, needed_dynamic); 73404ecd257SJohannes Berg } 7357b8a9cddSIdo Yariv 7367b8a9cddSIdo Yariv /* Disable SMPS for the monitor interface */ 7377b8a9cddSIdo Yariv sdata = rcu_dereference(local->monitor_sdata); 7387b8a9cddSIdo Yariv if (sdata && 7397b8a9cddSIdo Yariv rcu_access_pointer(sdata->vif.chanctx_conf) == &chanctx->conf) 7407b8a9cddSIdo Yariv rx_chains_dynamic = rx_chains_static = local->rx_chains; 7417b8a9cddSIdo Yariv 74204ecd257SJohannes Berg rcu_read_unlock(); 74304ecd257SJohannes Berg 74404ecd257SJohannes Berg if (!local->use_chanctx) { 74504ecd257SJohannes Berg if (rx_chains_static > 1) 74604ecd257SJohannes Berg local->smps_mode = IEEE80211_SMPS_OFF; 74704ecd257SJohannes Berg else if (rx_chains_dynamic > 1) 74804ecd257SJohannes Berg local->smps_mode = IEEE80211_SMPS_DYNAMIC; 74904ecd257SJohannes Berg else 75004ecd257SJohannes Berg local->smps_mode = IEEE80211_SMPS_STATIC; 75104ecd257SJohannes Berg ieee80211_hw_config(local, 0); 75204ecd257SJohannes Berg } 75304ecd257SJohannes Berg 75404ecd257SJohannes Berg if (rx_chains_static == chanctx->conf.rx_chains_static && 75504ecd257SJohannes Berg rx_chains_dynamic == chanctx->conf.rx_chains_dynamic) 75604ecd257SJohannes Berg return; 75704ecd257SJohannes Berg 75804ecd257SJohannes Berg chanctx->conf.rx_chains_static = rx_chains_static; 75904ecd257SJohannes Berg chanctx->conf.rx_chains_dynamic = rx_chains_dynamic; 76004ecd257SJohannes Berg drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RX_CHAINS); 76104ecd257SJohannes Berg } 76204ecd257SJohannes Berg 76311335a55SLuciano Coelho static void 76411335a55SLuciano Coelho __ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata, 76511335a55SLuciano Coelho bool clear) 76611335a55SLuciano Coelho { 76733926eb7SJohannes Berg struct ieee80211_local *local __maybe_unused = sdata->local; 76811335a55SLuciano Coelho struct ieee80211_sub_if_data *vlan; 76911335a55SLuciano Coelho struct ieee80211_chanctx_conf *conf; 77011335a55SLuciano Coelho 77111335a55SLuciano Coelho if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP)) 77211335a55SLuciano Coelho return; 77311335a55SLuciano Coelho 77411335a55SLuciano Coelho lockdep_assert_held(&local->mtx); 77511335a55SLuciano Coelho 77611335a55SLuciano Coelho /* Check that conf exists, even when clearing this function 77711335a55SLuciano Coelho * must be called with the AP's channel context still there 77811335a55SLuciano Coelho * as it would otherwise cause VLANs to have an invalid 77911335a55SLuciano Coelho * channel context pointer for a while, possibly pointing 78011335a55SLuciano Coelho * to a channel context that has already been freed. 78111335a55SLuciano Coelho */ 78211335a55SLuciano Coelho conf = rcu_dereference_protected(sdata->vif.chanctx_conf, 78311335a55SLuciano Coelho lockdep_is_held(&local->chanctx_mtx)); 78411335a55SLuciano Coelho WARN_ON(!conf); 78511335a55SLuciano Coelho 78611335a55SLuciano Coelho if (clear) 78711335a55SLuciano Coelho conf = NULL; 78811335a55SLuciano Coelho 78911335a55SLuciano Coelho list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) 79011335a55SLuciano Coelho rcu_assign_pointer(vlan->vif.chanctx_conf, conf); 79111335a55SLuciano Coelho } 79211335a55SLuciano Coelho 79311335a55SLuciano Coelho void ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata, 79411335a55SLuciano Coelho bool clear) 79511335a55SLuciano Coelho { 79611335a55SLuciano Coelho struct ieee80211_local *local = sdata->local; 79711335a55SLuciano Coelho 79811335a55SLuciano Coelho mutex_lock(&local->chanctx_mtx); 79911335a55SLuciano Coelho 80011335a55SLuciano Coelho __ieee80211_vif_copy_chanctx_to_vlans(sdata, clear); 80111335a55SLuciano Coelho 80211335a55SLuciano Coelho mutex_unlock(&local->chanctx_mtx); 80311335a55SLuciano Coelho } 80411335a55SLuciano Coelho 80511335a55SLuciano Coelho int ieee80211_vif_unreserve_chanctx(struct ieee80211_sub_if_data *sdata) 80611335a55SLuciano Coelho { 807e3afb920SMichal Kazior struct ieee80211_chanctx *ctx = sdata->reserved_chanctx; 808e3afb920SMichal Kazior 80911335a55SLuciano Coelho lockdep_assert_held(&sdata->local->chanctx_mtx); 81011335a55SLuciano Coelho 811e3afb920SMichal Kazior if (WARN_ON(!ctx)) 81211335a55SLuciano Coelho return -EINVAL; 81311335a55SLuciano Coelho 814e3afb920SMichal Kazior list_del(&sdata->reserved_chanctx_list); 81511335a55SLuciano Coelho sdata->reserved_chanctx = NULL; 81611335a55SLuciano Coelho 8175bcae31dSMichal Kazior if (ieee80211_chanctx_refcount(sdata->local, ctx) == 0) { 8185bcae31dSMichal Kazior if (ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER) { 8195bcae31dSMichal Kazior if (WARN_ON(!ctx->replace_ctx)) 8205bcae31dSMichal Kazior return -EINVAL; 8215bcae31dSMichal Kazior 8225bcae31dSMichal Kazior WARN_ON(ctx->replace_ctx->replace_state != 8235bcae31dSMichal Kazior IEEE80211_CHANCTX_WILL_BE_REPLACED); 8245bcae31dSMichal Kazior WARN_ON(ctx->replace_ctx->replace_ctx != ctx); 8255bcae31dSMichal Kazior 8265bcae31dSMichal Kazior ctx->replace_ctx->replace_ctx = NULL; 8275bcae31dSMichal Kazior ctx->replace_ctx->replace_state = 8285bcae31dSMichal Kazior IEEE80211_CHANCTX_REPLACE_NONE; 8295bcae31dSMichal Kazior 8305bcae31dSMichal Kazior list_del_rcu(&ctx->list); 8315bcae31dSMichal Kazior kfree_rcu(ctx, rcu_head); 8325bcae31dSMichal Kazior } else { 833e3afb920SMichal Kazior ieee80211_free_chanctx(sdata->local, ctx); 8345bcae31dSMichal Kazior } 8355bcae31dSMichal Kazior } 836e3afb920SMichal Kazior 83711335a55SLuciano Coelho return 0; 83811335a55SLuciano Coelho } 83911335a55SLuciano Coelho 84011335a55SLuciano Coelho int ieee80211_vif_reserve_chanctx(struct ieee80211_sub_if_data *sdata, 84111335a55SLuciano Coelho const struct cfg80211_chan_def *chandef, 84209332481SMichal Kazior enum ieee80211_chanctx_mode mode, 84309332481SMichal Kazior bool radar_required) 84411335a55SLuciano Coelho { 84511335a55SLuciano Coelho struct ieee80211_local *local = sdata->local; 8465bcae31dSMichal Kazior struct ieee80211_chanctx *new_ctx, *curr_ctx, *ctx; 84711335a55SLuciano Coelho 8485bcae31dSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 84911335a55SLuciano Coelho 8505bcae31dSMichal Kazior curr_ctx = ieee80211_vif_get_chanctx(sdata); 8515bcae31dSMichal Kazior if (curr_ctx && local->use_chanctx && !local->ops->switch_vif_chanctx) 8525bcae31dSMichal Kazior return -ENOTSUPP; 85311335a55SLuciano Coelho 85413f348a8SMichal Kazior new_ctx = ieee80211_find_reservation_chanctx(local, chandef, mode); 85511335a55SLuciano Coelho if (!new_ctx) { 8565bcae31dSMichal Kazior if (ieee80211_can_create_new_chanctx(local)) { 85711335a55SLuciano Coelho new_ctx = ieee80211_new_chanctx(local, chandef, mode); 8585bcae31dSMichal Kazior if (IS_ERR(new_ctx)) 8595bcae31dSMichal Kazior return PTR_ERR(new_ctx); 860c2b90ad8SMichal Kazior } else { 8615bcae31dSMichal Kazior if (!curr_ctx || 8625bcae31dSMichal Kazior (curr_ctx->replace_state == 8635bcae31dSMichal Kazior IEEE80211_CHANCTX_WILL_BE_REPLACED) || 8645bcae31dSMichal Kazior !list_empty(&curr_ctx->reserved_vifs)) { 8655bcae31dSMichal Kazior /* 8665bcae31dSMichal Kazior * Another vif already requested this context 8675bcae31dSMichal Kazior * for a reservation. Find another one hoping 8685bcae31dSMichal Kazior * all vifs assigned to it will also switch 8695bcae31dSMichal Kazior * soon enough. 8705bcae31dSMichal Kazior * 8715bcae31dSMichal Kazior * TODO: This needs a little more work as some 8725bcae31dSMichal Kazior * cases (more than 2 chanctx capable devices) 8735bcae31dSMichal Kazior * may fail which could otherwise succeed 8745bcae31dSMichal Kazior * provided some channel context juggling was 8755bcae31dSMichal Kazior * performed. 8765bcae31dSMichal Kazior * 8775bcae31dSMichal Kazior * Consider ctx1..3, vif1..6, each ctx has 2 8785bcae31dSMichal Kazior * vifs. vif1 and vif2 from ctx1 request new 8795bcae31dSMichal Kazior * different chandefs starting 2 in-place 8805bcae31dSMichal Kazior * reserations with ctx4 and ctx5 replacing 8815bcae31dSMichal Kazior * ctx1 and ctx2 respectively. Next vif5 and 8825bcae31dSMichal Kazior * vif6 from ctx3 reserve ctx4. If vif3 and 8835bcae31dSMichal Kazior * vif4 remain on ctx2 as they are then this 8845bcae31dSMichal Kazior * fails unless `replace_ctx` from ctx5 is 8855bcae31dSMichal Kazior * replaced with ctx3. 8865bcae31dSMichal Kazior */ 8875bcae31dSMichal Kazior list_for_each_entry(ctx, &local->chanctx_list, 8885bcae31dSMichal Kazior list) { 8895bcae31dSMichal Kazior if (ctx->replace_state != 8905bcae31dSMichal Kazior IEEE80211_CHANCTX_REPLACE_NONE) 8915bcae31dSMichal Kazior continue; 8925bcae31dSMichal Kazior 8935bcae31dSMichal Kazior if (!list_empty(&ctx->reserved_vifs)) 8945bcae31dSMichal Kazior continue; 8955bcae31dSMichal Kazior 8965bcae31dSMichal Kazior curr_ctx = ctx; 8975bcae31dSMichal Kazior break; 8985bcae31dSMichal Kazior } 8995bcae31dSMichal Kazior } 9005bcae31dSMichal Kazior 9015bcae31dSMichal Kazior /* 9025bcae31dSMichal Kazior * If that's true then all available contexts already 9035bcae31dSMichal Kazior * have reservations and cannot be used. 9045bcae31dSMichal Kazior */ 9055bcae31dSMichal Kazior if (!curr_ctx || 9065bcae31dSMichal Kazior (curr_ctx->replace_state == 9075bcae31dSMichal Kazior IEEE80211_CHANCTX_WILL_BE_REPLACED) || 9085bcae31dSMichal Kazior !list_empty(&curr_ctx->reserved_vifs)) 9095bcae31dSMichal Kazior return -EBUSY; 9105bcae31dSMichal Kazior 9115bcae31dSMichal Kazior new_ctx = ieee80211_alloc_chanctx(local, chandef, mode); 9125bcae31dSMichal Kazior if (!new_ctx) 9135bcae31dSMichal Kazior return -ENOMEM; 9145bcae31dSMichal Kazior 9155bcae31dSMichal Kazior new_ctx->replace_ctx = curr_ctx; 9165bcae31dSMichal Kazior new_ctx->replace_state = 9175bcae31dSMichal Kazior IEEE80211_CHANCTX_REPLACES_OTHER; 9185bcae31dSMichal Kazior 9195bcae31dSMichal Kazior curr_ctx->replace_ctx = new_ctx; 9205bcae31dSMichal Kazior curr_ctx->replace_state = 9215bcae31dSMichal Kazior IEEE80211_CHANCTX_WILL_BE_REPLACED; 9225bcae31dSMichal Kazior 9235bcae31dSMichal Kazior list_add_rcu(&new_ctx->list, &local->chanctx_list); 92411335a55SLuciano Coelho } 9255d52ee81SLuciano Coelho } 92611335a55SLuciano Coelho 927e3afb920SMichal Kazior list_add(&sdata->reserved_chanctx_list, &new_ctx->reserved_vifs); 92811335a55SLuciano Coelho sdata->reserved_chanctx = new_ctx; 92911335a55SLuciano Coelho sdata->reserved_chandef = *chandef; 93009332481SMichal Kazior sdata->reserved_radar_required = radar_required; 9315bcae31dSMichal Kazior sdata->reserved_ready = false; 9325bcae31dSMichal Kazior 9335bcae31dSMichal Kazior return 0; 93411335a55SLuciano Coelho } 93511335a55SLuciano Coelho 93603078de4SMichal Kazior static void 93703078de4SMichal Kazior ieee80211_vif_chanctx_reservation_complete(struct ieee80211_sub_if_data *sdata) 93803078de4SMichal Kazior { 93903078de4SMichal Kazior switch (sdata->vif.type) { 94003078de4SMichal Kazior case NL80211_IFTYPE_ADHOC: 94103078de4SMichal Kazior case NL80211_IFTYPE_AP: 94203078de4SMichal Kazior case NL80211_IFTYPE_MESH_POINT: 9436e0bd6c3SRostislav Lisovy case NL80211_IFTYPE_OCB: 94403078de4SMichal Kazior ieee80211_queue_work(&sdata->local->hw, 94503078de4SMichal Kazior &sdata->csa_finalize_work); 94603078de4SMichal Kazior break; 94703078de4SMichal Kazior case NL80211_IFTYPE_STATION: 9484c3ebc56SMichal Kazior ieee80211_queue_work(&sdata->local->hw, 9494c3ebc56SMichal Kazior &sdata->u.mgd.chswitch_work); 9504c3ebc56SMichal Kazior break; 9514c3ebc56SMichal Kazior case NL80211_IFTYPE_UNSPECIFIED: 95203078de4SMichal Kazior case NL80211_IFTYPE_AP_VLAN: 95303078de4SMichal Kazior case NL80211_IFTYPE_WDS: 95403078de4SMichal Kazior case NL80211_IFTYPE_MONITOR: 95503078de4SMichal Kazior case NL80211_IFTYPE_P2P_CLIENT: 95603078de4SMichal Kazior case NL80211_IFTYPE_P2P_GO: 95703078de4SMichal Kazior case NL80211_IFTYPE_P2P_DEVICE: 95803078de4SMichal Kazior case NUM_NL80211_IFTYPES: 95903078de4SMichal Kazior WARN_ON(1); 96003078de4SMichal Kazior break; 96103078de4SMichal Kazior } 96203078de4SMichal Kazior } 96303078de4SMichal Kazior 9642967e031SFelix Fietkau static void 9652967e031SFelix Fietkau ieee80211_vif_update_chandef(struct ieee80211_sub_if_data *sdata, 9662967e031SFelix Fietkau const struct cfg80211_chan_def *chandef) 9672967e031SFelix Fietkau { 9682967e031SFelix Fietkau struct ieee80211_sub_if_data *vlan; 9692967e031SFelix Fietkau 9702967e031SFelix Fietkau sdata->vif.bss_conf.chandef = *chandef; 9712967e031SFelix Fietkau 9722967e031SFelix Fietkau if (sdata->vif.type != NL80211_IFTYPE_AP) 9732967e031SFelix Fietkau return; 9742967e031SFelix Fietkau 9752967e031SFelix Fietkau list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) 9762967e031SFelix Fietkau vlan->vif.bss_conf.chandef = *chandef; 9772967e031SFelix Fietkau } 9782967e031SFelix Fietkau 9795bcae31dSMichal Kazior static int 9805bcae31dSMichal Kazior ieee80211_vif_use_reserved_reassign(struct ieee80211_sub_if_data *sdata) 98111335a55SLuciano Coelho { 98211335a55SLuciano Coelho struct ieee80211_local *local = sdata->local; 9835bcae31dSMichal Kazior struct ieee80211_vif_chanctx_switch vif_chsw[1] = {}; 9845bcae31dSMichal Kazior struct ieee80211_chanctx *old_ctx, *new_ctx; 9855bcae31dSMichal Kazior const struct cfg80211_chan_def *chandef; 9865bcae31dSMichal Kazior u32 changed = 0; 9875bcae31dSMichal Kazior int err; 98811335a55SLuciano Coelho 98911335a55SLuciano Coelho lockdep_assert_held(&local->mtx); 9905bcae31dSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 99111335a55SLuciano Coelho 9925bcae31dSMichal Kazior new_ctx = sdata->reserved_chanctx; 9935bcae31dSMichal Kazior old_ctx = ieee80211_vif_get_chanctx(sdata); 99411335a55SLuciano Coelho 9955bcae31dSMichal Kazior if (WARN_ON(!sdata->reserved_ready)) 9965bcae31dSMichal Kazior return -EBUSY; 99711335a55SLuciano Coelho 9985bcae31dSMichal Kazior if (WARN_ON(!new_ctx)) 9995bcae31dSMichal Kazior return -EINVAL; 100011335a55SLuciano Coelho 10015bcae31dSMichal Kazior if (WARN_ON(!old_ctx)) 10025bcae31dSMichal Kazior return -EINVAL; 100311335a55SLuciano Coelho 10045bcae31dSMichal Kazior if (WARN_ON(new_ctx->replace_state == 10055bcae31dSMichal Kazior IEEE80211_CHANCTX_REPLACES_OTHER)) 10065bcae31dSMichal Kazior return -EINVAL; 100711335a55SLuciano Coelho 10085bcae31dSMichal Kazior chandef = ieee80211_chanctx_non_reserved_chandef(local, new_ctx, 10095bcae31dSMichal Kazior &sdata->reserved_chandef); 10105bcae31dSMichal Kazior if (WARN_ON(!chandef)) 10115bcae31dSMichal Kazior return -EINVAL; 101211335a55SLuciano Coelho 10135bcae31dSMichal Kazior vif_chsw[0].vif = &sdata->vif; 10145bcae31dSMichal Kazior vif_chsw[0].old_ctx = &old_ctx->conf; 10155bcae31dSMichal Kazior vif_chsw[0].new_ctx = &new_ctx->conf; 10165bcae31dSMichal Kazior 1017e3afb920SMichal Kazior list_del(&sdata->reserved_chanctx_list); 10185bcae31dSMichal Kazior sdata->reserved_chanctx = NULL; 101911335a55SLuciano Coelho 10205bcae31dSMichal Kazior err = drv_switch_vif_chanctx(local, vif_chsw, 1, 10215bcae31dSMichal Kazior CHANCTX_SWMODE_REASSIGN_VIF); 10225bcae31dSMichal Kazior if (err) { 10235bcae31dSMichal Kazior if (ieee80211_chanctx_refcount(local, new_ctx) == 0) 10245bcae31dSMichal Kazior ieee80211_free_chanctx(local, new_ctx); 10255bcae31dSMichal Kazior 102603078de4SMichal Kazior goto out; 102711335a55SLuciano Coelho } 102811335a55SLuciano Coelho 10295bcae31dSMichal Kazior list_move(&sdata->assigned_chanctx_list, &new_ctx->assigned_vifs); 10305bcae31dSMichal Kazior rcu_assign_pointer(sdata->vif.chanctx_conf, &new_ctx->conf); 10315bcae31dSMichal Kazior 103211335a55SLuciano Coelho if (sdata->vif.type == NL80211_IFTYPE_AP) 103311335a55SLuciano Coelho __ieee80211_vif_copy_chanctx_to_vlans(sdata, false); 10345bcae31dSMichal Kazior 103517c18bf8SJohannes Berg ieee80211_check_fast_xmit_iface(sdata); 103617c18bf8SJohannes Berg 10375bcae31dSMichal Kazior if (ieee80211_chanctx_refcount(local, old_ctx) == 0) 10385bcae31dSMichal Kazior ieee80211_free_chanctx(local, old_ctx); 10395bcae31dSMichal Kazior 10405bcae31dSMichal Kazior if (sdata->vif.bss_conf.chandef.width != sdata->reserved_chandef.width) 10415bcae31dSMichal Kazior changed = BSS_CHANGED_BANDWIDTH; 10425bcae31dSMichal Kazior 10432967e031SFelix Fietkau ieee80211_vif_update_chandef(sdata, &sdata->reserved_chandef); 10445bcae31dSMichal Kazior 1045722ddb0dSEmmanuel Grumbach ieee80211_recalc_smps_chanctx(local, new_ctx); 1046722ddb0dSEmmanuel Grumbach ieee80211_recalc_radar_chanctx(local, new_ctx); 1047722ddb0dSEmmanuel Grumbach ieee80211_recalc_chanctx_min_def(local, new_ctx); 1048722ddb0dSEmmanuel Grumbach 10495bcae31dSMichal Kazior if (changed) 10505bcae31dSMichal Kazior ieee80211_bss_info_change_notify(sdata, changed); 10515bcae31dSMichal Kazior 105203078de4SMichal Kazior out: 105303078de4SMichal Kazior ieee80211_vif_chanctx_reservation_complete(sdata); 10545bcae31dSMichal Kazior return err; 10555d52ee81SLuciano Coelho } 105611335a55SLuciano Coelho 10575bcae31dSMichal Kazior static int 10585bcae31dSMichal Kazior ieee80211_vif_use_reserved_assign(struct ieee80211_sub_if_data *sdata) 10595bcae31dSMichal Kazior { 10605bcae31dSMichal Kazior struct ieee80211_local *local = sdata->local; 10615bcae31dSMichal Kazior struct ieee80211_chanctx *old_ctx, *new_ctx; 10625bcae31dSMichal Kazior const struct cfg80211_chan_def *chandef; 10635bcae31dSMichal Kazior int err; 10645bcae31dSMichal Kazior 10655bcae31dSMichal Kazior old_ctx = ieee80211_vif_get_chanctx(sdata); 10665bcae31dSMichal Kazior new_ctx = sdata->reserved_chanctx; 10675bcae31dSMichal Kazior 10685bcae31dSMichal Kazior if (WARN_ON(!sdata->reserved_ready)) 10695bcae31dSMichal Kazior return -EINVAL; 10705bcae31dSMichal Kazior 10715bcae31dSMichal Kazior if (WARN_ON(old_ctx)) 10725bcae31dSMichal Kazior return -EINVAL; 10735bcae31dSMichal Kazior 10745bcae31dSMichal Kazior if (WARN_ON(!new_ctx)) 10755bcae31dSMichal Kazior return -EINVAL; 10765bcae31dSMichal Kazior 10775bcae31dSMichal Kazior if (WARN_ON(new_ctx->replace_state == 10785bcae31dSMichal Kazior IEEE80211_CHANCTX_REPLACES_OTHER)) 10795bcae31dSMichal Kazior return -EINVAL; 10805bcae31dSMichal Kazior 10815bcae31dSMichal Kazior chandef = ieee80211_chanctx_non_reserved_chandef(local, new_ctx, 10825bcae31dSMichal Kazior &sdata->reserved_chandef); 10835bcae31dSMichal Kazior if (WARN_ON(!chandef)) 10845bcae31dSMichal Kazior return -EINVAL; 10855bcae31dSMichal Kazior 10865bcae31dSMichal Kazior list_del(&sdata->reserved_chanctx_list); 10875bcae31dSMichal Kazior sdata->reserved_chanctx = NULL; 10885bcae31dSMichal Kazior 10895bcae31dSMichal Kazior err = ieee80211_assign_vif_chanctx(sdata, new_ctx); 10905bcae31dSMichal Kazior if (err) { 10915bcae31dSMichal Kazior if (ieee80211_chanctx_refcount(local, new_ctx) == 0) 10925bcae31dSMichal Kazior ieee80211_free_chanctx(local, new_ctx); 10935bcae31dSMichal Kazior 10945bcae31dSMichal Kazior goto out; 10955bcae31dSMichal Kazior } 10965bcae31dSMichal Kazior 10975bcae31dSMichal Kazior out: 109803078de4SMichal Kazior ieee80211_vif_chanctx_reservation_complete(sdata); 10995bcae31dSMichal Kazior return err; 11005bcae31dSMichal Kazior } 11015bcae31dSMichal Kazior 11025bcae31dSMichal Kazior static bool 11035bcae31dSMichal Kazior ieee80211_vif_has_in_place_reservation(struct ieee80211_sub_if_data *sdata) 11045bcae31dSMichal Kazior { 11055bcae31dSMichal Kazior struct ieee80211_chanctx *old_ctx, *new_ctx; 11065bcae31dSMichal Kazior 11075bcae31dSMichal Kazior lockdep_assert_held(&sdata->local->chanctx_mtx); 11085bcae31dSMichal Kazior 11095bcae31dSMichal Kazior new_ctx = sdata->reserved_chanctx; 11105bcae31dSMichal Kazior old_ctx = ieee80211_vif_get_chanctx(sdata); 11115bcae31dSMichal Kazior 11125bcae31dSMichal Kazior if (!old_ctx) 11135bcae31dSMichal Kazior return false; 11145bcae31dSMichal Kazior 11155bcae31dSMichal Kazior if (WARN_ON(!new_ctx)) 11165bcae31dSMichal Kazior return false; 11175bcae31dSMichal Kazior 11185bcae31dSMichal Kazior if (old_ctx->replace_state != IEEE80211_CHANCTX_WILL_BE_REPLACED) 11195bcae31dSMichal Kazior return false; 11205bcae31dSMichal Kazior 11215bcae31dSMichal Kazior if (new_ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) 11225bcae31dSMichal Kazior return false; 11235bcae31dSMichal Kazior 11245bcae31dSMichal Kazior return true; 11255bcae31dSMichal Kazior } 11265bcae31dSMichal Kazior 11275bcae31dSMichal Kazior static int ieee80211_chsw_switch_hwconf(struct ieee80211_local *local, 11285bcae31dSMichal Kazior struct ieee80211_chanctx *new_ctx) 11295bcae31dSMichal Kazior { 11305bcae31dSMichal Kazior const struct cfg80211_chan_def *chandef; 11315bcae31dSMichal Kazior 11325bcae31dSMichal Kazior lockdep_assert_held(&local->mtx); 11335bcae31dSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 11345bcae31dSMichal Kazior 11355bcae31dSMichal Kazior chandef = ieee80211_chanctx_reserved_chandef(local, new_ctx, NULL); 11365bcae31dSMichal Kazior if (WARN_ON(!chandef)) 11375bcae31dSMichal Kazior return -EINVAL; 11385bcae31dSMichal Kazior 11395bcae31dSMichal Kazior local->hw.conf.radar_enabled = new_ctx->conf.radar_enabled; 11405bcae31dSMichal Kazior local->_oper_chandef = *chandef; 11415bcae31dSMichal Kazior ieee80211_hw_config(local, 0); 11425bcae31dSMichal Kazior 11435bcae31dSMichal Kazior return 0; 11445bcae31dSMichal Kazior } 11455bcae31dSMichal Kazior 11465bcae31dSMichal Kazior static int ieee80211_chsw_switch_vifs(struct ieee80211_local *local, 11475bcae31dSMichal Kazior int n_vifs) 11485bcae31dSMichal Kazior { 11495bcae31dSMichal Kazior struct ieee80211_vif_chanctx_switch *vif_chsw; 11505bcae31dSMichal Kazior struct ieee80211_sub_if_data *sdata; 11515bcae31dSMichal Kazior struct ieee80211_chanctx *ctx, *old_ctx; 11525bcae31dSMichal Kazior int i, err; 11535bcae31dSMichal Kazior 11545bcae31dSMichal Kazior lockdep_assert_held(&local->mtx); 11555bcae31dSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 11565bcae31dSMichal Kazior 11575bcae31dSMichal Kazior vif_chsw = kzalloc(sizeof(vif_chsw[0]) * n_vifs, GFP_KERNEL); 11585bcae31dSMichal Kazior if (!vif_chsw) 11595bcae31dSMichal Kazior return -ENOMEM; 11605bcae31dSMichal Kazior 11615bcae31dSMichal Kazior i = 0; 11625bcae31dSMichal Kazior list_for_each_entry(ctx, &local->chanctx_list, list) { 11635bcae31dSMichal Kazior if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) 11645bcae31dSMichal Kazior continue; 11655bcae31dSMichal Kazior 11665bcae31dSMichal Kazior if (WARN_ON(!ctx->replace_ctx)) { 11675bcae31dSMichal Kazior err = -EINVAL; 11685bcae31dSMichal Kazior goto out; 11695bcae31dSMichal Kazior } 11705bcae31dSMichal Kazior 11715bcae31dSMichal Kazior list_for_each_entry(sdata, &ctx->reserved_vifs, 11725bcae31dSMichal Kazior reserved_chanctx_list) { 11735bcae31dSMichal Kazior if (!ieee80211_vif_has_in_place_reservation( 11745bcae31dSMichal Kazior sdata)) 11755bcae31dSMichal Kazior continue; 11765bcae31dSMichal Kazior 11775bcae31dSMichal Kazior old_ctx = ieee80211_vif_get_chanctx(sdata); 11785bcae31dSMichal Kazior vif_chsw[i].vif = &sdata->vif; 11795bcae31dSMichal Kazior vif_chsw[i].old_ctx = &old_ctx->conf; 11805bcae31dSMichal Kazior vif_chsw[i].new_ctx = &ctx->conf; 11815bcae31dSMichal Kazior 11825bcae31dSMichal Kazior i++; 11835bcae31dSMichal Kazior } 11845bcae31dSMichal Kazior } 11855bcae31dSMichal Kazior 11865bcae31dSMichal Kazior err = drv_switch_vif_chanctx(local, vif_chsw, n_vifs, 11875bcae31dSMichal Kazior CHANCTX_SWMODE_SWAP_CONTEXTS); 11885bcae31dSMichal Kazior 11895bcae31dSMichal Kazior out: 11905bcae31dSMichal Kazior kfree(vif_chsw); 11915bcae31dSMichal Kazior return err; 11925bcae31dSMichal Kazior } 11935bcae31dSMichal Kazior 11945bcae31dSMichal Kazior static int ieee80211_chsw_switch_ctxs(struct ieee80211_local *local) 11955bcae31dSMichal Kazior { 11965bcae31dSMichal Kazior struct ieee80211_chanctx *ctx; 11975bcae31dSMichal Kazior int err; 11985bcae31dSMichal Kazior 11995bcae31dSMichal Kazior lockdep_assert_held(&local->mtx); 12005bcae31dSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 12015bcae31dSMichal Kazior 12025bcae31dSMichal Kazior list_for_each_entry(ctx, &local->chanctx_list, list) { 12035bcae31dSMichal Kazior if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) 12045bcae31dSMichal Kazior continue; 12055bcae31dSMichal Kazior 12065bcae31dSMichal Kazior if (!list_empty(&ctx->replace_ctx->assigned_vifs)) 12075bcae31dSMichal Kazior continue; 12085bcae31dSMichal Kazior 12095bcae31dSMichal Kazior ieee80211_del_chanctx(local, ctx->replace_ctx); 12105bcae31dSMichal Kazior err = ieee80211_add_chanctx(local, ctx); 12115bcae31dSMichal Kazior if (err) 12125bcae31dSMichal Kazior goto err; 12135bcae31dSMichal Kazior } 12145bcae31dSMichal Kazior 12155bcae31dSMichal Kazior return 0; 12165bcae31dSMichal Kazior 12175bcae31dSMichal Kazior err: 12185bcae31dSMichal Kazior WARN_ON(ieee80211_add_chanctx(local, ctx)); 12195bcae31dSMichal Kazior list_for_each_entry_continue_reverse(ctx, &local->chanctx_list, list) { 12205bcae31dSMichal Kazior if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) 12215bcae31dSMichal Kazior continue; 12225bcae31dSMichal Kazior 12235bcae31dSMichal Kazior if (!list_empty(&ctx->replace_ctx->assigned_vifs)) 12245bcae31dSMichal Kazior continue; 12255bcae31dSMichal Kazior 12265bcae31dSMichal Kazior ieee80211_del_chanctx(local, ctx); 12275bcae31dSMichal Kazior WARN_ON(ieee80211_add_chanctx(local, ctx->replace_ctx)); 12285bcae31dSMichal Kazior } 12295bcae31dSMichal Kazior 12305bcae31dSMichal Kazior return err; 12315bcae31dSMichal Kazior } 12325bcae31dSMichal Kazior 1233649b2a4dSJohannes Berg static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local) 12345bcae31dSMichal Kazior { 12355bcae31dSMichal Kazior struct ieee80211_sub_if_data *sdata, *sdata_tmp; 12365bcae31dSMichal Kazior struct ieee80211_chanctx *ctx, *ctx_tmp, *old_ctx; 12375bcae31dSMichal Kazior struct ieee80211_chanctx *new_ctx = NULL; 12385bcae31dSMichal Kazior int i, err, n_assigned, n_reserved, n_ready; 12395bcae31dSMichal Kazior int n_ctx = 0, n_vifs_switch = 0, n_vifs_assign = 0, n_vifs_ctxless = 0; 12405bcae31dSMichal Kazior 12415bcae31dSMichal Kazior lockdep_assert_held(&local->mtx); 12425bcae31dSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 12435bcae31dSMichal Kazior 12445bcae31dSMichal Kazior /* 12455bcae31dSMichal Kazior * If there are 2 independent pairs of channel contexts performing 12465bcae31dSMichal Kazior * cross-switch of their vifs this code will still wait until both are 12475bcae31dSMichal Kazior * ready even though it could be possible to switch one before the 12485bcae31dSMichal Kazior * other is ready. 12495bcae31dSMichal Kazior * 12505bcae31dSMichal Kazior * For practical reasons and code simplicity just do a single huge 12515bcae31dSMichal Kazior * switch. 12525bcae31dSMichal Kazior */ 12535bcae31dSMichal Kazior 12545bcae31dSMichal Kazior /* 12555bcae31dSMichal Kazior * Verify if the reservation is still feasible. 12565bcae31dSMichal Kazior * - if it's not then disconnect 12575bcae31dSMichal Kazior * - if it is but not all vifs necessary are ready then defer 12585bcae31dSMichal Kazior */ 12595bcae31dSMichal Kazior 12605bcae31dSMichal Kazior list_for_each_entry(ctx, &local->chanctx_list, list) { 12615bcae31dSMichal Kazior if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) 12625bcae31dSMichal Kazior continue; 12635bcae31dSMichal Kazior 12645bcae31dSMichal Kazior if (WARN_ON(!ctx->replace_ctx)) { 12655bcae31dSMichal Kazior err = -EINVAL; 12665bcae31dSMichal Kazior goto err; 12675bcae31dSMichal Kazior } 12685bcae31dSMichal Kazior 12695bcae31dSMichal Kazior if (!local->use_chanctx) 12705bcae31dSMichal Kazior new_ctx = ctx; 12715bcae31dSMichal Kazior 12725bcae31dSMichal Kazior n_ctx++; 12735bcae31dSMichal Kazior 12745bcae31dSMichal Kazior n_assigned = 0; 12755bcae31dSMichal Kazior n_reserved = 0; 12765bcae31dSMichal Kazior n_ready = 0; 12775bcae31dSMichal Kazior 12785bcae31dSMichal Kazior list_for_each_entry(sdata, &ctx->replace_ctx->assigned_vifs, 12795bcae31dSMichal Kazior assigned_chanctx_list) { 12805bcae31dSMichal Kazior n_assigned++; 12815bcae31dSMichal Kazior if (sdata->reserved_chanctx) { 12825bcae31dSMichal Kazior n_reserved++; 12835bcae31dSMichal Kazior if (sdata->reserved_ready) 12845bcae31dSMichal Kazior n_ready++; 12855bcae31dSMichal Kazior } 12865bcae31dSMichal Kazior } 12875bcae31dSMichal Kazior 12885bcae31dSMichal Kazior if (n_assigned != n_reserved) { 12895bcae31dSMichal Kazior if (n_ready == n_reserved) { 12905bcae31dSMichal Kazior wiphy_info(local->hw.wiphy, 12915bcae31dSMichal Kazior "channel context reservation cannot be finalized because some interfaces aren't switching\n"); 12925bcae31dSMichal Kazior err = -EBUSY; 12935bcae31dSMichal Kazior goto err; 12945bcae31dSMichal Kazior } 12955bcae31dSMichal Kazior 12965bcae31dSMichal Kazior return -EAGAIN; 12975bcae31dSMichal Kazior } 12985bcae31dSMichal Kazior 12995bcae31dSMichal Kazior ctx->conf.radar_enabled = false; 13005bcae31dSMichal Kazior list_for_each_entry(sdata, &ctx->reserved_vifs, 13015bcae31dSMichal Kazior reserved_chanctx_list) { 13025bcae31dSMichal Kazior if (ieee80211_vif_has_in_place_reservation(sdata) && 13035bcae31dSMichal Kazior !sdata->reserved_ready) 13045bcae31dSMichal Kazior return -EAGAIN; 13055bcae31dSMichal Kazior 13065bcae31dSMichal Kazior old_ctx = ieee80211_vif_get_chanctx(sdata); 13075bcae31dSMichal Kazior if (old_ctx) { 13085bcae31dSMichal Kazior if (old_ctx->replace_state == 13095bcae31dSMichal Kazior IEEE80211_CHANCTX_WILL_BE_REPLACED) 13105bcae31dSMichal Kazior n_vifs_switch++; 13115bcae31dSMichal Kazior else 13125bcae31dSMichal Kazior n_vifs_assign++; 13135bcae31dSMichal Kazior } else { 13145bcae31dSMichal Kazior n_vifs_ctxless++; 13155bcae31dSMichal Kazior } 13165bcae31dSMichal Kazior 13175bcae31dSMichal Kazior if (sdata->reserved_radar_required) 13185bcae31dSMichal Kazior ctx->conf.radar_enabled = true; 13195bcae31dSMichal Kazior } 13205bcae31dSMichal Kazior } 13215bcae31dSMichal Kazior 13225bcae31dSMichal Kazior if (WARN_ON(n_ctx == 0) || 13235bcae31dSMichal Kazior WARN_ON(n_vifs_switch == 0 && 13245bcae31dSMichal Kazior n_vifs_assign == 0 && 13255bcae31dSMichal Kazior n_vifs_ctxless == 0) || 13265bcae31dSMichal Kazior WARN_ON(n_ctx > 1 && !local->use_chanctx) || 13275bcae31dSMichal Kazior WARN_ON(!new_ctx && !local->use_chanctx)) { 13285bcae31dSMichal Kazior err = -EINVAL; 13295bcae31dSMichal Kazior goto err; 13305bcae31dSMichal Kazior } 13315bcae31dSMichal Kazior 13325bcae31dSMichal Kazior /* 13335bcae31dSMichal Kazior * All necessary vifs are ready. Perform the switch now depending on 13345bcae31dSMichal Kazior * reservations and driver capabilities. 13355bcae31dSMichal Kazior */ 13365bcae31dSMichal Kazior 13375bcae31dSMichal Kazior if (local->use_chanctx) { 13385bcae31dSMichal Kazior if (n_vifs_switch > 0) { 13395bcae31dSMichal Kazior err = ieee80211_chsw_switch_vifs(local, n_vifs_switch); 13405bcae31dSMichal Kazior if (err) 13415bcae31dSMichal Kazior goto err; 13425bcae31dSMichal Kazior } 13435bcae31dSMichal Kazior 13445bcae31dSMichal Kazior if (n_vifs_assign > 0 || n_vifs_ctxless > 0) { 13455bcae31dSMichal Kazior err = ieee80211_chsw_switch_ctxs(local); 13465bcae31dSMichal Kazior if (err) 13475bcae31dSMichal Kazior goto err; 13485bcae31dSMichal Kazior } 13495bcae31dSMichal Kazior } else { 13505bcae31dSMichal Kazior err = ieee80211_chsw_switch_hwconf(local, new_ctx); 13515bcae31dSMichal Kazior if (err) 13525bcae31dSMichal Kazior goto err; 13535bcae31dSMichal Kazior } 13545bcae31dSMichal Kazior 13555bcae31dSMichal Kazior /* 13565bcae31dSMichal Kazior * Update all structures, values and pointers to point to new channel 13575bcae31dSMichal Kazior * context(s). 13585bcae31dSMichal Kazior */ 13595bcae31dSMichal Kazior 13605bcae31dSMichal Kazior i = 0; 13615bcae31dSMichal Kazior list_for_each_entry(ctx, &local->chanctx_list, list) { 13625bcae31dSMichal Kazior if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) 13635bcae31dSMichal Kazior continue; 13645bcae31dSMichal Kazior 13655bcae31dSMichal Kazior if (WARN_ON(!ctx->replace_ctx)) { 13665bcae31dSMichal Kazior err = -EINVAL; 13675bcae31dSMichal Kazior goto err; 13685bcae31dSMichal Kazior } 13695bcae31dSMichal Kazior 13705bcae31dSMichal Kazior list_for_each_entry(sdata, &ctx->reserved_vifs, 13715bcae31dSMichal Kazior reserved_chanctx_list) { 13725bcae31dSMichal Kazior u32 changed = 0; 13735bcae31dSMichal Kazior 13745bcae31dSMichal Kazior if (!ieee80211_vif_has_in_place_reservation(sdata)) 13755bcae31dSMichal Kazior continue; 13765bcae31dSMichal Kazior 13775bcae31dSMichal Kazior rcu_assign_pointer(sdata->vif.chanctx_conf, &ctx->conf); 13785bcae31dSMichal Kazior 13795bcae31dSMichal Kazior if (sdata->vif.type == NL80211_IFTYPE_AP) 13805bcae31dSMichal Kazior __ieee80211_vif_copy_chanctx_to_vlans(sdata, 13815bcae31dSMichal Kazior false); 13825bcae31dSMichal Kazior 138317c18bf8SJohannes Berg ieee80211_check_fast_xmit_iface(sdata); 138417c18bf8SJohannes Berg 13855bcae31dSMichal Kazior sdata->radar_required = sdata->reserved_radar_required; 13865bcae31dSMichal Kazior 13875bcae31dSMichal Kazior if (sdata->vif.bss_conf.chandef.width != 13885bcae31dSMichal Kazior sdata->reserved_chandef.width) 13895bcae31dSMichal Kazior changed = BSS_CHANGED_BANDWIDTH; 13905bcae31dSMichal Kazior 13912967e031SFelix Fietkau ieee80211_vif_update_chandef(sdata, &sdata->reserved_chandef); 13925bcae31dSMichal Kazior if (changed) 13935bcae31dSMichal Kazior ieee80211_bss_info_change_notify(sdata, 13945bcae31dSMichal Kazior changed); 13955bcae31dSMichal Kazior 1396db82d8a9SLorenzo Bianconi ieee80211_recalc_txpower(sdata, false); 13975bcae31dSMichal Kazior } 139811335a55SLuciano Coelho 139911335a55SLuciano Coelho ieee80211_recalc_chanctx_chantype(local, ctx); 140011335a55SLuciano Coelho ieee80211_recalc_smps_chanctx(local, ctx); 140111335a55SLuciano Coelho ieee80211_recalc_radar_chanctx(local, ctx); 140211335a55SLuciano Coelho ieee80211_recalc_chanctx_min_def(local, ctx); 14035bcae31dSMichal Kazior 14045bcae31dSMichal Kazior list_for_each_entry_safe(sdata, sdata_tmp, &ctx->reserved_vifs, 14055bcae31dSMichal Kazior reserved_chanctx_list) { 14065bcae31dSMichal Kazior if (ieee80211_vif_get_chanctx(sdata) != ctx) 14075bcae31dSMichal Kazior continue; 14085bcae31dSMichal Kazior 14095bcae31dSMichal Kazior list_del(&sdata->reserved_chanctx_list); 14105bcae31dSMichal Kazior list_move(&sdata->assigned_chanctx_list, 141147e4df94SMichal Kazior &ctx->assigned_vifs); 14125bcae31dSMichal Kazior sdata->reserved_chanctx = NULL; 141303078de4SMichal Kazior 141403078de4SMichal Kazior ieee80211_vif_chanctx_reservation_complete(sdata); 14155bcae31dSMichal Kazior } 14165bcae31dSMichal Kazior 14175bcae31dSMichal Kazior /* 14185bcae31dSMichal Kazior * This context might have been a dependency for an already 14195bcae31dSMichal Kazior * ready re-assign reservation interface that was deferred. Do 14205bcae31dSMichal Kazior * not propagate error to the caller though. The in-place 14215bcae31dSMichal Kazior * reservation for originally requested interface has already 14225bcae31dSMichal Kazior * succeeded at this point. 14235bcae31dSMichal Kazior */ 14245bcae31dSMichal Kazior list_for_each_entry_safe(sdata, sdata_tmp, &ctx->reserved_vifs, 14255bcae31dSMichal Kazior reserved_chanctx_list) { 14265bcae31dSMichal Kazior if (WARN_ON(ieee80211_vif_has_in_place_reservation( 14275bcae31dSMichal Kazior sdata))) 14285bcae31dSMichal Kazior continue; 14295bcae31dSMichal Kazior 14305bcae31dSMichal Kazior if (WARN_ON(sdata->reserved_chanctx != ctx)) 14315bcae31dSMichal Kazior continue; 14325bcae31dSMichal Kazior 14335bcae31dSMichal Kazior if (!sdata->reserved_ready) 14345bcae31dSMichal Kazior continue; 14355bcae31dSMichal Kazior 14365bcae31dSMichal Kazior if (ieee80211_vif_get_chanctx(sdata)) 14375bcae31dSMichal Kazior err = ieee80211_vif_use_reserved_reassign( 14385bcae31dSMichal Kazior sdata); 14395bcae31dSMichal Kazior else 14405bcae31dSMichal Kazior err = ieee80211_vif_use_reserved_assign(sdata); 14415bcae31dSMichal Kazior 14425bcae31dSMichal Kazior if (err) { 14435bcae31dSMichal Kazior sdata_info(sdata, 14445bcae31dSMichal Kazior "failed to finalize (re-)assign reservation (err=%d)\n", 14455bcae31dSMichal Kazior err); 14465bcae31dSMichal Kazior ieee80211_vif_unreserve_chanctx(sdata); 14475bcae31dSMichal Kazior cfg80211_stop_iface(local->hw.wiphy, 14485bcae31dSMichal Kazior &sdata->wdev, 14495bcae31dSMichal Kazior GFP_KERNEL); 14505bcae31dSMichal Kazior } 14515bcae31dSMichal Kazior } 14525bcae31dSMichal Kazior } 14535bcae31dSMichal Kazior 14545bcae31dSMichal Kazior /* 14555bcae31dSMichal Kazior * Finally free old contexts 14565bcae31dSMichal Kazior */ 14575bcae31dSMichal Kazior 14585bcae31dSMichal Kazior list_for_each_entry_safe(ctx, ctx_tmp, &local->chanctx_list, list) { 14595bcae31dSMichal Kazior if (ctx->replace_state != IEEE80211_CHANCTX_WILL_BE_REPLACED) 14605bcae31dSMichal Kazior continue; 14615bcae31dSMichal Kazior 14625bcae31dSMichal Kazior ctx->replace_ctx->replace_ctx = NULL; 14635bcae31dSMichal Kazior ctx->replace_ctx->replace_state = 14645bcae31dSMichal Kazior IEEE80211_CHANCTX_REPLACE_NONE; 14655bcae31dSMichal Kazior 14665bcae31dSMichal Kazior list_del_rcu(&ctx->list); 14675bcae31dSMichal Kazior kfree_rcu(ctx, rcu_head); 14685bcae31dSMichal Kazior } 14695bcae31dSMichal Kazior 14705bcae31dSMichal Kazior return 0; 14715bcae31dSMichal Kazior 14725bcae31dSMichal Kazior err: 14735bcae31dSMichal Kazior list_for_each_entry(ctx, &local->chanctx_list, list) { 14745bcae31dSMichal Kazior if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) 14755bcae31dSMichal Kazior continue; 14765bcae31dSMichal Kazior 14775bcae31dSMichal Kazior list_for_each_entry_safe(sdata, sdata_tmp, &ctx->reserved_vifs, 147803078de4SMichal Kazior reserved_chanctx_list) { 14795bcae31dSMichal Kazior ieee80211_vif_unreserve_chanctx(sdata); 148003078de4SMichal Kazior ieee80211_vif_chanctx_reservation_complete(sdata); 148103078de4SMichal Kazior } 14825bcae31dSMichal Kazior } 14835bcae31dSMichal Kazior 14845bcae31dSMichal Kazior return err; 14855bcae31dSMichal Kazior } 14865bcae31dSMichal Kazior 1487649b2a4dSJohannes Berg static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata) 1488649b2a4dSJohannes Berg { 1489649b2a4dSJohannes Berg struct ieee80211_local *local = sdata->local; 1490649b2a4dSJohannes Berg struct ieee80211_chanctx_conf *conf; 1491649b2a4dSJohannes Berg struct ieee80211_chanctx *ctx; 1492649b2a4dSJohannes Berg bool use_reserved_switch = false; 1493649b2a4dSJohannes Berg 1494649b2a4dSJohannes Berg lockdep_assert_held(&local->chanctx_mtx); 1495649b2a4dSJohannes Berg 1496649b2a4dSJohannes Berg conf = rcu_dereference_protected(sdata->vif.chanctx_conf, 1497649b2a4dSJohannes Berg lockdep_is_held(&local->chanctx_mtx)); 1498649b2a4dSJohannes Berg if (!conf) 1499649b2a4dSJohannes Berg return; 1500649b2a4dSJohannes Berg 1501649b2a4dSJohannes Berg ctx = container_of(conf, struct ieee80211_chanctx, conf); 1502649b2a4dSJohannes Berg 1503649b2a4dSJohannes Berg if (sdata->reserved_chanctx) { 1504649b2a4dSJohannes Berg if (sdata->reserved_chanctx->replace_state == 1505649b2a4dSJohannes Berg IEEE80211_CHANCTX_REPLACES_OTHER && 1506649b2a4dSJohannes Berg ieee80211_chanctx_num_reserved(local, 1507649b2a4dSJohannes Berg sdata->reserved_chanctx) > 1) 1508649b2a4dSJohannes Berg use_reserved_switch = true; 1509649b2a4dSJohannes Berg 1510649b2a4dSJohannes Berg ieee80211_vif_unreserve_chanctx(sdata); 1511649b2a4dSJohannes Berg } 1512649b2a4dSJohannes Berg 1513649b2a4dSJohannes Berg ieee80211_assign_vif_chanctx(sdata, NULL); 1514649b2a4dSJohannes Berg if (ieee80211_chanctx_refcount(local, ctx) == 0) 1515649b2a4dSJohannes Berg ieee80211_free_chanctx(local, ctx); 1516649b2a4dSJohannes Berg 1517104f5a62SEliad Peller sdata->radar_required = false; 1518104f5a62SEliad Peller 1519649b2a4dSJohannes Berg /* Unreserving may ready an in-place reservation. */ 1520649b2a4dSJohannes Berg if (use_reserved_switch) 1521649b2a4dSJohannes Berg ieee80211_vif_use_reserved_switch(local); 1522649b2a4dSJohannes Berg } 1523649b2a4dSJohannes Berg 1524649b2a4dSJohannes Berg int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata, 1525649b2a4dSJohannes Berg const struct cfg80211_chan_def *chandef, 1526649b2a4dSJohannes Berg enum ieee80211_chanctx_mode mode) 1527649b2a4dSJohannes Berg { 1528649b2a4dSJohannes Berg struct ieee80211_local *local = sdata->local; 1529649b2a4dSJohannes Berg struct ieee80211_chanctx *ctx; 1530649b2a4dSJohannes Berg u8 radar_detect_width = 0; 1531649b2a4dSJohannes Berg int ret; 1532649b2a4dSJohannes Berg 1533649b2a4dSJohannes Berg lockdep_assert_held(&local->mtx); 1534649b2a4dSJohannes Berg 1535649b2a4dSJohannes Berg WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev)); 1536649b2a4dSJohannes Berg 1537649b2a4dSJohannes Berg mutex_lock(&local->chanctx_mtx); 1538649b2a4dSJohannes Berg 1539649b2a4dSJohannes Berg ret = cfg80211_chandef_dfs_required(local->hw.wiphy, 1540649b2a4dSJohannes Berg chandef, 1541649b2a4dSJohannes Berg sdata->wdev.iftype); 1542649b2a4dSJohannes Berg if (ret < 0) 1543649b2a4dSJohannes Berg goto out; 1544649b2a4dSJohannes Berg if (ret > 0) 1545649b2a4dSJohannes Berg radar_detect_width = BIT(chandef->width); 1546649b2a4dSJohannes Berg 1547649b2a4dSJohannes Berg sdata->radar_required = ret; 1548649b2a4dSJohannes Berg 1549649b2a4dSJohannes Berg ret = ieee80211_check_combinations(sdata, chandef, mode, 1550649b2a4dSJohannes Berg radar_detect_width); 1551649b2a4dSJohannes Berg if (ret < 0) 1552649b2a4dSJohannes Berg goto out; 1553649b2a4dSJohannes Berg 1554649b2a4dSJohannes Berg __ieee80211_vif_release_channel(sdata); 1555649b2a4dSJohannes Berg 1556649b2a4dSJohannes Berg ctx = ieee80211_find_chanctx(local, chandef, mode); 1557649b2a4dSJohannes Berg if (!ctx) 1558649b2a4dSJohannes Berg ctx = ieee80211_new_chanctx(local, chandef, mode); 1559649b2a4dSJohannes Berg if (IS_ERR(ctx)) { 1560649b2a4dSJohannes Berg ret = PTR_ERR(ctx); 1561649b2a4dSJohannes Berg goto out; 1562649b2a4dSJohannes Berg } 1563649b2a4dSJohannes Berg 15642967e031SFelix Fietkau ieee80211_vif_update_chandef(sdata, chandef); 1565649b2a4dSJohannes Berg 1566649b2a4dSJohannes Berg ret = ieee80211_assign_vif_chanctx(sdata, ctx); 1567649b2a4dSJohannes Berg if (ret) { 1568649b2a4dSJohannes Berg /* if assign fails refcount stays the same */ 1569649b2a4dSJohannes Berg if (ieee80211_chanctx_refcount(local, ctx) == 0) 1570649b2a4dSJohannes Berg ieee80211_free_chanctx(local, ctx); 1571649b2a4dSJohannes Berg goto out; 1572649b2a4dSJohannes Berg } 1573649b2a4dSJohannes Berg 1574649b2a4dSJohannes Berg ieee80211_recalc_smps_chanctx(local, ctx); 1575649b2a4dSJohannes Berg ieee80211_recalc_radar_chanctx(local, ctx); 1576649b2a4dSJohannes Berg out: 1577104f5a62SEliad Peller if (ret) 1578104f5a62SEliad Peller sdata->radar_required = false; 1579104f5a62SEliad Peller 1580649b2a4dSJohannes Berg mutex_unlock(&local->chanctx_mtx); 1581649b2a4dSJohannes Berg return ret; 1582649b2a4dSJohannes Berg } 1583649b2a4dSJohannes Berg 15845bcae31dSMichal Kazior int ieee80211_vif_use_reserved_context(struct ieee80211_sub_if_data *sdata) 15855bcae31dSMichal Kazior { 15865bcae31dSMichal Kazior struct ieee80211_local *local = sdata->local; 15875bcae31dSMichal Kazior struct ieee80211_chanctx *new_ctx; 15885bcae31dSMichal Kazior struct ieee80211_chanctx *old_ctx; 15895bcae31dSMichal Kazior int err; 15905bcae31dSMichal Kazior 15915bcae31dSMichal Kazior lockdep_assert_held(&local->mtx); 15925bcae31dSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 15935bcae31dSMichal Kazior 15945bcae31dSMichal Kazior new_ctx = sdata->reserved_chanctx; 15955bcae31dSMichal Kazior old_ctx = ieee80211_vif_get_chanctx(sdata); 15965bcae31dSMichal Kazior 15975bcae31dSMichal Kazior if (WARN_ON(!new_ctx)) 15985bcae31dSMichal Kazior return -EINVAL; 15995bcae31dSMichal Kazior 16005bcae31dSMichal Kazior if (WARN_ON(new_ctx->replace_state == 16015bcae31dSMichal Kazior IEEE80211_CHANCTX_WILL_BE_REPLACED)) 16025bcae31dSMichal Kazior return -EINVAL; 16035bcae31dSMichal Kazior 16045bcae31dSMichal Kazior if (WARN_ON(sdata->reserved_ready)) 16055bcae31dSMichal Kazior return -EINVAL; 16065bcae31dSMichal Kazior 16075bcae31dSMichal Kazior sdata->reserved_ready = true; 16085bcae31dSMichal Kazior 16095bcae31dSMichal Kazior if (new_ctx->replace_state == IEEE80211_CHANCTX_REPLACE_NONE) { 16105bcae31dSMichal Kazior if (old_ctx) 16115bcae31dSMichal Kazior err = ieee80211_vif_use_reserved_reassign(sdata); 16125bcae31dSMichal Kazior else 16135bcae31dSMichal Kazior err = ieee80211_vif_use_reserved_assign(sdata); 16145bcae31dSMichal Kazior 16155bcae31dSMichal Kazior if (err) 16165bcae31dSMichal Kazior return err; 16175bcae31dSMichal Kazior } 16185bcae31dSMichal Kazior 16195bcae31dSMichal Kazior /* 16205bcae31dSMichal Kazior * In-place reservation may need to be finalized now either if: 16215bcae31dSMichal Kazior * a) sdata is taking part in the swapping itself and is the last one 16225bcae31dSMichal Kazior * b) sdata has switched with a re-assign reservation to an existing 16235bcae31dSMichal Kazior * context readying in-place switching of old_ctx 16245bcae31dSMichal Kazior * 16255bcae31dSMichal Kazior * In case of (b) do not propagate the error up because the requested 16265bcae31dSMichal Kazior * sdata already switched successfully. Just spill an extra warning. 16275bcae31dSMichal Kazior * The ieee80211_vif_use_reserved_switch() already stops all necessary 16285bcae31dSMichal Kazior * interfaces upon failure. 16295bcae31dSMichal Kazior */ 16305bcae31dSMichal Kazior if ((old_ctx && 16315bcae31dSMichal Kazior old_ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED) || 16325bcae31dSMichal Kazior new_ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER) { 16335bcae31dSMichal Kazior err = ieee80211_vif_use_reserved_switch(local); 16345bcae31dSMichal Kazior if (err && err != -EAGAIN) { 16355bcae31dSMichal Kazior if (new_ctx->replace_state == 16365bcae31dSMichal Kazior IEEE80211_CHANCTX_REPLACES_OTHER) 16375bcae31dSMichal Kazior return err; 16385bcae31dSMichal Kazior 16395bcae31dSMichal Kazior wiphy_info(local->hw.wiphy, 16405bcae31dSMichal Kazior "depending in-place reservation failed (err=%d)\n", 16415bcae31dSMichal Kazior err); 16425bcae31dSMichal Kazior } 16435bcae31dSMichal Kazior } 16445bcae31dSMichal Kazior 16455bcae31dSMichal Kazior return 0; 164673da7d5bSSimon Wunderlich } 164773da7d5bSSimon Wunderlich 16482c9b7359SJohannes Berg int ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata, 16492c9b7359SJohannes Berg const struct cfg80211_chan_def *chandef, 16502c9b7359SJohannes Berg u32 *changed) 16512c9b7359SJohannes Berg { 16522c9b7359SJohannes Berg struct ieee80211_local *local = sdata->local; 16532c9b7359SJohannes Berg struct ieee80211_chanctx_conf *conf; 16542c9b7359SJohannes Berg struct ieee80211_chanctx *ctx; 16555bcae31dSMichal Kazior const struct cfg80211_chan_def *compat; 16562c9b7359SJohannes Berg int ret; 16572c9b7359SJohannes Berg 16582c9b7359SJohannes Berg if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef, 16592c9b7359SJohannes Berg IEEE80211_CHAN_DISABLED)) 16602c9b7359SJohannes Berg return -EINVAL; 16612c9b7359SJohannes Berg 16622c9b7359SJohannes Berg mutex_lock(&local->chanctx_mtx); 16632c9b7359SJohannes Berg if (cfg80211_chandef_identical(chandef, &sdata->vif.bss_conf.chandef)) { 16642c9b7359SJohannes Berg ret = 0; 16652c9b7359SJohannes Berg goto out; 16662c9b7359SJohannes Berg } 16672c9b7359SJohannes Berg 16682c9b7359SJohannes Berg if (chandef->width == NL80211_CHAN_WIDTH_20_NOHT || 16692c9b7359SJohannes Berg sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT) { 16702c9b7359SJohannes Berg ret = -EINVAL; 16712c9b7359SJohannes Berg goto out; 16722c9b7359SJohannes Berg } 16732c9b7359SJohannes Berg 16742c9b7359SJohannes Berg conf = rcu_dereference_protected(sdata->vif.chanctx_conf, 16752c9b7359SJohannes Berg lockdep_is_held(&local->chanctx_mtx)); 16762c9b7359SJohannes Berg if (!conf) { 16772c9b7359SJohannes Berg ret = -EINVAL; 16782c9b7359SJohannes Berg goto out; 16792c9b7359SJohannes Berg } 16802c9b7359SJohannes Berg 16812c9b7359SJohannes Berg ctx = container_of(conf, struct ieee80211_chanctx, conf); 16825bcae31dSMichal Kazior 16835bcae31dSMichal Kazior compat = cfg80211_chandef_compatible(&conf->def, chandef); 16845bcae31dSMichal Kazior if (!compat) { 16852c9b7359SJohannes Berg ret = -EINVAL; 16862c9b7359SJohannes Berg goto out; 16872c9b7359SJohannes Berg } 16882c9b7359SJohannes Berg 16895bcae31dSMichal Kazior switch (ctx->replace_state) { 16905bcae31dSMichal Kazior case IEEE80211_CHANCTX_REPLACE_NONE: 16915bcae31dSMichal Kazior if (!ieee80211_chanctx_reserved_chandef(local, ctx, compat)) { 16925bcae31dSMichal Kazior ret = -EBUSY; 16935bcae31dSMichal Kazior goto out; 16945bcae31dSMichal Kazior } 16955bcae31dSMichal Kazior break; 16965bcae31dSMichal Kazior case IEEE80211_CHANCTX_WILL_BE_REPLACED: 1697d070f913SStephen Hemminger /* TODO: Perhaps the bandwidth change could be treated as a 16985bcae31dSMichal Kazior * reservation itself? */ 16995bcae31dSMichal Kazior ret = -EBUSY; 17005bcae31dSMichal Kazior goto out; 17015bcae31dSMichal Kazior case IEEE80211_CHANCTX_REPLACES_OTHER: 17025bcae31dSMichal Kazior /* channel context that is going to replace another channel 17035bcae31dSMichal Kazior * context doesn't really exist and shouldn't be assigned 17045bcae31dSMichal Kazior * anywhere yet */ 17055bcae31dSMichal Kazior WARN_ON(1); 17065bcae31dSMichal Kazior break; 17075bcae31dSMichal Kazior } 17085bcae31dSMichal Kazior 17092967e031SFelix Fietkau ieee80211_vif_update_chandef(sdata, chandef); 17102c9b7359SJohannes Berg 17112c9b7359SJohannes Berg ieee80211_recalc_chanctx_chantype(local, ctx); 17122c9b7359SJohannes Berg 17132c9b7359SJohannes Berg *changed |= BSS_CHANGED_BANDWIDTH; 17142c9b7359SJohannes Berg ret = 0; 17152c9b7359SJohannes Berg out: 17162c9b7359SJohannes Berg mutex_unlock(&local->chanctx_mtx); 17172c9b7359SJohannes Berg return ret; 17182c9b7359SJohannes Berg } 17192c9b7359SJohannes Berg 1720d01a1e65SMichal Kazior void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata) 1721d01a1e65SMichal Kazior { 172255de908aSJohannes Berg WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev)); 172355de908aSJohannes Berg 172434a3740dSJohannes Berg lockdep_assert_held(&sdata->local->mtx); 172534a3740dSJohannes Berg 1726d01a1e65SMichal Kazior mutex_lock(&sdata->local->chanctx_mtx); 1727d01a1e65SMichal Kazior __ieee80211_vif_release_channel(sdata); 1728d01a1e65SMichal Kazior mutex_unlock(&sdata->local->chanctx_mtx); 1729d01a1e65SMichal Kazior } 17303448c005SJohannes Berg 17314d76d21bSJohannes Berg void ieee80211_vif_vlan_copy_chanctx(struct ieee80211_sub_if_data *sdata) 17324d76d21bSJohannes Berg { 17334d76d21bSJohannes Berg struct ieee80211_local *local = sdata->local; 17344d76d21bSJohannes Berg struct ieee80211_sub_if_data *ap; 17354d76d21bSJohannes Berg struct ieee80211_chanctx_conf *conf; 17364d76d21bSJohannes Berg 17374d76d21bSJohannes Berg if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP_VLAN || !sdata->bss)) 17384d76d21bSJohannes Berg return; 17394d76d21bSJohannes Berg 17404d76d21bSJohannes Berg ap = container_of(sdata->bss, struct ieee80211_sub_if_data, u.ap); 17414d76d21bSJohannes Berg 17424d76d21bSJohannes Berg mutex_lock(&local->chanctx_mtx); 17434d76d21bSJohannes Berg 17444d76d21bSJohannes Berg conf = rcu_dereference_protected(ap->vif.chanctx_conf, 17454d76d21bSJohannes Berg lockdep_is_held(&local->chanctx_mtx)); 17464d76d21bSJohannes Berg rcu_assign_pointer(sdata->vif.chanctx_conf, conf); 17474d76d21bSJohannes Berg mutex_unlock(&local->chanctx_mtx); 17484d76d21bSJohannes Berg } 17494d76d21bSJohannes Berg 17503448c005SJohannes Berg void ieee80211_iter_chan_contexts_atomic( 17513448c005SJohannes Berg struct ieee80211_hw *hw, 17523448c005SJohannes Berg void (*iter)(struct ieee80211_hw *hw, 17533448c005SJohannes Berg struct ieee80211_chanctx_conf *chanctx_conf, 17543448c005SJohannes Berg void *data), 17553448c005SJohannes Berg void *iter_data) 17563448c005SJohannes Berg { 17573448c005SJohannes Berg struct ieee80211_local *local = hw_to_local(hw); 17583448c005SJohannes Berg struct ieee80211_chanctx *ctx; 17593448c005SJohannes Berg 17603448c005SJohannes Berg rcu_read_lock(); 17613448c005SJohannes Berg list_for_each_entry_rcu(ctx, &local->chanctx_list, list) 17628a61af65SJohannes Berg if (ctx->driver_present) 17633448c005SJohannes Berg iter(hw, &ctx->conf, iter_data); 17643448c005SJohannes Berg rcu_read_unlock(); 17653448c005SJohannes Berg } 17663448c005SJohannes Berg EXPORT_SYMBOL_GPL(ieee80211_iter_chan_contexts_atomic); 1767