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; 544e89a96f5SMichal Kazior 5454bf88530SJohannes Berg if (!compat) 5464bf88530SJohannes Berg compat = &sdata->vif.bss_conf.chandef; 5474bf88530SJohannes Berg 5484bf88530SJohannes Berg compat = cfg80211_chandef_compatible( 5494bf88530SJohannes Berg &sdata->vif.bss_conf.chandef, compat); 5504bf88530SJohannes Berg if (!compat) 5514bf88530SJohannes Berg break; 552e89a96f5SMichal Kazior } 553e89a96f5SMichal Kazior rcu_read_unlock(); 554e89a96f5SMichal Kazior 5554bf88530SJohannes Berg if (WARN_ON_ONCE(!compat)) 5564bf88530SJohannes Berg return; 557e89a96f5SMichal Kazior 55818942d3bSJohannes Berg ieee80211_change_chanctx(local, ctx, compat); 559e89a96f5SMichal Kazior } 560e89a96f5SMichal Kazior 561367bbd10SJohannes Berg static void ieee80211_recalc_radar_chanctx(struct ieee80211_local *local, 562367bbd10SJohannes Berg struct ieee80211_chanctx *chanctx) 563367bbd10SJohannes Berg { 564367bbd10SJohannes Berg bool radar_enabled; 565367bbd10SJohannes Berg 566367bbd10SJohannes Berg lockdep_assert_held(&local->chanctx_mtx); 56734a3740dSJohannes Berg /* for setting local->radar_detect_enabled */ 56834a3740dSJohannes Berg lockdep_assert_held(&local->mtx); 569367bbd10SJohannes Berg 570367bbd10SJohannes Berg radar_enabled = ieee80211_is_radar_required(local); 571367bbd10SJohannes Berg 572367bbd10SJohannes Berg if (radar_enabled == chanctx->conf.radar_enabled) 573367bbd10SJohannes Berg return; 574367bbd10SJohannes Berg 575367bbd10SJohannes Berg chanctx->conf.radar_enabled = radar_enabled; 576367bbd10SJohannes Berg local->radar_detect_enabled = chanctx->conf.radar_enabled; 577367bbd10SJohannes Berg 578367bbd10SJohannes Berg if (!local->use_chanctx) { 579367bbd10SJohannes Berg local->hw.conf.radar_enabled = chanctx->conf.radar_enabled; 580367bbd10SJohannes Berg ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); 581367bbd10SJohannes Berg } 582367bbd10SJohannes Berg 583367bbd10SJohannes Berg drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RADAR); 584367bbd10SJohannes Berg } 585367bbd10SJohannes Berg 58677eeba97SLuciano Coelho static int ieee80211_assign_vif_chanctx(struct ieee80211_sub_if_data *sdata, 58777eeba97SLuciano Coelho struct ieee80211_chanctx *new_ctx) 588d01a1e65SMichal Kazior { 58935f2fce9SMichal Kazior struct ieee80211_local *local = sdata->local; 59077eeba97SLuciano Coelho struct ieee80211_chanctx_conf *conf; 59177eeba97SLuciano Coelho struct ieee80211_chanctx *curr_ctx = NULL; 59277eeba97SLuciano Coelho int ret = 0; 593d01a1e65SMichal Kazior 59477eeba97SLuciano Coelho conf = rcu_dereference_protected(sdata->vif.chanctx_conf, 59577eeba97SLuciano Coelho lockdep_is_held(&local->chanctx_mtx)); 596d01a1e65SMichal Kazior 59777eeba97SLuciano Coelho if (conf) { 59877eeba97SLuciano Coelho curr_ctx = container_of(conf, struct ieee80211_chanctx, conf); 59935f2fce9SMichal Kazior 60077eeba97SLuciano Coelho drv_unassign_vif_chanctx(local, sdata, curr_ctx); 60177eeba97SLuciano Coelho conf = NULL; 602484298adSMichal Kazior list_del(&sdata->assigned_chanctx_list); 60377eeba97SLuciano Coelho } 60477eeba97SLuciano Coelho 60577eeba97SLuciano Coelho if (new_ctx) { 60677eeba97SLuciano Coelho ret = drv_assign_vif_chanctx(local, sdata, new_ctx); 60777eeba97SLuciano Coelho if (ret) 60877eeba97SLuciano Coelho goto out; 60977eeba97SLuciano Coelho 61077eeba97SLuciano Coelho conf = &new_ctx->conf; 611484298adSMichal Kazior list_add(&sdata->assigned_chanctx_list, 612484298adSMichal Kazior &new_ctx->assigned_vifs); 61377eeba97SLuciano Coelho } 61477eeba97SLuciano Coelho 61577eeba97SLuciano Coelho out: 61677eeba97SLuciano Coelho rcu_assign_pointer(sdata->vif.chanctx_conf, conf); 61777eeba97SLuciano Coelho 61877eeba97SLuciano Coelho sdata->vif.bss_conf.idle = !conf; 61977eeba97SLuciano Coelho 620c0166da9SMichal Kazior if (curr_ctx && ieee80211_chanctx_num_assigned(local, curr_ctx) > 0) { 62177eeba97SLuciano Coelho ieee80211_recalc_chanctx_chantype(local, curr_ctx); 62277eeba97SLuciano Coelho ieee80211_recalc_smps_chanctx(local, curr_ctx); 62377eeba97SLuciano Coelho ieee80211_recalc_radar_chanctx(local, curr_ctx); 62477eeba97SLuciano Coelho ieee80211_recalc_chanctx_min_def(local, curr_ctx); 62577eeba97SLuciano Coelho } 62677eeba97SLuciano Coelho 627c0166da9SMichal Kazior if (new_ctx && ieee80211_chanctx_num_assigned(local, new_ctx) > 0) { 62877eeba97SLuciano Coelho ieee80211_recalc_txpower(sdata); 62977eeba97SLuciano Coelho ieee80211_recalc_chanctx_min_def(local, new_ctx); 63077eeba97SLuciano Coelho } 6315bbe754dSJohannes Berg 6325bbe754dSJohannes Berg if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE && 6335bbe754dSJohannes Berg sdata->vif.type != NL80211_IFTYPE_MONITOR) 63477eeba97SLuciano Coelho ieee80211_bss_info_change_notify(sdata, 63577eeba97SLuciano Coelho BSS_CHANGED_IDLE); 636fd0f979aSJohannes Berg 63777eeba97SLuciano Coelho return ret; 638d01a1e65SMichal Kazior } 639d01a1e65SMichal Kazior 640d01a1e65SMichal Kazior static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata) 641d01a1e65SMichal Kazior { 642d01a1e65SMichal Kazior struct ieee80211_local *local = sdata->local; 643d01a1e65SMichal Kazior struct ieee80211_chanctx_conf *conf; 644d01a1e65SMichal Kazior struct ieee80211_chanctx *ctx; 6455bcae31dSMichal Kazior bool use_reserved_switch = false; 646d01a1e65SMichal Kazior 647d01a1e65SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 648d01a1e65SMichal Kazior 649d01a1e65SMichal Kazior conf = rcu_dereference_protected(sdata->vif.chanctx_conf, 650d01a1e65SMichal Kazior lockdep_is_held(&local->chanctx_mtx)); 651d01a1e65SMichal Kazior if (!conf) 652d01a1e65SMichal Kazior return; 653d01a1e65SMichal Kazior 654d01a1e65SMichal Kazior ctx = container_of(conf, struct ieee80211_chanctx, conf); 655d01a1e65SMichal Kazior 6565bcae31dSMichal Kazior if (sdata->reserved_chanctx) { 6575bcae31dSMichal Kazior if (sdata->reserved_chanctx->replace_state == 6585bcae31dSMichal Kazior IEEE80211_CHANCTX_REPLACES_OTHER && 6595bcae31dSMichal Kazior ieee80211_chanctx_num_reserved(local, 6605bcae31dSMichal Kazior sdata->reserved_chanctx) > 1) 6615bcae31dSMichal Kazior use_reserved_switch = true; 6625bcae31dSMichal Kazior 66311335a55SLuciano Coelho ieee80211_vif_unreserve_chanctx(sdata); 6645bcae31dSMichal Kazior } 66511335a55SLuciano Coelho 66677eeba97SLuciano Coelho ieee80211_assign_vif_chanctx(sdata, NULL); 667c0166da9SMichal Kazior if (ieee80211_chanctx_refcount(local, ctx) == 0) 668d01a1e65SMichal Kazior ieee80211_free_chanctx(local, ctx); 6695bcae31dSMichal Kazior 6705bcae31dSMichal Kazior /* Unreserving may ready an in-place reservation. */ 6715bcae31dSMichal Kazior if (use_reserved_switch) 6725bcae31dSMichal Kazior ieee80211_vif_use_reserved_switch(local); 673d01a1e65SMichal Kazior } 674d01a1e65SMichal Kazior 67504ecd257SJohannes Berg void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local, 67604ecd257SJohannes Berg struct ieee80211_chanctx *chanctx) 67704ecd257SJohannes Berg { 67804ecd257SJohannes Berg struct ieee80211_sub_if_data *sdata; 67904ecd257SJohannes Berg u8 rx_chains_static, rx_chains_dynamic; 68004ecd257SJohannes Berg 68104ecd257SJohannes Berg lockdep_assert_held(&local->chanctx_mtx); 68204ecd257SJohannes Berg 68304ecd257SJohannes Berg rx_chains_static = 1; 68404ecd257SJohannes Berg rx_chains_dynamic = 1; 68504ecd257SJohannes Berg 68604ecd257SJohannes Berg rcu_read_lock(); 68704ecd257SJohannes Berg list_for_each_entry_rcu(sdata, &local->interfaces, list) { 68804ecd257SJohannes Berg u8 needed_static, needed_dynamic; 68904ecd257SJohannes Berg 69004ecd257SJohannes Berg if (!ieee80211_sdata_running(sdata)) 69104ecd257SJohannes Berg continue; 69204ecd257SJohannes Berg 69304ecd257SJohannes Berg if (rcu_access_pointer(sdata->vif.chanctx_conf) != 69404ecd257SJohannes Berg &chanctx->conf) 69504ecd257SJohannes Berg continue; 69604ecd257SJohannes Berg 69704ecd257SJohannes Berg switch (sdata->vif.type) { 69804ecd257SJohannes Berg case NL80211_IFTYPE_P2P_DEVICE: 69904ecd257SJohannes Berg continue; 70004ecd257SJohannes Berg case NL80211_IFTYPE_STATION: 70104ecd257SJohannes Berg if (!sdata->u.mgd.associated) 70204ecd257SJohannes Berg continue; 70304ecd257SJohannes Berg break; 70404ecd257SJohannes Berg case NL80211_IFTYPE_AP_VLAN: 70504ecd257SJohannes Berg continue; 70604ecd257SJohannes Berg case NL80211_IFTYPE_AP: 70704ecd257SJohannes Berg case NL80211_IFTYPE_ADHOC: 70804ecd257SJohannes Berg case NL80211_IFTYPE_WDS: 70904ecd257SJohannes Berg case NL80211_IFTYPE_MESH_POINT: 71004ecd257SJohannes Berg break; 71104ecd257SJohannes Berg default: 71204ecd257SJohannes Berg WARN_ON_ONCE(1); 71304ecd257SJohannes Berg } 71404ecd257SJohannes Berg 71504ecd257SJohannes Berg switch (sdata->smps_mode) { 71604ecd257SJohannes Berg default: 71704ecd257SJohannes Berg WARN_ONCE(1, "Invalid SMPS mode %d\n", 71804ecd257SJohannes Berg sdata->smps_mode); 71904ecd257SJohannes Berg /* fall through */ 72004ecd257SJohannes Berg case IEEE80211_SMPS_OFF: 72104ecd257SJohannes Berg needed_static = sdata->needed_rx_chains; 72204ecd257SJohannes Berg needed_dynamic = sdata->needed_rx_chains; 72304ecd257SJohannes Berg break; 72404ecd257SJohannes Berg case IEEE80211_SMPS_DYNAMIC: 72504ecd257SJohannes Berg needed_static = 1; 72604ecd257SJohannes Berg needed_dynamic = sdata->needed_rx_chains; 72704ecd257SJohannes Berg break; 72804ecd257SJohannes Berg case IEEE80211_SMPS_STATIC: 72904ecd257SJohannes Berg needed_static = 1; 73004ecd257SJohannes Berg needed_dynamic = 1; 73104ecd257SJohannes Berg break; 73204ecd257SJohannes Berg } 73304ecd257SJohannes Berg 73404ecd257SJohannes Berg rx_chains_static = max(rx_chains_static, needed_static); 73504ecd257SJohannes Berg rx_chains_dynamic = max(rx_chains_dynamic, needed_dynamic); 73604ecd257SJohannes Berg } 7377b8a9cddSIdo Yariv 7387b8a9cddSIdo Yariv /* Disable SMPS for the monitor interface */ 7397b8a9cddSIdo Yariv sdata = rcu_dereference(local->monitor_sdata); 7407b8a9cddSIdo Yariv if (sdata && 7417b8a9cddSIdo Yariv rcu_access_pointer(sdata->vif.chanctx_conf) == &chanctx->conf) 7427b8a9cddSIdo Yariv rx_chains_dynamic = rx_chains_static = local->rx_chains; 7437b8a9cddSIdo Yariv 74404ecd257SJohannes Berg rcu_read_unlock(); 74504ecd257SJohannes Berg 74604ecd257SJohannes Berg if (!local->use_chanctx) { 74704ecd257SJohannes Berg if (rx_chains_static > 1) 74804ecd257SJohannes Berg local->smps_mode = IEEE80211_SMPS_OFF; 74904ecd257SJohannes Berg else if (rx_chains_dynamic > 1) 75004ecd257SJohannes Berg local->smps_mode = IEEE80211_SMPS_DYNAMIC; 75104ecd257SJohannes Berg else 75204ecd257SJohannes Berg local->smps_mode = IEEE80211_SMPS_STATIC; 75304ecd257SJohannes Berg ieee80211_hw_config(local, 0); 75404ecd257SJohannes Berg } 75504ecd257SJohannes Berg 75604ecd257SJohannes Berg if (rx_chains_static == chanctx->conf.rx_chains_static && 75704ecd257SJohannes Berg rx_chains_dynamic == chanctx->conf.rx_chains_dynamic) 75804ecd257SJohannes Berg return; 75904ecd257SJohannes Berg 76004ecd257SJohannes Berg chanctx->conf.rx_chains_static = rx_chains_static; 76104ecd257SJohannes Berg chanctx->conf.rx_chains_dynamic = rx_chains_dynamic; 76204ecd257SJohannes Berg drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RX_CHAINS); 76304ecd257SJohannes Berg } 76404ecd257SJohannes Berg 765d01a1e65SMichal Kazior int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata, 7664bf88530SJohannes Berg const struct cfg80211_chan_def *chandef, 767d01a1e65SMichal Kazior enum ieee80211_chanctx_mode mode) 768d01a1e65SMichal Kazior { 769d01a1e65SMichal Kazior struct ieee80211_local *local = sdata->local; 770d01a1e65SMichal Kazior struct ieee80211_chanctx *ctx; 77173de86a3SLuciano Coelho u8 radar_detect_width = 0; 772d01a1e65SMichal Kazior int ret; 773d01a1e65SMichal Kazior 77434a3740dSJohannes Berg lockdep_assert_held(&local->mtx); 77534a3740dSJohannes Berg 77655de908aSJohannes Berg WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev)); 77755de908aSJohannes Berg 778d01a1e65SMichal Kazior mutex_lock(&local->chanctx_mtx); 77973de86a3SLuciano Coelho 78073de86a3SLuciano Coelho ret = cfg80211_chandef_dfs_required(local->hw.wiphy, 78173de86a3SLuciano Coelho chandef, 78273de86a3SLuciano Coelho sdata->wdev.iftype); 78373de86a3SLuciano Coelho if (ret < 0) 78473de86a3SLuciano Coelho goto out; 78573de86a3SLuciano Coelho if (ret > 0) 78673de86a3SLuciano Coelho radar_detect_width = BIT(chandef->width); 78773de86a3SLuciano Coelho 78873de86a3SLuciano Coelho sdata->radar_required = ret; 78973de86a3SLuciano Coelho 79073de86a3SLuciano Coelho ret = ieee80211_check_combinations(sdata, chandef, mode, 79173de86a3SLuciano Coelho radar_detect_width); 79273de86a3SLuciano Coelho if (ret < 0) 79373de86a3SLuciano Coelho goto out; 79473de86a3SLuciano Coelho 795d01a1e65SMichal Kazior __ieee80211_vif_release_channel(sdata); 796d01a1e65SMichal Kazior 7974bf88530SJohannes Berg ctx = ieee80211_find_chanctx(local, chandef, mode); 798d01a1e65SMichal Kazior if (!ctx) 7994bf88530SJohannes Berg ctx = ieee80211_new_chanctx(local, chandef, mode); 800d01a1e65SMichal Kazior if (IS_ERR(ctx)) { 801d01a1e65SMichal Kazior ret = PTR_ERR(ctx); 802d01a1e65SMichal Kazior goto out; 803d01a1e65SMichal Kazior } 804d01a1e65SMichal Kazior 8054bf88530SJohannes Berg sdata->vif.bss_conf.chandef = *chandef; 80655de908aSJohannes Berg 807d01a1e65SMichal Kazior ret = ieee80211_assign_vif_chanctx(sdata, ctx); 808d01a1e65SMichal Kazior if (ret) { 809d01a1e65SMichal Kazior /* if assign fails refcount stays the same */ 810c0166da9SMichal Kazior if (ieee80211_chanctx_refcount(local, ctx) == 0) 811d01a1e65SMichal Kazior ieee80211_free_chanctx(local, ctx); 812d01a1e65SMichal Kazior goto out; 813d01a1e65SMichal Kazior } 814d01a1e65SMichal Kazior 81504ecd257SJohannes Berg ieee80211_recalc_smps_chanctx(local, ctx); 816164eb02dSSimon Wunderlich ieee80211_recalc_radar_chanctx(local, ctx); 817d01a1e65SMichal Kazior out: 818d01a1e65SMichal Kazior mutex_unlock(&local->chanctx_mtx); 819d01a1e65SMichal Kazior return ret; 820d01a1e65SMichal Kazior } 821d01a1e65SMichal Kazior 82211335a55SLuciano Coelho static void 82311335a55SLuciano Coelho __ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata, 82411335a55SLuciano Coelho bool clear) 82511335a55SLuciano Coelho { 82633926eb7SJohannes Berg struct ieee80211_local *local __maybe_unused = sdata->local; 82711335a55SLuciano Coelho struct ieee80211_sub_if_data *vlan; 82811335a55SLuciano Coelho struct ieee80211_chanctx_conf *conf; 82911335a55SLuciano Coelho 83011335a55SLuciano Coelho if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP)) 83111335a55SLuciano Coelho return; 83211335a55SLuciano Coelho 83311335a55SLuciano Coelho lockdep_assert_held(&local->mtx); 83411335a55SLuciano Coelho 83511335a55SLuciano Coelho /* Check that conf exists, even when clearing this function 83611335a55SLuciano Coelho * must be called with the AP's channel context still there 83711335a55SLuciano Coelho * as it would otherwise cause VLANs to have an invalid 83811335a55SLuciano Coelho * channel context pointer for a while, possibly pointing 83911335a55SLuciano Coelho * to a channel context that has already been freed. 84011335a55SLuciano Coelho */ 84111335a55SLuciano Coelho conf = rcu_dereference_protected(sdata->vif.chanctx_conf, 84211335a55SLuciano Coelho lockdep_is_held(&local->chanctx_mtx)); 84311335a55SLuciano Coelho WARN_ON(!conf); 84411335a55SLuciano Coelho 84511335a55SLuciano Coelho if (clear) 84611335a55SLuciano Coelho conf = NULL; 84711335a55SLuciano Coelho 84811335a55SLuciano Coelho list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) 84911335a55SLuciano Coelho rcu_assign_pointer(vlan->vif.chanctx_conf, conf); 85011335a55SLuciano Coelho } 85111335a55SLuciano Coelho 85211335a55SLuciano Coelho void ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata, 85311335a55SLuciano Coelho bool clear) 85411335a55SLuciano Coelho { 85511335a55SLuciano Coelho struct ieee80211_local *local = sdata->local; 85611335a55SLuciano Coelho 85711335a55SLuciano Coelho mutex_lock(&local->chanctx_mtx); 85811335a55SLuciano Coelho 85911335a55SLuciano Coelho __ieee80211_vif_copy_chanctx_to_vlans(sdata, clear); 86011335a55SLuciano Coelho 86111335a55SLuciano Coelho mutex_unlock(&local->chanctx_mtx); 86211335a55SLuciano Coelho } 86311335a55SLuciano Coelho 86411335a55SLuciano Coelho int ieee80211_vif_unreserve_chanctx(struct ieee80211_sub_if_data *sdata) 86511335a55SLuciano Coelho { 866e3afb920SMichal Kazior struct ieee80211_chanctx *ctx = sdata->reserved_chanctx; 867e3afb920SMichal Kazior 86811335a55SLuciano Coelho lockdep_assert_held(&sdata->local->chanctx_mtx); 86911335a55SLuciano Coelho 870e3afb920SMichal Kazior if (WARN_ON(!ctx)) 87111335a55SLuciano Coelho return -EINVAL; 87211335a55SLuciano Coelho 873e3afb920SMichal Kazior list_del(&sdata->reserved_chanctx_list); 87411335a55SLuciano Coelho sdata->reserved_chanctx = NULL; 87511335a55SLuciano Coelho 8765bcae31dSMichal Kazior if (ieee80211_chanctx_refcount(sdata->local, ctx) == 0) { 8775bcae31dSMichal Kazior if (ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER) { 8785bcae31dSMichal Kazior if (WARN_ON(!ctx->replace_ctx)) 8795bcae31dSMichal Kazior return -EINVAL; 8805bcae31dSMichal Kazior 8815bcae31dSMichal Kazior WARN_ON(ctx->replace_ctx->replace_state != 8825bcae31dSMichal Kazior IEEE80211_CHANCTX_WILL_BE_REPLACED); 8835bcae31dSMichal Kazior WARN_ON(ctx->replace_ctx->replace_ctx != ctx); 8845bcae31dSMichal Kazior 8855bcae31dSMichal Kazior ctx->replace_ctx->replace_ctx = NULL; 8865bcae31dSMichal Kazior ctx->replace_ctx->replace_state = 8875bcae31dSMichal Kazior IEEE80211_CHANCTX_REPLACE_NONE; 8885bcae31dSMichal Kazior 8895bcae31dSMichal Kazior list_del_rcu(&ctx->list); 8905bcae31dSMichal Kazior kfree_rcu(ctx, rcu_head); 8915bcae31dSMichal Kazior } else { 892e3afb920SMichal Kazior ieee80211_free_chanctx(sdata->local, ctx); 8935bcae31dSMichal Kazior } 8945bcae31dSMichal Kazior } 895e3afb920SMichal Kazior 89611335a55SLuciano Coelho return 0; 89711335a55SLuciano Coelho } 89811335a55SLuciano Coelho 89911335a55SLuciano Coelho int ieee80211_vif_reserve_chanctx(struct ieee80211_sub_if_data *sdata, 90011335a55SLuciano Coelho const struct cfg80211_chan_def *chandef, 90109332481SMichal Kazior enum ieee80211_chanctx_mode mode, 90209332481SMichal Kazior bool radar_required) 90311335a55SLuciano Coelho { 90411335a55SLuciano Coelho struct ieee80211_local *local = sdata->local; 9055bcae31dSMichal Kazior struct ieee80211_chanctx *new_ctx, *curr_ctx, *ctx; 90611335a55SLuciano Coelho 9075bcae31dSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 90811335a55SLuciano Coelho 9095bcae31dSMichal Kazior curr_ctx = ieee80211_vif_get_chanctx(sdata); 9105bcae31dSMichal Kazior if (curr_ctx && local->use_chanctx && !local->ops->switch_vif_chanctx) 9115bcae31dSMichal Kazior return -ENOTSUPP; 91211335a55SLuciano Coelho 91313f348a8SMichal Kazior new_ctx = ieee80211_find_reservation_chanctx(local, chandef, mode); 91411335a55SLuciano Coelho if (!new_ctx) { 9155bcae31dSMichal Kazior if (ieee80211_can_create_new_chanctx(local)) { 91611335a55SLuciano Coelho new_ctx = ieee80211_new_chanctx(local, chandef, mode); 9175bcae31dSMichal Kazior if (IS_ERR(new_ctx)) 9185bcae31dSMichal Kazior return PTR_ERR(new_ctx); 919c2b90ad8SMichal Kazior } else { 9205bcae31dSMichal Kazior if (!curr_ctx || 9215bcae31dSMichal Kazior (curr_ctx->replace_state == 9225bcae31dSMichal Kazior IEEE80211_CHANCTX_WILL_BE_REPLACED) || 9235bcae31dSMichal Kazior !list_empty(&curr_ctx->reserved_vifs)) { 9245bcae31dSMichal Kazior /* 9255bcae31dSMichal Kazior * Another vif already requested this context 9265bcae31dSMichal Kazior * for a reservation. Find another one hoping 9275bcae31dSMichal Kazior * all vifs assigned to it will also switch 9285bcae31dSMichal Kazior * soon enough. 9295bcae31dSMichal Kazior * 9305bcae31dSMichal Kazior * TODO: This needs a little more work as some 9315bcae31dSMichal Kazior * cases (more than 2 chanctx capable devices) 9325bcae31dSMichal Kazior * may fail which could otherwise succeed 9335bcae31dSMichal Kazior * provided some channel context juggling was 9345bcae31dSMichal Kazior * performed. 9355bcae31dSMichal Kazior * 9365bcae31dSMichal Kazior * Consider ctx1..3, vif1..6, each ctx has 2 9375bcae31dSMichal Kazior * vifs. vif1 and vif2 from ctx1 request new 9385bcae31dSMichal Kazior * different chandefs starting 2 in-place 9395bcae31dSMichal Kazior * reserations with ctx4 and ctx5 replacing 9405bcae31dSMichal Kazior * ctx1 and ctx2 respectively. Next vif5 and 9415bcae31dSMichal Kazior * vif6 from ctx3 reserve ctx4. If vif3 and 9425bcae31dSMichal Kazior * vif4 remain on ctx2 as they are then this 9435bcae31dSMichal Kazior * fails unless `replace_ctx` from ctx5 is 9445bcae31dSMichal Kazior * replaced with ctx3. 9455bcae31dSMichal Kazior */ 9465bcae31dSMichal Kazior list_for_each_entry(ctx, &local->chanctx_list, 9475bcae31dSMichal Kazior list) { 9485bcae31dSMichal Kazior if (ctx->replace_state != 9495bcae31dSMichal Kazior IEEE80211_CHANCTX_REPLACE_NONE) 9505bcae31dSMichal Kazior continue; 9515bcae31dSMichal Kazior 9525bcae31dSMichal Kazior if (!list_empty(&ctx->reserved_vifs)) 9535bcae31dSMichal Kazior continue; 9545bcae31dSMichal Kazior 9555bcae31dSMichal Kazior curr_ctx = ctx; 9565bcae31dSMichal Kazior break; 9575bcae31dSMichal Kazior } 9585bcae31dSMichal Kazior } 9595bcae31dSMichal Kazior 9605bcae31dSMichal Kazior /* 9615bcae31dSMichal Kazior * If that's true then all available contexts already 9625bcae31dSMichal Kazior * have reservations and cannot be used. 9635bcae31dSMichal Kazior */ 9645bcae31dSMichal Kazior if (!curr_ctx || 9655bcae31dSMichal Kazior (curr_ctx->replace_state == 9665bcae31dSMichal Kazior IEEE80211_CHANCTX_WILL_BE_REPLACED) || 9675bcae31dSMichal Kazior !list_empty(&curr_ctx->reserved_vifs)) 9685bcae31dSMichal Kazior return -EBUSY; 9695bcae31dSMichal Kazior 9705bcae31dSMichal Kazior new_ctx = ieee80211_alloc_chanctx(local, chandef, mode); 9715bcae31dSMichal Kazior if (!new_ctx) 9725bcae31dSMichal Kazior return -ENOMEM; 9735bcae31dSMichal Kazior 9745bcae31dSMichal Kazior new_ctx->replace_ctx = curr_ctx; 9755bcae31dSMichal Kazior new_ctx->replace_state = 9765bcae31dSMichal Kazior IEEE80211_CHANCTX_REPLACES_OTHER; 9775bcae31dSMichal Kazior 9785bcae31dSMichal Kazior curr_ctx->replace_ctx = new_ctx; 9795bcae31dSMichal Kazior curr_ctx->replace_state = 9805bcae31dSMichal Kazior IEEE80211_CHANCTX_WILL_BE_REPLACED; 9815bcae31dSMichal Kazior 9825bcae31dSMichal Kazior list_add_rcu(&new_ctx->list, &local->chanctx_list); 98311335a55SLuciano Coelho } 9845d52ee81SLuciano Coelho } 98511335a55SLuciano Coelho 986e3afb920SMichal Kazior list_add(&sdata->reserved_chanctx_list, &new_ctx->reserved_vifs); 98711335a55SLuciano Coelho sdata->reserved_chanctx = new_ctx; 98811335a55SLuciano Coelho sdata->reserved_chandef = *chandef; 98909332481SMichal Kazior sdata->reserved_radar_required = radar_required; 9905bcae31dSMichal Kazior sdata->reserved_ready = false; 9915bcae31dSMichal Kazior 9925bcae31dSMichal Kazior return 0; 99311335a55SLuciano Coelho } 99411335a55SLuciano Coelho 99503078de4SMichal Kazior static void 99603078de4SMichal Kazior ieee80211_vif_chanctx_reservation_complete(struct ieee80211_sub_if_data *sdata) 99703078de4SMichal Kazior { 99803078de4SMichal Kazior switch (sdata->vif.type) { 99903078de4SMichal Kazior case NL80211_IFTYPE_ADHOC: 100003078de4SMichal Kazior case NL80211_IFTYPE_AP: 100103078de4SMichal Kazior case NL80211_IFTYPE_MESH_POINT: 100203078de4SMichal Kazior ieee80211_queue_work(&sdata->local->hw, 100303078de4SMichal Kazior &sdata->csa_finalize_work); 100403078de4SMichal Kazior break; 100503078de4SMichal Kazior case NL80211_IFTYPE_STATION: 10064c3ebc56SMichal Kazior ieee80211_queue_work(&sdata->local->hw, 10074c3ebc56SMichal Kazior &sdata->u.mgd.chswitch_work); 10084c3ebc56SMichal Kazior break; 10094c3ebc56SMichal Kazior case NL80211_IFTYPE_UNSPECIFIED: 101003078de4SMichal Kazior case NL80211_IFTYPE_AP_VLAN: 101103078de4SMichal Kazior case NL80211_IFTYPE_WDS: 101203078de4SMichal Kazior case NL80211_IFTYPE_MONITOR: 101303078de4SMichal Kazior case NL80211_IFTYPE_P2P_CLIENT: 101403078de4SMichal Kazior case NL80211_IFTYPE_P2P_GO: 101503078de4SMichal Kazior case NL80211_IFTYPE_P2P_DEVICE: 101603078de4SMichal Kazior case NUM_NL80211_IFTYPES: 101703078de4SMichal Kazior WARN_ON(1); 101803078de4SMichal Kazior break; 101903078de4SMichal Kazior } 102003078de4SMichal Kazior } 102103078de4SMichal Kazior 10225bcae31dSMichal Kazior static int 10235bcae31dSMichal Kazior ieee80211_vif_use_reserved_reassign(struct ieee80211_sub_if_data *sdata) 102411335a55SLuciano Coelho { 102511335a55SLuciano Coelho struct ieee80211_local *local = sdata->local; 10265bcae31dSMichal Kazior struct ieee80211_vif_chanctx_switch vif_chsw[1] = {}; 10275bcae31dSMichal Kazior struct ieee80211_chanctx *old_ctx, *new_ctx; 10285bcae31dSMichal Kazior const struct cfg80211_chan_def *chandef; 10295bcae31dSMichal Kazior u32 changed = 0; 10305bcae31dSMichal Kazior int err; 103111335a55SLuciano Coelho 103211335a55SLuciano Coelho lockdep_assert_held(&local->mtx); 10335bcae31dSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 103411335a55SLuciano Coelho 10355bcae31dSMichal Kazior new_ctx = sdata->reserved_chanctx; 10365bcae31dSMichal Kazior old_ctx = ieee80211_vif_get_chanctx(sdata); 103711335a55SLuciano Coelho 10385bcae31dSMichal Kazior if (WARN_ON(!sdata->reserved_ready)) 10395bcae31dSMichal Kazior return -EBUSY; 104011335a55SLuciano Coelho 10415bcae31dSMichal Kazior if (WARN_ON(!new_ctx)) 10425bcae31dSMichal Kazior return -EINVAL; 104311335a55SLuciano Coelho 10445bcae31dSMichal Kazior if (WARN_ON(!old_ctx)) 10455bcae31dSMichal Kazior return -EINVAL; 104611335a55SLuciano Coelho 10475bcae31dSMichal Kazior if (WARN_ON(new_ctx->replace_state == 10485bcae31dSMichal Kazior IEEE80211_CHANCTX_REPLACES_OTHER)) 10495bcae31dSMichal Kazior return -EINVAL; 105011335a55SLuciano Coelho 10515bcae31dSMichal Kazior chandef = ieee80211_chanctx_non_reserved_chandef(local, new_ctx, 10525bcae31dSMichal Kazior &sdata->reserved_chandef); 10535bcae31dSMichal Kazior if (WARN_ON(!chandef)) 10545bcae31dSMichal Kazior return -EINVAL; 105511335a55SLuciano Coelho 10565bcae31dSMichal Kazior vif_chsw[0].vif = &sdata->vif; 10575bcae31dSMichal Kazior vif_chsw[0].old_ctx = &old_ctx->conf; 10585bcae31dSMichal Kazior vif_chsw[0].new_ctx = &new_ctx->conf; 10595bcae31dSMichal Kazior 1060e3afb920SMichal Kazior list_del(&sdata->reserved_chanctx_list); 10615bcae31dSMichal Kazior sdata->reserved_chanctx = NULL; 106211335a55SLuciano Coelho 10635bcae31dSMichal Kazior err = drv_switch_vif_chanctx(local, vif_chsw, 1, 10645bcae31dSMichal Kazior CHANCTX_SWMODE_REASSIGN_VIF); 10655bcae31dSMichal Kazior if (err) { 10665bcae31dSMichal Kazior if (ieee80211_chanctx_refcount(local, new_ctx) == 0) 10675bcae31dSMichal Kazior ieee80211_free_chanctx(local, new_ctx); 10685bcae31dSMichal Kazior 106903078de4SMichal Kazior goto out; 107011335a55SLuciano Coelho } 107111335a55SLuciano Coelho 10725bcae31dSMichal Kazior list_move(&sdata->assigned_chanctx_list, &new_ctx->assigned_vifs); 10735bcae31dSMichal Kazior rcu_assign_pointer(sdata->vif.chanctx_conf, &new_ctx->conf); 10745bcae31dSMichal Kazior 107511335a55SLuciano Coelho if (sdata->vif.type == NL80211_IFTYPE_AP) 107611335a55SLuciano Coelho __ieee80211_vif_copy_chanctx_to_vlans(sdata, false); 10775bcae31dSMichal Kazior 10785bcae31dSMichal Kazior if (ieee80211_chanctx_refcount(local, old_ctx) == 0) 10795bcae31dSMichal Kazior ieee80211_free_chanctx(local, old_ctx); 10805bcae31dSMichal Kazior 10815bcae31dSMichal Kazior if (sdata->vif.bss_conf.chandef.width != sdata->reserved_chandef.width) 10825bcae31dSMichal Kazior changed = BSS_CHANGED_BANDWIDTH; 10835bcae31dSMichal Kazior 10845bcae31dSMichal Kazior sdata->vif.bss_conf.chandef = sdata->reserved_chandef; 10855bcae31dSMichal Kazior 10865bcae31dSMichal Kazior if (changed) 10875bcae31dSMichal Kazior ieee80211_bss_info_change_notify(sdata, changed); 10885bcae31dSMichal Kazior 108903078de4SMichal Kazior out: 109003078de4SMichal Kazior ieee80211_vif_chanctx_reservation_complete(sdata); 10915bcae31dSMichal Kazior return err; 10925d52ee81SLuciano Coelho } 109311335a55SLuciano Coelho 10945bcae31dSMichal Kazior static int 10955bcae31dSMichal Kazior ieee80211_vif_use_reserved_assign(struct ieee80211_sub_if_data *sdata) 10965bcae31dSMichal Kazior { 10975bcae31dSMichal Kazior struct ieee80211_local *local = sdata->local; 10985bcae31dSMichal Kazior struct ieee80211_chanctx *old_ctx, *new_ctx; 10995bcae31dSMichal Kazior const struct cfg80211_chan_def *chandef; 11005bcae31dSMichal Kazior int err; 11015bcae31dSMichal Kazior 11025bcae31dSMichal Kazior old_ctx = ieee80211_vif_get_chanctx(sdata); 11035bcae31dSMichal Kazior new_ctx = sdata->reserved_chanctx; 11045bcae31dSMichal Kazior 11055bcae31dSMichal Kazior if (WARN_ON(!sdata->reserved_ready)) 11065bcae31dSMichal Kazior return -EINVAL; 11075bcae31dSMichal Kazior 11085bcae31dSMichal Kazior if (WARN_ON(old_ctx)) 11095bcae31dSMichal Kazior return -EINVAL; 11105bcae31dSMichal Kazior 11115bcae31dSMichal Kazior if (WARN_ON(!new_ctx)) 11125bcae31dSMichal Kazior return -EINVAL; 11135bcae31dSMichal Kazior 11145bcae31dSMichal Kazior if (WARN_ON(new_ctx->replace_state == 11155bcae31dSMichal Kazior IEEE80211_CHANCTX_REPLACES_OTHER)) 11165bcae31dSMichal Kazior return -EINVAL; 11175bcae31dSMichal Kazior 11185bcae31dSMichal Kazior chandef = ieee80211_chanctx_non_reserved_chandef(local, new_ctx, 11195bcae31dSMichal Kazior &sdata->reserved_chandef); 11205bcae31dSMichal Kazior if (WARN_ON(!chandef)) 11215bcae31dSMichal Kazior return -EINVAL; 11225bcae31dSMichal Kazior 11235bcae31dSMichal Kazior list_del(&sdata->reserved_chanctx_list); 11245bcae31dSMichal Kazior sdata->reserved_chanctx = NULL; 11255bcae31dSMichal Kazior 11265bcae31dSMichal Kazior err = ieee80211_assign_vif_chanctx(sdata, new_ctx); 11275bcae31dSMichal Kazior if (err) { 11285bcae31dSMichal Kazior if (ieee80211_chanctx_refcount(local, new_ctx) == 0) 11295bcae31dSMichal Kazior ieee80211_free_chanctx(local, new_ctx); 11305bcae31dSMichal Kazior 11315bcae31dSMichal Kazior goto out; 11325bcae31dSMichal Kazior } 11335bcae31dSMichal Kazior 11345bcae31dSMichal Kazior out: 113503078de4SMichal Kazior ieee80211_vif_chanctx_reservation_complete(sdata); 11365bcae31dSMichal Kazior return err; 11375bcae31dSMichal Kazior } 11385bcae31dSMichal Kazior 11395bcae31dSMichal Kazior static bool 11405bcae31dSMichal Kazior ieee80211_vif_has_in_place_reservation(struct ieee80211_sub_if_data *sdata) 11415bcae31dSMichal Kazior { 11425bcae31dSMichal Kazior struct ieee80211_chanctx *old_ctx, *new_ctx; 11435bcae31dSMichal Kazior 11445bcae31dSMichal Kazior lockdep_assert_held(&sdata->local->chanctx_mtx); 11455bcae31dSMichal Kazior 11465bcae31dSMichal Kazior new_ctx = sdata->reserved_chanctx; 11475bcae31dSMichal Kazior old_ctx = ieee80211_vif_get_chanctx(sdata); 11485bcae31dSMichal Kazior 11495bcae31dSMichal Kazior if (!old_ctx) 11505bcae31dSMichal Kazior return false; 11515bcae31dSMichal Kazior 11525bcae31dSMichal Kazior if (WARN_ON(!new_ctx)) 11535bcae31dSMichal Kazior return false; 11545bcae31dSMichal Kazior 11555bcae31dSMichal Kazior if (old_ctx->replace_state != IEEE80211_CHANCTX_WILL_BE_REPLACED) 11565bcae31dSMichal Kazior return false; 11575bcae31dSMichal Kazior 11585bcae31dSMichal Kazior if (new_ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) 11595bcae31dSMichal Kazior return false; 11605bcae31dSMichal Kazior 11615bcae31dSMichal Kazior return true; 11625bcae31dSMichal Kazior } 11635bcae31dSMichal Kazior 11645bcae31dSMichal Kazior static int ieee80211_chsw_switch_hwconf(struct ieee80211_local *local, 11655bcae31dSMichal Kazior struct ieee80211_chanctx *new_ctx) 11665bcae31dSMichal Kazior { 11675bcae31dSMichal Kazior const struct cfg80211_chan_def *chandef; 11685bcae31dSMichal Kazior 11695bcae31dSMichal Kazior lockdep_assert_held(&local->mtx); 11705bcae31dSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 11715bcae31dSMichal Kazior 11725bcae31dSMichal Kazior chandef = ieee80211_chanctx_reserved_chandef(local, new_ctx, NULL); 11735bcae31dSMichal Kazior if (WARN_ON(!chandef)) 11745bcae31dSMichal Kazior return -EINVAL; 11755bcae31dSMichal Kazior 11765bcae31dSMichal Kazior local->hw.conf.radar_enabled = new_ctx->conf.radar_enabled; 11775bcae31dSMichal Kazior local->_oper_chandef = *chandef; 11785bcae31dSMichal Kazior ieee80211_hw_config(local, 0); 11795bcae31dSMichal Kazior 11805bcae31dSMichal Kazior return 0; 11815bcae31dSMichal Kazior } 11825bcae31dSMichal Kazior 11835bcae31dSMichal Kazior static int ieee80211_chsw_switch_vifs(struct ieee80211_local *local, 11845bcae31dSMichal Kazior int n_vifs) 11855bcae31dSMichal Kazior { 11865bcae31dSMichal Kazior struct ieee80211_vif_chanctx_switch *vif_chsw; 11875bcae31dSMichal Kazior struct ieee80211_sub_if_data *sdata; 11885bcae31dSMichal Kazior struct ieee80211_chanctx *ctx, *old_ctx; 11895bcae31dSMichal Kazior int i, err; 11905bcae31dSMichal Kazior 11915bcae31dSMichal Kazior lockdep_assert_held(&local->mtx); 11925bcae31dSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 11935bcae31dSMichal Kazior 11945bcae31dSMichal Kazior vif_chsw = kzalloc(sizeof(vif_chsw[0]) * n_vifs, GFP_KERNEL); 11955bcae31dSMichal Kazior if (!vif_chsw) 11965bcae31dSMichal Kazior return -ENOMEM; 11975bcae31dSMichal Kazior 11985bcae31dSMichal Kazior i = 0; 11995bcae31dSMichal Kazior list_for_each_entry(ctx, &local->chanctx_list, list) { 12005bcae31dSMichal Kazior if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) 12015bcae31dSMichal Kazior continue; 12025bcae31dSMichal Kazior 12035bcae31dSMichal Kazior if (WARN_ON(!ctx->replace_ctx)) { 12045bcae31dSMichal Kazior err = -EINVAL; 12055bcae31dSMichal Kazior goto out; 12065bcae31dSMichal Kazior } 12075bcae31dSMichal Kazior 12085bcae31dSMichal Kazior list_for_each_entry(sdata, &ctx->reserved_vifs, 12095bcae31dSMichal Kazior reserved_chanctx_list) { 12105bcae31dSMichal Kazior if (!ieee80211_vif_has_in_place_reservation( 12115bcae31dSMichal Kazior sdata)) 12125bcae31dSMichal Kazior continue; 12135bcae31dSMichal Kazior 12145bcae31dSMichal Kazior old_ctx = ieee80211_vif_get_chanctx(sdata); 12155bcae31dSMichal Kazior vif_chsw[i].vif = &sdata->vif; 12165bcae31dSMichal Kazior vif_chsw[i].old_ctx = &old_ctx->conf; 12175bcae31dSMichal Kazior vif_chsw[i].new_ctx = &ctx->conf; 12185bcae31dSMichal Kazior 12195bcae31dSMichal Kazior i++; 12205bcae31dSMichal Kazior } 12215bcae31dSMichal Kazior } 12225bcae31dSMichal Kazior 12235bcae31dSMichal Kazior err = drv_switch_vif_chanctx(local, vif_chsw, n_vifs, 12245bcae31dSMichal Kazior CHANCTX_SWMODE_SWAP_CONTEXTS); 12255bcae31dSMichal Kazior 12265bcae31dSMichal Kazior out: 12275bcae31dSMichal Kazior kfree(vif_chsw); 12285bcae31dSMichal Kazior return err; 12295bcae31dSMichal Kazior } 12305bcae31dSMichal Kazior 12315bcae31dSMichal Kazior static int ieee80211_chsw_switch_ctxs(struct ieee80211_local *local) 12325bcae31dSMichal Kazior { 12335bcae31dSMichal Kazior struct ieee80211_chanctx *ctx; 12345bcae31dSMichal Kazior int err; 12355bcae31dSMichal Kazior 12365bcae31dSMichal Kazior lockdep_assert_held(&local->mtx); 12375bcae31dSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 12385bcae31dSMichal Kazior 12395bcae31dSMichal Kazior list_for_each_entry(ctx, &local->chanctx_list, list) { 12405bcae31dSMichal Kazior if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) 12415bcae31dSMichal Kazior continue; 12425bcae31dSMichal Kazior 12435bcae31dSMichal Kazior if (!list_empty(&ctx->replace_ctx->assigned_vifs)) 12445bcae31dSMichal Kazior continue; 12455bcae31dSMichal Kazior 12465bcae31dSMichal Kazior ieee80211_del_chanctx(local, ctx->replace_ctx); 12475bcae31dSMichal Kazior err = ieee80211_add_chanctx(local, ctx); 12485bcae31dSMichal Kazior if (err) 12495bcae31dSMichal Kazior goto err; 12505bcae31dSMichal Kazior } 12515bcae31dSMichal Kazior 12525bcae31dSMichal Kazior return 0; 12535bcae31dSMichal Kazior 12545bcae31dSMichal Kazior err: 12555bcae31dSMichal Kazior WARN_ON(ieee80211_add_chanctx(local, ctx)); 12565bcae31dSMichal Kazior list_for_each_entry_continue_reverse(ctx, &local->chanctx_list, list) { 12575bcae31dSMichal Kazior if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) 12585bcae31dSMichal Kazior continue; 12595bcae31dSMichal Kazior 12605bcae31dSMichal Kazior if (!list_empty(&ctx->replace_ctx->assigned_vifs)) 12615bcae31dSMichal Kazior continue; 12625bcae31dSMichal Kazior 12635bcae31dSMichal Kazior ieee80211_del_chanctx(local, ctx); 12645bcae31dSMichal Kazior WARN_ON(ieee80211_add_chanctx(local, ctx->replace_ctx)); 12655bcae31dSMichal Kazior } 12665bcae31dSMichal Kazior 12675bcae31dSMichal Kazior return err; 12685bcae31dSMichal Kazior } 12695bcae31dSMichal Kazior 12705bcae31dSMichal Kazior int 12715bcae31dSMichal Kazior ieee80211_vif_use_reserved_switch(struct ieee80211_local *local) 12725bcae31dSMichal Kazior { 12735bcae31dSMichal Kazior struct ieee80211_sub_if_data *sdata, *sdata_tmp; 12745bcae31dSMichal Kazior struct ieee80211_chanctx *ctx, *ctx_tmp, *old_ctx; 12755bcae31dSMichal Kazior struct ieee80211_chanctx *new_ctx = NULL; 12765bcae31dSMichal Kazior int i, err, n_assigned, n_reserved, n_ready; 12775bcae31dSMichal Kazior int n_ctx = 0, n_vifs_switch = 0, n_vifs_assign = 0, n_vifs_ctxless = 0; 12785bcae31dSMichal Kazior 12795bcae31dSMichal Kazior lockdep_assert_held(&local->mtx); 12805bcae31dSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 12815bcae31dSMichal Kazior 12825bcae31dSMichal Kazior /* 12835bcae31dSMichal Kazior * If there are 2 independent pairs of channel contexts performing 12845bcae31dSMichal Kazior * cross-switch of their vifs this code will still wait until both are 12855bcae31dSMichal Kazior * ready even though it could be possible to switch one before the 12865bcae31dSMichal Kazior * other is ready. 12875bcae31dSMichal Kazior * 12885bcae31dSMichal Kazior * For practical reasons and code simplicity just do a single huge 12895bcae31dSMichal Kazior * switch. 12905bcae31dSMichal Kazior */ 12915bcae31dSMichal Kazior 12925bcae31dSMichal Kazior /* 12935bcae31dSMichal Kazior * Verify if the reservation is still feasible. 12945bcae31dSMichal Kazior * - if it's not then disconnect 12955bcae31dSMichal Kazior * - if it is but not all vifs necessary are ready then defer 12965bcae31dSMichal Kazior */ 12975bcae31dSMichal Kazior 12985bcae31dSMichal Kazior list_for_each_entry(ctx, &local->chanctx_list, list) { 12995bcae31dSMichal Kazior if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) 13005bcae31dSMichal Kazior continue; 13015bcae31dSMichal Kazior 13025bcae31dSMichal Kazior if (WARN_ON(!ctx->replace_ctx)) { 13035bcae31dSMichal Kazior err = -EINVAL; 13045bcae31dSMichal Kazior goto err; 13055bcae31dSMichal Kazior } 13065bcae31dSMichal Kazior 13075bcae31dSMichal Kazior if (!local->use_chanctx) 13085bcae31dSMichal Kazior new_ctx = ctx; 13095bcae31dSMichal Kazior 13105bcae31dSMichal Kazior n_ctx++; 13115bcae31dSMichal Kazior 13125bcae31dSMichal Kazior n_assigned = 0; 13135bcae31dSMichal Kazior n_reserved = 0; 13145bcae31dSMichal Kazior n_ready = 0; 13155bcae31dSMichal Kazior 13165bcae31dSMichal Kazior list_for_each_entry(sdata, &ctx->replace_ctx->assigned_vifs, 13175bcae31dSMichal Kazior assigned_chanctx_list) { 13185bcae31dSMichal Kazior n_assigned++; 13195bcae31dSMichal Kazior if (sdata->reserved_chanctx) { 13205bcae31dSMichal Kazior n_reserved++; 13215bcae31dSMichal Kazior if (sdata->reserved_ready) 13225bcae31dSMichal Kazior n_ready++; 13235bcae31dSMichal Kazior } 13245bcae31dSMichal Kazior } 13255bcae31dSMichal Kazior 13265bcae31dSMichal Kazior if (n_assigned != n_reserved) { 13275bcae31dSMichal Kazior if (n_ready == n_reserved) { 13285bcae31dSMichal Kazior wiphy_info(local->hw.wiphy, 13295bcae31dSMichal Kazior "channel context reservation cannot be finalized because some interfaces aren't switching\n"); 13305bcae31dSMichal Kazior err = -EBUSY; 13315bcae31dSMichal Kazior goto err; 13325bcae31dSMichal Kazior } 13335bcae31dSMichal Kazior 13345bcae31dSMichal Kazior return -EAGAIN; 13355bcae31dSMichal Kazior } 13365bcae31dSMichal Kazior 13375bcae31dSMichal Kazior ctx->conf.radar_enabled = false; 13385bcae31dSMichal Kazior list_for_each_entry(sdata, &ctx->reserved_vifs, 13395bcae31dSMichal Kazior reserved_chanctx_list) { 13405bcae31dSMichal Kazior if (ieee80211_vif_has_in_place_reservation(sdata) && 13415bcae31dSMichal Kazior !sdata->reserved_ready) 13425bcae31dSMichal Kazior return -EAGAIN; 13435bcae31dSMichal Kazior 13445bcae31dSMichal Kazior old_ctx = ieee80211_vif_get_chanctx(sdata); 13455bcae31dSMichal Kazior if (old_ctx) { 13465bcae31dSMichal Kazior if (old_ctx->replace_state == 13475bcae31dSMichal Kazior IEEE80211_CHANCTX_WILL_BE_REPLACED) 13485bcae31dSMichal Kazior n_vifs_switch++; 13495bcae31dSMichal Kazior else 13505bcae31dSMichal Kazior n_vifs_assign++; 13515bcae31dSMichal Kazior } else { 13525bcae31dSMichal Kazior n_vifs_ctxless++; 13535bcae31dSMichal Kazior } 13545bcae31dSMichal Kazior 13555bcae31dSMichal Kazior if (sdata->reserved_radar_required) 13565bcae31dSMichal Kazior ctx->conf.radar_enabled = true; 13575bcae31dSMichal Kazior } 13585bcae31dSMichal Kazior } 13595bcae31dSMichal Kazior 13605bcae31dSMichal Kazior if (WARN_ON(n_ctx == 0) || 13615bcae31dSMichal Kazior WARN_ON(n_vifs_switch == 0 && 13625bcae31dSMichal Kazior n_vifs_assign == 0 && 13635bcae31dSMichal Kazior n_vifs_ctxless == 0) || 13645bcae31dSMichal Kazior WARN_ON(n_ctx > 1 && !local->use_chanctx) || 13655bcae31dSMichal Kazior WARN_ON(!new_ctx && !local->use_chanctx)) { 13665bcae31dSMichal Kazior err = -EINVAL; 13675bcae31dSMichal Kazior goto err; 13685bcae31dSMichal Kazior } 13695bcae31dSMichal Kazior 13705bcae31dSMichal Kazior /* 13715bcae31dSMichal Kazior * All necessary vifs are ready. Perform the switch now depending on 13725bcae31dSMichal Kazior * reservations and driver capabilities. 13735bcae31dSMichal Kazior */ 13745bcae31dSMichal Kazior 13755bcae31dSMichal Kazior if (local->use_chanctx) { 13765bcae31dSMichal Kazior if (n_vifs_switch > 0) { 13775bcae31dSMichal Kazior err = ieee80211_chsw_switch_vifs(local, n_vifs_switch); 13785bcae31dSMichal Kazior if (err) 13795bcae31dSMichal Kazior goto err; 13805bcae31dSMichal Kazior } 13815bcae31dSMichal Kazior 13825bcae31dSMichal Kazior if (n_vifs_assign > 0 || n_vifs_ctxless > 0) { 13835bcae31dSMichal Kazior err = ieee80211_chsw_switch_ctxs(local); 13845bcae31dSMichal Kazior if (err) 13855bcae31dSMichal Kazior goto err; 13865bcae31dSMichal Kazior } 13875bcae31dSMichal Kazior } else { 13885bcae31dSMichal Kazior err = ieee80211_chsw_switch_hwconf(local, new_ctx); 13895bcae31dSMichal Kazior if (err) 13905bcae31dSMichal Kazior goto err; 13915bcae31dSMichal Kazior } 13925bcae31dSMichal Kazior 13935bcae31dSMichal Kazior /* 13945bcae31dSMichal Kazior * Update all structures, values and pointers to point to new channel 13955bcae31dSMichal Kazior * context(s). 13965bcae31dSMichal Kazior */ 13975bcae31dSMichal Kazior 13985bcae31dSMichal Kazior i = 0; 13995bcae31dSMichal Kazior list_for_each_entry(ctx, &local->chanctx_list, list) { 14005bcae31dSMichal Kazior if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) 14015bcae31dSMichal Kazior continue; 14025bcae31dSMichal Kazior 14035bcae31dSMichal Kazior if (WARN_ON(!ctx->replace_ctx)) { 14045bcae31dSMichal Kazior err = -EINVAL; 14055bcae31dSMichal Kazior goto err; 14065bcae31dSMichal Kazior } 14075bcae31dSMichal Kazior 14085bcae31dSMichal Kazior list_for_each_entry(sdata, &ctx->reserved_vifs, 14095bcae31dSMichal Kazior reserved_chanctx_list) { 14105bcae31dSMichal Kazior u32 changed = 0; 14115bcae31dSMichal Kazior 14125bcae31dSMichal Kazior if (!ieee80211_vif_has_in_place_reservation(sdata)) 14135bcae31dSMichal Kazior continue; 14145bcae31dSMichal Kazior 14155bcae31dSMichal Kazior rcu_assign_pointer(sdata->vif.chanctx_conf, &ctx->conf); 14165bcae31dSMichal Kazior 14175bcae31dSMichal Kazior if (sdata->vif.type == NL80211_IFTYPE_AP) 14185bcae31dSMichal Kazior __ieee80211_vif_copy_chanctx_to_vlans(sdata, 14195bcae31dSMichal Kazior false); 14205bcae31dSMichal Kazior 14215bcae31dSMichal Kazior sdata->radar_required = sdata->reserved_radar_required; 14225bcae31dSMichal Kazior 14235bcae31dSMichal Kazior if (sdata->vif.bss_conf.chandef.width != 14245bcae31dSMichal Kazior sdata->reserved_chandef.width) 14255bcae31dSMichal Kazior changed = BSS_CHANGED_BANDWIDTH; 14265bcae31dSMichal Kazior 14275bcae31dSMichal Kazior sdata->vif.bss_conf.chandef = sdata->reserved_chandef; 14285bcae31dSMichal Kazior if (changed) 14295bcae31dSMichal Kazior ieee80211_bss_info_change_notify(sdata, 14305bcae31dSMichal Kazior changed); 14315bcae31dSMichal Kazior 14325bcae31dSMichal Kazior ieee80211_recalc_txpower(sdata); 14335bcae31dSMichal Kazior } 143411335a55SLuciano Coelho 143511335a55SLuciano Coelho ieee80211_recalc_chanctx_chantype(local, ctx); 143611335a55SLuciano Coelho ieee80211_recalc_smps_chanctx(local, ctx); 143711335a55SLuciano Coelho ieee80211_recalc_radar_chanctx(local, ctx); 143811335a55SLuciano Coelho ieee80211_recalc_chanctx_min_def(local, ctx); 14395bcae31dSMichal Kazior 14405bcae31dSMichal Kazior list_for_each_entry_safe(sdata, sdata_tmp, &ctx->reserved_vifs, 14415bcae31dSMichal Kazior reserved_chanctx_list) { 14425bcae31dSMichal Kazior if (ieee80211_vif_get_chanctx(sdata) != ctx) 14435bcae31dSMichal Kazior continue; 14445bcae31dSMichal Kazior 14455bcae31dSMichal Kazior list_del(&sdata->reserved_chanctx_list); 14465bcae31dSMichal Kazior list_move(&sdata->assigned_chanctx_list, 14475bcae31dSMichal Kazior &new_ctx->assigned_vifs); 14485bcae31dSMichal Kazior sdata->reserved_chanctx = NULL; 144903078de4SMichal Kazior 145003078de4SMichal Kazior ieee80211_vif_chanctx_reservation_complete(sdata); 14515bcae31dSMichal Kazior } 14525bcae31dSMichal Kazior 14535bcae31dSMichal Kazior /* 14545bcae31dSMichal Kazior * This context might have been a dependency for an already 14555bcae31dSMichal Kazior * ready re-assign reservation interface that was deferred. Do 14565bcae31dSMichal Kazior * not propagate error to the caller though. The in-place 14575bcae31dSMichal Kazior * reservation for originally requested interface has already 14585bcae31dSMichal Kazior * succeeded at this point. 14595bcae31dSMichal Kazior */ 14605bcae31dSMichal Kazior list_for_each_entry_safe(sdata, sdata_tmp, &ctx->reserved_vifs, 14615bcae31dSMichal Kazior reserved_chanctx_list) { 14625bcae31dSMichal Kazior if (WARN_ON(ieee80211_vif_has_in_place_reservation( 14635bcae31dSMichal Kazior sdata))) 14645bcae31dSMichal Kazior continue; 14655bcae31dSMichal Kazior 14665bcae31dSMichal Kazior if (WARN_ON(sdata->reserved_chanctx != ctx)) 14675bcae31dSMichal Kazior continue; 14685bcae31dSMichal Kazior 14695bcae31dSMichal Kazior if (!sdata->reserved_ready) 14705bcae31dSMichal Kazior continue; 14715bcae31dSMichal Kazior 14725bcae31dSMichal Kazior if (ieee80211_vif_get_chanctx(sdata)) 14735bcae31dSMichal Kazior err = ieee80211_vif_use_reserved_reassign( 14745bcae31dSMichal Kazior sdata); 14755bcae31dSMichal Kazior else 14765bcae31dSMichal Kazior err = ieee80211_vif_use_reserved_assign(sdata); 14775bcae31dSMichal Kazior 14785bcae31dSMichal Kazior if (err) { 14795bcae31dSMichal Kazior sdata_info(sdata, 14805bcae31dSMichal Kazior "failed to finalize (re-)assign reservation (err=%d)\n", 14815bcae31dSMichal Kazior err); 14825bcae31dSMichal Kazior ieee80211_vif_unreserve_chanctx(sdata); 14835bcae31dSMichal Kazior cfg80211_stop_iface(local->hw.wiphy, 14845bcae31dSMichal Kazior &sdata->wdev, 14855bcae31dSMichal Kazior GFP_KERNEL); 14865bcae31dSMichal Kazior } 14875bcae31dSMichal Kazior } 14885bcae31dSMichal Kazior } 14895bcae31dSMichal Kazior 14905bcae31dSMichal Kazior /* 14915bcae31dSMichal Kazior * Finally free old contexts 14925bcae31dSMichal Kazior */ 14935bcae31dSMichal Kazior 14945bcae31dSMichal Kazior list_for_each_entry_safe(ctx, ctx_tmp, &local->chanctx_list, list) { 14955bcae31dSMichal Kazior if (ctx->replace_state != IEEE80211_CHANCTX_WILL_BE_REPLACED) 14965bcae31dSMichal Kazior continue; 14975bcae31dSMichal Kazior 14985bcae31dSMichal Kazior ctx->replace_ctx->replace_ctx = NULL; 14995bcae31dSMichal Kazior ctx->replace_ctx->replace_state = 15005bcae31dSMichal Kazior IEEE80211_CHANCTX_REPLACE_NONE; 15015bcae31dSMichal Kazior 15025bcae31dSMichal Kazior list_del_rcu(&ctx->list); 15035bcae31dSMichal Kazior kfree_rcu(ctx, rcu_head); 15045bcae31dSMichal Kazior } 15055bcae31dSMichal Kazior 15065bcae31dSMichal Kazior return 0; 15075bcae31dSMichal Kazior 15085bcae31dSMichal Kazior err: 15095bcae31dSMichal Kazior list_for_each_entry(ctx, &local->chanctx_list, list) { 15105bcae31dSMichal Kazior if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) 15115bcae31dSMichal Kazior continue; 15125bcae31dSMichal Kazior 15135bcae31dSMichal Kazior list_for_each_entry_safe(sdata, sdata_tmp, &ctx->reserved_vifs, 151403078de4SMichal Kazior reserved_chanctx_list) { 15155bcae31dSMichal Kazior ieee80211_vif_unreserve_chanctx(sdata); 151603078de4SMichal Kazior ieee80211_vif_chanctx_reservation_complete(sdata); 151703078de4SMichal Kazior } 15185bcae31dSMichal Kazior } 15195bcae31dSMichal Kazior 15205bcae31dSMichal Kazior return err; 15215bcae31dSMichal Kazior } 15225bcae31dSMichal Kazior 15235bcae31dSMichal Kazior int ieee80211_vif_use_reserved_context(struct ieee80211_sub_if_data *sdata) 15245bcae31dSMichal Kazior { 15255bcae31dSMichal Kazior struct ieee80211_local *local = sdata->local; 15265bcae31dSMichal Kazior struct ieee80211_chanctx *new_ctx; 15275bcae31dSMichal Kazior struct ieee80211_chanctx *old_ctx; 15285bcae31dSMichal Kazior int err; 15295bcae31dSMichal Kazior 15305bcae31dSMichal Kazior lockdep_assert_held(&local->mtx); 15315bcae31dSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 15325bcae31dSMichal Kazior 15335bcae31dSMichal Kazior new_ctx = sdata->reserved_chanctx; 15345bcae31dSMichal Kazior old_ctx = ieee80211_vif_get_chanctx(sdata); 15355bcae31dSMichal Kazior 15365bcae31dSMichal Kazior if (WARN_ON(!new_ctx)) 15375bcae31dSMichal Kazior return -EINVAL; 15385bcae31dSMichal Kazior 15395bcae31dSMichal Kazior if (WARN_ON(new_ctx->replace_state == 15405bcae31dSMichal Kazior IEEE80211_CHANCTX_WILL_BE_REPLACED)) 15415bcae31dSMichal Kazior return -EINVAL; 15425bcae31dSMichal Kazior 15435bcae31dSMichal Kazior if (WARN_ON(sdata->reserved_ready)) 15445bcae31dSMichal Kazior return -EINVAL; 15455bcae31dSMichal Kazior 15465bcae31dSMichal Kazior sdata->reserved_ready = true; 15475bcae31dSMichal Kazior 15485bcae31dSMichal Kazior if (new_ctx->replace_state == IEEE80211_CHANCTX_REPLACE_NONE) { 15495bcae31dSMichal Kazior if (old_ctx) 15505bcae31dSMichal Kazior err = ieee80211_vif_use_reserved_reassign(sdata); 15515bcae31dSMichal Kazior else 15525bcae31dSMichal Kazior err = ieee80211_vif_use_reserved_assign(sdata); 15535bcae31dSMichal Kazior 15545bcae31dSMichal Kazior if (err) 15555bcae31dSMichal Kazior return err; 15565bcae31dSMichal Kazior } 15575bcae31dSMichal Kazior 15585bcae31dSMichal Kazior /* 15595bcae31dSMichal Kazior * In-place reservation may need to be finalized now either if: 15605bcae31dSMichal Kazior * a) sdata is taking part in the swapping itself and is the last one 15615bcae31dSMichal Kazior * b) sdata has switched with a re-assign reservation to an existing 15625bcae31dSMichal Kazior * context readying in-place switching of old_ctx 15635bcae31dSMichal Kazior * 15645bcae31dSMichal Kazior * In case of (b) do not propagate the error up because the requested 15655bcae31dSMichal Kazior * sdata already switched successfully. Just spill an extra warning. 15665bcae31dSMichal Kazior * The ieee80211_vif_use_reserved_switch() already stops all necessary 15675bcae31dSMichal Kazior * interfaces upon failure. 15685bcae31dSMichal Kazior */ 15695bcae31dSMichal Kazior if ((old_ctx && 15705bcae31dSMichal Kazior old_ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED) || 15715bcae31dSMichal Kazior new_ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER) { 15725bcae31dSMichal Kazior err = ieee80211_vif_use_reserved_switch(local); 15735bcae31dSMichal Kazior if (err && err != -EAGAIN) { 15745bcae31dSMichal Kazior if (new_ctx->replace_state == 15755bcae31dSMichal Kazior IEEE80211_CHANCTX_REPLACES_OTHER) 15765bcae31dSMichal Kazior return err; 15775bcae31dSMichal Kazior 15785bcae31dSMichal Kazior wiphy_info(local->hw.wiphy, 15795bcae31dSMichal Kazior "depending in-place reservation failed (err=%d)\n", 15805bcae31dSMichal Kazior err); 15815bcae31dSMichal Kazior } 15825bcae31dSMichal Kazior } 15835bcae31dSMichal Kazior 15845bcae31dSMichal Kazior return 0; 158573da7d5bSSimon Wunderlich } 158673da7d5bSSimon Wunderlich 15872c9b7359SJohannes Berg int ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata, 15882c9b7359SJohannes Berg const struct cfg80211_chan_def *chandef, 15892c9b7359SJohannes Berg u32 *changed) 15902c9b7359SJohannes Berg { 15912c9b7359SJohannes Berg struct ieee80211_local *local = sdata->local; 15922c9b7359SJohannes Berg struct ieee80211_chanctx_conf *conf; 15932c9b7359SJohannes Berg struct ieee80211_chanctx *ctx; 15945bcae31dSMichal Kazior const struct cfg80211_chan_def *compat; 15952c9b7359SJohannes Berg int ret; 15962c9b7359SJohannes Berg 15972c9b7359SJohannes Berg if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef, 15982c9b7359SJohannes Berg IEEE80211_CHAN_DISABLED)) 15992c9b7359SJohannes Berg return -EINVAL; 16002c9b7359SJohannes Berg 16012c9b7359SJohannes Berg mutex_lock(&local->chanctx_mtx); 16022c9b7359SJohannes Berg if (cfg80211_chandef_identical(chandef, &sdata->vif.bss_conf.chandef)) { 16032c9b7359SJohannes Berg ret = 0; 16042c9b7359SJohannes Berg goto out; 16052c9b7359SJohannes Berg } 16062c9b7359SJohannes Berg 16072c9b7359SJohannes Berg if (chandef->width == NL80211_CHAN_WIDTH_20_NOHT || 16082c9b7359SJohannes Berg sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT) { 16092c9b7359SJohannes Berg ret = -EINVAL; 16102c9b7359SJohannes Berg goto out; 16112c9b7359SJohannes Berg } 16122c9b7359SJohannes Berg 16132c9b7359SJohannes Berg conf = rcu_dereference_protected(sdata->vif.chanctx_conf, 16142c9b7359SJohannes Berg lockdep_is_held(&local->chanctx_mtx)); 16152c9b7359SJohannes Berg if (!conf) { 16162c9b7359SJohannes Berg ret = -EINVAL; 16172c9b7359SJohannes Berg goto out; 16182c9b7359SJohannes Berg } 16192c9b7359SJohannes Berg 16202c9b7359SJohannes Berg ctx = container_of(conf, struct ieee80211_chanctx, conf); 16215bcae31dSMichal Kazior 16225bcae31dSMichal Kazior compat = cfg80211_chandef_compatible(&conf->def, chandef); 16235bcae31dSMichal Kazior if (!compat) { 16242c9b7359SJohannes Berg ret = -EINVAL; 16252c9b7359SJohannes Berg goto out; 16262c9b7359SJohannes Berg } 16272c9b7359SJohannes Berg 16285bcae31dSMichal Kazior switch (ctx->replace_state) { 16295bcae31dSMichal Kazior case IEEE80211_CHANCTX_REPLACE_NONE: 16305bcae31dSMichal Kazior if (!ieee80211_chanctx_reserved_chandef(local, ctx, compat)) { 16315bcae31dSMichal Kazior ret = -EBUSY; 16325bcae31dSMichal Kazior goto out; 16335bcae31dSMichal Kazior } 16345bcae31dSMichal Kazior break; 16355bcae31dSMichal Kazior case IEEE80211_CHANCTX_WILL_BE_REPLACED: 16365bcae31dSMichal Kazior /* TODO: Perhaps the bandwith change could be treated as a 16375bcae31dSMichal Kazior * reservation itself? */ 16385bcae31dSMichal Kazior ret = -EBUSY; 16395bcae31dSMichal Kazior goto out; 16405bcae31dSMichal Kazior case IEEE80211_CHANCTX_REPLACES_OTHER: 16415bcae31dSMichal Kazior /* channel context that is going to replace another channel 16425bcae31dSMichal Kazior * context doesn't really exist and shouldn't be assigned 16435bcae31dSMichal Kazior * anywhere yet */ 16445bcae31dSMichal Kazior WARN_ON(1); 16455bcae31dSMichal Kazior break; 16465bcae31dSMichal Kazior } 16475bcae31dSMichal Kazior 16482c9b7359SJohannes Berg sdata->vif.bss_conf.chandef = *chandef; 16492c9b7359SJohannes Berg 16502c9b7359SJohannes Berg ieee80211_recalc_chanctx_chantype(local, ctx); 16512c9b7359SJohannes Berg 16522c9b7359SJohannes Berg *changed |= BSS_CHANGED_BANDWIDTH; 16532c9b7359SJohannes Berg ret = 0; 16542c9b7359SJohannes Berg out: 16552c9b7359SJohannes Berg mutex_unlock(&local->chanctx_mtx); 16562c9b7359SJohannes Berg return ret; 16572c9b7359SJohannes Berg } 16582c9b7359SJohannes Berg 1659d01a1e65SMichal Kazior void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata) 1660d01a1e65SMichal Kazior { 166155de908aSJohannes Berg WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev)); 166255de908aSJohannes Berg 166334a3740dSJohannes Berg lockdep_assert_held(&sdata->local->mtx); 166434a3740dSJohannes Berg 1665d01a1e65SMichal Kazior mutex_lock(&sdata->local->chanctx_mtx); 1666d01a1e65SMichal Kazior __ieee80211_vif_release_channel(sdata); 1667d01a1e65SMichal Kazior mutex_unlock(&sdata->local->chanctx_mtx); 1668d01a1e65SMichal Kazior } 16693448c005SJohannes Berg 16704d76d21bSJohannes Berg void ieee80211_vif_vlan_copy_chanctx(struct ieee80211_sub_if_data *sdata) 16714d76d21bSJohannes Berg { 16724d76d21bSJohannes Berg struct ieee80211_local *local = sdata->local; 16734d76d21bSJohannes Berg struct ieee80211_sub_if_data *ap; 16744d76d21bSJohannes Berg struct ieee80211_chanctx_conf *conf; 16754d76d21bSJohannes Berg 16764d76d21bSJohannes Berg if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP_VLAN || !sdata->bss)) 16774d76d21bSJohannes Berg return; 16784d76d21bSJohannes Berg 16794d76d21bSJohannes Berg ap = container_of(sdata->bss, struct ieee80211_sub_if_data, u.ap); 16804d76d21bSJohannes Berg 16814d76d21bSJohannes Berg mutex_lock(&local->chanctx_mtx); 16824d76d21bSJohannes Berg 16834d76d21bSJohannes Berg conf = rcu_dereference_protected(ap->vif.chanctx_conf, 16844d76d21bSJohannes Berg lockdep_is_held(&local->chanctx_mtx)); 16854d76d21bSJohannes Berg rcu_assign_pointer(sdata->vif.chanctx_conf, conf); 16864d76d21bSJohannes Berg mutex_unlock(&local->chanctx_mtx); 16874d76d21bSJohannes Berg } 16884d76d21bSJohannes Berg 16893448c005SJohannes Berg void ieee80211_iter_chan_contexts_atomic( 16903448c005SJohannes Berg struct ieee80211_hw *hw, 16913448c005SJohannes Berg void (*iter)(struct ieee80211_hw *hw, 16923448c005SJohannes Berg struct ieee80211_chanctx_conf *chanctx_conf, 16933448c005SJohannes Berg void *data), 16943448c005SJohannes Berg void *iter_data) 16953448c005SJohannes Berg { 16963448c005SJohannes Berg struct ieee80211_local *local = hw_to_local(hw); 16973448c005SJohannes Berg struct ieee80211_chanctx *ctx; 16983448c005SJohannes Berg 16993448c005SJohannes Berg rcu_read_lock(); 17003448c005SJohannes Berg list_for_each_entry_rcu(ctx, &local->chanctx_list, list) 17018a61af65SJohannes Berg if (ctx->driver_present) 17023448c005SJohannes Berg iter(hw, &ctx->conf, iter_data); 17033448c005SJohannes Berg rcu_read_unlock(); 17043448c005SJohannes Berg } 17053448c005SJohannes Berg EXPORT_SYMBOL_GPL(ieee80211_iter_chan_contexts_atomic); 1706