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: 27321f659bfSEliad Peller width = vif->bss_conf.chandef.width; 27421f659bfSEliad Peller break; 27521f659bfSEliad Peller case NL80211_IFTYPE_UNSPECIFIED: 27621f659bfSEliad Peller case NUM_NL80211_IFTYPES: 27721f659bfSEliad Peller case NL80211_IFTYPE_MONITOR: 27821f659bfSEliad Peller case NL80211_IFTYPE_P2P_CLIENT: 27921f659bfSEliad Peller case NL80211_IFTYPE_P2P_GO: 28021f659bfSEliad Peller WARN_ON_ONCE(1); 28121f659bfSEliad Peller } 28221f659bfSEliad Peller max_bw = max(max_bw, width); 28321f659bfSEliad Peller } 2841c37a72cSEliad Peller 2851c37a72cSEliad Peller /* use the configured bandwidth in case of monitor interface */ 2861c37a72cSEliad Peller sdata = rcu_dereference(local->monitor_sdata); 2871c37a72cSEliad Peller if (sdata && rcu_access_pointer(sdata->vif.chanctx_conf) == conf) 2881c37a72cSEliad Peller max_bw = max(max_bw, conf->def.width); 2891c37a72cSEliad Peller 29021f659bfSEliad Peller rcu_read_unlock(); 29121f659bfSEliad Peller 29221f659bfSEliad Peller return max_bw; 29321f659bfSEliad Peller } 29421f659bfSEliad Peller 29521f659bfSEliad Peller /* 29621f659bfSEliad Peller * recalc the min required chan width of the channel context, which is 29721f659bfSEliad Peller * the max of min required widths of all the interfaces bound to this 29821f659bfSEliad Peller * channel context. 29921f659bfSEliad Peller */ 30021f659bfSEliad Peller void ieee80211_recalc_chanctx_min_def(struct ieee80211_local *local, 30121f659bfSEliad Peller struct ieee80211_chanctx *ctx) 30221f659bfSEliad Peller { 30321f659bfSEliad Peller enum nl80211_chan_width max_bw; 30421f659bfSEliad Peller struct cfg80211_chan_def min_def; 30521f659bfSEliad Peller 30621f659bfSEliad Peller lockdep_assert_held(&local->chanctx_mtx); 30721f659bfSEliad Peller 30821f659bfSEliad Peller /* don't optimize 5MHz, 10MHz, and radar_enabled confs */ 30921f659bfSEliad Peller if (ctx->conf.def.width == NL80211_CHAN_WIDTH_5 || 31021f659bfSEliad Peller ctx->conf.def.width == NL80211_CHAN_WIDTH_10 || 31121f659bfSEliad Peller ctx->conf.radar_enabled) { 31221f659bfSEliad Peller ctx->conf.min_def = ctx->conf.def; 31321f659bfSEliad Peller return; 31421f659bfSEliad Peller } 31521f659bfSEliad Peller 31621f659bfSEliad Peller max_bw = ieee80211_get_chanctx_max_required_bw(local, &ctx->conf); 31721f659bfSEliad Peller 31821f659bfSEliad Peller /* downgrade chandef up to max_bw */ 31921f659bfSEliad Peller min_def = ctx->conf.def; 32021f659bfSEliad Peller while (min_def.width > max_bw) 32121f659bfSEliad Peller ieee80211_chandef_downgrade(&min_def); 32221f659bfSEliad Peller 32321f659bfSEliad Peller if (cfg80211_chandef_identical(&ctx->conf.min_def, &min_def)) 32421f659bfSEliad Peller return; 32521f659bfSEliad Peller 32621f659bfSEliad Peller ctx->conf.min_def = min_def; 32721f659bfSEliad Peller if (!ctx->driver_present) 32821f659bfSEliad Peller return; 32921f659bfSEliad Peller 33021f659bfSEliad Peller drv_change_chanctx(local, ctx, IEEE80211_CHANCTX_CHANGE_MIN_WIDTH); 33121f659bfSEliad Peller } 33221f659bfSEliad Peller 33318942d3bSJohannes Berg static void ieee80211_change_chanctx(struct ieee80211_local *local, 334e89a96f5SMichal Kazior struct ieee80211_chanctx *ctx, 3354bf88530SJohannes Berg const struct cfg80211_chan_def *chandef) 336e89a96f5SMichal Kazior { 3374bf88530SJohannes Berg if (cfg80211_chandef_identical(&ctx->conf.def, chandef)) 338e89a96f5SMichal Kazior return; 339e89a96f5SMichal Kazior 3404bf88530SJohannes Berg WARN_ON(!cfg80211_chandef_compatible(&ctx->conf.def, chandef)); 3414bf88530SJohannes Berg 3424bf88530SJohannes Berg ctx->conf.def = *chandef; 3434bf88530SJohannes Berg drv_change_chanctx(local, ctx, IEEE80211_CHANCTX_CHANGE_WIDTH); 34421f659bfSEliad Peller ieee80211_recalc_chanctx_min_def(local, ctx); 34555de908aSJohannes Berg 34655de908aSJohannes Berg if (!local->use_chanctx) { 347675a0b04SKarl Beldan local->_oper_chandef = *chandef; 34855de908aSJohannes Berg ieee80211_hw_config(local, 0); 34955de908aSJohannes Berg } 3500aaffa9bSJohannes Berg } 351d01a1e65SMichal Kazior 352d01a1e65SMichal Kazior static struct ieee80211_chanctx * 353d01a1e65SMichal Kazior ieee80211_find_chanctx(struct ieee80211_local *local, 3544bf88530SJohannes Berg const struct cfg80211_chan_def *chandef, 355d01a1e65SMichal Kazior enum ieee80211_chanctx_mode mode) 356d01a1e65SMichal Kazior { 357d01a1e65SMichal Kazior struct ieee80211_chanctx *ctx; 358d01a1e65SMichal Kazior 359d01a1e65SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 360d01a1e65SMichal Kazior 361d01a1e65SMichal Kazior if (mode == IEEE80211_CHANCTX_EXCLUSIVE) 362d01a1e65SMichal Kazior return NULL; 363d01a1e65SMichal Kazior 364d01a1e65SMichal Kazior list_for_each_entry(ctx, &local->chanctx_list, list) { 3654bf88530SJohannes Berg const struct cfg80211_chan_def *compat; 366e89a96f5SMichal Kazior 3675bcae31dSMichal Kazior if (ctx->replace_state != IEEE80211_CHANCTX_REPLACE_NONE) 3685bcae31dSMichal Kazior continue; 3695bcae31dSMichal Kazior 370d01a1e65SMichal Kazior if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE) 371d01a1e65SMichal Kazior continue; 3724bf88530SJohannes Berg 3734bf88530SJohannes Berg compat = cfg80211_chandef_compatible(&ctx->conf.def, chandef); 3744bf88530SJohannes Berg if (!compat) 375d01a1e65SMichal Kazior continue; 376d01a1e65SMichal Kazior 3770288157bSMichal Kazior compat = ieee80211_chanctx_reserved_chandef(local, ctx, 3780288157bSMichal Kazior compat); 3790288157bSMichal Kazior if (!compat) 3800288157bSMichal Kazior continue; 3810288157bSMichal Kazior 38218942d3bSJohannes Berg ieee80211_change_chanctx(local, ctx, compat); 383e89a96f5SMichal Kazior 384d01a1e65SMichal Kazior return ctx; 385d01a1e65SMichal Kazior } 386d01a1e65SMichal Kazior 387d01a1e65SMichal Kazior return NULL; 388d01a1e65SMichal Kazior } 389d01a1e65SMichal Kazior 390e4746851SSimon Wunderlich static bool ieee80211_is_radar_required(struct ieee80211_local *local) 391e4746851SSimon Wunderlich { 392e4746851SSimon Wunderlich struct ieee80211_sub_if_data *sdata; 393e4746851SSimon Wunderlich 394cc901de1SMichal Kazior lockdep_assert_held(&local->mtx); 395cc901de1SMichal Kazior 396e4746851SSimon Wunderlich rcu_read_lock(); 397e4746851SSimon Wunderlich list_for_each_entry_rcu(sdata, &local->interfaces, list) { 398e4746851SSimon Wunderlich if (sdata->radar_required) { 399e4746851SSimon Wunderlich rcu_read_unlock(); 400e4746851SSimon Wunderlich return true; 401e4746851SSimon Wunderlich } 402e4746851SSimon Wunderlich } 403e4746851SSimon Wunderlich rcu_read_unlock(); 404e4746851SSimon Wunderlich 405e4746851SSimon Wunderlich return false; 406e4746851SSimon Wunderlich } 407e4746851SSimon Wunderlich 408d01a1e65SMichal Kazior static struct ieee80211_chanctx * 409ed68ebcaSMichal Kazior ieee80211_alloc_chanctx(struct ieee80211_local *local, 4104bf88530SJohannes Berg const struct cfg80211_chan_def *chandef, 411d01a1e65SMichal Kazior enum ieee80211_chanctx_mode mode) 412d01a1e65SMichal Kazior { 413d01a1e65SMichal Kazior struct ieee80211_chanctx *ctx; 414d01a1e65SMichal Kazior 415d01a1e65SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 416d01a1e65SMichal Kazior 417d01a1e65SMichal Kazior ctx = kzalloc(sizeof(*ctx) + local->hw.chanctx_data_size, GFP_KERNEL); 418d01a1e65SMichal Kazior if (!ctx) 419ed68ebcaSMichal Kazior return NULL; 420d01a1e65SMichal Kazior 421484298adSMichal Kazior INIT_LIST_HEAD(&ctx->assigned_vifs); 422e3afb920SMichal Kazior INIT_LIST_HEAD(&ctx->reserved_vifs); 4234bf88530SJohannes Berg ctx->conf.def = *chandef; 42404ecd257SJohannes Berg ctx->conf.rx_chains_static = 1; 42504ecd257SJohannes Berg ctx->conf.rx_chains_dynamic = 1; 426d01a1e65SMichal Kazior ctx->mode = mode; 427e4746851SSimon Wunderlich ctx->conf.radar_enabled = ieee80211_is_radar_required(local); 42821f659bfSEliad Peller ieee80211_recalc_chanctx_min_def(local, ctx); 429ed68ebcaSMichal Kazior 430ed68ebcaSMichal Kazior return ctx; 431ed68ebcaSMichal Kazior } 432ed68ebcaSMichal Kazior 433ed68ebcaSMichal Kazior static int ieee80211_add_chanctx(struct ieee80211_local *local, 434ed68ebcaSMichal Kazior struct ieee80211_chanctx *ctx) 435ed68ebcaSMichal Kazior { 436ed68ebcaSMichal Kazior u32 changed; 437ed68ebcaSMichal Kazior int err; 438ed68ebcaSMichal Kazior 439ed68ebcaSMichal Kazior lockdep_assert_held(&local->mtx); 440ed68ebcaSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 441ed68ebcaSMichal Kazior 442e4746851SSimon Wunderlich if (!local->use_chanctx) 443e4746851SSimon Wunderlich local->hw.conf.radar_enabled = ctx->conf.radar_enabled; 444d01a1e65SMichal Kazior 445382a103bSJohannes Berg /* turn idle off *before* setting channel -- some drivers need that */ 446382a103bSJohannes Berg changed = ieee80211_idle_off(local); 447382a103bSJohannes Berg if (changed) 448382a103bSJohannes Berg ieee80211_hw_config(local, changed); 449382a103bSJohannes Berg 45055de908aSJohannes Berg if (!local->use_chanctx) { 451ed68ebcaSMichal Kazior local->_oper_chandef = ctx->conf.def; 4529b4816f5SMichal Kazior ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); 45355de908aSJohannes Berg } else { 45435f2fce9SMichal Kazior err = drv_add_chanctx(local, ctx); 45535f2fce9SMichal Kazior if (err) { 456382a103bSJohannes Berg ieee80211_recalc_idle(local); 457ed68ebcaSMichal Kazior return err; 458ed68ebcaSMichal Kazior } 459ed68ebcaSMichal Kazior } 460ed68ebcaSMichal Kazior 461ed68ebcaSMichal Kazior return 0; 462ed68ebcaSMichal Kazior } 463ed68ebcaSMichal Kazior 464ed68ebcaSMichal Kazior static struct ieee80211_chanctx * 465ed68ebcaSMichal Kazior ieee80211_new_chanctx(struct ieee80211_local *local, 466ed68ebcaSMichal Kazior const struct cfg80211_chan_def *chandef, 467ed68ebcaSMichal Kazior enum ieee80211_chanctx_mode mode) 468ed68ebcaSMichal Kazior { 469ed68ebcaSMichal Kazior struct ieee80211_chanctx *ctx; 470ed68ebcaSMichal Kazior int err; 471ed68ebcaSMichal Kazior 472ed68ebcaSMichal Kazior lockdep_assert_held(&local->mtx); 473ed68ebcaSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 474ed68ebcaSMichal Kazior 475ed68ebcaSMichal Kazior ctx = ieee80211_alloc_chanctx(local, chandef, mode); 476ed68ebcaSMichal Kazior if (!ctx) 477ed68ebcaSMichal Kazior return ERR_PTR(-ENOMEM); 478ed68ebcaSMichal Kazior 479ed68ebcaSMichal Kazior err = ieee80211_add_chanctx(local, ctx); 480ed68ebcaSMichal Kazior if (err) { 481ed68ebcaSMichal Kazior kfree(ctx); 48234a3740dSJohannes Berg return ERR_PTR(err); 48335f2fce9SMichal Kazior } 48435f2fce9SMichal Kazior 4853448c005SJohannes Berg list_add_rcu(&ctx->list, &local->chanctx_list); 486d01a1e65SMichal Kazior return ctx; 487d01a1e65SMichal Kazior } 488d01a1e65SMichal Kazior 4891f0d54cdSMichal Kazior static void ieee80211_del_chanctx(struct ieee80211_local *local, 490d01a1e65SMichal Kazior struct ieee80211_chanctx *ctx) 491d01a1e65SMichal Kazior { 492d01a1e65SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 493d01a1e65SMichal Kazior 49455de908aSJohannes Berg if (!local->use_chanctx) { 495675a0b04SKarl Beldan struct cfg80211_chan_def *chandef = &local->_oper_chandef; 496675a0b04SKarl Beldan chandef->width = NL80211_CHAN_WIDTH_20_NOHT; 497675a0b04SKarl Beldan chandef->center_freq1 = chandef->chan->center_freq; 498675a0b04SKarl Beldan chandef->center_freq2 = 0; 499e4746851SSimon Wunderlich 500e4746851SSimon Wunderlich /* NOTE: Disabling radar is only valid here for 501e4746851SSimon Wunderlich * single channel context. To be sure, check it ... 502e4746851SSimon Wunderlich */ 5031f0d54cdSMichal Kazior WARN_ON(local->hw.conf.radar_enabled && 5041f0d54cdSMichal Kazior !list_empty(&local->chanctx_list)); 5051f0d54cdSMichal Kazior 506e4746851SSimon Wunderlich local->hw.conf.radar_enabled = false; 507e4746851SSimon Wunderlich 5089b4816f5SMichal Kazior ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); 50955de908aSJohannes Berg } else { 51035f2fce9SMichal Kazior drv_remove_chanctx(local, ctx); 51155de908aSJohannes Berg } 51235f2fce9SMichal Kazior 513fd0f979aSJohannes Berg ieee80211_recalc_idle(local); 514d01a1e65SMichal Kazior } 515d01a1e65SMichal Kazior 5161f0d54cdSMichal Kazior static void ieee80211_free_chanctx(struct ieee80211_local *local, 517d01a1e65SMichal Kazior struct ieee80211_chanctx *ctx) 518d01a1e65SMichal Kazior { 519d01a1e65SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 520d01a1e65SMichal Kazior 521c0166da9SMichal Kazior WARN_ON_ONCE(ieee80211_chanctx_refcount(local, ctx) != 0); 52235f2fce9SMichal Kazior 5231f0d54cdSMichal Kazior list_del_rcu(&ctx->list); 5241f0d54cdSMichal Kazior ieee80211_del_chanctx(local, ctx); 5251f0d54cdSMichal Kazior kfree_rcu(ctx, rcu_head); 526d01a1e65SMichal Kazior } 527d01a1e65SMichal Kazior 5284bf88530SJohannes Berg static void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local, 529e89a96f5SMichal Kazior struct ieee80211_chanctx *ctx) 530e89a96f5SMichal Kazior { 531e89a96f5SMichal Kazior struct ieee80211_chanctx_conf *conf = &ctx->conf; 532e89a96f5SMichal Kazior struct ieee80211_sub_if_data *sdata; 5334bf88530SJohannes Berg const struct cfg80211_chan_def *compat = NULL; 534e89a96f5SMichal Kazior 535e89a96f5SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 536e89a96f5SMichal Kazior 537e89a96f5SMichal Kazior rcu_read_lock(); 538e89a96f5SMichal Kazior list_for_each_entry_rcu(sdata, &local->interfaces, list) { 5394bf88530SJohannes Berg 540e89a96f5SMichal Kazior if (!ieee80211_sdata_running(sdata)) 541e89a96f5SMichal Kazior continue; 542e89a96f5SMichal Kazior if (rcu_access_pointer(sdata->vif.chanctx_conf) != conf) 543e89a96f5SMichal Kazior continue; 5440e67c136SFelix Fietkau if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) 5450e67c136SFelix Fietkau continue; 546e89a96f5SMichal Kazior 5474bf88530SJohannes Berg if (!compat) 5484bf88530SJohannes Berg compat = &sdata->vif.bss_conf.chandef; 5494bf88530SJohannes Berg 5504bf88530SJohannes Berg compat = cfg80211_chandef_compatible( 5514bf88530SJohannes Berg &sdata->vif.bss_conf.chandef, compat); 5524bf88530SJohannes Berg if (!compat) 5534bf88530SJohannes Berg break; 554e89a96f5SMichal Kazior } 555e89a96f5SMichal Kazior rcu_read_unlock(); 556e89a96f5SMichal Kazior 5574bf88530SJohannes Berg if (WARN_ON_ONCE(!compat)) 5584bf88530SJohannes Berg return; 559e89a96f5SMichal Kazior 56018942d3bSJohannes Berg ieee80211_change_chanctx(local, ctx, compat); 561e89a96f5SMichal Kazior } 562e89a96f5SMichal Kazior 563367bbd10SJohannes Berg static void ieee80211_recalc_radar_chanctx(struct ieee80211_local *local, 564367bbd10SJohannes Berg struct ieee80211_chanctx *chanctx) 565367bbd10SJohannes Berg { 566367bbd10SJohannes Berg bool radar_enabled; 567367bbd10SJohannes Berg 568367bbd10SJohannes Berg lockdep_assert_held(&local->chanctx_mtx); 56934a3740dSJohannes Berg /* for setting local->radar_detect_enabled */ 57034a3740dSJohannes Berg lockdep_assert_held(&local->mtx); 571367bbd10SJohannes Berg 572367bbd10SJohannes Berg radar_enabled = ieee80211_is_radar_required(local); 573367bbd10SJohannes Berg 574367bbd10SJohannes Berg if (radar_enabled == chanctx->conf.radar_enabled) 575367bbd10SJohannes Berg return; 576367bbd10SJohannes Berg 577367bbd10SJohannes Berg chanctx->conf.radar_enabled = radar_enabled; 578367bbd10SJohannes Berg local->radar_detect_enabled = chanctx->conf.radar_enabled; 579367bbd10SJohannes Berg 580367bbd10SJohannes Berg if (!local->use_chanctx) { 581367bbd10SJohannes Berg local->hw.conf.radar_enabled = chanctx->conf.radar_enabled; 582367bbd10SJohannes Berg ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); 583367bbd10SJohannes Berg } 584367bbd10SJohannes Berg 585367bbd10SJohannes Berg drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RADAR); 586367bbd10SJohannes Berg } 587367bbd10SJohannes Berg 58877eeba97SLuciano Coelho static int ieee80211_assign_vif_chanctx(struct ieee80211_sub_if_data *sdata, 58977eeba97SLuciano Coelho struct ieee80211_chanctx *new_ctx) 590d01a1e65SMichal Kazior { 59135f2fce9SMichal Kazior struct ieee80211_local *local = sdata->local; 59277eeba97SLuciano Coelho struct ieee80211_chanctx_conf *conf; 59377eeba97SLuciano Coelho struct ieee80211_chanctx *curr_ctx = NULL; 59477eeba97SLuciano Coelho int ret = 0; 595d01a1e65SMichal Kazior 59677eeba97SLuciano Coelho conf = rcu_dereference_protected(sdata->vif.chanctx_conf, 59777eeba97SLuciano Coelho lockdep_is_held(&local->chanctx_mtx)); 598d01a1e65SMichal Kazior 59977eeba97SLuciano Coelho if (conf) { 60077eeba97SLuciano Coelho curr_ctx = container_of(conf, struct ieee80211_chanctx, conf); 60135f2fce9SMichal Kazior 60277eeba97SLuciano Coelho drv_unassign_vif_chanctx(local, sdata, curr_ctx); 60377eeba97SLuciano Coelho conf = NULL; 604484298adSMichal Kazior list_del(&sdata->assigned_chanctx_list); 60577eeba97SLuciano Coelho } 60677eeba97SLuciano Coelho 60777eeba97SLuciano Coelho if (new_ctx) { 60877eeba97SLuciano Coelho ret = drv_assign_vif_chanctx(local, sdata, new_ctx); 60977eeba97SLuciano Coelho if (ret) 61077eeba97SLuciano Coelho goto out; 61177eeba97SLuciano Coelho 61277eeba97SLuciano Coelho conf = &new_ctx->conf; 613484298adSMichal Kazior list_add(&sdata->assigned_chanctx_list, 614484298adSMichal Kazior &new_ctx->assigned_vifs); 61577eeba97SLuciano Coelho } 61677eeba97SLuciano Coelho 61777eeba97SLuciano Coelho out: 61877eeba97SLuciano Coelho rcu_assign_pointer(sdata->vif.chanctx_conf, conf); 61977eeba97SLuciano Coelho 62077eeba97SLuciano Coelho sdata->vif.bss_conf.idle = !conf; 62177eeba97SLuciano Coelho 622c0166da9SMichal Kazior if (curr_ctx && ieee80211_chanctx_num_assigned(local, curr_ctx) > 0) { 62377eeba97SLuciano Coelho ieee80211_recalc_chanctx_chantype(local, curr_ctx); 62477eeba97SLuciano Coelho ieee80211_recalc_smps_chanctx(local, curr_ctx); 62577eeba97SLuciano Coelho ieee80211_recalc_radar_chanctx(local, curr_ctx); 62677eeba97SLuciano Coelho ieee80211_recalc_chanctx_min_def(local, curr_ctx); 62777eeba97SLuciano Coelho } 62877eeba97SLuciano Coelho 629c0166da9SMichal Kazior if (new_ctx && ieee80211_chanctx_num_assigned(local, new_ctx) > 0) { 63077eeba97SLuciano Coelho ieee80211_recalc_txpower(sdata); 63177eeba97SLuciano Coelho ieee80211_recalc_chanctx_min_def(local, new_ctx); 63277eeba97SLuciano Coelho } 6335bbe754dSJohannes Berg 6345bbe754dSJohannes Berg if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE && 6355bbe754dSJohannes Berg sdata->vif.type != NL80211_IFTYPE_MONITOR) 63677eeba97SLuciano Coelho ieee80211_bss_info_change_notify(sdata, 63777eeba97SLuciano Coelho BSS_CHANGED_IDLE); 638fd0f979aSJohannes Berg 63977eeba97SLuciano Coelho return ret; 640d01a1e65SMichal Kazior } 641d01a1e65SMichal Kazior 642d01a1e65SMichal Kazior static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata) 643d01a1e65SMichal Kazior { 644d01a1e65SMichal Kazior struct ieee80211_local *local = sdata->local; 645d01a1e65SMichal Kazior struct ieee80211_chanctx_conf *conf; 646d01a1e65SMichal Kazior struct ieee80211_chanctx *ctx; 6475bcae31dSMichal Kazior bool use_reserved_switch = false; 648d01a1e65SMichal Kazior 649d01a1e65SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 650d01a1e65SMichal Kazior 651d01a1e65SMichal Kazior conf = rcu_dereference_protected(sdata->vif.chanctx_conf, 652d01a1e65SMichal Kazior lockdep_is_held(&local->chanctx_mtx)); 653d01a1e65SMichal Kazior if (!conf) 654d01a1e65SMichal Kazior return; 655d01a1e65SMichal Kazior 656d01a1e65SMichal Kazior ctx = container_of(conf, struct ieee80211_chanctx, conf); 657d01a1e65SMichal Kazior 6585bcae31dSMichal Kazior if (sdata->reserved_chanctx) { 6595bcae31dSMichal Kazior if (sdata->reserved_chanctx->replace_state == 6605bcae31dSMichal Kazior IEEE80211_CHANCTX_REPLACES_OTHER && 6615bcae31dSMichal Kazior ieee80211_chanctx_num_reserved(local, 6625bcae31dSMichal Kazior sdata->reserved_chanctx) > 1) 6635bcae31dSMichal Kazior use_reserved_switch = true; 6645bcae31dSMichal Kazior 66511335a55SLuciano Coelho ieee80211_vif_unreserve_chanctx(sdata); 6665bcae31dSMichal Kazior } 66711335a55SLuciano Coelho 66877eeba97SLuciano Coelho ieee80211_assign_vif_chanctx(sdata, NULL); 669c0166da9SMichal Kazior if (ieee80211_chanctx_refcount(local, ctx) == 0) 670d01a1e65SMichal Kazior ieee80211_free_chanctx(local, ctx); 6715bcae31dSMichal Kazior 6725bcae31dSMichal Kazior /* Unreserving may ready an in-place reservation. */ 6735bcae31dSMichal Kazior if (use_reserved_switch) 6745bcae31dSMichal Kazior ieee80211_vif_use_reserved_switch(local); 675d01a1e65SMichal Kazior } 676d01a1e65SMichal Kazior 67704ecd257SJohannes Berg void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local, 67804ecd257SJohannes Berg struct ieee80211_chanctx *chanctx) 67904ecd257SJohannes Berg { 68004ecd257SJohannes Berg struct ieee80211_sub_if_data *sdata; 68104ecd257SJohannes Berg u8 rx_chains_static, rx_chains_dynamic; 68204ecd257SJohannes Berg 68304ecd257SJohannes Berg lockdep_assert_held(&local->chanctx_mtx); 68404ecd257SJohannes Berg 68504ecd257SJohannes Berg rx_chains_static = 1; 68604ecd257SJohannes Berg rx_chains_dynamic = 1; 68704ecd257SJohannes Berg 68804ecd257SJohannes Berg rcu_read_lock(); 68904ecd257SJohannes Berg list_for_each_entry_rcu(sdata, &local->interfaces, list) { 69004ecd257SJohannes Berg u8 needed_static, needed_dynamic; 69104ecd257SJohannes Berg 69204ecd257SJohannes Berg if (!ieee80211_sdata_running(sdata)) 69304ecd257SJohannes Berg continue; 69404ecd257SJohannes Berg 69504ecd257SJohannes Berg if (rcu_access_pointer(sdata->vif.chanctx_conf) != 69604ecd257SJohannes Berg &chanctx->conf) 69704ecd257SJohannes Berg continue; 69804ecd257SJohannes Berg 69904ecd257SJohannes Berg switch (sdata->vif.type) { 70004ecd257SJohannes Berg case NL80211_IFTYPE_P2P_DEVICE: 70104ecd257SJohannes Berg continue; 70204ecd257SJohannes Berg case NL80211_IFTYPE_STATION: 70304ecd257SJohannes Berg if (!sdata->u.mgd.associated) 70404ecd257SJohannes Berg continue; 70504ecd257SJohannes Berg break; 70604ecd257SJohannes Berg case NL80211_IFTYPE_AP_VLAN: 70704ecd257SJohannes Berg continue; 70804ecd257SJohannes Berg case NL80211_IFTYPE_AP: 70904ecd257SJohannes Berg case NL80211_IFTYPE_ADHOC: 71004ecd257SJohannes Berg case NL80211_IFTYPE_WDS: 71104ecd257SJohannes Berg case NL80211_IFTYPE_MESH_POINT: 71204ecd257SJohannes Berg break; 71304ecd257SJohannes Berg default: 71404ecd257SJohannes Berg WARN_ON_ONCE(1); 71504ecd257SJohannes Berg } 71604ecd257SJohannes Berg 71704ecd257SJohannes Berg switch (sdata->smps_mode) { 71804ecd257SJohannes Berg default: 71904ecd257SJohannes Berg WARN_ONCE(1, "Invalid SMPS mode %d\n", 72004ecd257SJohannes Berg sdata->smps_mode); 72104ecd257SJohannes Berg /* fall through */ 72204ecd257SJohannes Berg case IEEE80211_SMPS_OFF: 72304ecd257SJohannes Berg needed_static = sdata->needed_rx_chains; 72404ecd257SJohannes Berg needed_dynamic = sdata->needed_rx_chains; 72504ecd257SJohannes Berg break; 72604ecd257SJohannes Berg case IEEE80211_SMPS_DYNAMIC: 72704ecd257SJohannes Berg needed_static = 1; 72804ecd257SJohannes Berg needed_dynamic = sdata->needed_rx_chains; 72904ecd257SJohannes Berg break; 73004ecd257SJohannes Berg case IEEE80211_SMPS_STATIC: 73104ecd257SJohannes Berg needed_static = 1; 73204ecd257SJohannes Berg needed_dynamic = 1; 73304ecd257SJohannes Berg break; 73404ecd257SJohannes Berg } 73504ecd257SJohannes Berg 73604ecd257SJohannes Berg rx_chains_static = max(rx_chains_static, needed_static); 73704ecd257SJohannes Berg rx_chains_dynamic = max(rx_chains_dynamic, needed_dynamic); 73804ecd257SJohannes Berg } 7397b8a9cddSIdo Yariv 7407b8a9cddSIdo Yariv /* Disable SMPS for the monitor interface */ 7417b8a9cddSIdo Yariv sdata = rcu_dereference(local->monitor_sdata); 7427b8a9cddSIdo Yariv if (sdata && 7437b8a9cddSIdo Yariv rcu_access_pointer(sdata->vif.chanctx_conf) == &chanctx->conf) 7447b8a9cddSIdo Yariv rx_chains_dynamic = rx_chains_static = local->rx_chains; 7457b8a9cddSIdo Yariv 74604ecd257SJohannes Berg rcu_read_unlock(); 74704ecd257SJohannes Berg 74804ecd257SJohannes Berg if (!local->use_chanctx) { 74904ecd257SJohannes Berg if (rx_chains_static > 1) 75004ecd257SJohannes Berg local->smps_mode = IEEE80211_SMPS_OFF; 75104ecd257SJohannes Berg else if (rx_chains_dynamic > 1) 75204ecd257SJohannes Berg local->smps_mode = IEEE80211_SMPS_DYNAMIC; 75304ecd257SJohannes Berg else 75404ecd257SJohannes Berg local->smps_mode = IEEE80211_SMPS_STATIC; 75504ecd257SJohannes Berg ieee80211_hw_config(local, 0); 75604ecd257SJohannes Berg } 75704ecd257SJohannes Berg 75804ecd257SJohannes Berg if (rx_chains_static == chanctx->conf.rx_chains_static && 75904ecd257SJohannes Berg rx_chains_dynamic == chanctx->conf.rx_chains_dynamic) 76004ecd257SJohannes Berg return; 76104ecd257SJohannes Berg 76204ecd257SJohannes Berg chanctx->conf.rx_chains_static = rx_chains_static; 76304ecd257SJohannes Berg chanctx->conf.rx_chains_dynamic = rx_chains_dynamic; 76404ecd257SJohannes Berg drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RX_CHAINS); 76504ecd257SJohannes Berg } 76604ecd257SJohannes Berg 767d01a1e65SMichal Kazior int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata, 7684bf88530SJohannes Berg const struct cfg80211_chan_def *chandef, 769d01a1e65SMichal Kazior enum ieee80211_chanctx_mode mode) 770d01a1e65SMichal Kazior { 771d01a1e65SMichal Kazior struct ieee80211_local *local = sdata->local; 772d01a1e65SMichal Kazior struct ieee80211_chanctx *ctx; 77373de86a3SLuciano Coelho u8 radar_detect_width = 0; 774d01a1e65SMichal Kazior int ret; 775d01a1e65SMichal Kazior 77634a3740dSJohannes Berg lockdep_assert_held(&local->mtx); 77734a3740dSJohannes Berg 77855de908aSJohannes Berg WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev)); 77955de908aSJohannes Berg 780d01a1e65SMichal Kazior mutex_lock(&local->chanctx_mtx); 78173de86a3SLuciano Coelho 78273de86a3SLuciano Coelho ret = cfg80211_chandef_dfs_required(local->hw.wiphy, 78373de86a3SLuciano Coelho chandef, 78473de86a3SLuciano Coelho sdata->wdev.iftype); 78573de86a3SLuciano Coelho if (ret < 0) 78673de86a3SLuciano Coelho goto out; 78773de86a3SLuciano Coelho if (ret > 0) 78873de86a3SLuciano Coelho radar_detect_width = BIT(chandef->width); 78973de86a3SLuciano Coelho 79073de86a3SLuciano Coelho sdata->radar_required = ret; 79173de86a3SLuciano Coelho 79273de86a3SLuciano Coelho ret = ieee80211_check_combinations(sdata, chandef, mode, 79373de86a3SLuciano Coelho radar_detect_width); 79473de86a3SLuciano Coelho if (ret < 0) 79573de86a3SLuciano Coelho goto out; 79673de86a3SLuciano Coelho 797d01a1e65SMichal Kazior __ieee80211_vif_release_channel(sdata); 798d01a1e65SMichal Kazior 7994bf88530SJohannes Berg ctx = ieee80211_find_chanctx(local, chandef, mode); 800d01a1e65SMichal Kazior if (!ctx) 8014bf88530SJohannes Berg ctx = ieee80211_new_chanctx(local, chandef, mode); 802d01a1e65SMichal Kazior if (IS_ERR(ctx)) { 803d01a1e65SMichal Kazior ret = PTR_ERR(ctx); 804d01a1e65SMichal Kazior goto out; 805d01a1e65SMichal Kazior } 806d01a1e65SMichal Kazior 8074bf88530SJohannes Berg sdata->vif.bss_conf.chandef = *chandef; 80855de908aSJohannes Berg 809d01a1e65SMichal Kazior ret = ieee80211_assign_vif_chanctx(sdata, ctx); 810d01a1e65SMichal Kazior if (ret) { 811d01a1e65SMichal Kazior /* if assign fails refcount stays the same */ 812c0166da9SMichal Kazior if (ieee80211_chanctx_refcount(local, ctx) == 0) 813d01a1e65SMichal Kazior ieee80211_free_chanctx(local, ctx); 814d01a1e65SMichal Kazior goto out; 815d01a1e65SMichal Kazior } 816d01a1e65SMichal Kazior 81704ecd257SJohannes Berg ieee80211_recalc_smps_chanctx(local, ctx); 818164eb02dSSimon Wunderlich ieee80211_recalc_radar_chanctx(local, ctx); 819d01a1e65SMichal Kazior out: 820d01a1e65SMichal Kazior mutex_unlock(&local->chanctx_mtx); 821d01a1e65SMichal Kazior return ret; 822d01a1e65SMichal Kazior } 823d01a1e65SMichal Kazior 82411335a55SLuciano Coelho static void 82511335a55SLuciano Coelho __ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata, 82611335a55SLuciano Coelho bool clear) 82711335a55SLuciano Coelho { 82833926eb7SJohannes Berg struct ieee80211_local *local __maybe_unused = sdata->local; 82911335a55SLuciano Coelho struct ieee80211_sub_if_data *vlan; 83011335a55SLuciano Coelho struct ieee80211_chanctx_conf *conf; 83111335a55SLuciano Coelho 83211335a55SLuciano Coelho if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP)) 83311335a55SLuciano Coelho return; 83411335a55SLuciano Coelho 83511335a55SLuciano Coelho lockdep_assert_held(&local->mtx); 83611335a55SLuciano Coelho 83711335a55SLuciano Coelho /* Check that conf exists, even when clearing this function 83811335a55SLuciano Coelho * must be called with the AP's channel context still there 83911335a55SLuciano Coelho * as it would otherwise cause VLANs to have an invalid 84011335a55SLuciano Coelho * channel context pointer for a while, possibly pointing 84111335a55SLuciano Coelho * to a channel context that has already been freed. 84211335a55SLuciano Coelho */ 84311335a55SLuciano Coelho conf = rcu_dereference_protected(sdata->vif.chanctx_conf, 84411335a55SLuciano Coelho lockdep_is_held(&local->chanctx_mtx)); 84511335a55SLuciano Coelho WARN_ON(!conf); 84611335a55SLuciano Coelho 84711335a55SLuciano Coelho if (clear) 84811335a55SLuciano Coelho conf = NULL; 84911335a55SLuciano Coelho 85011335a55SLuciano Coelho list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) 85111335a55SLuciano Coelho rcu_assign_pointer(vlan->vif.chanctx_conf, conf); 85211335a55SLuciano Coelho } 85311335a55SLuciano Coelho 85411335a55SLuciano Coelho void ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata, 85511335a55SLuciano Coelho bool clear) 85611335a55SLuciano Coelho { 85711335a55SLuciano Coelho struct ieee80211_local *local = sdata->local; 85811335a55SLuciano Coelho 85911335a55SLuciano Coelho mutex_lock(&local->chanctx_mtx); 86011335a55SLuciano Coelho 86111335a55SLuciano Coelho __ieee80211_vif_copy_chanctx_to_vlans(sdata, clear); 86211335a55SLuciano Coelho 86311335a55SLuciano Coelho mutex_unlock(&local->chanctx_mtx); 86411335a55SLuciano Coelho } 86511335a55SLuciano Coelho 86611335a55SLuciano Coelho int ieee80211_vif_unreserve_chanctx(struct ieee80211_sub_if_data *sdata) 86711335a55SLuciano Coelho { 868e3afb920SMichal Kazior struct ieee80211_chanctx *ctx = sdata->reserved_chanctx; 869e3afb920SMichal Kazior 87011335a55SLuciano Coelho lockdep_assert_held(&sdata->local->chanctx_mtx); 87111335a55SLuciano Coelho 872e3afb920SMichal Kazior if (WARN_ON(!ctx)) 87311335a55SLuciano Coelho return -EINVAL; 87411335a55SLuciano Coelho 875e3afb920SMichal Kazior list_del(&sdata->reserved_chanctx_list); 87611335a55SLuciano Coelho sdata->reserved_chanctx = NULL; 87711335a55SLuciano Coelho 8785bcae31dSMichal Kazior if (ieee80211_chanctx_refcount(sdata->local, ctx) == 0) { 8795bcae31dSMichal Kazior if (ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER) { 8805bcae31dSMichal Kazior if (WARN_ON(!ctx->replace_ctx)) 8815bcae31dSMichal Kazior return -EINVAL; 8825bcae31dSMichal Kazior 8835bcae31dSMichal Kazior WARN_ON(ctx->replace_ctx->replace_state != 8845bcae31dSMichal Kazior IEEE80211_CHANCTX_WILL_BE_REPLACED); 8855bcae31dSMichal Kazior WARN_ON(ctx->replace_ctx->replace_ctx != ctx); 8865bcae31dSMichal Kazior 8875bcae31dSMichal Kazior ctx->replace_ctx->replace_ctx = NULL; 8885bcae31dSMichal Kazior ctx->replace_ctx->replace_state = 8895bcae31dSMichal Kazior IEEE80211_CHANCTX_REPLACE_NONE; 8905bcae31dSMichal Kazior 8915bcae31dSMichal Kazior list_del_rcu(&ctx->list); 8925bcae31dSMichal Kazior kfree_rcu(ctx, rcu_head); 8935bcae31dSMichal Kazior } else { 894e3afb920SMichal Kazior ieee80211_free_chanctx(sdata->local, ctx); 8955bcae31dSMichal Kazior } 8965bcae31dSMichal Kazior } 897e3afb920SMichal Kazior 89811335a55SLuciano Coelho return 0; 89911335a55SLuciano Coelho } 90011335a55SLuciano Coelho 90111335a55SLuciano Coelho int ieee80211_vif_reserve_chanctx(struct ieee80211_sub_if_data *sdata, 90211335a55SLuciano Coelho const struct cfg80211_chan_def *chandef, 90309332481SMichal Kazior enum ieee80211_chanctx_mode mode, 90409332481SMichal Kazior bool radar_required) 90511335a55SLuciano Coelho { 90611335a55SLuciano Coelho struct ieee80211_local *local = sdata->local; 9075bcae31dSMichal Kazior struct ieee80211_chanctx *new_ctx, *curr_ctx, *ctx; 90811335a55SLuciano Coelho 9095bcae31dSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 91011335a55SLuciano Coelho 9115bcae31dSMichal Kazior curr_ctx = ieee80211_vif_get_chanctx(sdata); 9125bcae31dSMichal Kazior if (curr_ctx && local->use_chanctx && !local->ops->switch_vif_chanctx) 9135bcae31dSMichal Kazior return -ENOTSUPP; 91411335a55SLuciano Coelho 91513f348a8SMichal Kazior new_ctx = ieee80211_find_reservation_chanctx(local, chandef, mode); 91611335a55SLuciano Coelho if (!new_ctx) { 9175bcae31dSMichal Kazior if (ieee80211_can_create_new_chanctx(local)) { 91811335a55SLuciano Coelho new_ctx = ieee80211_new_chanctx(local, chandef, mode); 9195bcae31dSMichal Kazior if (IS_ERR(new_ctx)) 9205bcae31dSMichal Kazior return PTR_ERR(new_ctx); 921c2b90ad8SMichal Kazior } else { 9225bcae31dSMichal Kazior if (!curr_ctx || 9235bcae31dSMichal Kazior (curr_ctx->replace_state == 9245bcae31dSMichal Kazior IEEE80211_CHANCTX_WILL_BE_REPLACED) || 9255bcae31dSMichal Kazior !list_empty(&curr_ctx->reserved_vifs)) { 9265bcae31dSMichal Kazior /* 9275bcae31dSMichal Kazior * Another vif already requested this context 9285bcae31dSMichal Kazior * for a reservation. Find another one hoping 9295bcae31dSMichal Kazior * all vifs assigned to it will also switch 9305bcae31dSMichal Kazior * soon enough. 9315bcae31dSMichal Kazior * 9325bcae31dSMichal Kazior * TODO: This needs a little more work as some 9335bcae31dSMichal Kazior * cases (more than 2 chanctx capable devices) 9345bcae31dSMichal Kazior * may fail which could otherwise succeed 9355bcae31dSMichal Kazior * provided some channel context juggling was 9365bcae31dSMichal Kazior * performed. 9375bcae31dSMichal Kazior * 9385bcae31dSMichal Kazior * Consider ctx1..3, vif1..6, each ctx has 2 9395bcae31dSMichal Kazior * vifs. vif1 and vif2 from ctx1 request new 9405bcae31dSMichal Kazior * different chandefs starting 2 in-place 9415bcae31dSMichal Kazior * reserations with ctx4 and ctx5 replacing 9425bcae31dSMichal Kazior * ctx1 and ctx2 respectively. Next vif5 and 9435bcae31dSMichal Kazior * vif6 from ctx3 reserve ctx4. If vif3 and 9445bcae31dSMichal Kazior * vif4 remain on ctx2 as they are then this 9455bcae31dSMichal Kazior * fails unless `replace_ctx` from ctx5 is 9465bcae31dSMichal Kazior * replaced with ctx3. 9475bcae31dSMichal Kazior */ 9485bcae31dSMichal Kazior list_for_each_entry(ctx, &local->chanctx_list, 9495bcae31dSMichal Kazior list) { 9505bcae31dSMichal Kazior if (ctx->replace_state != 9515bcae31dSMichal Kazior IEEE80211_CHANCTX_REPLACE_NONE) 9525bcae31dSMichal Kazior continue; 9535bcae31dSMichal Kazior 9545bcae31dSMichal Kazior if (!list_empty(&ctx->reserved_vifs)) 9555bcae31dSMichal Kazior continue; 9565bcae31dSMichal Kazior 9575bcae31dSMichal Kazior curr_ctx = ctx; 9585bcae31dSMichal Kazior break; 9595bcae31dSMichal Kazior } 9605bcae31dSMichal Kazior } 9615bcae31dSMichal Kazior 9625bcae31dSMichal Kazior /* 9635bcae31dSMichal Kazior * If that's true then all available contexts already 9645bcae31dSMichal Kazior * have reservations and cannot be used. 9655bcae31dSMichal Kazior */ 9665bcae31dSMichal Kazior if (!curr_ctx || 9675bcae31dSMichal Kazior (curr_ctx->replace_state == 9685bcae31dSMichal Kazior IEEE80211_CHANCTX_WILL_BE_REPLACED) || 9695bcae31dSMichal Kazior !list_empty(&curr_ctx->reserved_vifs)) 9705bcae31dSMichal Kazior return -EBUSY; 9715bcae31dSMichal Kazior 9725bcae31dSMichal Kazior new_ctx = ieee80211_alloc_chanctx(local, chandef, mode); 9735bcae31dSMichal Kazior if (!new_ctx) 9745bcae31dSMichal Kazior return -ENOMEM; 9755bcae31dSMichal Kazior 9765bcae31dSMichal Kazior new_ctx->replace_ctx = curr_ctx; 9775bcae31dSMichal Kazior new_ctx->replace_state = 9785bcae31dSMichal Kazior IEEE80211_CHANCTX_REPLACES_OTHER; 9795bcae31dSMichal Kazior 9805bcae31dSMichal Kazior curr_ctx->replace_ctx = new_ctx; 9815bcae31dSMichal Kazior curr_ctx->replace_state = 9825bcae31dSMichal Kazior IEEE80211_CHANCTX_WILL_BE_REPLACED; 9835bcae31dSMichal Kazior 9845bcae31dSMichal Kazior list_add_rcu(&new_ctx->list, &local->chanctx_list); 98511335a55SLuciano Coelho } 9865d52ee81SLuciano Coelho } 98711335a55SLuciano Coelho 988e3afb920SMichal Kazior list_add(&sdata->reserved_chanctx_list, &new_ctx->reserved_vifs); 98911335a55SLuciano Coelho sdata->reserved_chanctx = new_ctx; 99011335a55SLuciano Coelho sdata->reserved_chandef = *chandef; 99109332481SMichal Kazior sdata->reserved_radar_required = radar_required; 9925bcae31dSMichal Kazior sdata->reserved_ready = false; 9935bcae31dSMichal Kazior 9945bcae31dSMichal Kazior return 0; 99511335a55SLuciano Coelho } 99611335a55SLuciano Coelho 99703078de4SMichal Kazior static void 99803078de4SMichal Kazior ieee80211_vif_chanctx_reservation_complete(struct ieee80211_sub_if_data *sdata) 99903078de4SMichal Kazior { 100003078de4SMichal Kazior switch (sdata->vif.type) { 100103078de4SMichal Kazior case NL80211_IFTYPE_ADHOC: 100203078de4SMichal Kazior case NL80211_IFTYPE_AP: 100303078de4SMichal Kazior case NL80211_IFTYPE_MESH_POINT: 100403078de4SMichal Kazior ieee80211_queue_work(&sdata->local->hw, 100503078de4SMichal Kazior &sdata->csa_finalize_work); 100603078de4SMichal Kazior break; 100703078de4SMichal Kazior case NL80211_IFTYPE_STATION: 10084c3ebc56SMichal Kazior ieee80211_queue_work(&sdata->local->hw, 10094c3ebc56SMichal Kazior &sdata->u.mgd.chswitch_work); 10104c3ebc56SMichal Kazior break; 10114c3ebc56SMichal Kazior case NL80211_IFTYPE_UNSPECIFIED: 101203078de4SMichal Kazior case NL80211_IFTYPE_AP_VLAN: 101303078de4SMichal Kazior case NL80211_IFTYPE_WDS: 101403078de4SMichal Kazior case NL80211_IFTYPE_MONITOR: 101503078de4SMichal Kazior case NL80211_IFTYPE_P2P_CLIENT: 101603078de4SMichal Kazior case NL80211_IFTYPE_P2P_GO: 101703078de4SMichal Kazior case NL80211_IFTYPE_P2P_DEVICE: 101803078de4SMichal Kazior case NUM_NL80211_IFTYPES: 101903078de4SMichal Kazior WARN_ON(1); 102003078de4SMichal Kazior break; 102103078de4SMichal Kazior } 102203078de4SMichal Kazior } 102303078de4SMichal Kazior 10245bcae31dSMichal Kazior static int 10255bcae31dSMichal Kazior ieee80211_vif_use_reserved_reassign(struct ieee80211_sub_if_data *sdata) 102611335a55SLuciano Coelho { 102711335a55SLuciano Coelho struct ieee80211_local *local = sdata->local; 10285bcae31dSMichal Kazior struct ieee80211_vif_chanctx_switch vif_chsw[1] = {}; 10295bcae31dSMichal Kazior struct ieee80211_chanctx *old_ctx, *new_ctx; 10305bcae31dSMichal Kazior const struct cfg80211_chan_def *chandef; 10315bcae31dSMichal Kazior u32 changed = 0; 10325bcae31dSMichal Kazior int err; 103311335a55SLuciano Coelho 103411335a55SLuciano Coelho lockdep_assert_held(&local->mtx); 10355bcae31dSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 103611335a55SLuciano Coelho 10375bcae31dSMichal Kazior new_ctx = sdata->reserved_chanctx; 10385bcae31dSMichal Kazior old_ctx = ieee80211_vif_get_chanctx(sdata); 103911335a55SLuciano Coelho 10405bcae31dSMichal Kazior if (WARN_ON(!sdata->reserved_ready)) 10415bcae31dSMichal Kazior return -EBUSY; 104211335a55SLuciano Coelho 10435bcae31dSMichal Kazior if (WARN_ON(!new_ctx)) 10445bcae31dSMichal Kazior return -EINVAL; 104511335a55SLuciano Coelho 10465bcae31dSMichal Kazior if (WARN_ON(!old_ctx)) 10475bcae31dSMichal Kazior return -EINVAL; 104811335a55SLuciano Coelho 10495bcae31dSMichal Kazior if (WARN_ON(new_ctx->replace_state == 10505bcae31dSMichal Kazior IEEE80211_CHANCTX_REPLACES_OTHER)) 10515bcae31dSMichal Kazior return -EINVAL; 105211335a55SLuciano Coelho 10535bcae31dSMichal Kazior chandef = ieee80211_chanctx_non_reserved_chandef(local, new_ctx, 10545bcae31dSMichal Kazior &sdata->reserved_chandef); 10555bcae31dSMichal Kazior if (WARN_ON(!chandef)) 10565bcae31dSMichal Kazior return -EINVAL; 105711335a55SLuciano Coelho 10585bcae31dSMichal Kazior vif_chsw[0].vif = &sdata->vif; 10595bcae31dSMichal Kazior vif_chsw[0].old_ctx = &old_ctx->conf; 10605bcae31dSMichal Kazior vif_chsw[0].new_ctx = &new_ctx->conf; 10615bcae31dSMichal Kazior 1062e3afb920SMichal Kazior list_del(&sdata->reserved_chanctx_list); 10635bcae31dSMichal Kazior sdata->reserved_chanctx = NULL; 106411335a55SLuciano Coelho 10655bcae31dSMichal Kazior err = drv_switch_vif_chanctx(local, vif_chsw, 1, 10665bcae31dSMichal Kazior CHANCTX_SWMODE_REASSIGN_VIF); 10675bcae31dSMichal Kazior if (err) { 10685bcae31dSMichal Kazior if (ieee80211_chanctx_refcount(local, new_ctx) == 0) 10695bcae31dSMichal Kazior ieee80211_free_chanctx(local, new_ctx); 10705bcae31dSMichal Kazior 107103078de4SMichal Kazior goto out; 107211335a55SLuciano Coelho } 107311335a55SLuciano Coelho 10745bcae31dSMichal Kazior list_move(&sdata->assigned_chanctx_list, &new_ctx->assigned_vifs); 10755bcae31dSMichal Kazior rcu_assign_pointer(sdata->vif.chanctx_conf, &new_ctx->conf); 10765bcae31dSMichal Kazior 107711335a55SLuciano Coelho if (sdata->vif.type == NL80211_IFTYPE_AP) 107811335a55SLuciano Coelho __ieee80211_vif_copy_chanctx_to_vlans(sdata, false); 10795bcae31dSMichal Kazior 10805bcae31dSMichal Kazior if (ieee80211_chanctx_refcount(local, old_ctx) == 0) 10815bcae31dSMichal Kazior ieee80211_free_chanctx(local, old_ctx); 10825bcae31dSMichal Kazior 10835bcae31dSMichal Kazior if (sdata->vif.bss_conf.chandef.width != sdata->reserved_chandef.width) 10845bcae31dSMichal Kazior changed = BSS_CHANGED_BANDWIDTH; 10855bcae31dSMichal Kazior 10865bcae31dSMichal Kazior sdata->vif.bss_conf.chandef = sdata->reserved_chandef; 10875bcae31dSMichal Kazior 10885bcae31dSMichal Kazior if (changed) 10895bcae31dSMichal Kazior ieee80211_bss_info_change_notify(sdata, changed); 10905bcae31dSMichal Kazior 109103078de4SMichal Kazior out: 109203078de4SMichal Kazior ieee80211_vif_chanctx_reservation_complete(sdata); 10935bcae31dSMichal Kazior return err; 10945d52ee81SLuciano Coelho } 109511335a55SLuciano Coelho 10965bcae31dSMichal Kazior static int 10975bcae31dSMichal Kazior ieee80211_vif_use_reserved_assign(struct ieee80211_sub_if_data *sdata) 10985bcae31dSMichal Kazior { 10995bcae31dSMichal Kazior struct ieee80211_local *local = sdata->local; 11005bcae31dSMichal Kazior struct ieee80211_chanctx *old_ctx, *new_ctx; 11015bcae31dSMichal Kazior const struct cfg80211_chan_def *chandef; 11025bcae31dSMichal Kazior int err; 11035bcae31dSMichal Kazior 11045bcae31dSMichal Kazior old_ctx = ieee80211_vif_get_chanctx(sdata); 11055bcae31dSMichal Kazior new_ctx = sdata->reserved_chanctx; 11065bcae31dSMichal Kazior 11075bcae31dSMichal Kazior if (WARN_ON(!sdata->reserved_ready)) 11085bcae31dSMichal Kazior return -EINVAL; 11095bcae31dSMichal Kazior 11105bcae31dSMichal Kazior if (WARN_ON(old_ctx)) 11115bcae31dSMichal Kazior return -EINVAL; 11125bcae31dSMichal Kazior 11135bcae31dSMichal Kazior if (WARN_ON(!new_ctx)) 11145bcae31dSMichal Kazior return -EINVAL; 11155bcae31dSMichal Kazior 11165bcae31dSMichal Kazior if (WARN_ON(new_ctx->replace_state == 11175bcae31dSMichal Kazior IEEE80211_CHANCTX_REPLACES_OTHER)) 11185bcae31dSMichal Kazior return -EINVAL; 11195bcae31dSMichal Kazior 11205bcae31dSMichal Kazior chandef = ieee80211_chanctx_non_reserved_chandef(local, new_ctx, 11215bcae31dSMichal Kazior &sdata->reserved_chandef); 11225bcae31dSMichal Kazior if (WARN_ON(!chandef)) 11235bcae31dSMichal Kazior return -EINVAL; 11245bcae31dSMichal Kazior 11255bcae31dSMichal Kazior list_del(&sdata->reserved_chanctx_list); 11265bcae31dSMichal Kazior sdata->reserved_chanctx = NULL; 11275bcae31dSMichal Kazior 11285bcae31dSMichal Kazior err = ieee80211_assign_vif_chanctx(sdata, new_ctx); 11295bcae31dSMichal Kazior if (err) { 11305bcae31dSMichal Kazior if (ieee80211_chanctx_refcount(local, new_ctx) == 0) 11315bcae31dSMichal Kazior ieee80211_free_chanctx(local, new_ctx); 11325bcae31dSMichal Kazior 11335bcae31dSMichal Kazior goto out; 11345bcae31dSMichal Kazior } 11355bcae31dSMichal Kazior 11365bcae31dSMichal Kazior out: 113703078de4SMichal Kazior ieee80211_vif_chanctx_reservation_complete(sdata); 11385bcae31dSMichal Kazior return err; 11395bcae31dSMichal Kazior } 11405bcae31dSMichal Kazior 11415bcae31dSMichal Kazior static bool 11425bcae31dSMichal Kazior ieee80211_vif_has_in_place_reservation(struct ieee80211_sub_if_data *sdata) 11435bcae31dSMichal Kazior { 11445bcae31dSMichal Kazior struct ieee80211_chanctx *old_ctx, *new_ctx; 11455bcae31dSMichal Kazior 11465bcae31dSMichal Kazior lockdep_assert_held(&sdata->local->chanctx_mtx); 11475bcae31dSMichal Kazior 11485bcae31dSMichal Kazior new_ctx = sdata->reserved_chanctx; 11495bcae31dSMichal Kazior old_ctx = ieee80211_vif_get_chanctx(sdata); 11505bcae31dSMichal Kazior 11515bcae31dSMichal Kazior if (!old_ctx) 11525bcae31dSMichal Kazior return false; 11535bcae31dSMichal Kazior 11545bcae31dSMichal Kazior if (WARN_ON(!new_ctx)) 11555bcae31dSMichal Kazior return false; 11565bcae31dSMichal Kazior 11575bcae31dSMichal Kazior if (old_ctx->replace_state != IEEE80211_CHANCTX_WILL_BE_REPLACED) 11585bcae31dSMichal Kazior return false; 11595bcae31dSMichal Kazior 11605bcae31dSMichal Kazior if (new_ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) 11615bcae31dSMichal Kazior return false; 11625bcae31dSMichal Kazior 11635bcae31dSMichal Kazior return true; 11645bcae31dSMichal Kazior } 11655bcae31dSMichal Kazior 11665bcae31dSMichal Kazior static int ieee80211_chsw_switch_hwconf(struct ieee80211_local *local, 11675bcae31dSMichal Kazior struct ieee80211_chanctx *new_ctx) 11685bcae31dSMichal Kazior { 11695bcae31dSMichal Kazior const struct cfg80211_chan_def *chandef; 11705bcae31dSMichal Kazior 11715bcae31dSMichal Kazior lockdep_assert_held(&local->mtx); 11725bcae31dSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 11735bcae31dSMichal Kazior 11745bcae31dSMichal Kazior chandef = ieee80211_chanctx_reserved_chandef(local, new_ctx, NULL); 11755bcae31dSMichal Kazior if (WARN_ON(!chandef)) 11765bcae31dSMichal Kazior return -EINVAL; 11775bcae31dSMichal Kazior 11785bcae31dSMichal Kazior local->hw.conf.radar_enabled = new_ctx->conf.radar_enabled; 11795bcae31dSMichal Kazior local->_oper_chandef = *chandef; 11805bcae31dSMichal Kazior ieee80211_hw_config(local, 0); 11815bcae31dSMichal Kazior 11825bcae31dSMichal Kazior return 0; 11835bcae31dSMichal Kazior } 11845bcae31dSMichal Kazior 11855bcae31dSMichal Kazior static int ieee80211_chsw_switch_vifs(struct ieee80211_local *local, 11865bcae31dSMichal Kazior int n_vifs) 11875bcae31dSMichal Kazior { 11885bcae31dSMichal Kazior struct ieee80211_vif_chanctx_switch *vif_chsw; 11895bcae31dSMichal Kazior struct ieee80211_sub_if_data *sdata; 11905bcae31dSMichal Kazior struct ieee80211_chanctx *ctx, *old_ctx; 11915bcae31dSMichal Kazior int i, err; 11925bcae31dSMichal Kazior 11935bcae31dSMichal Kazior lockdep_assert_held(&local->mtx); 11945bcae31dSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 11955bcae31dSMichal Kazior 11965bcae31dSMichal Kazior vif_chsw = kzalloc(sizeof(vif_chsw[0]) * n_vifs, GFP_KERNEL); 11975bcae31dSMichal Kazior if (!vif_chsw) 11985bcae31dSMichal Kazior return -ENOMEM; 11995bcae31dSMichal Kazior 12005bcae31dSMichal Kazior i = 0; 12015bcae31dSMichal Kazior list_for_each_entry(ctx, &local->chanctx_list, list) { 12025bcae31dSMichal Kazior if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) 12035bcae31dSMichal Kazior continue; 12045bcae31dSMichal Kazior 12055bcae31dSMichal Kazior if (WARN_ON(!ctx->replace_ctx)) { 12065bcae31dSMichal Kazior err = -EINVAL; 12075bcae31dSMichal Kazior goto out; 12085bcae31dSMichal Kazior } 12095bcae31dSMichal Kazior 12105bcae31dSMichal Kazior list_for_each_entry(sdata, &ctx->reserved_vifs, 12115bcae31dSMichal Kazior reserved_chanctx_list) { 12125bcae31dSMichal Kazior if (!ieee80211_vif_has_in_place_reservation( 12135bcae31dSMichal Kazior sdata)) 12145bcae31dSMichal Kazior continue; 12155bcae31dSMichal Kazior 12165bcae31dSMichal Kazior old_ctx = ieee80211_vif_get_chanctx(sdata); 12175bcae31dSMichal Kazior vif_chsw[i].vif = &sdata->vif; 12185bcae31dSMichal Kazior vif_chsw[i].old_ctx = &old_ctx->conf; 12195bcae31dSMichal Kazior vif_chsw[i].new_ctx = &ctx->conf; 12205bcae31dSMichal Kazior 12215bcae31dSMichal Kazior i++; 12225bcae31dSMichal Kazior } 12235bcae31dSMichal Kazior } 12245bcae31dSMichal Kazior 12255bcae31dSMichal Kazior err = drv_switch_vif_chanctx(local, vif_chsw, n_vifs, 12265bcae31dSMichal Kazior CHANCTX_SWMODE_SWAP_CONTEXTS); 12275bcae31dSMichal Kazior 12285bcae31dSMichal Kazior out: 12295bcae31dSMichal Kazior kfree(vif_chsw); 12305bcae31dSMichal Kazior return err; 12315bcae31dSMichal Kazior } 12325bcae31dSMichal Kazior 12335bcae31dSMichal Kazior static int ieee80211_chsw_switch_ctxs(struct ieee80211_local *local) 12345bcae31dSMichal Kazior { 12355bcae31dSMichal Kazior struct ieee80211_chanctx *ctx; 12365bcae31dSMichal Kazior int err; 12375bcae31dSMichal Kazior 12385bcae31dSMichal Kazior lockdep_assert_held(&local->mtx); 12395bcae31dSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 12405bcae31dSMichal Kazior 12415bcae31dSMichal Kazior list_for_each_entry(ctx, &local->chanctx_list, list) { 12425bcae31dSMichal Kazior if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) 12435bcae31dSMichal Kazior continue; 12445bcae31dSMichal Kazior 12455bcae31dSMichal Kazior if (!list_empty(&ctx->replace_ctx->assigned_vifs)) 12465bcae31dSMichal Kazior continue; 12475bcae31dSMichal Kazior 12485bcae31dSMichal Kazior ieee80211_del_chanctx(local, ctx->replace_ctx); 12495bcae31dSMichal Kazior err = ieee80211_add_chanctx(local, ctx); 12505bcae31dSMichal Kazior if (err) 12515bcae31dSMichal Kazior goto err; 12525bcae31dSMichal Kazior } 12535bcae31dSMichal Kazior 12545bcae31dSMichal Kazior return 0; 12555bcae31dSMichal Kazior 12565bcae31dSMichal Kazior err: 12575bcae31dSMichal Kazior WARN_ON(ieee80211_add_chanctx(local, ctx)); 12585bcae31dSMichal Kazior list_for_each_entry_continue_reverse(ctx, &local->chanctx_list, list) { 12595bcae31dSMichal Kazior if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) 12605bcae31dSMichal Kazior continue; 12615bcae31dSMichal Kazior 12625bcae31dSMichal Kazior if (!list_empty(&ctx->replace_ctx->assigned_vifs)) 12635bcae31dSMichal Kazior continue; 12645bcae31dSMichal Kazior 12655bcae31dSMichal Kazior ieee80211_del_chanctx(local, ctx); 12665bcae31dSMichal Kazior WARN_ON(ieee80211_add_chanctx(local, ctx->replace_ctx)); 12675bcae31dSMichal Kazior } 12685bcae31dSMichal Kazior 12695bcae31dSMichal Kazior return err; 12705bcae31dSMichal Kazior } 12715bcae31dSMichal Kazior 12725bcae31dSMichal Kazior int 12735bcae31dSMichal Kazior ieee80211_vif_use_reserved_switch(struct ieee80211_local *local) 12745bcae31dSMichal Kazior { 12755bcae31dSMichal Kazior struct ieee80211_sub_if_data *sdata, *sdata_tmp; 12765bcae31dSMichal Kazior struct ieee80211_chanctx *ctx, *ctx_tmp, *old_ctx; 12775bcae31dSMichal Kazior struct ieee80211_chanctx *new_ctx = NULL; 12785bcae31dSMichal Kazior int i, err, n_assigned, n_reserved, n_ready; 12795bcae31dSMichal Kazior int n_ctx = 0, n_vifs_switch = 0, n_vifs_assign = 0, n_vifs_ctxless = 0; 12805bcae31dSMichal Kazior 12815bcae31dSMichal Kazior lockdep_assert_held(&local->mtx); 12825bcae31dSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 12835bcae31dSMichal Kazior 12845bcae31dSMichal Kazior /* 12855bcae31dSMichal Kazior * If there are 2 independent pairs of channel contexts performing 12865bcae31dSMichal Kazior * cross-switch of their vifs this code will still wait until both are 12875bcae31dSMichal Kazior * ready even though it could be possible to switch one before the 12885bcae31dSMichal Kazior * other is ready. 12895bcae31dSMichal Kazior * 12905bcae31dSMichal Kazior * For practical reasons and code simplicity just do a single huge 12915bcae31dSMichal Kazior * switch. 12925bcae31dSMichal Kazior */ 12935bcae31dSMichal Kazior 12945bcae31dSMichal Kazior /* 12955bcae31dSMichal Kazior * Verify if the reservation is still feasible. 12965bcae31dSMichal Kazior * - if it's not then disconnect 12975bcae31dSMichal Kazior * - if it is but not all vifs necessary are ready then defer 12985bcae31dSMichal Kazior */ 12995bcae31dSMichal Kazior 13005bcae31dSMichal Kazior list_for_each_entry(ctx, &local->chanctx_list, list) { 13015bcae31dSMichal Kazior if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) 13025bcae31dSMichal Kazior continue; 13035bcae31dSMichal Kazior 13045bcae31dSMichal Kazior if (WARN_ON(!ctx->replace_ctx)) { 13055bcae31dSMichal Kazior err = -EINVAL; 13065bcae31dSMichal Kazior goto err; 13075bcae31dSMichal Kazior } 13085bcae31dSMichal Kazior 13095bcae31dSMichal Kazior if (!local->use_chanctx) 13105bcae31dSMichal Kazior new_ctx = ctx; 13115bcae31dSMichal Kazior 13125bcae31dSMichal Kazior n_ctx++; 13135bcae31dSMichal Kazior 13145bcae31dSMichal Kazior n_assigned = 0; 13155bcae31dSMichal Kazior n_reserved = 0; 13165bcae31dSMichal Kazior n_ready = 0; 13175bcae31dSMichal Kazior 13185bcae31dSMichal Kazior list_for_each_entry(sdata, &ctx->replace_ctx->assigned_vifs, 13195bcae31dSMichal Kazior assigned_chanctx_list) { 13205bcae31dSMichal Kazior n_assigned++; 13215bcae31dSMichal Kazior if (sdata->reserved_chanctx) { 13225bcae31dSMichal Kazior n_reserved++; 13235bcae31dSMichal Kazior if (sdata->reserved_ready) 13245bcae31dSMichal Kazior n_ready++; 13255bcae31dSMichal Kazior } 13265bcae31dSMichal Kazior } 13275bcae31dSMichal Kazior 13285bcae31dSMichal Kazior if (n_assigned != n_reserved) { 13295bcae31dSMichal Kazior if (n_ready == n_reserved) { 13305bcae31dSMichal Kazior wiphy_info(local->hw.wiphy, 13315bcae31dSMichal Kazior "channel context reservation cannot be finalized because some interfaces aren't switching\n"); 13325bcae31dSMichal Kazior err = -EBUSY; 13335bcae31dSMichal Kazior goto err; 13345bcae31dSMichal Kazior } 13355bcae31dSMichal Kazior 13365bcae31dSMichal Kazior return -EAGAIN; 13375bcae31dSMichal Kazior } 13385bcae31dSMichal Kazior 13395bcae31dSMichal Kazior ctx->conf.radar_enabled = false; 13405bcae31dSMichal Kazior list_for_each_entry(sdata, &ctx->reserved_vifs, 13415bcae31dSMichal Kazior reserved_chanctx_list) { 13425bcae31dSMichal Kazior if (ieee80211_vif_has_in_place_reservation(sdata) && 13435bcae31dSMichal Kazior !sdata->reserved_ready) 13445bcae31dSMichal Kazior return -EAGAIN; 13455bcae31dSMichal Kazior 13465bcae31dSMichal Kazior old_ctx = ieee80211_vif_get_chanctx(sdata); 13475bcae31dSMichal Kazior if (old_ctx) { 13485bcae31dSMichal Kazior if (old_ctx->replace_state == 13495bcae31dSMichal Kazior IEEE80211_CHANCTX_WILL_BE_REPLACED) 13505bcae31dSMichal Kazior n_vifs_switch++; 13515bcae31dSMichal Kazior else 13525bcae31dSMichal Kazior n_vifs_assign++; 13535bcae31dSMichal Kazior } else { 13545bcae31dSMichal Kazior n_vifs_ctxless++; 13555bcae31dSMichal Kazior } 13565bcae31dSMichal Kazior 13575bcae31dSMichal Kazior if (sdata->reserved_radar_required) 13585bcae31dSMichal Kazior ctx->conf.radar_enabled = true; 13595bcae31dSMichal Kazior } 13605bcae31dSMichal Kazior } 13615bcae31dSMichal Kazior 13625bcae31dSMichal Kazior if (WARN_ON(n_ctx == 0) || 13635bcae31dSMichal Kazior WARN_ON(n_vifs_switch == 0 && 13645bcae31dSMichal Kazior n_vifs_assign == 0 && 13655bcae31dSMichal Kazior n_vifs_ctxless == 0) || 13665bcae31dSMichal Kazior WARN_ON(n_ctx > 1 && !local->use_chanctx) || 13675bcae31dSMichal Kazior WARN_ON(!new_ctx && !local->use_chanctx)) { 13685bcae31dSMichal Kazior err = -EINVAL; 13695bcae31dSMichal Kazior goto err; 13705bcae31dSMichal Kazior } 13715bcae31dSMichal Kazior 13725bcae31dSMichal Kazior /* 13735bcae31dSMichal Kazior * All necessary vifs are ready. Perform the switch now depending on 13745bcae31dSMichal Kazior * reservations and driver capabilities. 13755bcae31dSMichal Kazior */ 13765bcae31dSMichal Kazior 13775bcae31dSMichal Kazior if (local->use_chanctx) { 13785bcae31dSMichal Kazior if (n_vifs_switch > 0) { 13795bcae31dSMichal Kazior err = ieee80211_chsw_switch_vifs(local, n_vifs_switch); 13805bcae31dSMichal Kazior if (err) 13815bcae31dSMichal Kazior goto err; 13825bcae31dSMichal Kazior } 13835bcae31dSMichal Kazior 13845bcae31dSMichal Kazior if (n_vifs_assign > 0 || n_vifs_ctxless > 0) { 13855bcae31dSMichal Kazior err = ieee80211_chsw_switch_ctxs(local); 13865bcae31dSMichal Kazior if (err) 13875bcae31dSMichal Kazior goto err; 13885bcae31dSMichal Kazior } 13895bcae31dSMichal Kazior } else { 13905bcae31dSMichal Kazior err = ieee80211_chsw_switch_hwconf(local, new_ctx); 13915bcae31dSMichal Kazior if (err) 13925bcae31dSMichal Kazior goto err; 13935bcae31dSMichal Kazior } 13945bcae31dSMichal Kazior 13955bcae31dSMichal Kazior /* 13965bcae31dSMichal Kazior * Update all structures, values and pointers to point to new channel 13975bcae31dSMichal Kazior * context(s). 13985bcae31dSMichal Kazior */ 13995bcae31dSMichal Kazior 14005bcae31dSMichal Kazior i = 0; 14015bcae31dSMichal Kazior list_for_each_entry(ctx, &local->chanctx_list, list) { 14025bcae31dSMichal Kazior if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) 14035bcae31dSMichal Kazior continue; 14045bcae31dSMichal Kazior 14055bcae31dSMichal Kazior if (WARN_ON(!ctx->replace_ctx)) { 14065bcae31dSMichal Kazior err = -EINVAL; 14075bcae31dSMichal Kazior goto err; 14085bcae31dSMichal Kazior } 14095bcae31dSMichal Kazior 14105bcae31dSMichal Kazior list_for_each_entry(sdata, &ctx->reserved_vifs, 14115bcae31dSMichal Kazior reserved_chanctx_list) { 14125bcae31dSMichal Kazior u32 changed = 0; 14135bcae31dSMichal Kazior 14145bcae31dSMichal Kazior if (!ieee80211_vif_has_in_place_reservation(sdata)) 14155bcae31dSMichal Kazior continue; 14165bcae31dSMichal Kazior 14175bcae31dSMichal Kazior rcu_assign_pointer(sdata->vif.chanctx_conf, &ctx->conf); 14185bcae31dSMichal Kazior 14195bcae31dSMichal Kazior if (sdata->vif.type == NL80211_IFTYPE_AP) 14205bcae31dSMichal Kazior __ieee80211_vif_copy_chanctx_to_vlans(sdata, 14215bcae31dSMichal Kazior false); 14225bcae31dSMichal Kazior 14235bcae31dSMichal Kazior sdata->radar_required = sdata->reserved_radar_required; 14245bcae31dSMichal Kazior 14255bcae31dSMichal Kazior if (sdata->vif.bss_conf.chandef.width != 14265bcae31dSMichal Kazior sdata->reserved_chandef.width) 14275bcae31dSMichal Kazior changed = BSS_CHANGED_BANDWIDTH; 14285bcae31dSMichal Kazior 14295bcae31dSMichal Kazior sdata->vif.bss_conf.chandef = sdata->reserved_chandef; 14305bcae31dSMichal Kazior if (changed) 14315bcae31dSMichal Kazior ieee80211_bss_info_change_notify(sdata, 14325bcae31dSMichal Kazior changed); 14335bcae31dSMichal Kazior 14345bcae31dSMichal Kazior ieee80211_recalc_txpower(sdata); 14355bcae31dSMichal Kazior } 143611335a55SLuciano Coelho 143711335a55SLuciano Coelho ieee80211_recalc_chanctx_chantype(local, ctx); 143811335a55SLuciano Coelho ieee80211_recalc_smps_chanctx(local, ctx); 143911335a55SLuciano Coelho ieee80211_recalc_radar_chanctx(local, ctx); 144011335a55SLuciano Coelho ieee80211_recalc_chanctx_min_def(local, ctx); 14415bcae31dSMichal Kazior 14425bcae31dSMichal Kazior list_for_each_entry_safe(sdata, sdata_tmp, &ctx->reserved_vifs, 14435bcae31dSMichal Kazior reserved_chanctx_list) { 14445bcae31dSMichal Kazior if (ieee80211_vif_get_chanctx(sdata) != ctx) 14455bcae31dSMichal Kazior continue; 14465bcae31dSMichal Kazior 14475bcae31dSMichal Kazior list_del(&sdata->reserved_chanctx_list); 14485bcae31dSMichal Kazior list_move(&sdata->assigned_chanctx_list, 14495bcae31dSMichal Kazior &new_ctx->assigned_vifs); 14505bcae31dSMichal Kazior sdata->reserved_chanctx = NULL; 145103078de4SMichal Kazior 145203078de4SMichal Kazior ieee80211_vif_chanctx_reservation_complete(sdata); 14535bcae31dSMichal Kazior } 14545bcae31dSMichal Kazior 14555bcae31dSMichal Kazior /* 14565bcae31dSMichal Kazior * This context might have been a dependency for an already 14575bcae31dSMichal Kazior * ready re-assign reservation interface that was deferred. Do 14585bcae31dSMichal Kazior * not propagate error to the caller though. The in-place 14595bcae31dSMichal Kazior * reservation for originally requested interface has already 14605bcae31dSMichal Kazior * succeeded at this point. 14615bcae31dSMichal Kazior */ 14625bcae31dSMichal Kazior list_for_each_entry_safe(sdata, sdata_tmp, &ctx->reserved_vifs, 14635bcae31dSMichal Kazior reserved_chanctx_list) { 14645bcae31dSMichal Kazior if (WARN_ON(ieee80211_vif_has_in_place_reservation( 14655bcae31dSMichal Kazior sdata))) 14665bcae31dSMichal Kazior continue; 14675bcae31dSMichal Kazior 14685bcae31dSMichal Kazior if (WARN_ON(sdata->reserved_chanctx != ctx)) 14695bcae31dSMichal Kazior continue; 14705bcae31dSMichal Kazior 14715bcae31dSMichal Kazior if (!sdata->reserved_ready) 14725bcae31dSMichal Kazior continue; 14735bcae31dSMichal Kazior 14745bcae31dSMichal Kazior if (ieee80211_vif_get_chanctx(sdata)) 14755bcae31dSMichal Kazior err = ieee80211_vif_use_reserved_reassign( 14765bcae31dSMichal Kazior sdata); 14775bcae31dSMichal Kazior else 14785bcae31dSMichal Kazior err = ieee80211_vif_use_reserved_assign(sdata); 14795bcae31dSMichal Kazior 14805bcae31dSMichal Kazior if (err) { 14815bcae31dSMichal Kazior sdata_info(sdata, 14825bcae31dSMichal Kazior "failed to finalize (re-)assign reservation (err=%d)\n", 14835bcae31dSMichal Kazior err); 14845bcae31dSMichal Kazior ieee80211_vif_unreserve_chanctx(sdata); 14855bcae31dSMichal Kazior cfg80211_stop_iface(local->hw.wiphy, 14865bcae31dSMichal Kazior &sdata->wdev, 14875bcae31dSMichal Kazior GFP_KERNEL); 14885bcae31dSMichal Kazior } 14895bcae31dSMichal Kazior } 14905bcae31dSMichal Kazior } 14915bcae31dSMichal Kazior 14925bcae31dSMichal Kazior /* 14935bcae31dSMichal Kazior * Finally free old contexts 14945bcae31dSMichal Kazior */ 14955bcae31dSMichal Kazior 14965bcae31dSMichal Kazior list_for_each_entry_safe(ctx, ctx_tmp, &local->chanctx_list, list) { 14975bcae31dSMichal Kazior if (ctx->replace_state != IEEE80211_CHANCTX_WILL_BE_REPLACED) 14985bcae31dSMichal Kazior continue; 14995bcae31dSMichal Kazior 15005bcae31dSMichal Kazior ctx->replace_ctx->replace_ctx = NULL; 15015bcae31dSMichal Kazior ctx->replace_ctx->replace_state = 15025bcae31dSMichal Kazior IEEE80211_CHANCTX_REPLACE_NONE; 15035bcae31dSMichal Kazior 15045bcae31dSMichal Kazior list_del_rcu(&ctx->list); 15055bcae31dSMichal Kazior kfree_rcu(ctx, rcu_head); 15065bcae31dSMichal Kazior } 15075bcae31dSMichal Kazior 15085bcae31dSMichal Kazior return 0; 15095bcae31dSMichal Kazior 15105bcae31dSMichal Kazior err: 15115bcae31dSMichal Kazior list_for_each_entry(ctx, &local->chanctx_list, list) { 15125bcae31dSMichal Kazior if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) 15135bcae31dSMichal Kazior continue; 15145bcae31dSMichal Kazior 15155bcae31dSMichal Kazior list_for_each_entry_safe(sdata, sdata_tmp, &ctx->reserved_vifs, 151603078de4SMichal Kazior reserved_chanctx_list) { 15175bcae31dSMichal Kazior ieee80211_vif_unreserve_chanctx(sdata); 151803078de4SMichal Kazior ieee80211_vif_chanctx_reservation_complete(sdata); 151903078de4SMichal Kazior } 15205bcae31dSMichal Kazior } 15215bcae31dSMichal Kazior 15225bcae31dSMichal Kazior return err; 15235bcae31dSMichal Kazior } 15245bcae31dSMichal Kazior 15255bcae31dSMichal Kazior int ieee80211_vif_use_reserved_context(struct ieee80211_sub_if_data *sdata) 15265bcae31dSMichal Kazior { 15275bcae31dSMichal Kazior struct ieee80211_local *local = sdata->local; 15285bcae31dSMichal Kazior struct ieee80211_chanctx *new_ctx; 15295bcae31dSMichal Kazior struct ieee80211_chanctx *old_ctx; 15305bcae31dSMichal Kazior int err; 15315bcae31dSMichal Kazior 15325bcae31dSMichal Kazior lockdep_assert_held(&local->mtx); 15335bcae31dSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 15345bcae31dSMichal Kazior 15355bcae31dSMichal Kazior new_ctx = sdata->reserved_chanctx; 15365bcae31dSMichal Kazior old_ctx = ieee80211_vif_get_chanctx(sdata); 15375bcae31dSMichal Kazior 15385bcae31dSMichal Kazior if (WARN_ON(!new_ctx)) 15395bcae31dSMichal Kazior return -EINVAL; 15405bcae31dSMichal Kazior 15415bcae31dSMichal Kazior if (WARN_ON(new_ctx->replace_state == 15425bcae31dSMichal Kazior IEEE80211_CHANCTX_WILL_BE_REPLACED)) 15435bcae31dSMichal Kazior return -EINVAL; 15445bcae31dSMichal Kazior 15455bcae31dSMichal Kazior if (WARN_ON(sdata->reserved_ready)) 15465bcae31dSMichal Kazior return -EINVAL; 15475bcae31dSMichal Kazior 15485bcae31dSMichal Kazior sdata->reserved_ready = true; 15495bcae31dSMichal Kazior 15505bcae31dSMichal Kazior if (new_ctx->replace_state == IEEE80211_CHANCTX_REPLACE_NONE) { 15515bcae31dSMichal Kazior if (old_ctx) 15525bcae31dSMichal Kazior err = ieee80211_vif_use_reserved_reassign(sdata); 15535bcae31dSMichal Kazior else 15545bcae31dSMichal Kazior err = ieee80211_vif_use_reserved_assign(sdata); 15555bcae31dSMichal Kazior 15565bcae31dSMichal Kazior if (err) 15575bcae31dSMichal Kazior return err; 15585bcae31dSMichal Kazior } 15595bcae31dSMichal Kazior 15605bcae31dSMichal Kazior /* 15615bcae31dSMichal Kazior * In-place reservation may need to be finalized now either if: 15625bcae31dSMichal Kazior * a) sdata is taking part in the swapping itself and is the last one 15635bcae31dSMichal Kazior * b) sdata has switched with a re-assign reservation to an existing 15645bcae31dSMichal Kazior * context readying in-place switching of old_ctx 15655bcae31dSMichal Kazior * 15665bcae31dSMichal Kazior * In case of (b) do not propagate the error up because the requested 15675bcae31dSMichal Kazior * sdata already switched successfully. Just spill an extra warning. 15685bcae31dSMichal Kazior * The ieee80211_vif_use_reserved_switch() already stops all necessary 15695bcae31dSMichal Kazior * interfaces upon failure. 15705bcae31dSMichal Kazior */ 15715bcae31dSMichal Kazior if ((old_ctx && 15725bcae31dSMichal Kazior old_ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED) || 15735bcae31dSMichal Kazior new_ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER) { 15745bcae31dSMichal Kazior err = ieee80211_vif_use_reserved_switch(local); 15755bcae31dSMichal Kazior if (err && err != -EAGAIN) { 15765bcae31dSMichal Kazior if (new_ctx->replace_state == 15775bcae31dSMichal Kazior IEEE80211_CHANCTX_REPLACES_OTHER) 15785bcae31dSMichal Kazior return err; 15795bcae31dSMichal Kazior 15805bcae31dSMichal Kazior wiphy_info(local->hw.wiphy, 15815bcae31dSMichal Kazior "depending in-place reservation failed (err=%d)\n", 15825bcae31dSMichal Kazior err); 15835bcae31dSMichal Kazior } 15845bcae31dSMichal Kazior } 15855bcae31dSMichal Kazior 15865bcae31dSMichal Kazior return 0; 158773da7d5bSSimon Wunderlich } 158873da7d5bSSimon Wunderlich 15892c9b7359SJohannes Berg int ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata, 15902c9b7359SJohannes Berg const struct cfg80211_chan_def *chandef, 15912c9b7359SJohannes Berg u32 *changed) 15922c9b7359SJohannes Berg { 15932c9b7359SJohannes Berg struct ieee80211_local *local = sdata->local; 15942c9b7359SJohannes Berg struct ieee80211_chanctx_conf *conf; 15952c9b7359SJohannes Berg struct ieee80211_chanctx *ctx; 15965bcae31dSMichal Kazior const struct cfg80211_chan_def *compat; 15972c9b7359SJohannes Berg int ret; 15982c9b7359SJohannes Berg 15992c9b7359SJohannes Berg if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef, 16002c9b7359SJohannes Berg IEEE80211_CHAN_DISABLED)) 16012c9b7359SJohannes Berg return -EINVAL; 16022c9b7359SJohannes Berg 16032c9b7359SJohannes Berg mutex_lock(&local->chanctx_mtx); 16042c9b7359SJohannes Berg if (cfg80211_chandef_identical(chandef, &sdata->vif.bss_conf.chandef)) { 16052c9b7359SJohannes Berg ret = 0; 16062c9b7359SJohannes Berg goto out; 16072c9b7359SJohannes Berg } 16082c9b7359SJohannes Berg 16092c9b7359SJohannes Berg if (chandef->width == NL80211_CHAN_WIDTH_20_NOHT || 16102c9b7359SJohannes Berg sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT) { 16112c9b7359SJohannes Berg ret = -EINVAL; 16122c9b7359SJohannes Berg goto out; 16132c9b7359SJohannes Berg } 16142c9b7359SJohannes Berg 16152c9b7359SJohannes Berg conf = rcu_dereference_protected(sdata->vif.chanctx_conf, 16162c9b7359SJohannes Berg lockdep_is_held(&local->chanctx_mtx)); 16172c9b7359SJohannes Berg if (!conf) { 16182c9b7359SJohannes Berg ret = -EINVAL; 16192c9b7359SJohannes Berg goto out; 16202c9b7359SJohannes Berg } 16212c9b7359SJohannes Berg 16222c9b7359SJohannes Berg ctx = container_of(conf, struct ieee80211_chanctx, conf); 16235bcae31dSMichal Kazior 16245bcae31dSMichal Kazior compat = cfg80211_chandef_compatible(&conf->def, chandef); 16255bcae31dSMichal Kazior if (!compat) { 16262c9b7359SJohannes Berg ret = -EINVAL; 16272c9b7359SJohannes Berg goto out; 16282c9b7359SJohannes Berg } 16292c9b7359SJohannes Berg 16305bcae31dSMichal Kazior switch (ctx->replace_state) { 16315bcae31dSMichal Kazior case IEEE80211_CHANCTX_REPLACE_NONE: 16325bcae31dSMichal Kazior if (!ieee80211_chanctx_reserved_chandef(local, ctx, compat)) { 16335bcae31dSMichal Kazior ret = -EBUSY; 16345bcae31dSMichal Kazior goto out; 16355bcae31dSMichal Kazior } 16365bcae31dSMichal Kazior break; 16375bcae31dSMichal Kazior case IEEE80211_CHANCTX_WILL_BE_REPLACED: 16385bcae31dSMichal Kazior /* TODO: Perhaps the bandwith change could be treated as a 16395bcae31dSMichal Kazior * reservation itself? */ 16405bcae31dSMichal Kazior ret = -EBUSY; 16415bcae31dSMichal Kazior goto out; 16425bcae31dSMichal Kazior case IEEE80211_CHANCTX_REPLACES_OTHER: 16435bcae31dSMichal Kazior /* channel context that is going to replace another channel 16445bcae31dSMichal Kazior * context doesn't really exist and shouldn't be assigned 16455bcae31dSMichal Kazior * anywhere yet */ 16465bcae31dSMichal Kazior WARN_ON(1); 16475bcae31dSMichal Kazior break; 16485bcae31dSMichal Kazior } 16495bcae31dSMichal Kazior 16502c9b7359SJohannes Berg sdata->vif.bss_conf.chandef = *chandef; 16512c9b7359SJohannes Berg 16522c9b7359SJohannes Berg ieee80211_recalc_chanctx_chantype(local, ctx); 16532c9b7359SJohannes Berg 16542c9b7359SJohannes Berg *changed |= BSS_CHANGED_BANDWIDTH; 16552c9b7359SJohannes Berg ret = 0; 16562c9b7359SJohannes Berg out: 16572c9b7359SJohannes Berg mutex_unlock(&local->chanctx_mtx); 16582c9b7359SJohannes Berg return ret; 16592c9b7359SJohannes Berg } 16602c9b7359SJohannes Berg 1661d01a1e65SMichal Kazior void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata) 1662d01a1e65SMichal Kazior { 166355de908aSJohannes Berg WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev)); 166455de908aSJohannes Berg 166534a3740dSJohannes Berg lockdep_assert_held(&sdata->local->mtx); 166634a3740dSJohannes Berg 1667d01a1e65SMichal Kazior mutex_lock(&sdata->local->chanctx_mtx); 1668d01a1e65SMichal Kazior __ieee80211_vif_release_channel(sdata); 1669d01a1e65SMichal Kazior mutex_unlock(&sdata->local->chanctx_mtx); 1670d01a1e65SMichal Kazior } 16713448c005SJohannes Berg 16724d76d21bSJohannes Berg void ieee80211_vif_vlan_copy_chanctx(struct ieee80211_sub_if_data *sdata) 16734d76d21bSJohannes Berg { 16744d76d21bSJohannes Berg struct ieee80211_local *local = sdata->local; 16754d76d21bSJohannes Berg struct ieee80211_sub_if_data *ap; 16764d76d21bSJohannes Berg struct ieee80211_chanctx_conf *conf; 16774d76d21bSJohannes Berg 16784d76d21bSJohannes Berg if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP_VLAN || !sdata->bss)) 16794d76d21bSJohannes Berg return; 16804d76d21bSJohannes Berg 16814d76d21bSJohannes Berg ap = container_of(sdata->bss, struct ieee80211_sub_if_data, u.ap); 16824d76d21bSJohannes Berg 16834d76d21bSJohannes Berg mutex_lock(&local->chanctx_mtx); 16844d76d21bSJohannes Berg 16854d76d21bSJohannes Berg conf = rcu_dereference_protected(ap->vif.chanctx_conf, 16864d76d21bSJohannes Berg lockdep_is_held(&local->chanctx_mtx)); 16874d76d21bSJohannes Berg rcu_assign_pointer(sdata->vif.chanctx_conf, conf); 16884d76d21bSJohannes Berg mutex_unlock(&local->chanctx_mtx); 16894d76d21bSJohannes Berg } 16904d76d21bSJohannes Berg 16913448c005SJohannes Berg void ieee80211_iter_chan_contexts_atomic( 16923448c005SJohannes Berg struct ieee80211_hw *hw, 16933448c005SJohannes Berg void (*iter)(struct ieee80211_hw *hw, 16943448c005SJohannes Berg struct ieee80211_chanctx_conf *chanctx_conf, 16953448c005SJohannes Berg void *data), 16963448c005SJohannes Berg void *iter_data) 16973448c005SJohannes Berg { 16983448c005SJohannes Berg struct ieee80211_local *local = hw_to_local(hw); 16993448c005SJohannes Berg struct ieee80211_chanctx *ctx; 17003448c005SJohannes Berg 17013448c005SJohannes Berg rcu_read_lock(); 17023448c005SJohannes Berg list_for_each_entry_rcu(ctx, &local->chanctx_list, list) 17038a61af65SJohannes Berg if (ctx->driver_present) 17043448c005SJohannes Berg iter(hw, &ctx->conf, iter_data); 17053448c005SJohannes Berg rcu_read_unlock(); 17063448c005SJohannes Berg } 17073448c005SJohannes Berg EXPORT_SYMBOL_GPL(ieee80211_iter_chan_contexts_atomic); 1708