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 1930fabfaafSArik Nemtsov 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; 2670fabfaafSArik Nemtsov case NL80211_IFTYPE_STATION: 2680fabfaafSArik Nemtsov /* 2690fabfaafSArik Nemtsov * The ap's sta->bandwidth is not set yet at this 2700fabfaafSArik Nemtsov * point, so take the width from the chandef, but 2710fabfaafSArik Nemtsov * account also for TDLS peers 2720fabfaafSArik Nemtsov */ 2730fabfaafSArik Nemtsov width = max(vif->bss_conf.chandef.width, 2740fabfaafSArik Nemtsov ieee80211_get_max_required_bw(sdata)); 2750fabfaafSArik Nemtsov break; 27621f659bfSEliad Peller case NL80211_IFTYPE_P2P_DEVICE: 27721f659bfSEliad Peller continue; 27821f659bfSEliad Peller case NL80211_IFTYPE_ADHOC: 27921f659bfSEliad Peller case NL80211_IFTYPE_WDS: 28021f659bfSEliad Peller case NL80211_IFTYPE_MESH_POINT: 2816e0bd6c3SRostislav Lisovy case NL80211_IFTYPE_OCB: 28221f659bfSEliad Peller width = vif->bss_conf.chandef.width; 28321f659bfSEliad Peller break; 28421f659bfSEliad Peller case NL80211_IFTYPE_UNSPECIFIED: 28521f659bfSEliad Peller case NUM_NL80211_IFTYPES: 28621f659bfSEliad Peller case NL80211_IFTYPE_MONITOR: 28721f659bfSEliad Peller case NL80211_IFTYPE_P2P_CLIENT: 28821f659bfSEliad Peller case NL80211_IFTYPE_P2P_GO: 28921f659bfSEliad Peller WARN_ON_ONCE(1); 29021f659bfSEliad Peller } 29121f659bfSEliad Peller max_bw = max(max_bw, width); 29221f659bfSEliad Peller } 2931c37a72cSEliad Peller 2941c37a72cSEliad Peller /* use the configured bandwidth in case of monitor interface */ 2951c37a72cSEliad Peller sdata = rcu_dereference(local->monitor_sdata); 2961c37a72cSEliad Peller if (sdata && rcu_access_pointer(sdata->vif.chanctx_conf) == conf) 2971c37a72cSEliad Peller max_bw = max(max_bw, conf->def.width); 2981c37a72cSEliad Peller 29921f659bfSEliad Peller rcu_read_unlock(); 30021f659bfSEliad Peller 30121f659bfSEliad Peller return max_bw; 30221f659bfSEliad Peller } 30321f659bfSEliad Peller 30421f659bfSEliad Peller /* 30521f659bfSEliad Peller * recalc the min required chan width of the channel context, which is 30621f659bfSEliad Peller * the max of min required widths of all the interfaces bound to this 30721f659bfSEliad Peller * channel context. 30821f659bfSEliad Peller */ 30921f659bfSEliad Peller void ieee80211_recalc_chanctx_min_def(struct ieee80211_local *local, 31021f659bfSEliad Peller struct ieee80211_chanctx *ctx) 31121f659bfSEliad Peller { 31221f659bfSEliad Peller enum nl80211_chan_width max_bw; 31321f659bfSEliad Peller struct cfg80211_chan_def min_def; 31421f659bfSEliad Peller 31521f659bfSEliad Peller lockdep_assert_held(&local->chanctx_mtx); 31621f659bfSEliad Peller 31721f659bfSEliad Peller /* don't optimize 5MHz, 10MHz, and radar_enabled confs */ 31821f659bfSEliad Peller if (ctx->conf.def.width == NL80211_CHAN_WIDTH_5 || 31921f659bfSEliad Peller ctx->conf.def.width == NL80211_CHAN_WIDTH_10 || 32021f659bfSEliad Peller ctx->conf.radar_enabled) { 32121f659bfSEliad Peller ctx->conf.min_def = ctx->conf.def; 32221f659bfSEliad Peller return; 32321f659bfSEliad Peller } 32421f659bfSEliad Peller 32521f659bfSEliad Peller max_bw = ieee80211_get_chanctx_max_required_bw(local, &ctx->conf); 32621f659bfSEliad Peller 32721f659bfSEliad Peller /* downgrade chandef up to max_bw */ 32821f659bfSEliad Peller min_def = ctx->conf.def; 32921f659bfSEliad Peller while (min_def.width > max_bw) 33021f659bfSEliad Peller ieee80211_chandef_downgrade(&min_def); 33121f659bfSEliad Peller 33221f659bfSEliad Peller if (cfg80211_chandef_identical(&ctx->conf.min_def, &min_def)) 33321f659bfSEliad Peller return; 33421f659bfSEliad Peller 33521f659bfSEliad Peller ctx->conf.min_def = min_def; 33621f659bfSEliad Peller if (!ctx->driver_present) 33721f659bfSEliad Peller return; 33821f659bfSEliad Peller 33921f659bfSEliad Peller drv_change_chanctx(local, ctx, IEEE80211_CHANCTX_CHANGE_MIN_WIDTH); 34021f659bfSEliad Peller } 34121f659bfSEliad Peller 34218942d3bSJohannes Berg static void ieee80211_change_chanctx(struct ieee80211_local *local, 343e89a96f5SMichal Kazior struct ieee80211_chanctx *ctx, 3444bf88530SJohannes Berg const struct cfg80211_chan_def *chandef) 345e89a96f5SMichal Kazior { 3464bf88530SJohannes Berg if (cfg80211_chandef_identical(&ctx->conf.def, chandef)) 347e89a96f5SMichal Kazior return; 348e89a96f5SMichal Kazior 3494bf88530SJohannes Berg WARN_ON(!cfg80211_chandef_compatible(&ctx->conf.def, chandef)); 3504bf88530SJohannes Berg 3514bf88530SJohannes Berg ctx->conf.def = *chandef; 3524bf88530SJohannes Berg drv_change_chanctx(local, ctx, IEEE80211_CHANCTX_CHANGE_WIDTH); 35321f659bfSEliad Peller ieee80211_recalc_chanctx_min_def(local, ctx); 35455de908aSJohannes Berg 35555de908aSJohannes Berg if (!local->use_chanctx) { 356675a0b04SKarl Beldan local->_oper_chandef = *chandef; 35755de908aSJohannes Berg ieee80211_hw_config(local, 0); 35855de908aSJohannes Berg } 3590aaffa9bSJohannes Berg } 360d01a1e65SMichal Kazior 361d01a1e65SMichal Kazior static struct ieee80211_chanctx * 362d01a1e65SMichal Kazior ieee80211_find_chanctx(struct ieee80211_local *local, 3634bf88530SJohannes Berg const struct cfg80211_chan_def *chandef, 364d01a1e65SMichal Kazior enum ieee80211_chanctx_mode mode) 365d01a1e65SMichal Kazior { 366d01a1e65SMichal Kazior struct ieee80211_chanctx *ctx; 367d01a1e65SMichal Kazior 368d01a1e65SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 369d01a1e65SMichal Kazior 370d01a1e65SMichal Kazior if (mode == IEEE80211_CHANCTX_EXCLUSIVE) 371d01a1e65SMichal Kazior return NULL; 372d01a1e65SMichal Kazior 373d01a1e65SMichal Kazior list_for_each_entry(ctx, &local->chanctx_list, list) { 3744bf88530SJohannes Berg const struct cfg80211_chan_def *compat; 375e89a96f5SMichal Kazior 3765bcae31dSMichal Kazior if (ctx->replace_state != IEEE80211_CHANCTX_REPLACE_NONE) 3775bcae31dSMichal Kazior continue; 3785bcae31dSMichal Kazior 379d01a1e65SMichal Kazior if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE) 380d01a1e65SMichal Kazior continue; 3814bf88530SJohannes Berg 3824bf88530SJohannes Berg compat = cfg80211_chandef_compatible(&ctx->conf.def, chandef); 3834bf88530SJohannes Berg if (!compat) 384d01a1e65SMichal Kazior continue; 385d01a1e65SMichal Kazior 3860288157bSMichal Kazior compat = ieee80211_chanctx_reserved_chandef(local, ctx, 3870288157bSMichal Kazior compat); 3880288157bSMichal Kazior if (!compat) 3890288157bSMichal Kazior continue; 3900288157bSMichal Kazior 39118942d3bSJohannes Berg ieee80211_change_chanctx(local, ctx, compat); 392e89a96f5SMichal Kazior 393d01a1e65SMichal Kazior return ctx; 394d01a1e65SMichal Kazior } 395d01a1e65SMichal Kazior 396d01a1e65SMichal Kazior return NULL; 397d01a1e65SMichal Kazior } 398d01a1e65SMichal Kazior 3995cbc95a7SEliad Peller bool ieee80211_is_radar_required(struct ieee80211_local *local) 400e4746851SSimon Wunderlich { 401e4746851SSimon Wunderlich struct ieee80211_sub_if_data *sdata; 402e4746851SSimon Wunderlich 403cc901de1SMichal Kazior lockdep_assert_held(&local->mtx); 404cc901de1SMichal Kazior 405e4746851SSimon Wunderlich rcu_read_lock(); 406e4746851SSimon Wunderlich list_for_each_entry_rcu(sdata, &local->interfaces, list) { 407e4746851SSimon Wunderlich if (sdata->radar_required) { 408e4746851SSimon Wunderlich rcu_read_unlock(); 409e4746851SSimon Wunderlich return true; 410e4746851SSimon Wunderlich } 411e4746851SSimon Wunderlich } 412e4746851SSimon Wunderlich rcu_read_unlock(); 413e4746851SSimon Wunderlich 414e4746851SSimon Wunderlich return false; 415e4746851SSimon Wunderlich } 416e4746851SSimon Wunderlich 417e7f2337aSEliad Peller static bool 418e7f2337aSEliad Peller ieee80211_chanctx_radar_required(struct ieee80211_local *local, 419e7f2337aSEliad Peller struct ieee80211_chanctx *ctx) 420e7f2337aSEliad Peller { 421e7f2337aSEliad Peller struct ieee80211_chanctx_conf *conf = &ctx->conf; 422e7f2337aSEliad Peller struct ieee80211_sub_if_data *sdata; 423e7f2337aSEliad Peller bool required = false; 424e7f2337aSEliad Peller 425e7f2337aSEliad Peller lockdep_assert_held(&local->chanctx_mtx); 426e7f2337aSEliad Peller lockdep_assert_held(&local->mtx); 427e7f2337aSEliad Peller 428e7f2337aSEliad Peller rcu_read_lock(); 429e7f2337aSEliad Peller list_for_each_entry_rcu(sdata, &local->interfaces, list) { 430e7f2337aSEliad Peller if (!ieee80211_sdata_running(sdata)) 431e7f2337aSEliad Peller continue; 432e7f2337aSEliad Peller if (rcu_access_pointer(sdata->vif.chanctx_conf) != conf) 433e7f2337aSEliad Peller continue; 434e7f2337aSEliad Peller if (!sdata->radar_required) 435e7f2337aSEliad Peller continue; 436e7f2337aSEliad Peller 437e7f2337aSEliad Peller required = true; 438e7f2337aSEliad Peller break; 439e7f2337aSEliad Peller } 440e7f2337aSEliad Peller rcu_read_unlock(); 441e7f2337aSEliad Peller 442e7f2337aSEliad Peller return required; 443e7f2337aSEliad Peller } 444e7f2337aSEliad Peller 445d01a1e65SMichal Kazior static struct ieee80211_chanctx * 446ed68ebcaSMichal Kazior ieee80211_alloc_chanctx(struct ieee80211_local *local, 4474bf88530SJohannes Berg const struct cfg80211_chan_def *chandef, 448d01a1e65SMichal Kazior enum ieee80211_chanctx_mode mode) 449d01a1e65SMichal Kazior { 450d01a1e65SMichal Kazior struct ieee80211_chanctx *ctx; 451d01a1e65SMichal Kazior 452d01a1e65SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 453d01a1e65SMichal Kazior 454d01a1e65SMichal Kazior ctx = kzalloc(sizeof(*ctx) + local->hw.chanctx_data_size, GFP_KERNEL); 455d01a1e65SMichal Kazior if (!ctx) 456ed68ebcaSMichal Kazior return NULL; 457d01a1e65SMichal Kazior 458484298adSMichal Kazior INIT_LIST_HEAD(&ctx->assigned_vifs); 459e3afb920SMichal Kazior INIT_LIST_HEAD(&ctx->reserved_vifs); 4604bf88530SJohannes Berg ctx->conf.def = *chandef; 46104ecd257SJohannes Berg ctx->conf.rx_chains_static = 1; 46204ecd257SJohannes Berg ctx->conf.rx_chains_dynamic = 1; 463d01a1e65SMichal Kazior ctx->mode = mode; 464e7f2337aSEliad Peller ctx->conf.radar_enabled = false; 46521f659bfSEliad Peller ieee80211_recalc_chanctx_min_def(local, ctx); 466ed68ebcaSMichal Kazior 467ed68ebcaSMichal Kazior return ctx; 468ed68ebcaSMichal Kazior } 469ed68ebcaSMichal Kazior 470ed68ebcaSMichal Kazior static int ieee80211_add_chanctx(struct ieee80211_local *local, 471ed68ebcaSMichal Kazior struct ieee80211_chanctx *ctx) 472ed68ebcaSMichal Kazior { 473ed68ebcaSMichal Kazior u32 changed; 474ed68ebcaSMichal Kazior int err; 475ed68ebcaSMichal Kazior 476ed68ebcaSMichal Kazior lockdep_assert_held(&local->mtx); 477ed68ebcaSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 478ed68ebcaSMichal Kazior 479e4746851SSimon Wunderlich if (!local->use_chanctx) 480e4746851SSimon Wunderlich local->hw.conf.radar_enabled = ctx->conf.radar_enabled; 481d01a1e65SMichal Kazior 482382a103bSJohannes Berg /* turn idle off *before* setting channel -- some drivers need that */ 483382a103bSJohannes Berg changed = ieee80211_idle_off(local); 484382a103bSJohannes Berg if (changed) 485382a103bSJohannes Berg ieee80211_hw_config(local, changed); 486382a103bSJohannes Berg 48755de908aSJohannes Berg if (!local->use_chanctx) { 488ed68ebcaSMichal Kazior local->_oper_chandef = ctx->conf.def; 4899b4816f5SMichal Kazior ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); 49055de908aSJohannes Berg } else { 49135f2fce9SMichal Kazior err = drv_add_chanctx(local, ctx); 49235f2fce9SMichal Kazior if (err) { 493382a103bSJohannes Berg ieee80211_recalc_idle(local); 494ed68ebcaSMichal Kazior return err; 495ed68ebcaSMichal Kazior } 496ed68ebcaSMichal Kazior } 497ed68ebcaSMichal Kazior 498ed68ebcaSMichal Kazior return 0; 499ed68ebcaSMichal Kazior } 500ed68ebcaSMichal Kazior 501ed68ebcaSMichal Kazior static struct ieee80211_chanctx * 502ed68ebcaSMichal Kazior ieee80211_new_chanctx(struct ieee80211_local *local, 503ed68ebcaSMichal Kazior const struct cfg80211_chan_def *chandef, 504ed68ebcaSMichal Kazior enum ieee80211_chanctx_mode mode) 505ed68ebcaSMichal Kazior { 506ed68ebcaSMichal Kazior struct ieee80211_chanctx *ctx; 507ed68ebcaSMichal Kazior int err; 508ed68ebcaSMichal Kazior 509ed68ebcaSMichal Kazior lockdep_assert_held(&local->mtx); 510ed68ebcaSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 511ed68ebcaSMichal Kazior 512ed68ebcaSMichal Kazior ctx = ieee80211_alloc_chanctx(local, chandef, mode); 513ed68ebcaSMichal Kazior if (!ctx) 514ed68ebcaSMichal Kazior return ERR_PTR(-ENOMEM); 515ed68ebcaSMichal Kazior 516ed68ebcaSMichal Kazior err = ieee80211_add_chanctx(local, ctx); 517ed68ebcaSMichal Kazior if (err) { 518ed68ebcaSMichal Kazior kfree(ctx); 51934a3740dSJohannes Berg return ERR_PTR(err); 52035f2fce9SMichal Kazior } 52135f2fce9SMichal Kazior 5223448c005SJohannes Berg list_add_rcu(&ctx->list, &local->chanctx_list); 523d01a1e65SMichal Kazior return ctx; 524d01a1e65SMichal Kazior } 525d01a1e65SMichal Kazior 5261f0d54cdSMichal Kazior static void ieee80211_del_chanctx(struct ieee80211_local *local, 527d01a1e65SMichal Kazior struct ieee80211_chanctx *ctx) 528d01a1e65SMichal Kazior { 529d01a1e65SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 530d01a1e65SMichal Kazior 53155de908aSJohannes Berg if (!local->use_chanctx) { 532675a0b04SKarl Beldan struct cfg80211_chan_def *chandef = &local->_oper_chandef; 533675a0b04SKarl Beldan chandef->width = NL80211_CHAN_WIDTH_20_NOHT; 534675a0b04SKarl Beldan chandef->center_freq1 = chandef->chan->center_freq; 535675a0b04SKarl Beldan chandef->center_freq2 = 0; 536e4746851SSimon Wunderlich 537e4746851SSimon Wunderlich /* NOTE: Disabling radar is only valid here for 538e4746851SSimon Wunderlich * single channel context. To be sure, check it ... 539e4746851SSimon Wunderlich */ 5401f0d54cdSMichal Kazior WARN_ON(local->hw.conf.radar_enabled && 5411f0d54cdSMichal Kazior !list_empty(&local->chanctx_list)); 5421f0d54cdSMichal Kazior 543e4746851SSimon Wunderlich local->hw.conf.radar_enabled = false; 544e4746851SSimon Wunderlich 5459b4816f5SMichal Kazior ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); 54655de908aSJohannes Berg } else { 54735f2fce9SMichal Kazior drv_remove_chanctx(local, ctx); 54855de908aSJohannes Berg } 54935f2fce9SMichal Kazior 550fd0f979aSJohannes Berg ieee80211_recalc_idle(local); 551d01a1e65SMichal Kazior } 552d01a1e65SMichal Kazior 5531f0d54cdSMichal Kazior static void ieee80211_free_chanctx(struct ieee80211_local *local, 554d01a1e65SMichal Kazior struct ieee80211_chanctx *ctx) 555d01a1e65SMichal Kazior { 556d01a1e65SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 557d01a1e65SMichal Kazior 558c0166da9SMichal Kazior WARN_ON_ONCE(ieee80211_chanctx_refcount(local, ctx) != 0); 55935f2fce9SMichal Kazior 5601f0d54cdSMichal Kazior list_del_rcu(&ctx->list); 5611f0d54cdSMichal Kazior ieee80211_del_chanctx(local, ctx); 5621f0d54cdSMichal Kazior kfree_rcu(ctx, rcu_head); 563d01a1e65SMichal Kazior } 564d01a1e65SMichal Kazior 5650fabfaafSArik Nemtsov void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local, 566e89a96f5SMichal Kazior struct ieee80211_chanctx *ctx) 567e89a96f5SMichal Kazior { 568e89a96f5SMichal Kazior struct ieee80211_chanctx_conf *conf = &ctx->conf; 569e89a96f5SMichal Kazior struct ieee80211_sub_if_data *sdata; 5704bf88530SJohannes Berg const struct cfg80211_chan_def *compat = NULL; 5710fabfaafSArik Nemtsov struct sta_info *sta; 572e89a96f5SMichal Kazior 573e89a96f5SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 574e89a96f5SMichal Kazior 575e89a96f5SMichal Kazior rcu_read_lock(); 576e89a96f5SMichal Kazior list_for_each_entry_rcu(sdata, &local->interfaces, list) { 5774bf88530SJohannes Berg 578e89a96f5SMichal Kazior if (!ieee80211_sdata_running(sdata)) 579e89a96f5SMichal Kazior continue; 580e89a96f5SMichal Kazior if (rcu_access_pointer(sdata->vif.chanctx_conf) != conf) 581e89a96f5SMichal Kazior continue; 5820e67c136SFelix Fietkau if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) 5830e67c136SFelix Fietkau continue; 584e89a96f5SMichal Kazior 5854bf88530SJohannes Berg if (!compat) 5864bf88530SJohannes Berg compat = &sdata->vif.bss_conf.chandef; 5874bf88530SJohannes Berg 5884bf88530SJohannes Berg compat = cfg80211_chandef_compatible( 5894bf88530SJohannes Berg &sdata->vif.bss_conf.chandef, compat); 590a00f4f6eSMichal Kazior if (WARN_ON_ONCE(!compat)) 5914bf88530SJohannes Berg break; 592e89a96f5SMichal Kazior } 5930fabfaafSArik Nemtsov 5940fabfaafSArik Nemtsov /* TDLS peers can sometimes affect the chandef width */ 5950fabfaafSArik Nemtsov list_for_each_entry_rcu(sta, &local->sta_list, list) { 5960fabfaafSArik Nemtsov if (!sta->uploaded || 5970fabfaafSArik Nemtsov !test_sta_flag(sta, WLAN_STA_TDLS_WIDER_BW) || 5980fabfaafSArik Nemtsov !test_sta_flag(sta, WLAN_STA_AUTHORIZED) || 5990fabfaafSArik Nemtsov !sta->tdls_chandef.chan) 6000fabfaafSArik Nemtsov continue; 6010fabfaafSArik Nemtsov 6020fabfaafSArik Nemtsov compat = cfg80211_chandef_compatible(&sta->tdls_chandef, 6030fabfaafSArik Nemtsov compat); 6040fabfaafSArik Nemtsov if (WARN_ON_ONCE(!compat)) 6050fabfaafSArik Nemtsov break; 6060fabfaafSArik Nemtsov } 607e89a96f5SMichal Kazior rcu_read_unlock(); 608e89a96f5SMichal Kazior 609a00f4f6eSMichal Kazior if (!compat) 6104bf88530SJohannes Berg return; 611e89a96f5SMichal Kazior 61218942d3bSJohannes Berg ieee80211_change_chanctx(local, ctx, compat); 613e89a96f5SMichal Kazior } 614e89a96f5SMichal Kazior 615367bbd10SJohannes Berg static void ieee80211_recalc_radar_chanctx(struct ieee80211_local *local, 616367bbd10SJohannes Berg struct ieee80211_chanctx *chanctx) 617367bbd10SJohannes Berg { 618367bbd10SJohannes Berg bool radar_enabled; 619367bbd10SJohannes Berg 620367bbd10SJohannes Berg lockdep_assert_held(&local->chanctx_mtx); 6215cbc95a7SEliad Peller /* for ieee80211_is_radar_required */ 62234a3740dSJohannes Berg lockdep_assert_held(&local->mtx); 623367bbd10SJohannes Berg 624e7f2337aSEliad Peller radar_enabled = ieee80211_chanctx_radar_required(local, chanctx); 625367bbd10SJohannes Berg 626367bbd10SJohannes Berg if (radar_enabled == chanctx->conf.radar_enabled) 627367bbd10SJohannes Berg return; 628367bbd10SJohannes Berg 629367bbd10SJohannes Berg chanctx->conf.radar_enabled = radar_enabled; 630367bbd10SJohannes Berg 631367bbd10SJohannes Berg if (!local->use_chanctx) { 632367bbd10SJohannes Berg local->hw.conf.radar_enabled = chanctx->conf.radar_enabled; 633367bbd10SJohannes Berg ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); 634367bbd10SJohannes Berg } 635367bbd10SJohannes Berg 636367bbd10SJohannes Berg drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RADAR); 637367bbd10SJohannes Berg } 638367bbd10SJohannes Berg 63977eeba97SLuciano Coelho static int ieee80211_assign_vif_chanctx(struct ieee80211_sub_if_data *sdata, 64077eeba97SLuciano Coelho struct ieee80211_chanctx *new_ctx) 641d01a1e65SMichal Kazior { 64235f2fce9SMichal Kazior struct ieee80211_local *local = sdata->local; 64377eeba97SLuciano Coelho struct ieee80211_chanctx_conf *conf; 64477eeba97SLuciano Coelho struct ieee80211_chanctx *curr_ctx = NULL; 64577eeba97SLuciano Coelho int ret = 0; 646d01a1e65SMichal Kazior 64777eeba97SLuciano Coelho conf = rcu_dereference_protected(sdata->vif.chanctx_conf, 64877eeba97SLuciano Coelho lockdep_is_held(&local->chanctx_mtx)); 649d01a1e65SMichal Kazior 65077eeba97SLuciano Coelho if (conf) { 65177eeba97SLuciano Coelho curr_ctx = container_of(conf, struct ieee80211_chanctx, conf); 65235f2fce9SMichal Kazior 65377eeba97SLuciano Coelho drv_unassign_vif_chanctx(local, sdata, curr_ctx); 65477eeba97SLuciano Coelho conf = NULL; 655484298adSMichal Kazior list_del(&sdata->assigned_chanctx_list); 65677eeba97SLuciano Coelho } 65777eeba97SLuciano Coelho 65877eeba97SLuciano Coelho if (new_ctx) { 65977eeba97SLuciano Coelho ret = drv_assign_vif_chanctx(local, sdata, new_ctx); 66077eeba97SLuciano Coelho if (ret) 66177eeba97SLuciano Coelho goto out; 66277eeba97SLuciano Coelho 66377eeba97SLuciano Coelho conf = &new_ctx->conf; 664484298adSMichal Kazior list_add(&sdata->assigned_chanctx_list, 665484298adSMichal Kazior &new_ctx->assigned_vifs); 66677eeba97SLuciano Coelho } 66777eeba97SLuciano Coelho 66877eeba97SLuciano Coelho out: 66977eeba97SLuciano Coelho rcu_assign_pointer(sdata->vif.chanctx_conf, conf); 67077eeba97SLuciano Coelho 67177eeba97SLuciano Coelho sdata->vif.bss_conf.idle = !conf; 67277eeba97SLuciano Coelho 673c0166da9SMichal Kazior if (curr_ctx && ieee80211_chanctx_num_assigned(local, curr_ctx) > 0) { 67477eeba97SLuciano Coelho ieee80211_recalc_chanctx_chantype(local, curr_ctx); 67577eeba97SLuciano Coelho ieee80211_recalc_smps_chanctx(local, curr_ctx); 67677eeba97SLuciano Coelho ieee80211_recalc_radar_chanctx(local, curr_ctx); 67777eeba97SLuciano Coelho ieee80211_recalc_chanctx_min_def(local, curr_ctx); 67877eeba97SLuciano Coelho } 67977eeba97SLuciano Coelho 680c0166da9SMichal Kazior if (new_ctx && ieee80211_chanctx_num_assigned(local, new_ctx) > 0) { 681db82d8a9SLorenzo Bianconi ieee80211_recalc_txpower(sdata, false); 68277eeba97SLuciano Coelho ieee80211_recalc_chanctx_min_def(local, new_ctx); 68377eeba97SLuciano Coelho } 6845bbe754dSJohannes Berg 6855bbe754dSJohannes Berg if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE && 6865bbe754dSJohannes Berg sdata->vif.type != NL80211_IFTYPE_MONITOR) 68777eeba97SLuciano Coelho ieee80211_bss_info_change_notify(sdata, 68877eeba97SLuciano Coelho BSS_CHANGED_IDLE); 689fd0f979aSJohannes Berg 69017c18bf8SJohannes Berg ieee80211_check_fast_xmit_iface(sdata); 69117c18bf8SJohannes Berg 69277eeba97SLuciano Coelho return ret; 693d01a1e65SMichal Kazior } 694d01a1e65SMichal Kazior 69504ecd257SJohannes Berg void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local, 69604ecd257SJohannes Berg struct ieee80211_chanctx *chanctx) 69704ecd257SJohannes Berg { 69804ecd257SJohannes Berg struct ieee80211_sub_if_data *sdata; 69904ecd257SJohannes Berg u8 rx_chains_static, rx_chains_dynamic; 70004ecd257SJohannes Berg 70104ecd257SJohannes Berg lockdep_assert_held(&local->chanctx_mtx); 70204ecd257SJohannes Berg 70304ecd257SJohannes Berg rx_chains_static = 1; 70404ecd257SJohannes Berg rx_chains_dynamic = 1; 70504ecd257SJohannes Berg 70604ecd257SJohannes Berg rcu_read_lock(); 70704ecd257SJohannes Berg list_for_each_entry_rcu(sdata, &local->interfaces, list) { 70804ecd257SJohannes Berg u8 needed_static, needed_dynamic; 70904ecd257SJohannes Berg 71004ecd257SJohannes Berg if (!ieee80211_sdata_running(sdata)) 71104ecd257SJohannes Berg continue; 71204ecd257SJohannes Berg 71304ecd257SJohannes Berg if (rcu_access_pointer(sdata->vif.chanctx_conf) != 71404ecd257SJohannes Berg &chanctx->conf) 71504ecd257SJohannes Berg continue; 71604ecd257SJohannes Berg 71704ecd257SJohannes Berg switch (sdata->vif.type) { 71804ecd257SJohannes Berg case NL80211_IFTYPE_P2P_DEVICE: 71904ecd257SJohannes Berg continue; 72004ecd257SJohannes Berg case NL80211_IFTYPE_STATION: 72104ecd257SJohannes Berg if (!sdata->u.mgd.associated) 72204ecd257SJohannes Berg continue; 72304ecd257SJohannes Berg break; 72404ecd257SJohannes Berg case NL80211_IFTYPE_AP_VLAN: 72504ecd257SJohannes Berg continue; 72604ecd257SJohannes Berg case NL80211_IFTYPE_AP: 72704ecd257SJohannes Berg case NL80211_IFTYPE_ADHOC: 72804ecd257SJohannes Berg case NL80211_IFTYPE_WDS: 72904ecd257SJohannes Berg case NL80211_IFTYPE_MESH_POINT: 730239281f8SRostislav Lisovy case NL80211_IFTYPE_OCB: 73104ecd257SJohannes Berg break; 73204ecd257SJohannes Berg default: 73304ecd257SJohannes Berg WARN_ON_ONCE(1); 73404ecd257SJohannes Berg } 73504ecd257SJohannes Berg 73604ecd257SJohannes Berg switch (sdata->smps_mode) { 73704ecd257SJohannes Berg default: 73804ecd257SJohannes Berg WARN_ONCE(1, "Invalid SMPS mode %d\n", 73904ecd257SJohannes Berg sdata->smps_mode); 74004ecd257SJohannes Berg /* fall through */ 74104ecd257SJohannes Berg case IEEE80211_SMPS_OFF: 74204ecd257SJohannes Berg needed_static = sdata->needed_rx_chains; 74304ecd257SJohannes Berg needed_dynamic = sdata->needed_rx_chains; 74404ecd257SJohannes Berg break; 74504ecd257SJohannes Berg case IEEE80211_SMPS_DYNAMIC: 74604ecd257SJohannes Berg needed_static = 1; 74704ecd257SJohannes Berg needed_dynamic = sdata->needed_rx_chains; 74804ecd257SJohannes Berg break; 74904ecd257SJohannes Berg case IEEE80211_SMPS_STATIC: 75004ecd257SJohannes Berg needed_static = 1; 75104ecd257SJohannes Berg needed_dynamic = 1; 75204ecd257SJohannes Berg break; 75304ecd257SJohannes Berg } 75404ecd257SJohannes Berg 75504ecd257SJohannes Berg rx_chains_static = max(rx_chains_static, needed_static); 75604ecd257SJohannes Berg rx_chains_dynamic = max(rx_chains_dynamic, needed_dynamic); 75704ecd257SJohannes Berg } 7587b8a9cddSIdo Yariv 7597b8a9cddSIdo Yariv /* Disable SMPS for the monitor interface */ 7607b8a9cddSIdo Yariv sdata = rcu_dereference(local->monitor_sdata); 7617b8a9cddSIdo Yariv if (sdata && 7627b8a9cddSIdo Yariv rcu_access_pointer(sdata->vif.chanctx_conf) == &chanctx->conf) 7637b8a9cddSIdo Yariv rx_chains_dynamic = rx_chains_static = local->rx_chains; 7647b8a9cddSIdo Yariv 76504ecd257SJohannes Berg rcu_read_unlock(); 76604ecd257SJohannes Berg 76704ecd257SJohannes Berg if (!local->use_chanctx) { 76804ecd257SJohannes Berg if (rx_chains_static > 1) 76904ecd257SJohannes Berg local->smps_mode = IEEE80211_SMPS_OFF; 77004ecd257SJohannes Berg else if (rx_chains_dynamic > 1) 77104ecd257SJohannes Berg local->smps_mode = IEEE80211_SMPS_DYNAMIC; 77204ecd257SJohannes Berg else 77304ecd257SJohannes Berg local->smps_mode = IEEE80211_SMPS_STATIC; 77404ecd257SJohannes Berg ieee80211_hw_config(local, 0); 77504ecd257SJohannes Berg } 77604ecd257SJohannes Berg 77704ecd257SJohannes Berg if (rx_chains_static == chanctx->conf.rx_chains_static && 77804ecd257SJohannes Berg rx_chains_dynamic == chanctx->conf.rx_chains_dynamic) 77904ecd257SJohannes Berg return; 78004ecd257SJohannes Berg 78104ecd257SJohannes Berg chanctx->conf.rx_chains_static = rx_chains_static; 78204ecd257SJohannes Berg chanctx->conf.rx_chains_dynamic = rx_chains_dynamic; 78304ecd257SJohannes Berg drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RX_CHAINS); 78404ecd257SJohannes Berg } 78504ecd257SJohannes Berg 78611335a55SLuciano Coelho static void 78711335a55SLuciano Coelho __ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata, 78811335a55SLuciano Coelho bool clear) 78911335a55SLuciano Coelho { 79033926eb7SJohannes Berg struct ieee80211_local *local __maybe_unused = sdata->local; 79111335a55SLuciano Coelho struct ieee80211_sub_if_data *vlan; 79211335a55SLuciano Coelho struct ieee80211_chanctx_conf *conf; 79311335a55SLuciano Coelho 79411335a55SLuciano Coelho if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP)) 79511335a55SLuciano Coelho return; 79611335a55SLuciano Coelho 79711335a55SLuciano Coelho lockdep_assert_held(&local->mtx); 79811335a55SLuciano Coelho 79911335a55SLuciano Coelho /* Check that conf exists, even when clearing this function 80011335a55SLuciano Coelho * must be called with the AP's channel context still there 80111335a55SLuciano Coelho * as it would otherwise cause VLANs to have an invalid 80211335a55SLuciano Coelho * channel context pointer for a while, possibly pointing 80311335a55SLuciano Coelho * to a channel context that has already been freed. 80411335a55SLuciano Coelho */ 80511335a55SLuciano Coelho conf = rcu_dereference_protected(sdata->vif.chanctx_conf, 80611335a55SLuciano Coelho lockdep_is_held(&local->chanctx_mtx)); 80711335a55SLuciano Coelho WARN_ON(!conf); 80811335a55SLuciano Coelho 80911335a55SLuciano Coelho if (clear) 81011335a55SLuciano Coelho conf = NULL; 81111335a55SLuciano Coelho 81211335a55SLuciano Coelho list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) 81311335a55SLuciano Coelho rcu_assign_pointer(vlan->vif.chanctx_conf, conf); 81411335a55SLuciano Coelho } 81511335a55SLuciano Coelho 81611335a55SLuciano Coelho void ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata, 81711335a55SLuciano Coelho bool clear) 81811335a55SLuciano Coelho { 81911335a55SLuciano Coelho struct ieee80211_local *local = sdata->local; 82011335a55SLuciano Coelho 82111335a55SLuciano Coelho mutex_lock(&local->chanctx_mtx); 82211335a55SLuciano Coelho 82311335a55SLuciano Coelho __ieee80211_vif_copy_chanctx_to_vlans(sdata, clear); 82411335a55SLuciano Coelho 82511335a55SLuciano Coelho mutex_unlock(&local->chanctx_mtx); 82611335a55SLuciano Coelho } 82711335a55SLuciano Coelho 82811335a55SLuciano Coelho int ieee80211_vif_unreserve_chanctx(struct ieee80211_sub_if_data *sdata) 82911335a55SLuciano Coelho { 830e3afb920SMichal Kazior struct ieee80211_chanctx *ctx = sdata->reserved_chanctx; 831e3afb920SMichal Kazior 83211335a55SLuciano Coelho lockdep_assert_held(&sdata->local->chanctx_mtx); 83311335a55SLuciano Coelho 834e3afb920SMichal Kazior if (WARN_ON(!ctx)) 83511335a55SLuciano Coelho return -EINVAL; 83611335a55SLuciano Coelho 837e3afb920SMichal Kazior list_del(&sdata->reserved_chanctx_list); 83811335a55SLuciano Coelho sdata->reserved_chanctx = NULL; 83911335a55SLuciano Coelho 8405bcae31dSMichal Kazior if (ieee80211_chanctx_refcount(sdata->local, ctx) == 0) { 8415bcae31dSMichal Kazior if (ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER) { 8425bcae31dSMichal Kazior if (WARN_ON(!ctx->replace_ctx)) 8435bcae31dSMichal Kazior return -EINVAL; 8445bcae31dSMichal Kazior 8455bcae31dSMichal Kazior WARN_ON(ctx->replace_ctx->replace_state != 8465bcae31dSMichal Kazior IEEE80211_CHANCTX_WILL_BE_REPLACED); 8475bcae31dSMichal Kazior WARN_ON(ctx->replace_ctx->replace_ctx != ctx); 8485bcae31dSMichal Kazior 8495bcae31dSMichal Kazior ctx->replace_ctx->replace_ctx = NULL; 8505bcae31dSMichal Kazior ctx->replace_ctx->replace_state = 8515bcae31dSMichal Kazior IEEE80211_CHANCTX_REPLACE_NONE; 8525bcae31dSMichal Kazior 8535bcae31dSMichal Kazior list_del_rcu(&ctx->list); 8545bcae31dSMichal Kazior kfree_rcu(ctx, rcu_head); 8555bcae31dSMichal Kazior } else { 856e3afb920SMichal Kazior ieee80211_free_chanctx(sdata->local, ctx); 8575bcae31dSMichal Kazior } 8585bcae31dSMichal Kazior } 859e3afb920SMichal Kazior 86011335a55SLuciano Coelho return 0; 86111335a55SLuciano Coelho } 86211335a55SLuciano Coelho 86311335a55SLuciano Coelho int ieee80211_vif_reserve_chanctx(struct ieee80211_sub_if_data *sdata, 86411335a55SLuciano Coelho const struct cfg80211_chan_def *chandef, 86509332481SMichal Kazior enum ieee80211_chanctx_mode mode, 86609332481SMichal Kazior bool radar_required) 86711335a55SLuciano Coelho { 86811335a55SLuciano Coelho struct ieee80211_local *local = sdata->local; 8695bcae31dSMichal Kazior struct ieee80211_chanctx *new_ctx, *curr_ctx, *ctx; 87011335a55SLuciano Coelho 8715bcae31dSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 87211335a55SLuciano Coelho 8735bcae31dSMichal Kazior curr_ctx = ieee80211_vif_get_chanctx(sdata); 8745bcae31dSMichal Kazior if (curr_ctx && local->use_chanctx && !local->ops->switch_vif_chanctx) 8755bcae31dSMichal Kazior return -ENOTSUPP; 87611335a55SLuciano Coelho 87713f348a8SMichal Kazior new_ctx = ieee80211_find_reservation_chanctx(local, chandef, mode); 87811335a55SLuciano Coelho if (!new_ctx) { 8795bcae31dSMichal Kazior if (ieee80211_can_create_new_chanctx(local)) { 88011335a55SLuciano Coelho new_ctx = ieee80211_new_chanctx(local, chandef, mode); 8815bcae31dSMichal Kazior if (IS_ERR(new_ctx)) 8825bcae31dSMichal Kazior return PTR_ERR(new_ctx); 883c2b90ad8SMichal Kazior } else { 8845bcae31dSMichal Kazior if (!curr_ctx || 8855bcae31dSMichal Kazior (curr_ctx->replace_state == 8865bcae31dSMichal Kazior IEEE80211_CHANCTX_WILL_BE_REPLACED) || 8875bcae31dSMichal Kazior !list_empty(&curr_ctx->reserved_vifs)) { 8885bcae31dSMichal Kazior /* 8895bcae31dSMichal Kazior * Another vif already requested this context 8905bcae31dSMichal Kazior * for a reservation. Find another one hoping 8915bcae31dSMichal Kazior * all vifs assigned to it will also switch 8925bcae31dSMichal Kazior * soon enough. 8935bcae31dSMichal Kazior * 8945bcae31dSMichal Kazior * TODO: This needs a little more work as some 8955bcae31dSMichal Kazior * cases (more than 2 chanctx capable devices) 8965bcae31dSMichal Kazior * may fail which could otherwise succeed 8975bcae31dSMichal Kazior * provided some channel context juggling was 8985bcae31dSMichal Kazior * performed. 8995bcae31dSMichal Kazior * 9005bcae31dSMichal Kazior * Consider ctx1..3, vif1..6, each ctx has 2 9015bcae31dSMichal Kazior * vifs. vif1 and vif2 from ctx1 request new 9025bcae31dSMichal Kazior * different chandefs starting 2 in-place 9035bcae31dSMichal Kazior * reserations with ctx4 and ctx5 replacing 9045bcae31dSMichal Kazior * ctx1 and ctx2 respectively. Next vif5 and 9055bcae31dSMichal Kazior * vif6 from ctx3 reserve ctx4. If vif3 and 9065bcae31dSMichal Kazior * vif4 remain on ctx2 as they are then this 9075bcae31dSMichal Kazior * fails unless `replace_ctx` from ctx5 is 9085bcae31dSMichal Kazior * replaced with ctx3. 9095bcae31dSMichal Kazior */ 9105bcae31dSMichal Kazior list_for_each_entry(ctx, &local->chanctx_list, 9115bcae31dSMichal Kazior list) { 9125bcae31dSMichal Kazior if (ctx->replace_state != 9135bcae31dSMichal Kazior IEEE80211_CHANCTX_REPLACE_NONE) 9145bcae31dSMichal Kazior continue; 9155bcae31dSMichal Kazior 9165bcae31dSMichal Kazior if (!list_empty(&ctx->reserved_vifs)) 9175bcae31dSMichal Kazior continue; 9185bcae31dSMichal Kazior 9195bcae31dSMichal Kazior curr_ctx = ctx; 9205bcae31dSMichal Kazior break; 9215bcae31dSMichal Kazior } 9225bcae31dSMichal Kazior } 9235bcae31dSMichal Kazior 9245bcae31dSMichal Kazior /* 9255bcae31dSMichal Kazior * If that's true then all available contexts already 9265bcae31dSMichal Kazior * have reservations and cannot be used. 9275bcae31dSMichal Kazior */ 9285bcae31dSMichal Kazior if (!curr_ctx || 9295bcae31dSMichal Kazior (curr_ctx->replace_state == 9305bcae31dSMichal Kazior IEEE80211_CHANCTX_WILL_BE_REPLACED) || 9315bcae31dSMichal Kazior !list_empty(&curr_ctx->reserved_vifs)) 9325bcae31dSMichal Kazior return -EBUSY; 9335bcae31dSMichal Kazior 9345bcae31dSMichal Kazior new_ctx = ieee80211_alloc_chanctx(local, chandef, mode); 9355bcae31dSMichal Kazior if (!new_ctx) 9365bcae31dSMichal Kazior return -ENOMEM; 9375bcae31dSMichal Kazior 9385bcae31dSMichal Kazior new_ctx->replace_ctx = curr_ctx; 9395bcae31dSMichal Kazior new_ctx->replace_state = 9405bcae31dSMichal Kazior IEEE80211_CHANCTX_REPLACES_OTHER; 9415bcae31dSMichal Kazior 9425bcae31dSMichal Kazior curr_ctx->replace_ctx = new_ctx; 9435bcae31dSMichal Kazior curr_ctx->replace_state = 9445bcae31dSMichal Kazior IEEE80211_CHANCTX_WILL_BE_REPLACED; 9455bcae31dSMichal Kazior 9465bcae31dSMichal Kazior list_add_rcu(&new_ctx->list, &local->chanctx_list); 94711335a55SLuciano Coelho } 9485d52ee81SLuciano Coelho } 94911335a55SLuciano Coelho 950e3afb920SMichal Kazior list_add(&sdata->reserved_chanctx_list, &new_ctx->reserved_vifs); 95111335a55SLuciano Coelho sdata->reserved_chanctx = new_ctx; 95211335a55SLuciano Coelho sdata->reserved_chandef = *chandef; 95309332481SMichal Kazior sdata->reserved_radar_required = radar_required; 9545bcae31dSMichal Kazior sdata->reserved_ready = false; 9555bcae31dSMichal Kazior 9565bcae31dSMichal Kazior return 0; 95711335a55SLuciano Coelho } 95811335a55SLuciano Coelho 95903078de4SMichal Kazior static void 96003078de4SMichal Kazior ieee80211_vif_chanctx_reservation_complete(struct ieee80211_sub_if_data *sdata) 96103078de4SMichal Kazior { 96203078de4SMichal Kazior switch (sdata->vif.type) { 96303078de4SMichal Kazior case NL80211_IFTYPE_ADHOC: 96403078de4SMichal Kazior case NL80211_IFTYPE_AP: 96503078de4SMichal Kazior case NL80211_IFTYPE_MESH_POINT: 9666e0bd6c3SRostislav Lisovy case NL80211_IFTYPE_OCB: 96703078de4SMichal Kazior ieee80211_queue_work(&sdata->local->hw, 96803078de4SMichal Kazior &sdata->csa_finalize_work); 96903078de4SMichal Kazior break; 97003078de4SMichal Kazior case NL80211_IFTYPE_STATION: 9714c3ebc56SMichal Kazior ieee80211_queue_work(&sdata->local->hw, 9724c3ebc56SMichal Kazior &sdata->u.mgd.chswitch_work); 9734c3ebc56SMichal Kazior break; 9744c3ebc56SMichal Kazior case NL80211_IFTYPE_UNSPECIFIED: 97503078de4SMichal Kazior case NL80211_IFTYPE_AP_VLAN: 97603078de4SMichal Kazior case NL80211_IFTYPE_WDS: 97703078de4SMichal Kazior case NL80211_IFTYPE_MONITOR: 97803078de4SMichal Kazior case NL80211_IFTYPE_P2P_CLIENT: 97903078de4SMichal Kazior case NL80211_IFTYPE_P2P_GO: 98003078de4SMichal Kazior case NL80211_IFTYPE_P2P_DEVICE: 98103078de4SMichal Kazior case NUM_NL80211_IFTYPES: 98203078de4SMichal Kazior WARN_ON(1); 98303078de4SMichal Kazior break; 98403078de4SMichal Kazior } 98503078de4SMichal Kazior } 98603078de4SMichal Kazior 9872967e031SFelix Fietkau static void 9882967e031SFelix Fietkau ieee80211_vif_update_chandef(struct ieee80211_sub_if_data *sdata, 9892967e031SFelix Fietkau const struct cfg80211_chan_def *chandef) 9902967e031SFelix Fietkau { 9912967e031SFelix Fietkau struct ieee80211_sub_if_data *vlan; 9922967e031SFelix Fietkau 9932967e031SFelix Fietkau sdata->vif.bss_conf.chandef = *chandef; 9942967e031SFelix Fietkau 9952967e031SFelix Fietkau if (sdata->vif.type != NL80211_IFTYPE_AP) 9962967e031SFelix Fietkau return; 9972967e031SFelix Fietkau 9982967e031SFelix Fietkau list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) 9992967e031SFelix Fietkau vlan->vif.bss_conf.chandef = *chandef; 10002967e031SFelix Fietkau } 10012967e031SFelix Fietkau 10025bcae31dSMichal Kazior static int 10035bcae31dSMichal Kazior ieee80211_vif_use_reserved_reassign(struct ieee80211_sub_if_data *sdata) 100411335a55SLuciano Coelho { 100511335a55SLuciano Coelho struct ieee80211_local *local = sdata->local; 10065bcae31dSMichal Kazior struct ieee80211_vif_chanctx_switch vif_chsw[1] = {}; 10075bcae31dSMichal Kazior struct ieee80211_chanctx *old_ctx, *new_ctx; 10085bcae31dSMichal Kazior const struct cfg80211_chan_def *chandef; 10095bcae31dSMichal Kazior u32 changed = 0; 10105bcae31dSMichal Kazior int err; 101111335a55SLuciano Coelho 101211335a55SLuciano Coelho lockdep_assert_held(&local->mtx); 10135bcae31dSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 101411335a55SLuciano Coelho 10155bcae31dSMichal Kazior new_ctx = sdata->reserved_chanctx; 10165bcae31dSMichal Kazior old_ctx = ieee80211_vif_get_chanctx(sdata); 101711335a55SLuciano Coelho 10185bcae31dSMichal Kazior if (WARN_ON(!sdata->reserved_ready)) 10195bcae31dSMichal Kazior return -EBUSY; 102011335a55SLuciano Coelho 10215bcae31dSMichal Kazior if (WARN_ON(!new_ctx)) 10225bcae31dSMichal Kazior return -EINVAL; 102311335a55SLuciano Coelho 10245bcae31dSMichal Kazior if (WARN_ON(!old_ctx)) 10255bcae31dSMichal Kazior return -EINVAL; 102611335a55SLuciano Coelho 10275bcae31dSMichal Kazior if (WARN_ON(new_ctx->replace_state == 10285bcae31dSMichal Kazior IEEE80211_CHANCTX_REPLACES_OTHER)) 10295bcae31dSMichal Kazior return -EINVAL; 103011335a55SLuciano Coelho 10315bcae31dSMichal Kazior chandef = ieee80211_chanctx_non_reserved_chandef(local, new_ctx, 10325bcae31dSMichal Kazior &sdata->reserved_chandef); 10335bcae31dSMichal Kazior if (WARN_ON(!chandef)) 10345bcae31dSMichal Kazior return -EINVAL; 103511335a55SLuciano Coelho 1036bf45a242SAndrei Otcheretianski ieee80211_change_chanctx(local, new_ctx, chandef); 1037bf45a242SAndrei Otcheretianski 10385bcae31dSMichal Kazior vif_chsw[0].vif = &sdata->vif; 10395bcae31dSMichal Kazior vif_chsw[0].old_ctx = &old_ctx->conf; 10405bcae31dSMichal Kazior vif_chsw[0].new_ctx = &new_ctx->conf; 10415bcae31dSMichal Kazior 1042e3afb920SMichal Kazior list_del(&sdata->reserved_chanctx_list); 10435bcae31dSMichal Kazior sdata->reserved_chanctx = NULL; 104411335a55SLuciano Coelho 10455bcae31dSMichal Kazior err = drv_switch_vif_chanctx(local, vif_chsw, 1, 10465bcae31dSMichal Kazior CHANCTX_SWMODE_REASSIGN_VIF); 10475bcae31dSMichal Kazior if (err) { 10485bcae31dSMichal Kazior if (ieee80211_chanctx_refcount(local, new_ctx) == 0) 10495bcae31dSMichal Kazior ieee80211_free_chanctx(local, new_ctx); 10505bcae31dSMichal Kazior 105103078de4SMichal Kazior goto out; 105211335a55SLuciano Coelho } 105311335a55SLuciano Coelho 10545bcae31dSMichal Kazior list_move(&sdata->assigned_chanctx_list, &new_ctx->assigned_vifs); 10555bcae31dSMichal Kazior rcu_assign_pointer(sdata->vif.chanctx_conf, &new_ctx->conf); 10565bcae31dSMichal Kazior 105711335a55SLuciano Coelho if (sdata->vif.type == NL80211_IFTYPE_AP) 105811335a55SLuciano Coelho __ieee80211_vif_copy_chanctx_to_vlans(sdata, false); 10595bcae31dSMichal Kazior 106017c18bf8SJohannes Berg ieee80211_check_fast_xmit_iface(sdata); 106117c18bf8SJohannes Berg 10625bcae31dSMichal Kazior if (ieee80211_chanctx_refcount(local, old_ctx) == 0) 10635bcae31dSMichal Kazior ieee80211_free_chanctx(local, old_ctx); 10645bcae31dSMichal Kazior 10655bcae31dSMichal Kazior if (sdata->vif.bss_conf.chandef.width != sdata->reserved_chandef.width) 10665bcae31dSMichal Kazior changed = BSS_CHANGED_BANDWIDTH; 10675bcae31dSMichal Kazior 10682967e031SFelix Fietkau ieee80211_vif_update_chandef(sdata, &sdata->reserved_chandef); 10695bcae31dSMichal Kazior 1070722ddb0dSEmmanuel Grumbach ieee80211_recalc_smps_chanctx(local, new_ctx); 1071722ddb0dSEmmanuel Grumbach ieee80211_recalc_radar_chanctx(local, new_ctx); 1072722ddb0dSEmmanuel Grumbach ieee80211_recalc_chanctx_min_def(local, new_ctx); 1073722ddb0dSEmmanuel Grumbach 10745bcae31dSMichal Kazior if (changed) 10755bcae31dSMichal Kazior ieee80211_bss_info_change_notify(sdata, changed); 10765bcae31dSMichal Kazior 107703078de4SMichal Kazior out: 107803078de4SMichal Kazior ieee80211_vif_chanctx_reservation_complete(sdata); 10795bcae31dSMichal Kazior return err; 10805d52ee81SLuciano Coelho } 108111335a55SLuciano Coelho 10825bcae31dSMichal Kazior static int 10835bcae31dSMichal Kazior ieee80211_vif_use_reserved_assign(struct ieee80211_sub_if_data *sdata) 10845bcae31dSMichal Kazior { 10855bcae31dSMichal Kazior struct ieee80211_local *local = sdata->local; 10865bcae31dSMichal Kazior struct ieee80211_chanctx *old_ctx, *new_ctx; 10875bcae31dSMichal Kazior const struct cfg80211_chan_def *chandef; 10885bcae31dSMichal Kazior int err; 10895bcae31dSMichal Kazior 10905bcae31dSMichal Kazior old_ctx = ieee80211_vif_get_chanctx(sdata); 10915bcae31dSMichal Kazior new_ctx = sdata->reserved_chanctx; 10925bcae31dSMichal Kazior 10935bcae31dSMichal Kazior if (WARN_ON(!sdata->reserved_ready)) 10945bcae31dSMichal Kazior return -EINVAL; 10955bcae31dSMichal Kazior 10965bcae31dSMichal Kazior if (WARN_ON(old_ctx)) 10975bcae31dSMichal Kazior return -EINVAL; 10985bcae31dSMichal Kazior 10995bcae31dSMichal Kazior if (WARN_ON(!new_ctx)) 11005bcae31dSMichal Kazior return -EINVAL; 11015bcae31dSMichal Kazior 11025bcae31dSMichal Kazior if (WARN_ON(new_ctx->replace_state == 11035bcae31dSMichal Kazior IEEE80211_CHANCTX_REPLACES_OTHER)) 11045bcae31dSMichal Kazior return -EINVAL; 11055bcae31dSMichal Kazior 11065bcae31dSMichal Kazior chandef = ieee80211_chanctx_non_reserved_chandef(local, new_ctx, 11075bcae31dSMichal Kazior &sdata->reserved_chandef); 11085bcae31dSMichal Kazior if (WARN_ON(!chandef)) 11095bcae31dSMichal Kazior return -EINVAL; 11105bcae31dSMichal Kazior 1111bf45a242SAndrei Otcheretianski ieee80211_change_chanctx(local, new_ctx, chandef); 1112bf45a242SAndrei Otcheretianski 11135bcae31dSMichal Kazior list_del(&sdata->reserved_chanctx_list); 11145bcae31dSMichal Kazior sdata->reserved_chanctx = NULL; 11155bcae31dSMichal Kazior 11165bcae31dSMichal Kazior err = ieee80211_assign_vif_chanctx(sdata, new_ctx); 11175bcae31dSMichal Kazior if (err) { 11185bcae31dSMichal Kazior if (ieee80211_chanctx_refcount(local, new_ctx) == 0) 11195bcae31dSMichal Kazior ieee80211_free_chanctx(local, new_ctx); 11205bcae31dSMichal Kazior 11215bcae31dSMichal Kazior goto out; 11225bcae31dSMichal Kazior } 11235bcae31dSMichal Kazior 11245bcae31dSMichal Kazior out: 112503078de4SMichal Kazior ieee80211_vif_chanctx_reservation_complete(sdata); 11265bcae31dSMichal Kazior return err; 11275bcae31dSMichal Kazior } 11285bcae31dSMichal Kazior 11295bcae31dSMichal Kazior static bool 11305bcae31dSMichal Kazior ieee80211_vif_has_in_place_reservation(struct ieee80211_sub_if_data *sdata) 11315bcae31dSMichal Kazior { 11325bcae31dSMichal Kazior struct ieee80211_chanctx *old_ctx, *new_ctx; 11335bcae31dSMichal Kazior 11345bcae31dSMichal Kazior lockdep_assert_held(&sdata->local->chanctx_mtx); 11355bcae31dSMichal Kazior 11365bcae31dSMichal Kazior new_ctx = sdata->reserved_chanctx; 11375bcae31dSMichal Kazior old_ctx = ieee80211_vif_get_chanctx(sdata); 11385bcae31dSMichal Kazior 11395bcae31dSMichal Kazior if (!old_ctx) 11405bcae31dSMichal Kazior return false; 11415bcae31dSMichal Kazior 11425bcae31dSMichal Kazior if (WARN_ON(!new_ctx)) 11435bcae31dSMichal Kazior return false; 11445bcae31dSMichal Kazior 11455bcae31dSMichal Kazior if (old_ctx->replace_state != IEEE80211_CHANCTX_WILL_BE_REPLACED) 11465bcae31dSMichal Kazior return false; 11475bcae31dSMichal Kazior 11485bcae31dSMichal Kazior if (new_ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) 11495bcae31dSMichal Kazior return false; 11505bcae31dSMichal Kazior 11515bcae31dSMichal Kazior return true; 11525bcae31dSMichal Kazior } 11535bcae31dSMichal Kazior 11545bcae31dSMichal Kazior static int ieee80211_chsw_switch_hwconf(struct ieee80211_local *local, 11555bcae31dSMichal Kazior struct ieee80211_chanctx *new_ctx) 11565bcae31dSMichal Kazior { 11575bcae31dSMichal Kazior const struct cfg80211_chan_def *chandef; 11585bcae31dSMichal Kazior 11595bcae31dSMichal Kazior lockdep_assert_held(&local->mtx); 11605bcae31dSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 11615bcae31dSMichal Kazior 11625bcae31dSMichal Kazior chandef = ieee80211_chanctx_reserved_chandef(local, new_ctx, NULL); 11635bcae31dSMichal Kazior if (WARN_ON(!chandef)) 11645bcae31dSMichal Kazior return -EINVAL; 11655bcae31dSMichal Kazior 11665bcae31dSMichal Kazior local->hw.conf.radar_enabled = new_ctx->conf.radar_enabled; 11675bcae31dSMichal Kazior local->_oper_chandef = *chandef; 11685bcae31dSMichal Kazior ieee80211_hw_config(local, 0); 11695bcae31dSMichal Kazior 11705bcae31dSMichal Kazior return 0; 11715bcae31dSMichal Kazior } 11725bcae31dSMichal Kazior 11735bcae31dSMichal Kazior static int ieee80211_chsw_switch_vifs(struct ieee80211_local *local, 11745bcae31dSMichal Kazior int n_vifs) 11755bcae31dSMichal Kazior { 11765bcae31dSMichal Kazior struct ieee80211_vif_chanctx_switch *vif_chsw; 11775bcae31dSMichal Kazior struct ieee80211_sub_if_data *sdata; 11785bcae31dSMichal Kazior struct ieee80211_chanctx *ctx, *old_ctx; 11795bcae31dSMichal Kazior int i, err; 11805bcae31dSMichal Kazior 11815bcae31dSMichal Kazior lockdep_assert_held(&local->mtx); 11825bcae31dSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 11835bcae31dSMichal Kazior 11845bcae31dSMichal Kazior vif_chsw = kzalloc(sizeof(vif_chsw[0]) * n_vifs, GFP_KERNEL); 11855bcae31dSMichal Kazior if (!vif_chsw) 11865bcae31dSMichal Kazior return -ENOMEM; 11875bcae31dSMichal Kazior 11885bcae31dSMichal Kazior i = 0; 11895bcae31dSMichal Kazior list_for_each_entry(ctx, &local->chanctx_list, list) { 11905bcae31dSMichal Kazior if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) 11915bcae31dSMichal Kazior continue; 11925bcae31dSMichal Kazior 11935bcae31dSMichal Kazior if (WARN_ON(!ctx->replace_ctx)) { 11945bcae31dSMichal Kazior err = -EINVAL; 11955bcae31dSMichal Kazior goto out; 11965bcae31dSMichal Kazior } 11975bcae31dSMichal Kazior 11985bcae31dSMichal Kazior list_for_each_entry(sdata, &ctx->reserved_vifs, 11995bcae31dSMichal Kazior reserved_chanctx_list) { 12005bcae31dSMichal Kazior if (!ieee80211_vif_has_in_place_reservation( 12015bcae31dSMichal Kazior sdata)) 12025bcae31dSMichal Kazior continue; 12035bcae31dSMichal Kazior 12045bcae31dSMichal Kazior old_ctx = ieee80211_vif_get_chanctx(sdata); 12055bcae31dSMichal Kazior vif_chsw[i].vif = &sdata->vif; 12065bcae31dSMichal Kazior vif_chsw[i].old_ctx = &old_ctx->conf; 12075bcae31dSMichal Kazior vif_chsw[i].new_ctx = &ctx->conf; 12085bcae31dSMichal Kazior 12095bcae31dSMichal Kazior i++; 12105bcae31dSMichal Kazior } 12115bcae31dSMichal Kazior } 12125bcae31dSMichal Kazior 12135bcae31dSMichal Kazior err = drv_switch_vif_chanctx(local, vif_chsw, n_vifs, 12145bcae31dSMichal Kazior CHANCTX_SWMODE_SWAP_CONTEXTS); 12155bcae31dSMichal Kazior 12165bcae31dSMichal Kazior out: 12175bcae31dSMichal Kazior kfree(vif_chsw); 12185bcae31dSMichal Kazior return err; 12195bcae31dSMichal Kazior } 12205bcae31dSMichal Kazior 12215bcae31dSMichal Kazior static int ieee80211_chsw_switch_ctxs(struct ieee80211_local *local) 12225bcae31dSMichal Kazior { 12235bcae31dSMichal Kazior struct ieee80211_chanctx *ctx; 12245bcae31dSMichal Kazior int err; 12255bcae31dSMichal Kazior 12265bcae31dSMichal Kazior lockdep_assert_held(&local->mtx); 12275bcae31dSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 12285bcae31dSMichal Kazior 12295bcae31dSMichal Kazior list_for_each_entry(ctx, &local->chanctx_list, list) { 12305bcae31dSMichal Kazior if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) 12315bcae31dSMichal Kazior continue; 12325bcae31dSMichal Kazior 12335bcae31dSMichal Kazior if (!list_empty(&ctx->replace_ctx->assigned_vifs)) 12345bcae31dSMichal Kazior continue; 12355bcae31dSMichal Kazior 12365bcae31dSMichal Kazior ieee80211_del_chanctx(local, ctx->replace_ctx); 12375bcae31dSMichal Kazior err = ieee80211_add_chanctx(local, ctx); 12385bcae31dSMichal Kazior if (err) 12395bcae31dSMichal Kazior goto err; 12405bcae31dSMichal Kazior } 12415bcae31dSMichal Kazior 12425bcae31dSMichal Kazior return 0; 12435bcae31dSMichal Kazior 12445bcae31dSMichal Kazior err: 12455bcae31dSMichal Kazior WARN_ON(ieee80211_add_chanctx(local, ctx)); 12465bcae31dSMichal Kazior list_for_each_entry_continue_reverse(ctx, &local->chanctx_list, list) { 12475bcae31dSMichal Kazior if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) 12485bcae31dSMichal Kazior continue; 12495bcae31dSMichal Kazior 12505bcae31dSMichal Kazior if (!list_empty(&ctx->replace_ctx->assigned_vifs)) 12515bcae31dSMichal Kazior continue; 12525bcae31dSMichal Kazior 12535bcae31dSMichal Kazior ieee80211_del_chanctx(local, ctx); 12545bcae31dSMichal Kazior WARN_ON(ieee80211_add_chanctx(local, ctx->replace_ctx)); 12555bcae31dSMichal Kazior } 12565bcae31dSMichal Kazior 12575bcae31dSMichal Kazior return err; 12585bcae31dSMichal Kazior } 12595bcae31dSMichal Kazior 1260649b2a4dSJohannes Berg static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local) 12615bcae31dSMichal Kazior { 12625bcae31dSMichal Kazior struct ieee80211_sub_if_data *sdata, *sdata_tmp; 12635bcae31dSMichal Kazior struct ieee80211_chanctx *ctx, *ctx_tmp, *old_ctx; 12645bcae31dSMichal Kazior struct ieee80211_chanctx *new_ctx = NULL; 12655bcae31dSMichal Kazior int i, err, n_assigned, n_reserved, n_ready; 12665bcae31dSMichal Kazior int n_ctx = 0, n_vifs_switch = 0, n_vifs_assign = 0, n_vifs_ctxless = 0; 12675bcae31dSMichal Kazior 12685bcae31dSMichal Kazior lockdep_assert_held(&local->mtx); 12695bcae31dSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 12705bcae31dSMichal Kazior 12715bcae31dSMichal Kazior /* 12725bcae31dSMichal Kazior * If there are 2 independent pairs of channel contexts performing 12735bcae31dSMichal Kazior * cross-switch of their vifs this code will still wait until both are 12745bcae31dSMichal Kazior * ready even though it could be possible to switch one before the 12755bcae31dSMichal Kazior * other is ready. 12765bcae31dSMichal Kazior * 12775bcae31dSMichal Kazior * For practical reasons and code simplicity just do a single huge 12785bcae31dSMichal Kazior * switch. 12795bcae31dSMichal Kazior */ 12805bcae31dSMichal Kazior 12815bcae31dSMichal Kazior /* 12825bcae31dSMichal Kazior * Verify if the reservation is still feasible. 12835bcae31dSMichal Kazior * - if it's not then disconnect 12845bcae31dSMichal Kazior * - if it is but not all vifs necessary are ready then defer 12855bcae31dSMichal Kazior */ 12865bcae31dSMichal Kazior 12875bcae31dSMichal Kazior list_for_each_entry(ctx, &local->chanctx_list, list) { 12885bcae31dSMichal Kazior if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) 12895bcae31dSMichal Kazior continue; 12905bcae31dSMichal Kazior 12915bcae31dSMichal Kazior if (WARN_ON(!ctx->replace_ctx)) { 12925bcae31dSMichal Kazior err = -EINVAL; 12935bcae31dSMichal Kazior goto err; 12945bcae31dSMichal Kazior } 12955bcae31dSMichal Kazior 12965bcae31dSMichal Kazior if (!local->use_chanctx) 12975bcae31dSMichal Kazior new_ctx = ctx; 12985bcae31dSMichal Kazior 12995bcae31dSMichal Kazior n_ctx++; 13005bcae31dSMichal Kazior 13015bcae31dSMichal Kazior n_assigned = 0; 13025bcae31dSMichal Kazior n_reserved = 0; 13035bcae31dSMichal Kazior n_ready = 0; 13045bcae31dSMichal Kazior 13055bcae31dSMichal Kazior list_for_each_entry(sdata, &ctx->replace_ctx->assigned_vifs, 13065bcae31dSMichal Kazior assigned_chanctx_list) { 13075bcae31dSMichal Kazior n_assigned++; 13085bcae31dSMichal Kazior if (sdata->reserved_chanctx) { 13095bcae31dSMichal Kazior n_reserved++; 13105bcae31dSMichal Kazior if (sdata->reserved_ready) 13115bcae31dSMichal Kazior n_ready++; 13125bcae31dSMichal Kazior } 13135bcae31dSMichal Kazior } 13145bcae31dSMichal Kazior 13155bcae31dSMichal Kazior if (n_assigned != n_reserved) { 13165bcae31dSMichal Kazior if (n_ready == n_reserved) { 13175bcae31dSMichal Kazior wiphy_info(local->hw.wiphy, 13185bcae31dSMichal Kazior "channel context reservation cannot be finalized because some interfaces aren't switching\n"); 13195bcae31dSMichal Kazior err = -EBUSY; 13205bcae31dSMichal Kazior goto err; 13215bcae31dSMichal Kazior } 13225bcae31dSMichal Kazior 13235bcae31dSMichal Kazior return -EAGAIN; 13245bcae31dSMichal Kazior } 13255bcae31dSMichal Kazior 13265bcae31dSMichal Kazior ctx->conf.radar_enabled = false; 13275bcae31dSMichal Kazior list_for_each_entry(sdata, &ctx->reserved_vifs, 13285bcae31dSMichal Kazior reserved_chanctx_list) { 13295bcae31dSMichal Kazior if (ieee80211_vif_has_in_place_reservation(sdata) && 13305bcae31dSMichal Kazior !sdata->reserved_ready) 13315bcae31dSMichal Kazior return -EAGAIN; 13325bcae31dSMichal Kazior 13335bcae31dSMichal Kazior old_ctx = ieee80211_vif_get_chanctx(sdata); 13345bcae31dSMichal Kazior if (old_ctx) { 13355bcae31dSMichal Kazior if (old_ctx->replace_state == 13365bcae31dSMichal Kazior IEEE80211_CHANCTX_WILL_BE_REPLACED) 13375bcae31dSMichal Kazior n_vifs_switch++; 13385bcae31dSMichal Kazior else 13395bcae31dSMichal Kazior n_vifs_assign++; 13405bcae31dSMichal Kazior } else { 13415bcae31dSMichal Kazior n_vifs_ctxless++; 13425bcae31dSMichal Kazior } 13435bcae31dSMichal Kazior 13445bcae31dSMichal Kazior if (sdata->reserved_radar_required) 13455bcae31dSMichal Kazior ctx->conf.radar_enabled = true; 13465bcae31dSMichal Kazior } 13475bcae31dSMichal Kazior } 13485bcae31dSMichal Kazior 13495bcae31dSMichal Kazior if (WARN_ON(n_ctx == 0) || 13505bcae31dSMichal Kazior WARN_ON(n_vifs_switch == 0 && 13515bcae31dSMichal Kazior n_vifs_assign == 0 && 13525bcae31dSMichal Kazior n_vifs_ctxless == 0) || 13535bcae31dSMichal Kazior WARN_ON(n_ctx > 1 && !local->use_chanctx) || 13545bcae31dSMichal Kazior WARN_ON(!new_ctx && !local->use_chanctx)) { 13555bcae31dSMichal Kazior err = -EINVAL; 13565bcae31dSMichal Kazior goto err; 13575bcae31dSMichal Kazior } 13585bcae31dSMichal Kazior 13595bcae31dSMichal Kazior /* 13605bcae31dSMichal Kazior * All necessary vifs are ready. Perform the switch now depending on 13615bcae31dSMichal Kazior * reservations and driver capabilities. 13625bcae31dSMichal Kazior */ 13635bcae31dSMichal Kazior 13645bcae31dSMichal Kazior if (local->use_chanctx) { 13655bcae31dSMichal Kazior if (n_vifs_switch > 0) { 13665bcae31dSMichal Kazior err = ieee80211_chsw_switch_vifs(local, n_vifs_switch); 13675bcae31dSMichal Kazior if (err) 13685bcae31dSMichal Kazior goto err; 13695bcae31dSMichal Kazior } 13705bcae31dSMichal Kazior 13715bcae31dSMichal Kazior if (n_vifs_assign > 0 || n_vifs_ctxless > 0) { 13725bcae31dSMichal Kazior err = ieee80211_chsw_switch_ctxs(local); 13735bcae31dSMichal Kazior if (err) 13745bcae31dSMichal Kazior goto err; 13755bcae31dSMichal Kazior } 13765bcae31dSMichal Kazior } else { 13775bcae31dSMichal Kazior err = ieee80211_chsw_switch_hwconf(local, new_ctx); 13785bcae31dSMichal Kazior if (err) 13795bcae31dSMichal Kazior goto err; 13805bcae31dSMichal Kazior } 13815bcae31dSMichal Kazior 13825bcae31dSMichal Kazior /* 13835bcae31dSMichal Kazior * Update all structures, values and pointers to point to new channel 13845bcae31dSMichal Kazior * context(s). 13855bcae31dSMichal Kazior */ 13865bcae31dSMichal Kazior 13875bcae31dSMichal Kazior i = 0; 13885bcae31dSMichal Kazior list_for_each_entry(ctx, &local->chanctx_list, list) { 13895bcae31dSMichal Kazior if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) 13905bcae31dSMichal Kazior continue; 13915bcae31dSMichal Kazior 13925bcae31dSMichal Kazior if (WARN_ON(!ctx->replace_ctx)) { 13935bcae31dSMichal Kazior err = -EINVAL; 13945bcae31dSMichal Kazior goto err; 13955bcae31dSMichal Kazior } 13965bcae31dSMichal Kazior 13975bcae31dSMichal Kazior list_for_each_entry(sdata, &ctx->reserved_vifs, 13985bcae31dSMichal Kazior reserved_chanctx_list) { 13995bcae31dSMichal Kazior u32 changed = 0; 14005bcae31dSMichal Kazior 14015bcae31dSMichal Kazior if (!ieee80211_vif_has_in_place_reservation(sdata)) 14025bcae31dSMichal Kazior continue; 14035bcae31dSMichal Kazior 14045bcae31dSMichal Kazior rcu_assign_pointer(sdata->vif.chanctx_conf, &ctx->conf); 14055bcae31dSMichal Kazior 14065bcae31dSMichal Kazior if (sdata->vif.type == NL80211_IFTYPE_AP) 14075bcae31dSMichal Kazior __ieee80211_vif_copy_chanctx_to_vlans(sdata, 14085bcae31dSMichal Kazior false); 14095bcae31dSMichal Kazior 141017c18bf8SJohannes Berg ieee80211_check_fast_xmit_iface(sdata); 141117c18bf8SJohannes Berg 14125bcae31dSMichal Kazior sdata->radar_required = sdata->reserved_radar_required; 14135bcae31dSMichal Kazior 14145bcae31dSMichal Kazior if (sdata->vif.bss_conf.chandef.width != 14155bcae31dSMichal Kazior sdata->reserved_chandef.width) 14165bcae31dSMichal Kazior changed = BSS_CHANGED_BANDWIDTH; 14175bcae31dSMichal Kazior 14182967e031SFelix Fietkau ieee80211_vif_update_chandef(sdata, &sdata->reserved_chandef); 14195bcae31dSMichal Kazior if (changed) 14205bcae31dSMichal Kazior ieee80211_bss_info_change_notify(sdata, 14215bcae31dSMichal Kazior changed); 14225bcae31dSMichal Kazior 1423db82d8a9SLorenzo Bianconi ieee80211_recalc_txpower(sdata, false); 14245bcae31dSMichal Kazior } 142511335a55SLuciano Coelho 142611335a55SLuciano Coelho ieee80211_recalc_chanctx_chantype(local, ctx); 142711335a55SLuciano Coelho ieee80211_recalc_smps_chanctx(local, ctx); 142811335a55SLuciano Coelho ieee80211_recalc_radar_chanctx(local, ctx); 142911335a55SLuciano Coelho ieee80211_recalc_chanctx_min_def(local, ctx); 14305bcae31dSMichal Kazior 14315bcae31dSMichal Kazior list_for_each_entry_safe(sdata, sdata_tmp, &ctx->reserved_vifs, 14325bcae31dSMichal Kazior reserved_chanctx_list) { 14335bcae31dSMichal Kazior if (ieee80211_vif_get_chanctx(sdata) != ctx) 14345bcae31dSMichal Kazior continue; 14355bcae31dSMichal Kazior 14365bcae31dSMichal Kazior list_del(&sdata->reserved_chanctx_list); 14375bcae31dSMichal Kazior list_move(&sdata->assigned_chanctx_list, 143847e4df94SMichal Kazior &ctx->assigned_vifs); 14395bcae31dSMichal Kazior sdata->reserved_chanctx = NULL; 144003078de4SMichal Kazior 144103078de4SMichal Kazior ieee80211_vif_chanctx_reservation_complete(sdata); 14425bcae31dSMichal Kazior } 14435bcae31dSMichal Kazior 14445bcae31dSMichal Kazior /* 14455bcae31dSMichal Kazior * This context might have been a dependency for an already 14465bcae31dSMichal Kazior * ready re-assign reservation interface that was deferred. Do 14475bcae31dSMichal Kazior * not propagate error to the caller though. The in-place 14485bcae31dSMichal Kazior * reservation for originally requested interface has already 14495bcae31dSMichal Kazior * succeeded at this point. 14505bcae31dSMichal Kazior */ 14515bcae31dSMichal Kazior list_for_each_entry_safe(sdata, sdata_tmp, &ctx->reserved_vifs, 14525bcae31dSMichal Kazior reserved_chanctx_list) { 14535bcae31dSMichal Kazior if (WARN_ON(ieee80211_vif_has_in_place_reservation( 14545bcae31dSMichal Kazior sdata))) 14555bcae31dSMichal Kazior continue; 14565bcae31dSMichal Kazior 14575bcae31dSMichal Kazior if (WARN_ON(sdata->reserved_chanctx != ctx)) 14585bcae31dSMichal Kazior continue; 14595bcae31dSMichal Kazior 14605bcae31dSMichal Kazior if (!sdata->reserved_ready) 14615bcae31dSMichal Kazior continue; 14625bcae31dSMichal Kazior 14635bcae31dSMichal Kazior if (ieee80211_vif_get_chanctx(sdata)) 14645bcae31dSMichal Kazior err = ieee80211_vif_use_reserved_reassign( 14655bcae31dSMichal Kazior sdata); 14665bcae31dSMichal Kazior else 14675bcae31dSMichal Kazior err = ieee80211_vif_use_reserved_assign(sdata); 14685bcae31dSMichal Kazior 14695bcae31dSMichal Kazior if (err) { 14705bcae31dSMichal Kazior sdata_info(sdata, 14715bcae31dSMichal Kazior "failed to finalize (re-)assign reservation (err=%d)\n", 14725bcae31dSMichal Kazior err); 14735bcae31dSMichal Kazior ieee80211_vif_unreserve_chanctx(sdata); 14745bcae31dSMichal Kazior cfg80211_stop_iface(local->hw.wiphy, 14755bcae31dSMichal Kazior &sdata->wdev, 14765bcae31dSMichal Kazior GFP_KERNEL); 14775bcae31dSMichal Kazior } 14785bcae31dSMichal Kazior } 14795bcae31dSMichal Kazior } 14805bcae31dSMichal Kazior 14815bcae31dSMichal Kazior /* 14825bcae31dSMichal Kazior * Finally free old contexts 14835bcae31dSMichal Kazior */ 14845bcae31dSMichal Kazior 14855bcae31dSMichal Kazior list_for_each_entry_safe(ctx, ctx_tmp, &local->chanctx_list, list) { 14865bcae31dSMichal Kazior if (ctx->replace_state != IEEE80211_CHANCTX_WILL_BE_REPLACED) 14875bcae31dSMichal Kazior continue; 14885bcae31dSMichal Kazior 14895bcae31dSMichal Kazior ctx->replace_ctx->replace_ctx = NULL; 14905bcae31dSMichal Kazior ctx->replace_ctx->replace_state = 14915bcae31dSMichal Kazior IEEE80211_CHANCTX_REPLACE_NONE; 14925bcae31dSMichal Kazior 14935bcae31dSMichal Kazior list_del_rcu(&ctx->list); 14945bcae31dSMichal Kazior kfree_rcu(ctx, rcu_head); 14955bcae31dSMichal Kazior } 14965bcae31dSMichal Kazior 14975bcae31dSMichal Kazior return 0; 14985bcae31dSMichal Kazior 14995bcae31dSMichal Kazior err: 15005bcae31dSMichal Kazior list_for_each_entry(ctx, &local->chanctx_list, list) { 15015bcae31dSMichal Kazior if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) 15025bcae31dSMichal Kazior continue; 15035bcae31dSMichal Kazior 15045bcae31dSMichal Kazior list_for_each_entry_safe(sdata, sdata_tmp, &ctx->reserved_vifs, 150503078de4SMichal Kazior reserved_chanctx_list) { 15065bcae31dSMichal Kazior ieee80211_vif_unreserve_chanctx(sdata); 150703078de4SMichal Kazior ieee80211_vif_chanctx_reservation_complete(sdata); 150803078de4SMichal Kazior } 15095bcae31dSMichal Kazior } 15105bcae31dSMichal Kazior 15115bcae31dSMichal Kazior return err; 15125bcae31dSMichal Kazior } 15135bcae31dSMichal Kazior 1514649b2a4dSJohannes Berg static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata) 1515649b2a4dSJohannes Berg { 1516649b2a4dSJohannes Berg struct ieee80211_local *local = sdata->local; 1517649b2a4dSJohannes Berg struct ieee80211_chanctx_conf *conf; 1518649b2a4dSJohannes Berg struct ieee80211_chanctx *ctx; 1519649b2a4dSJohannes Berg bool use_reserved_switch = false; 1520649b2a4dSJohannes Berg 1521649b2a4dSJohannes Berg lockdep_assert_held(&local->chanctx_mtx); 1522649b2a4dSJohannes Berg 1523649b2a4dSJohannes Berg conf = rcu_dereference_protected(sdata->vif.chanctx_conf, 1524649b2a4dSJohannes Berg lockdep_is_held(&local->chanctx_mtx)); 1525649b2a4dSJohannes Berg if (!conf) 1526649b2a4dSJohannes Berg return; 1527649b2a4dSJohannes Berg 1528649b2a4dSJohannes Berg ctx = container_of(conf, struct ieee80211_chanctx, conf); 1529649b2a4dSJohannes Berg 1530649b2a4dSJohannes Berg if (sdata->reserved_chanctx) { 1531649b2a4dSJohannes Berg if (sdata->reserved_chanctx->replace_state == 1532649b2a4dSJohannes Berg IEEE80211_CHANCTX_REPLACES_OTHER && 1533649b2a4dSJohannes Berg ieee80211_chanctx_num_reserved(local, 1534649b2a4dSJohannes Berg sdata->reserved_chanctx) > 1) 1535649b2a4dSJohannes Berg use_reserved_switch = true; 1536649b2a4dSJohannes Berg 1537649b2a4dSJohannes Berg ieee80211_vif_unreserve_chanctx(sdata); 1538649b2a4dSJohannes Berg } 1539649b2a4dSJohannes Berg 1540649b2a4dSJohannes Berg ieee80211_assign_vif_chanctx(sdata, NULL); 1541649b2a4dSJohannes Berg if (ieee80211_chanctx_refcount(local, ctx) == 0) 1542649b2a4dSJohannes Berg ieee80211_free_chanctx(local, ctx); 1543649b2a4dSJohannes Berg 1544104f5a62SEliad Peller sdata->radar_required = false; 1545104f5a62SEliad Peller 1546649b2a4dSJohannes Berg /* Unreserving may ready an in-place reservation. */ 1547649b2a4dSJohannes Berg if (use_reserved_switch) 1548649b2a4dSJohannes Berg ieee80211_vif_use_reserved_switch(local); 1549649b2a4dSJohannes Berg } 1550649b2a4dSJohannes Berg 1551649b2a4dSJohannes Berg int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata, 1552649b2a4dSJohannes Berg const struct cfg80211_chan_def *chandef, 1553649b2a4dSJohannes Berg enum ieee80211_chanctx_mode mode) 1554649b2a4dSJohannes Berg { 1555649b2a4dSJohannes Berg struct ieee80211_local *local = sdata->local; 1556649b2a4dSJohannes Berg struct ieee80211_chanctx *ctx; 1557649b2a4dSJohannes Berg u8 radar_detect_width = 0; 1558649b2a4dSJohannes Berg int ret; 1559649b2a4dSJohannes Berg 1560649b2a4dSJohannes Berg lockdep_assert_held(&local->mtx); 1561649b2a4dSJohannes Berg 1562649b2a4dSJohannes Berg WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev)); 1563649b2a4dSJohannes Berg 1564649b2a4dSJohannes Berg mutex_lock(&local->chanctx_mtx); 1565649b2a4dSJohannes Berg 1566649b2a4dSJohannes Berg ret = cfg80211_chandef_dfs_required(local->hw.wiphy, 1567649b2a4dSJohannes Berg chandef, 1568649b2a4dSJohannes Berg sdata->wdev.iftype); 1569649b2a4dSJohannes Berg if (ret < 0) 1570649b2a4dSJohannes Berg goto out; 1571649b2a4dSJohannes Berg if (ret > 0) 1572649b2a4dSJohannes Berg radar_detect_width = BIT(chandef->width); 1573649b2a4dSJohannes Berg 1574649b2a4dSJohannes Berg sdata->radar_required = ret; 1575649b2a4dSJohannes Berg 1576649b2a4dSJohannes Berg ret = ieee80211_check_combinations(sdata, chandef, mode, 1577649b2a4dSJohannes Berg radar_detect_width); 1578649b2a4dSJohannes Berg if (ret < 0) 1579649b2a4dSJohannes Berg goto out; 1580649b2a4dSJohannes Berg 1581649b2a4dSJohannes Berg __ieee80211_vif_release_channel(sdata); 1582649b2a4dSJohannes Berg 1583649b2a4dSJohannes Berg ctx = ieee80211_find_chanctx(local, chandef, mode); 1584649b2a4dSJohannes Berg if (!ctx) 1585649b2a4dSJohannes Berg ctx = ieee80211_new_chanctx(local, chandef, mode); 1586649b2a4dSJohannes Berg if (IS_ERR(ctx)) { 1587649b2a4dSJohannes Berg ret = PTR_ERR(ctx); 1588649b2a4dSJohannes Berg goto out; 1589649b2a4dSJohannes Berg } 1590649b2a4dSJohannes Berg 15912967e031SFelix Fietkau ieee80211_vif_update_chandef(sdata, chandef); 1592649b2a4dSJohannes Berg 1593649b2a4dSJohannes Berg ret = ieee80211_assign_vif_chanctx(sdata, ctx); 1594649b2a4dSJohannes Berg if (ret) { 1595649b2a4dSJohannes Berg /* if assign fails refcount stays the same */ 1596649b2a4dSJohannes Berg if (ieee80211_chanctx_refcount(local, ctx) == 0) 1597649b2a4dSJohannes Berg ieee80211_free_chanctx(local, ctx); 1598649b2a4dSJohannes Berg goto out; 1599649b2a4dSJohannes Berg } 1600649b2a4dSJohannes Berg 1601649b2a4dSJohannes Berg ieee80211_recalc_smps_chanctx(local, ctx); 1602649b2a4dSJohannes Berg ieee80211_recalc_radar_chanctx(local, ctx); 1603649b2a4dSJohannes Berg out: 1604104f5a62SEliad Peller if (ret) 1605104f5a62SEliad Peller sdata->radar_required = false; 1606104f5a62SEliad Peller 1607649b2a4dSJohannes Berg mutex_unlock(&local->chanctx_mtx); 1608649b2a4dSJohannes Berg return ret; 1609649b2a4dSJohannes Berg } 1610649b2a4dSJohannes Berg 16115bcae31dSMichal Kazior int ieee80211_vif_use_reserved_context(struct ieee80211_sub_if_data *sdata) 16125bcae31dSMichal Kazior { 16135bcae31dSMichal Kazior struct ieee80211_local *local = sdata->local; 16145bcae31dSMichal Kazior struct ieee80211_chanctx *new_ctx; 16155bcae31dSMichal Kazior struct ieee80211_chanctx *old_ctx; 16165bcae31dSMichal Kazior int err; 16175bcae31dSMichal Kazior 16185bcae31dSMichal Kazior lockdep_assert_held(&local->mtx); 16195bcae31dSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 16205bcae31dSMichal Kazior 16215bcae31dSMichal Kazior new_ctx = sdata->reserved_chanctx; 16225bcae31dSMichal Kazior old_ctx = ieee80211_vif_get_chanctx(sdata); 16235bcae31dSMichal Kazior 16245bcae31dSMichal Kazior if (WARN_ON(!new_ctx)) 16255bcae31dSMichal Kazior return -EINVAL; 16265bcae31dSMichal Kazior 16275bcae31dSMichal Kazior if (WARN_ON(new_ctx->replace_state == 16285bcae31dSMichal Kazior IEEE80211_CHANCTX_WILL_BE_REPLACED)) 16295bcae31dSMichal Kazior return -EINVAL; 16305bcae31dSMichal Kazior 16315bcae31dSMichal Kazior if (WARN_ON(sdata->reserved_ready)) 16325bcae31dSMichal Kazior return -EINVAL; 16335bcae31dSMichal Kazior 16345bcae31dSMichal Kazior sdata->reserved_ready = true; 16355bcae31dSMichal Kazior 16365bcae31dSMichal Kazior if (new_ctx->replace_state == IEEE80211_CHANCTX_REPLACE_NONE) { 16375bcae31dSMichal Kazior if (old_ctx) 16385bcae31dSMichal Kazior err = ieee80211_vif_use_reserved_reassign(sdata); 16395bcae31dSMichal Kazior else 16405bcae31dSMichal Kazior err = ieee80211_vif_use_reserved_assign(sdata); 16415bcae31dSMichal Kazior 16425bcae31dSMichal Kazior if (err) 16435bcae31dSMichal Kazior return err; 16445bcae31dSMichal Kazior } 16455bcae31dSMichal Kazior 16465bcae31dSMichal Kazior /* 16475bcae31dSMichal Kazior * In-place reservation may need to be finalized now either if: 16485bcae31dSMichal Kazior * a) sdata is taking part in the swapping itself and is the last one 16495bcae31dSMichal Kazior * b) sdata has switched with a re-assign reservation to an existing 16505bcae31dSMichal Kazior * context readying in-place switching of old_ctx 16515bcae31dSMichal Kazior * 16525bcae31dSMichal Kazior * In case of (b) do not propagate the error up because the requested 16535bcae31dSMichal Kazior * sdata already switched successfully. Just spill an extra warning. 16545bcae31dSMichal Kazior * The ieee80211_vif_use_reserved_switch() already stops all necessary 16555bcae31dSMichal Kazior * interfaces upon failure. 16565bcae31dSMichal Kazior */ 16575bcae31dSMichal Kazior if ((old_ctx && 16585bcae31dSMichal Kazior old_ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED) || 16595bcae31dSMichal Kazior new_ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER) { 16605bcae31dSMichal Kazior err = ieee80211_vif_use_reserved_switch(local); 16615bcae31dSMichal Kazior if (err && err != -EAGAIN) { 16625bcae31dSMichal Kazior if (new_ctx->replace_state == 16635bcae31dSMichal Kazior IEEE80211_CHANCTX_REPLACES_OTHER) 16645bcae31dSMichal Kazior return err; 16655bcae31dSMichal Kazior 16665bcae31dSMichal Kazior wiphy_info(local->hw.wiphy, 16675bcae31dSMichal Kazior "depending in-place reservation failed (err=%d)\n", 16685bcae31dSMichal Kazior err); 16695bcae31dSMichal Kazior } 16705bcae31dSMichal Kazior } 16715bcae31dSMichal Kazior 16725bcae31dSMichal Kazior return 0; 167373da7d5bSSimon Wunderlich } 167473da7d5bSSimon Wunderlich 16752c9b7359SJohannes Berg int ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata, 16762c9b7359SJohannes Berg const struct cfg80211_chan_def *chandef, 16772c9b7359SJohannes Berg u32 *changed) 16782c9b7359SJohannes Berg { 16792c9b7359SJohannes Berg struct ieee80211_local *local = sdata->local; 16802c9b7359SJohannes Berg struct ieee80211_chanctx_conf *conf; 16812c9b7359SJohannes Berg struct ieee80211_chanctx *ctx; 16825bcae31dSMichal Kazior const struct cfg80211_chan_def *compat; 16832c9b7359SJohannes Berg int ret; 16842c9b7359SJohannes Berg 16852c9b7359SJohannes Berg if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef, 16862c9b7359SJohannes Berg IEEE80211_CHAN_DISABLED)) 16872c9b7359SJohannes Berg return -EINVAL; 16882c9b7359SJohannes Berg 16892c9b7359SJohannes Berg mutex_lock(&local->chanctx_mtx); 16902c9b7359SJohannes Berg if (cfg80211_chandef_identical(chandef, &sdata->vif.bss_conf.chandef)) { 16912c9b7359SJohannes Berg ret = 0; 16922c9b7359SJohannes Berg goto out; 16932c9b7359SJohannes Berg } 16942c9b7359SJohannes Berg 16952c9b7359SJohannes Berg if (chandef->width == NL80211_CHAN_WIDTH_20_NOHT || 16962c9b7359SJohannes Berg sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT) { 16972c9b7359SJohannes Berg ret = -EINVAL; 16982c9b7359SJohannes Berg goto out; 16992c9b7359SJohannes Berg } 17002c9b7359SJohannes Berg 17012c9b7359SJohannes Berg conf = rcu_dereference_protected(sdata->vif.chanctx_conf, 17022c9b7359SJohannes Berg lockdep_is_held(&local->chanctx_mtx)); 17032c9b7359SJohannes Berg if (!conf) { 17042c9b7359SJohannes Berg ret = -EINVAL; 17052c9b7359SJohannes Berg goto out; 17062c9b7359SJohannes Berg } 17072c9b7359SJohannes Berg 17082c9b7359SJohannes Berg ctx = container_of(conf, struct ieee80211_chanctx, conf); 17095bcae31dSMichal Kazior 17105bcae31dSMichal Kazior compat = cfg80211_chandef_compatible(&conf->def, chandef); 17115bcae31dSMichal Kazior if (!compat) { 17122c9b7359SJohannes Berg ret = -EINVAL; 17132c9b7359SJohannes Berg goto out; 17142c9b7359SJohannes Berg } 17152c9b7359SJohannes Berg 17165bcae31dSMichal Kazior switch (ctx->replace_state) { 17175bcae31dSMichal Kazior case IEEE80211_CHANCTX_REPLACE_NONE: 17185bcae31dSMichal Kazior if (!ieee80211_chanctx_reserved_chandef(local, ctx, compat)) { 17195bcae31dSMichal Kazior ret = -EBUSY; 17205bcae31dSMichal Kazior goto out; 17215bcae31dSMichal Kazior } 17225bcae31dSMichal Kazior break; 17235bcae31dSMichal Kazior case IEEE80211_CHANCTX_WILL_BE_REPLACED: 1724d070f913SStephen Hemminger /* TODO: Perhaps the bandwidth change could be treated as a 17255bcae31dSMichal Kazior * reservation itself? */ 17265bcae31dSMichal Kazior ret = -EBUSY; 17275bcae31dSMichal Kazior goto out; 17285bcae31dSMichal Kazior case IEEE80211_CHANCTX_REPLACES_OTHER: 17295bcae31dSMichal Kazior /* channel context that is going to replace another channel 17305bcae31dSMichal Kazior * context doesn't really exist and shouldn't be assigned 17315bcae31dSMichal Kazior * anywhere yet */ 17325bcae31dSMichal Kazior WARN_ON(1); 17335bcae31dSMichal Kazior break; 17345bcae31dSMichal Kazior } 17355bcae31dSMichal Kazior 17362967e031SFelix Fietkau ieee80211_vif_update_chandef(sdata, chandef); 17372c9b7359SJohannes Berg 17382c9b7359SJohannes Berg ieee80211_recalc_chanctx_chantype(local, ctx); 17392c9b7359SJohannes Berg 17402c9b7359SJohannes Berg *changed |= BSS_CHANGED_BANDWIDTH; 17412c9b7359SJohannes Berg ret = 0; 17422c9b7359SJohannes Berg out: 17432c9b7359SJohannes Berg mutex_unlock(&local->chanctx_mtx); 17442c9b7359SJohannes Berg return ret; 17452c9b7359SJohannes Berg } 17462c9b7359SJohannes Berg 1747d01a1e65SMichal Kazior void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata) 1748d01a1e65SMichal Kazior { 174955de908aSJohannes Berg WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev)); 175055de908aSJohannes Berg 175134a3740dSJohannes Berg lockdep_assert_held(&sdata->local->mtx); 175234a3740dSJohannes Berg 1753d01a1e65SMichal Kazior mutex_lock(&sdata->local->chanctx_mtx); 1754d01a1e65SMichal Kazior __ieee80211_vif_release_channel(sdata); 1755d01a1e65SMichal Kazior mutex_unlock(&sdata->local->chanctx_mtx); 1756d01a1e65SMichal Kazior } 17573448c005SJohannes Berg 17584d76d21bSJohannes Berg void ieee80211_vif_vlan_copy_chanctx(struct ieee80211_sub_if_data *sdata) 17594d76d21bSJohannes Berg { 17604d76d21bSJohannes Berg struct ieee80211_local *local = sdata->local; 17614d76d21bSJohannes Berg struct ieee80211_sub_if_data *ap; 17624d76d21bSJohannes Berg struct ieee80211_chanctx_conf *conf; 17634d76d21bSJohannes Berg 17644d76d21bSJohannes Berg if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP_VLAN || !sdata->bss)) 17654d76d21bSJohannes Berg return; 17664d76d21bSJohannes Berg 17674d76d21bSJohannes Berg ap = container_of(sdata->bss, struct ieee80211_sub_if_data, u.ap); 17684d76d21bSJohannes Berg 17694d76d21bSJohannes Berg mutex_lock(&local->chanctx_mtx); 17704d76d21bSJohannes Berg 17714d76d21bSJohannes Berg conf = rcu_dereference_protected(ap->vif.chanctx_conf, 17724d76d21bSJohannes Berg lockdep_is_held(&local->chanctx_mtx)); 17734d76d21bSJohannes Berg rcu_assign_pointer(sdata->vif.chanctx_conf, conf); 17744d76d21bSJohannes Berg mutex_unlock(&local->chanctx_mtx); 17754d76d21bSJohannes Berg } 17764d76d21bSJohannes Berg 17773448c005SJohannes Berg void ieee80211_iter_chan_contexts_atomic( 17783448c005SJohannes Berg struct ieee80211_hw *hw, 17793448c005SJohannes Berg void (*iter)(struct ieee80211_hw *hw, 17803448c005SJohannes Berg struct ieee80211_chanctx_conf *chanctx_conf, 17813448c005SJohannes Berg void *data), 17823448c005SJohannes Berg void *iter_data) 17833448c005SJohannes Berg { 17843448c005SJohannes Berg struct ieee80211_local *local = hw_to_local(hw); 17853448c005SJohannes Berg struct ieee80211_chanctx *ctx; 17863448c005SJohannes Berg 17873448c005SJohannes Berg rcu_read_lock(); 17883448c005SJohannes Berg list_for_each_entry_rcu(ctx, &local->chanctx_list, list) 17898a61af65SJohannes Berg if (ctx->driver_present) 17903448c005SJohannes Berg iter(hw, &ctx->conf, iter_data); 17913448c005SJohannes Berg rcu_read_unlock(); 17923448c005SJohannes Berg } 17933448c005SJohannes Berg EXPORT_SYMBOL_GPL(ieee80211_iter_chan_contexts_atomic); 1794