1457c8996SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2f444de05SJohannes Berg /* 3f444de05SJohannes Berg * mac80211 - channel management 4f444de05SJohannes Berg */ 5f444de05SJohannes Berg 60aaffa9bSJohannes Berg #include <linux/nl80211.h> 73448c005SJohannes Berg #include <linux/export.h> 84d76d21bSJohannes Berg #include <linux/rtnetlink.h> 93117bbdbSPaul Stewart #include <net/cfg80211.h> 10f444de05SJohannes Berg #include "ieee80211_i.h" 1135f2fce9SMichal Kazior #include "driver-ops.h" 12f444de05SJohannes Berg 13c0166da9SMichal Kazior static int ieee80211_chanctx_num_assigned(struct ieee80211_local *local, 14c0166da9SMichal Kazior struct ieee80211_chanctx *ctx) 15c0166da9SMichal Kazior { 16c0166da9SMichal Kazior struct ieee80211_sub_if_data *sdata; 17c0166da9SMichal Kazior int num = 0; 18c0166da9SMichal Kazior 19c0166da9SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 20c0166da9SMichal Kazior 21c0166da9SMichal Kazior list_for_each_entry(sdata, &ctx->assigned_vifs, assigned_chanctx_list) 22c0166da9SMichal Kazior num++; 23c0166da9SMichal Kazior 24c0166da9SMichal Kazior return num; 25c0166da9SMichal Kazior } 26c0166da9SMichal Kazior 27c0166da9SMichal Kazior static int ieee80211_chanctx_num_reserved(struct ieee80211_local *local, 28c0166da9SMichal Kazior struct ieee80211_chanctx *ctx) 29c0166da9SMichal Kazior { 30c0166da9SMichal Kazior struct ieee80211_sub_if_data *sdata; 31c0166da9SMichal Kazior int num = 0; 32c0166da9SMichal Kazior 33c0166da9SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 34c0166da9SMichal Kazior 35c0166da9SMichal Kazior list_for_each_entry(sdata, &ctx->reserved_vifs, reserved_chanctx_list) 36c0166da9SMichal Kazior num++; 37c0166da9SMichal Kazior 38c0166da9SMichal Kazior return num; 39c0166da9SMichal Kazior } 40c0166da9SMichal Kazior 41c0166da9SMichal Kazior int ieee80211_chanctx_refcount(struct ieee80211_local *local, 42c0166da9SMichal Kazior struct ieee80211_chanctx *ctx) 43c0166da9SMichal Kazior { 44c0166da9SMichal Kazior return ieee80211_chanctx_num_assigned(local, ctx) + 45c0166da9SMichal Kazior ieee80211_chanctx_num_reserved(local, ctx); 46c0166da9SMichal Kazior } 47c0166da9SMichal Kazior 48c2b90ad8SMichal Kazior static int ieee80211_num_chanctx(struct ieee80211_local *local) 49c2b90ad8SMichal Kazior { 50c2b90ad8SMichal Kazior struct ieee80211_chanctx *ctx; 51c2b90ad8SMichal Kazior int num = 0; 52c2b90ad8SMichal Kazior 53c2b90ad8SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 54c2b90ad8SMichal Kazior 55c2b90ad8SMichal Kazior list_for_each_entry(ctx, &local->chanctx_list, list) 56c2b90ad8SMichal Kazior num++; 57c2b90ad8SMichal Kazior 58c2b90ad8SMichal Kazior return num; 59c2b90ad8SMichal Kazior } 60c2b90ad8SMichal Kazior 61c2b90ad8SMichal Kazior static bool ieee80211_can_create_new_chanctx(struct ieee80211_local *local) 62c2b90ad8SMichal Kazior { 63c2b90ad8SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 64c2b90ad8SMichal Kazior return ieee80211_num_chanctx(local) < ieee80211_max_num_channels(local); 65c2b90ad8SMichal Kazior } 66c2b90ad8SMichal Kazior 675bcae31dSMichal Kazior static struct ieee80211_chanctx * 685bcae31dSMichal Kazior ieee80211_vif_get_chanctx(struct ieee80211_sub_if_data *sdata) 695bcae31dSMichal Kazior { 701d4cc30cSJohannes Berg struct ieee80211_local *local __maybe_unused = sdata->local; 715bcae31dSMichal Kazior struct ieee80211_chanctx_conf *conf; 725bcae31dSMichal Kazior 735bcae31dSMichal Kazior conf = rcu_dereference_protected(sdata->vif.chanctx_conf, 745bcae31dSMichal Kazior lockdep_is_held(&local->chanctx_mtx)); 755bcae31dSMichal Kazior if (!conf) 765bcae31dSMichal Kazior return NULL; 775bcae31dSMichal Kazior 785bcae31dSMichal Kazior return container_of(conf, struct ieee80211_chanctx, conf); 795bcae31dSMichal Kazior } 805bcae31dSMichal Kazior 810288157bSMichal Kazior static const struct cfg80211_chan_def * 820288157bSMichal Kazior ieee80211_chanctx_reserved_chandef(struct ieee80211_local *local, 830288157bSMichal Kazior struct ieee80211_chanctx *ctx, 840288157bSMichal Kazior const struct cfg80211_chan_def *compat) 850288157bSMichal Kazior { 860288157bSMichal Kazior struct ieee80211_sub_if_data *sdata; 870288157bSMichal Kazior 880288157bSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 890288157bSMichal Kazior 900288157bSMichal Kazior list_for_each_entry(sdata, &ctx->reserved_vifs, 910288157bSMichal Kazior reserved_chanctx_list) { 920288157bSMichal Kazior if (!compat) 930288157bSMichal Kazior compat = &sdata->reserved_chandef; 940288157bSMichal Kazior 950288157bSMichal Kazior compat = cfg80211_chandef_compatible(&sdata->reserved_chandef, 960288157bSMichal Kazior compat); 970288157bSMichal Kazior if (!compat) 980288157bSMichal Kazior break; 990288157bSMichal Kazior } 1000288157bSMichal Kazior 1010288157bSMichal Kazior return compat; 1020288157bSMichal Kazior } 1030288157bSMichal Kazior 10413f348a8SMichal Kazior static const struct cfg80211_chan_def * 10513f348a8SMichal Kazior ieee80211_chanctx_non_reserved_chandef(struct ieee80211_local *local, 10613f348a8SMichal Kazior struct ieee80211_chanctx *ctx, 10713f348a8SMichal Kazior const struct cfg80211_chan_def *compat) 10813f348a8SMichal Kazior { 10913f348a8SMichal Kazior struct ieee80211_sub_if_data *sdata; 11013f348a8SMichal Kazior 11113f348a8SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 11213f348a8SMichal Kazior 11313f348a8SMichal Kazior list_for_each_entry(sdata, &ctx->assigned_vifs, 11413f348a8SMichal Kazior assigned_chanctx_list) { 11513f348a8SMichal Kazior if (sdata->reserved_chanctx != NULL) 11613f348a8SMichal Kazior continue; 11713f348a8SMichal Kazior 11813f348a8SMichal Kazior if (!compat) 11913f348a8SMichal Kazior compat = &sdata->vif.bss_conf.chandef; 12013f348a8SMichal Kazior 12113f348a8SMichal Kazior compat = cfg80211_chandef_compatible( 12213f348a8SMichal Kazior &sdata->vif.bss_conf.chandef, compat); 12313f348a8SMichal Kazior if (!compat) 12413f348a8SMichal Kazior break; 12513f348a8SMichal Kazior } 12613f348a8SMichal Kazior 12713f348a8SMichal Kazior return compat; 12813f348a8SMichal Kazior } 12913f348a8SMichal Kazior 13013f348a8SMichal Kazior static const struct cfg80211_chan_def * 13113f348a8SMichal Kazior ieee80211_chanctx_combined_chandef(struct ieee80211_local *local, 13213f348a8SMichal Kazior struct ieee80211_chanctx *ctx, 13313f348a8SMichal Kazior const struct cfg80211_chan_def *compat) 13413f348a8SMichal Kazior { 13513f348a8SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 13613f348a8SMichal Kazior 13713f348a8SMichal Kazior compat = ieee80211_chanctx_reserved_chandef(local, ctx, compat); 13813f348a8SMichal Kazior if (!compat) 13913f348a8SMichal Kazior return NULL; 14013f348a8SMichal Kazior 14113f348a8SMichal Kazior compat = ieee80211_chanctx_non_reserved_chandef(local, ctx, compat); 14213f348a8SMichal Kazior if (!compat) 14313f348a8SMichal Kazior return NULL; 14413f348a8SMichal Kazior 14513f348a8SMichal Kazior return compat; 14613f348a8SMichal Kazior } 14713f348a8SMichal Kazior 14813f348a8SMichal Kazior static bool 14913f348a8SMichal Kazior ieee80211_chanctx_can_reserve_chandef(struct ieee80211_local *local, 15013f348a8SMichal Kazior struct ieee80211_chanctx *ctx, 15113f348a8SMichal Kazior const struct cfg80211_chan_def *def) 15213f348a8SMichal Kazior { 15313f348a8SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 15413f348a8SMichal Kazior 15513f348a8SMichal Kazior if (ieee80211_chanctx_combined_chandef(local, ctx, def)) 15613f348a8SMichal Kazior return true; 15713f348a8SMichal Kazior 15813f348a8SMichal Kazior if (!list_empty(&ctx->reserved_vifs) && 15913f348a8SMichal Kazior ieee80211_chanctx_reserved_chandef(local, ctx, def)) 16013f348a8SMichal Kazior return true; 16113f348a8SMichal Kazior 16213f348a8SMichal Kazior return false; 16313f348a8SMichal Kazior } 16413f348a8SMichal Kazior 16513f348a8SMichal Kazior static struct ieee80211_chanctx * 16613f348a8SMichal Kazior ieee80211_find_reservation_chanctx(struct ieee80211_local *local, 16713f348a8SMichal Kazior const struct cfg80211_chan_def *chandef, 16813f348a8SMichal Kazior enum ieee80211_chanctx_mode mode) 16913f348a8SMichal Kazior { 17013f348a8SMichal Kazior struct ieee80211_chanctx *ctx; 17113f348a8SMichal Kazior 17213f348a8SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 17313f348a8SMichal Kazior 17413f348a8SMichal Kazior if (mode == IEEE80211_CHANCTX_EXCLUSIVE) 17513f348a8SMichal Kazior return NULL; 17613f348a8SMichal Kazior 17713f348a8SMichal Kazior list_for_each_entry(ctx, &local->chanctx_list, list) { 1785bcae31dSMichal Kazior if (ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED) 1795bcae31dSMichal Kazior continue; 1805bcae31dSMichal Kazior 18113f348a8SMichal Kazior if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE) 18213f348a8SMichal Kazior continue; 18313f348a8SMichal Kazior 18413f348a8SMichal Kazior if (!ieee80211_chanctx_can_reserve_chandef(local, ctx, 18513f348a8SMichal Kazior chandef)) 18613f348a8SMichal Kazior continue; 18713f348a8SMichal Kazior 18813f348a8SMichal Kazior return ctx; 18913f348a8SMichal Kazior } 19013f348a8SMichal Kazior 19113f348a8SMichal Kazior return NULL; 19213f348a8SMichal Kazior } 19313f348a8SMichal Kazior 1940fabfaafSArik Nemtsov enum nl80211_chan_width ieee80211_get_sta_bw(struct ieee80211_sta *sta) 19521f659bfSEliad Peller { 19621f659bfSEliad Peller switch (sta->bandwidth) { 19721f659bfSEliad Peller case IEEE80211_STA_RX_BW_20: 19821f659bfSEliad Peller if (sta->ht_cap.ht_supported) 19921f659bfSEliad Peller return NL80211_CHAN_WIDTH_20; 20021f659bfSEliad Peller else 20121f659bfSEliad Peller return NL80211_CHAN_WIDTH_20_NOHT; 20221f659bfSEliad Peller case IEEE80211_STA_RX_BW_40: 20321f659bfSEliad Peller return NL80211_CHAN_WIDTH_40; 20421f659bfSEliad Peller case IEEE80211_STA_RX_BW_80: 20521f659bfSEliad Peller return NL80211_CHAN_WIDTH_80; 20621f659bfSEliad Peller case IEEE80211_STA_RX_BW_160: 20721f659bfSEliad Peller /* 20821f659bfSEliad Peller * This applied for both 160 and 80+80. since we use 20921f659bfSEliad Peller * the returned value to consider degradation of 21021f659bfSEliad Peller * ctx->conf.min_def, we have to make sure to take 21121f659bfSEliad Peller * the bigger one (NL80211_CHAN_WIDTH_160). 21221f659bfSEliad Peller * Otherwise we might try degrading even when not 21321f659bfSEliad Peller * needed, as the max required sta_bw returned (80+80) 21421f659bfSEliad Peller * might be smaller than the configured bw (160). 21521f659bfSEliad Peller */ 21621f659bfSEliad Peller return NL80211_CHAN_WIDTH_160; 21721f659bfSEliad Peller default: 21821f659bfSEliad Peller WARN_ON(1); 21921f659bfSEliad Peller return NL80211_CHAN_WIDTH_20; 22021f659bfSEliad Peller } 22121f659bfSEliad Peller } 22221f659bfSEliad Peller 22321f659bfSEliad Peller static enum nl80211_chan_width 22421f659bfSEliad Peller ieee80211_get_max_required_bw(struct ieee80211_sub_if_data *sdata) 22521f659bfSEliad Peller { 22621f659bfSEliad Peller enum nl80211_chan_width max_bw = NL80211_CHAN_WIDTH_20_NOHT; 22721f659bfSEliad Peller struct sta_info *sta; 22821f659bfSEliad Peller 22921f659bfSEliad Peller rcu_read_lock(); 23021f659bfSEliad Peller list_for_each_entry_rcu(sta, &sdata->local->sta_list, list) { 23121f659bfSEliad Peller if (sdata != sta->sdata && 23221f659bfSEliad Peller !(sta->sdata->bss && sta->sdata->bss == sdata->bss)) 23321f659bfSEliad Peller continue; 23421f659bfSEliad Peller 23521f659bfSEliad Peller max_bw = max(max_bw, ieee80211_get_sta_bw(&sta->sta)); 23621f659bfSEliad Peller } 23721f659bfSEliad Peller rcu_read_unlock(); 23821f659bfSEliad Peller 23921f659bfSEliad Peller return max_bw; 24021f659bfSEliad Peller } 24121f659bfSEliad Peller 24221f659bfSEliad Peller static enum nl80211_chan_width 24321f659bfSEliad Peller ieee80211_get_chanctx_max_required_bw(struct ieee80211_local *local, 24421f659bfSEliad Peller struct ieee80211_chanctx_conf *conf) 24521f659bfSEliad Peller { 24621f659bfSEliad Peller struct ieee80211_sub_if_data *sdata; 24721f659bfSEliad Peller enum nl80211_chan_width max_bw = NL80211_CHAN_WIDTH_20_NOHT; 24821f659bfSEliad Peller 24921f659bfSEliad Peller rcu_read_lock(); 25021f659bfSEliad Peller list_for_each_entry_rcu(sdata, &local->interfaces, list) { 25121f659bfSEliad Peller struct ieee80211_vif *vif = &sdata->vif; 25221f659bfSEliad Peller enum nl80211_chan_width width = NL80211_CHAN_WIDTH_20_NOHT; 25321f659bfSEliad Peller 25421f659bfSEliad Peller if (!ieee80211_sdata_running(sdata)) 25521f659bfSEliad Peller continue; 25621f659bfSEliad Peller 25721f659bfSEliad Peller if (rcu_access_pointer(sdata->vif.chanctx_conf) != conf) 25821f659bfSEliad Peller continue; 25921f659bfSEliad Peller 26021f659bfSEliad Peller switch (vif->type) { 26121f659bfSEliad Peller case NL80211_IFTYPE_AP: 26221f659bfSEliad Peller case NL80211_IFTYPE_AP_VLAN: 26321f659bfSEliad Peller width = ieee80211_get_max_required_bw(sdata); 26421f659bfSEliad Peller break; 2650fabfaafSArik Nemtsov case NL80211_IFTYPE_STATION: 2660fabfaafSArik Nemtsov /* 2670fabfaafSArik Nemtsov * The ap's sta->bandwidth is not set yet at this 2680fabfaafSArik Nemtsov * point, so take the width from the chandef, but 2690fabfaafSArik Nemtsov * account also for TDLS peers 2700fabfaafSArik Nemtsov */ 2710fabfaafSArik Nemtsov width = max(vif->bss_conf.chandef.width, 2720fabfaafSArik Nemtsov ieee80211_get_max_required_bw(sdata)); 2730fabfaafSArik Nemtsov break; 27421f659bfSEliad Peller case NL80211_IFTYPE_P2P_DEVICE: 275cb3b7d87SAyala Beker case NL80211_IFTYPE_NAN: 27621f659bfSEliad Peller continue; 27721f659bfSEliad Peller case NL80211_IFTYPE_ADHOC: 27821f659bfSEliad Peller case NL80211_IFTYPE_WDS: 27921f659bfSEliad Peller case NL80211_IFTYPE_MESH_POINT: 2806e0bd6c3SRostislav Lisovy case NL80211_IFTYPE_OCB: 28121f659bfSEliad Peller width = vif->bss_conf.chandef.width; 28221f659bfSEliad Peller break; 28321f659bfSEliad Peller case NL80211_IFTYPE_UNSPECIFIED: 28421f659bfSEliad Peller case NUM_NL80211_IFTYPES: 28521f659bfSEliad Peller case NL80211_IFTYPE_MONITOR: 28621f659bfSEliad Peller case NL80211_IFTYPE_P2P_CLIENT: 28721f659bfSEliad Peller case NL80211_IFTYPE_P2P_GO: 28821f659bfSEliad Peller WARN_ON_ONCE(1); 28921f659bfSEliad Peller } 29021f659bfSEliad Peller max_bw = max(max_bw, width); 29121f659bfSEliad Peller } 2921c37a72cSEliad Peller 2931c37a72cSEliad Peller /* use the configured bandwidth in case of monitor interface */ 2941c37a72cSEliad Peller sdata = rcu_dereference(local->monitor_sdata); 2951c37a72cSEliad Peller if (sdata && rcu_access_pointer(sdata->vif.chanctx_conf) == conf) 2961c37a72cSEliad Peller max_bw = max(max_bw, conf->def.width); 2971c37a72cSEliad Peller 29821f659bfSEliad Peller rcu_read_unlock(); 29921f659bfSEliad Peller 30021f659bfSEliad Peller return max_bw; 30121f659bfSEliad Peller } 30221f659bfSEliad Peller 30321f659bfSEliad Peller /* 30421f659bfSEliad Peller * recalc the min required chan width of the channel context, which is 30521f659bfSEliad Peller * the max of min required widths of all the interfaces bound to this 30621f659bfSEliad Peller * channel context. 30721f659bfSEliad Peller */ 30821f659bfSEliad Peller void ieee80211_recalc_chanctx_min_def(struct ieee80211_local *local, 30921f659bfSEliad Peller struct ieee80211_chanctx *ctx) 31021f659bfSEliad Peller { 31121f659bfSEliad Peller enum nl80211_chan_width max_bw; 31221f659bfSEliad Peller struct cfg80211_chan_def min_def; 31321f659bfSEliad Peller 31421f659bfSEliad Peller lockdep_assert_held(&local->chanctx_mtx); 31521f659bfSEliad Peller 316df78a0c0SThomas Pedersen /* don't optimize non-20MHz based and radar_enabled confs */ 31721f659bfSEliad Peller if (ctx->conf.def.width == NL80211_CHAN_WIDTH_5 || 31821f659bfSEliad Peller ctx->conf.def.width == NL80211_CHAN_WIDTH_10 || 319df78a0c0SThomas Pedersen ctx->conf.def.width == NL80211_CHAN_WIDTH_1 || 320df78a0c0SThomas Pedersen ctx->conf.def.width == NL80211_CHAN_WIDTH_2 || 321df78a0c0SThomas Pedersen ctx->conf.def.width == NL80211_CHAN_WIDTH_4 || 322df78a0c0SThomas Pedersen ctx->conf.def.width == NL80211_CHAN_WIDTH_8 || 323df78a0c0SThomas Pedersen ctx->conf.def.width == NL80211_CHAN_WIDTH_16 || 32421f659bfSEliad Peller ctx->conf.radar_enabled) { 32521f659bfSEliad Peller ctx->conf.min_def = ctx->conf.def; 32621f659bfSEliad Peller return; 32721f659bfSEliad Peller } 32821f659bfSEliad Peller 32921f659bfSEliad Peller max_bw = ieee80211_get_chanctx_max_required_bw(local, &ctx->conf); 33021f659bfSEliad Peller 33121f659bfSEliad Peller /* downgrade chandef up to max_bw */ 33221f659bfSEliad Peller min_def = ctx->conf.def; 33321f659bfSEliad Peller while (min_def.width > max_bw) 33421f659bfSEliad Peller ieee80211_chandef_downgrade(&min_def); 33521f659bfSEliad Peller 33621f659bfSEliad Peller if (cfg80211_chandef_identical(&ctx->conf.min_def, &min_def)) 33721f659bfSEliad Peller return; 33821f659bfSEliad Peller 33921f659bfSEliad Peller ctx->conf.min_def = min_def; 34021f659bfSEliad Peller if (!ctx->driver_present) 34121f659bfSEliad Peller return; 34221f659bfSEliad Peller 34321f659bfSEliad Peller drv_change_chanctx(local, ctx, IEEE80211_CHANCTX_CHANGE_MIN_WIDTH); 34421f659bfSEliad Peller } 34521f659bfSEliad Peller 34618942d3bSJohannes Berg static void ieee80211_change_chanctx(struct ieee80211_local *local, 347e89a96f5SMichal Kazior struct ieee80211_chanctx *ctx, 3484bf88530SJohannes Berg const struct cfg80211_chan_def *chandef) 349e89a96f5SMichal Kazior { 350aa507a7bSArik Nemtsov if (cfg80211_chandef_identical(&ctx->conf.def, chandef)) { 351aa507a7bSArik Nemtsov ieee80211_recalc_chanctx_min_def(local, ctx); 352e89a96f5SMichal Kazior return; 353aa507a7bSArik Nemtsov } 354e89a96f5SMichal Kazior 3554bf88530SJohannes Berg WARN_ON(!cfg80211_chandef_compatible(&ctx->conf.def, chandef)); 3564bf88530SJohannes Berg 3574bf88530SJohannes Berg ctx->conf.def = *chandef; 3584bf88530SJohannes Berg drv_change_chanctx(local, ctx, IEEE80211_CHANCTX_CHANGE_WIDTH); 35921f659bfSEliad Peller ieee80211_recalc_chanctx_min_def(local, ctx); 36055de908aSJohannes Berg 36155de908aSJohannes Berg if (!local->use_chanctx) { 362675a0b04SKarl Beldan local->_oper_chandef = *chandef; 36355de908aSJohannes Berg ieee80211_hw_config(local, 0); 36455de908aSJohannes Berg } 3650aaffa9bSJohannes Berg } 366d01a1e65SMichal Kazior 367d01a1e65SMichal Kazior static struct ieee80211_chanctx * 368d01a1e65SMichal Kazior ieee80211_find_chanctx(struct ieee80211_local *local, 3694bf88530SJohannes Berg const struct cfg80211_chan_def *chandef, 370d01a1e65SMichal Kazior enum ieee80211_chanctx_mode mode) 371d01a1e65SMichal Kazior { 372d01a1e65SMichal Kazior struct ieee80211_chanctx *ctx; 373d01a1e65SMichal Kazior 374d01a1e65SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 375d01a1e65SMichal Kazior 376d01a1e65SMichal Kazior if (mode == IEEE80211_CHANCTX_EXCLUSIVE) 377d01a1e65SMichal Kazior return NULL; 378d01a1e65SMichal Kazior 379d01a1e65SMichal Kazior list_for_each_entry(ctx, &local->chanctx_list, list) { 3804bf88530SJohannes Berg const struct cfg80211_chan_def *compat; 381e89a96f5SMichal Kazior 3825bcae31dSMichal Kazior if (ctx->replace_state != IEEE80211_CHANCTX_REPLACE_NONE) 3835bcae31dSMichal Kazior continue; 3845bcae31dSMichal Kazior 385d01a1e65SMichal Kazior if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE) 386d01a1e65SMichal Kazior continue; 3874bf88530SJohannes Berg 3884bf88530SJohannes Berg compat = cfg80211_chandef_compatible(&ctx->conf.def, chandef); 3894bf88530SJohannes Berg if (!compat) 390d01a1e65SMichal Kazior continue; 391d01a1e65SMichal Kazior 3920288157bSMichal Kazior compat = ieee80211_chanctx_reserved_chandef(local, ctx, 3930288157bSMichal Kazior compat); 3940288157bSMichal Kazior if (!compat) 3950288157bSMichal Kazior continue; 3960288157bSMichal Kazior 39718942d3bSJohannes Berg ieee80211_change_chanctx(local, ctx, compat); 398e89a96f5SMichal Kazior 399d01a1e65SMichal Kazior return ctx; 400d01a1e65SMichal Kazior } 401d01a1e65SMichal Kazior 402d01a1e65SMichal Kazior return NULL; 403d01a1e65SMichal Kazior } 404d01a1e65SMichal Kazior 4055cbc95a7SEliad Peller bool ieee80211_is_radar_required(struct ieee80211_local *local) 406e4746851SSimon Wunderlich { 407e4746851SSimon Wunderlich struct ieee80211_sub_if_data *sdata; 408e4746851SSimon Wunderlich 409cc901de1SMichal Kazior lockdep_assert_held(&local->mtx); 410cc901de1SMichal Kazior 411e4746851SSimon Wunderlich rcu_read_lock(); 412e4746851SSimon Wunderlich list_for_each_entry_rcu(sdata, &local->interfaces, list) { 413e4746851SSimon Wunderlich if (sdata->radar_required) { 414e4746851SSimon Wunderlich rcu_read_unlock(); 415e4746851SSimon Wunderlich return true; 416e4746851SSimon Wunderlich } 417e4746851SSimon Wunderlich } 418e4746851SSimon Wunderlich rcu_read_unlock(); 419e4746851SSimon Wunderlich 420e4746851SSimon Wunderlich return false; 421e4746851SSimon Wunderlich } 422e4746851SSimon Wunderlich 423e7f2337aSEliad Peller static bool 424e7f2337aSEliad Peller ieee80211_chanctx_radar_required(struct ieee80211_local *local, 425e7f2337aSEliad Peller struct ieee80211_chanctx *ctx) 426e7f2337aSEliad Peller { 427e7f2337aSEliad Peller struct ieee80211_chanctx_conf *conf = &ctx->conf; 428e7f2337aSEliad Peller struct ieee80211_sub_if_data *sdata; 429e7f2337aSEliad Peller bool required = false; 430e7f2337aSEliad Peller 431e7f2337aSEliad Peller lockdep_assert_held(&local->chanctx_mtx); 432e7f2337aSEliad Peller lockdep_assert_held(&local->mtx); 433e7f2337aSEliad Peller 434e7f2337aSEliad Peller rcu_read_lock(); 435e7f2337aSEliad Peller list_for_each_entry_rcu(sdata, &local->interfaces, list) { 436e7f2337aSEliad Peller if (!ieee80211_sdata_running(sdata)) 437e7f2337aSEliad Peller continue; 438e7f2337aSEliad Peller if (rcu_access_pointer(sdata->vif.chanctx_conf) != conf) 439e7f2337aSEliad Peller continue; 440e7f2337aSEliad Peller if (!sdata->radar_required) 441e7f2337aSEliad Peller continue; 442e7f2337aSEliad Peller 443e7f2337aSEliad Peller required = true; 444e7f2337aSEliad Peller break; 445e7f2337aSEliad Peller } 446e7f2337aSEliad Peller rcu_read_unlock(); 447e7f2337aSEliad Peller 448e7f2337aSEliad Peller return required; 449e7f2337aSEliad Peller } 450e7f2337aSEliad Peller 451d01a1e65SMichal Kazior static struct ieee80211_chanctx * 452ed68ebcaSMichal Kazior ieee80211_alloc_chanctx(struct ieee80211_local *local, 4534bf88530SJohannes Berg const struct cfg80211_chan_def *chandef, 454d01a1e65SMichal Kazior enum ieee80211_chanctx_mode mode) 455d01a1e65SMichal Kazior { 456d01a1e65SMichal Kazior struct ieee80211_chanctx *ctx; 457d01a1e65SMichal Kazior 458d01a1e65SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 459d01a1e65SMichal Kazior 460d01a1e65SMichal Kazior ctx = kzalloc(sizeof(*ctx) + local->hw.chanctx_data_size, GFP_KERNEL); 461d01a1e65SMichal Kazior if (!ctx) 462ed68ebcaSMichal Kazior return NULL; 463d01a1e65SMichal Kazior 464484298adSMichal Kazior INIT_LIST_HEAD(&ctx->assigned_vifs); 465e3afb920SMichal Kazior INIT_LIST_HEAD(&ctx->reserved_vifs); 4664bf88530SJohannes Berg ctx->conf.def = *chandef; 46704ecd257SJohannes Berg ctx->conf.rx_chains_static = 1; 46804ecd257SJohannes Berg ctx->conf.rx_chains_dynamic = 1; 469d01a1e65SMichal Kazior ctx->mode = mode; 470e7f2337aSEliad Peller ctx->conf.radar_enabled = false; 47121f659bfSEliad Peller ieee80211_recalc_chanctx_min_def(local, ctx); 472ed68ebcaSMichal Kazior 473ed68ebcaSMichal Kazior return ctx; 474ed68ebcaSMichal Kazior } 475ed68ebcaSMichal Kazior 476ed68ebcaSMichal Kazior static int ieee80211_add_chanctx(struct ieee80211_local *local, 477ed68ebcaSMichal Kazior struct ieee80211_chanctx *ctx) 478ed68ebcaSMichal Kazior { 479ed68ebcaSMichal Kazior u32 changed; 480ed68ebcaSMichal Kazior int err; 481ed68ebcaSMichal Kazior 482ed68ebcaSMichal Kazior lockdep_assert_held(&local->mtx); 483ed68ebcaSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 484ed68ebcaSMichal Kazior 485e4746851SSimon Wunderlich if (!local->use_chanctx) 486e4746851SSimon Wunderlich local->hw.conf.radar_enabled = ctx->conf.radar_enabled; 487d01a1e65SMichal Kazior 488382a103bSJohannes Berg /* turn idle off *before* setting channel -- some drivers need that */ 489382a103bSJohannes Berg changed = ieee80211_idle_off(local); 490382a103bSJohannes Berg if (changed) 491382a103bSJohannes Berg ieee80211_hw_config(local, changed); 492382a103bSJohannes Berg 49355de908aSJohannes Berg if (!local->use_chanctx) { 494ed68ebcaSMichal Kazior local->_oper_chandef = ctx->conf.def; 4959b4816f5SMichal Kazior ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); 49655de908aSJohannes Berg } else { 49735f2fce9SMichal Kazior err = drv_add_chanctx(local, ctx); 49835f2fce9SMichal Kazior if (err) { 499382a103bSJohannes Berg ieee80211_recalc_idle(local); 500ed68ebcaSMichal Kazior return err; 501ed68ebcaSMichal Kazior } 502ed68ebcaSMichal Kazior } 503ed68ebcaSMichal Kazior 504ed68ebcaSMichal Kazior return 0; 505ed68ebcaSMichal Kazior } 506ed68ebcaSMichal Kazior 507ed68ebcaSMichal Kazior static struct ieee80211_chanctx * 508ed68ebcaSMichal Kazior ieee80211_new_chanctx(struct ieee80211_local *local, 509ed68ebcaSMichal Kazior const struct cfg80211_chan_def *chandef, 510ed68ebcaSMichal Kazior enum ieee80211_chanctx_mode mode) 511ed68ebcaSMichal Kazior { 512ed68ebcaSMichal Kazior struct ieee80211_chanctx *ctx; 513ed68ebcaSMichal Kazior int err; 514ed68ebcaSMichal Kazior 515ed68ebcaSMichal Kazior lockdep_assert_held(&local->mtx); 516ed68ebcaSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 517ed68ebcaSMichal Kazior 518ed68ebcaSMichal Kazior ctx = ieee80211_alloc_chanctx(local, chandef, mode); 519ed68ebcaSMichal Kazior if (!ctx) 520ed68ebcaSMichal Kazior return ERR_PTR(-ENOMEM); 521ed68ebcaSMichal Kazior 522ed68ebcaSMichal Kazior err = ieee80211_add_chanctx(local, ctx); 523ed68ebcaSMichal Kazior if (err) { 524ed68ebcaSMichal Kazior kfree(ctx); 52534a3740dSJohannes Berg return ERR_PTR(err); 52635f2fce9SMichal Kazior } 52735f2fce9SMichal Kazior 5283448c005SJohannes Berg list_add_rcu(&ctx->list, &local->chanctx_list); 529d01a1e65SMichal Kazior return ctx; 530d01a1e65SMichal Kazior } 531d01a1e65SMichal Kazior 5321f0d54cdSMichal Kazior static void ieee80211_del_chanctx(struct ieee80211_local *local, 533d01a1e65SMichal Kazior struct ieee80211_chanctx *ctx) 534d01a1e65SMichal Kazior { 535d01a1e65SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 536d01a1e65SMichal Kazior 53755de908aSJohannes Berg if (!local->use_chanctx) { 538675a0b04SKarl Beldan struct cfg80211_chan_def *chandef = &local->_oper_chandef; 539675a0b04SKarl Beldan chandef->width = NL80211_CHAN_WIDTH_20_NOHT; 540675a0b04SKarl Beldan chandef->center_freq1 = chandef->chan->center_freq; 541b6011960SThomas Pedersen chandef->freq1_offset = chandef->chan->freq_offset; 542675a0b04SKarl Beldan chandef->center_freq2 = 0; 543e4746851SSimon Wunderlich 544e4746851SSimon Wunderlich /* NOTE: Disabling radar is only valid here for 545e4746851SSimon Wunderlich * single channel context. To be sure, check it ... 546e4746851SSimon Wunderlich */ 5471f0d54cdSMichal Kazior WARN_ON(local->hw.conf.radar_enabled && 5481f0d54cdSMichal Kazior !list_empty(&local->chanctx_list)); 5491f0d54cdSMichal Kazior 550e4746851SSimon Wunderlich local->hw.conf.radar_enabled = false; 551e4746851SSimon Wunderlich 5529b4816f5SMichal Kazior ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); 55355de908aSJohannes Berg } else { 55435f2fce9SMichal Kazior drv_remove_chanctx(local, ctx); 55555de908aSJohannes Berg } 55635f2fce9SMichal Kazior 557fd0f979aSJohannes Berg ieee80211_recalc_idle(local); 558d01a1e65SMichal Kazior } 559d01a1e65SMichal Kazior 5601f0d54cdSMichal Kazior static void ieee80211_free_chanctx(struct ieee80211_local *local, 561d01a1e65SMichal Kazior struct ieee80211_chanctx *ctx) 562d01a1e65SMichal Kazior { 563d01a1e65SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 564d01a1e65SMichal Kazior 565c0166da9SMichal Kazior WARN_ON_ONCE(ieee80211_chanctx_refcount(local, ctx) != 0); 56635f2fce9SMichal Kazior 5671f0d54cdSMichal Kazior list_del_rcu(&ctx->list); 5681f0d54cdSMichal Kazior ieee80211_del_chanctx(local, ctx); 5691f0d54cdSMichal Kazior kfree_rcu(ctx, rcu_head); 570d01a1e65SMichal Kazior } 571d01a1e65SMichal Kazior 5720fabfaafSArik Nemtsov void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local, 573e89a96f5SMichal Kazior struct ieee80211_chanctx *ctx) 574e89a96f5SMichal Kazior { 575e89a96f5SMichal Kazior struct ieee80211_chanctx_conf *conf = &ctx->conf; 576e89a96f5SMichal Kazior struct ieee80211_sub_if_data *sdata; 5774bf88530SJohannes Berg const struct cfg80211_chan_def *compat = NULL; 5780fabfaafSArik Nemtsov struct sta_info *sta; 579e89a96f5SMichal Kazior 580e89a96f5SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 581e89a96f5SMichal Kazior 582e89a96f5SMichal Kazior rcu_read_lock(); 583e89a96f5SMichal Kazior list_for_each_entry_rcu(sdata, &local->interfaces, list) { 5844bf88530SJohannes Berg 585e89a96f5SMichal Kazior if (!ieee80211_sdata_running(sdata)) 586e89a96f5SMichal Kazior continue; 587e89a96f5SMichal Kazior if (rcu_access_pointer(sdata->vif.chanctx_conf) != conf) 588e89a96f5SMichal Kazior continue; 5890e67c136SFelix Fietkau if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) 5900e67c136SFelix Fietkau continue; 591e89a96f5SMichal Kazior 5924bf88530SJohannes Berg if (!compat) 5934bf88530SJohannes Berg compat = &sdata->vif.bss_conf.chandef; 5944bf88530SJohannes Berg 5954bf88530SJohannes Berg compat = cfg80211_chandef_compatible( 5964bf88530SJohannes Berg &sdata->vif.bss_conf.chandef, compat); 597a00f4f6eSMichal Kazior if (WARN_ON_ONCE(!compat)) 5984bf88530SJohannes Berg break; 599e89a96f5SMichal Kazior } 6000fabfaafSArik Nemtsov 6010fabfaafSArik Nemtsov /* TDLS peers can sometimes affect the chandef width */ 6020fabfaafSArik Nemtsov list_for_each_entry_rcu(sta, &local->sta_list, list) { 6030fabfaafSArik Nemtsov if (!sta->uploaded || 6040fabfaafSArik Nemtsov !test_sta_flag(sta, WLAN_STA_TDLS_WIDER_BW) || 6050fabfaafSArik Nemtsov !test_sta_flag(sta, WLAN_STA_AUTHORIZED) || 6060fabfaafSArik Nemtsov !sta->tdls_chandef.chan) 6070fabfaafSArik Nemtsov continue; 6080fabfaafSArik Nemtsov 6090fabfaafSArik Nemtsov compat = cfg80211_chandef_compatible(&sta->tdls_chandef, 6100fabfaafSArik Nemtsov compat); 6110fabfaafSArik Nemtsov if (WARN_ON_ONCE(!compat)) 6120fabfaafSArik Nemtsov break; 6130fabfaafSArik Nemtsov } 614e89a96f5SMichal Kazior rcu_read_unlock(); 615e89a96f5SMichal Kazior 616a00f4f6eSMichal Kazior if (!compat) 6174bf88530SJohannes Berg return; 618e89a96f5SMichal Kazior 61918942d3bSJohannes Berg ieee80211_change_chanctx(local, ctx, compat); 620e89a96f5SMichal Kazior } 621e89a96f5SMichal Kazior 622367bbd10SJohannes Berg static void ieee80211_recalc_radar_chanctx(struct ieee80211_local *local, 623367bbd10SJohannes Berg struct ieee80211_chanctx *chanctx) 624367bbd10SJohannes Berg { 625367bbd10SJohannes Berg bool radar_enabled; 626367bbd10SJohannes Berg 627367bbd10SJohannes Berg lockdep_assert_held(&local->chanctx_mtx); 6285cbc95a7SEliad Peller /* for ieee80211_is_radar_required */ 62934a3740dSJohannes Berg lockdep_assert_held(&local->mtx); 630367bbd10SJohannes Berg 631e7f2337aSEliad Peller radar_enabled = ieee80211_chanctx_radar_required(local, chanctx); 632367bbd10SJohannes Berg 633367bbd10SJohannes Berg if (radar_enabled == chanctx->conf.radar_enabled) 634367bbd10SJohannes Berg return; 635367bbd10SJohannes Berg 636367bbd10SJohannes Berg chanctx->conf.radar_enabled = radar_enabled; 637367bbd10SJohannes Berg 638367bbd10SJohannes Berg if (!local->use_chanctx) { 639367bbd10SJohannes Berg local->hw.conf.radar_enabled = chanctx->conf.radar_enabled; 640367bbd10SJohannes Berg ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); 641367bbd10SJohannes Berg } 642367bbd10SJohannes Berg 643367bbd10SJohannes Berg drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RADAR); 644367bbd10SJohannes Berg } 645367bbd10SJohannes Berg 64677eeba97SLuciano Coelho static int ieee80211_assign_vif_chanctx(struct ieee80211_sub_if_data *sdata, 64777eeba97SLuciano Coelho struct ieee80211_chanctx *new_ctx) 648d01a1e65SMichal Kazior { 64935f2fce9SMichal Kazior struct ieee80211_local *local = sdata->local; 65077eeba97SLuciano Coelho struct ieee80211_chanctx_conf *conf; 65177eeba97SLuciano Coelho struct ieee80211_chanctx *curr_ctx = NULL; 65277eeba97SLuciano Coelho int ret = 0; 653d01a1e65SMichal Kazior 654708d50edSAyala Beker if (WARN_ON(sdata->vif.type == NL80211_IFTYPE_NAN)) 655708d50edSAyala Beker return -ENOTSUPP; 656708d50edSAyala Beker 65777eeba97SLuciano Coelho conf = rcu_dereference_protected(sdata->vif.chanctx_conf, 65877eeba97SLuciano Coelho lockdep_is_held(&local->chanctx_mtx)); 659d01a1e65SMichal Kazior 66077eeba97SLuciano Coelho if (conf) { 66177eeba97SLuciano Coelho curr_ctx = container_of(conf, struct ieee80211_chanctx, conf); 66235f2fce9SMichal Kazior 66377eeba97SLuciano Coelho drv_unassign_vif_chanctx(local, sdata, curr_ctx); 66477eeba97SLuciano Coelho conf = NULL; 665484298adSMichal Kazior list_del(&sdata->assigned_chanctx_list); 66677eeba97SLuciano Coelho } 66777eeba97SLuciano Coelho 66877eeba97SLuciano Coelho if (new_ctx) { 66977eeba97SLuciano Coelho ret = drv_assign_vif_chanctx(local, sdata, new_ctx); 67077eeba97SLuciano Coelho if (ret) 67177eeba97SLuciano Coelho goto out; 67277eeba97SLuciano Coelho 67377eeba97SLuciano Coelho conf = &new_ctx->conf; 674484298adSMichal Kazior list_add(&sdata->assigned_chanctx_list, 675484298adSMichal Kazior &new_ctx->assigned_vifs); 67677eeba97SLuciano Coelho } 67777eeba97SLuciano Coelho 67877eeba97SLuciano Coelho out: 67977eeba97SLuciano Coelho rcu_assign_pointer(sdata->vif.chanctx_conf, conf); 68077eeba97SLuciano Coelho 68177eeba97SLuciano Coelho sdata->vif.bss_conf.idle = !conf; 68277eeba97SLuciano Coelho 683c0166da9SMichal Kazior if (curr_ctx && ieee80211_chanctx_num_assigned(local, curr_ctx) > 0) { 68477eeba97SLuciano Coelho ieee80211_recalc_chanctx_chantype(local, curr_ctx); 68577eeba97SLuciano Coelho ieee80211_recalc_smps_chanctx(local, curr_ctx); 68677eeba97SLuciano Coelho ieee80211_recalc_radar_chanctx(local, curr_ctx); 68777eeba97SLuciano Coelho ieee80211_recalc_chanctx_min_def(local, curr_ctx); 68877eeba97SLuciano Coelho } 68977eeba97SLuciano Coelho 690c0166da9SMichal Kazior if (new_ctx && ieee80211_chanctx_num_assigned(local, new_ctx) > 0) { 691db82d8a9SLorenzo Bianconi ieee80211_recalc_txpower(sdata, false); 69277eeba97SLuciano Coelho ieee80211_recalc_chanctx_min_def(local, new_ctx); 69377eeba97SLuciano Coelho } 6945bbe754dSJohannes Berg 6955bbe754dSJohannes Berg if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE && 6965bbe754dSJohannes Berg sdata->vif.type != NL80211_IFTYPE_MONITOR) 69777eeba97SLuciano Coelho ieee80211_bss_info_change_notify(sdata, 69877eeba97SLuciano Coelho BSS_CHANGED_IDLE); 699fd0f979aSJohannes Berg 70017c18bf8SJohannes Berg ieee80211_check_fast_xmit_iface(sdata); 70117c18bf8SJohannes Berg 70277eeba97SLuciano Coelho return ret; 703d01a1e65SMichal Kazior } 704d01a1e65SMichal Kazior 70504ecd257SJohannes Berg void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local, 70604ecd257SJohannes Berg struct ieee80211_chanctx *chanctx) 70704ecd257SJohannes Berg { 70804ecd257SJohannes Berg struct ieee80211_sub_if_data *sdata; 70904ecd257SJohannes Berg u8 rx_chains_static, rx_chains_dynamic; 71004ecd257SJohannes Berg 71104ecd257SJohannes Berg lockdep_assert_held(&local->chanctx_mtx); 71204ecd257SJohannes Berg 71304ecd257SJohannes Berg rx_chains_static = 1; 71404ecd257SJohannes Berg rx_chains_dynamic = 1; 71504ecd257SJohannes Berg 71604ecd257SJohannes Berg rcu_read_lock(); 71704ecd257SJohannes Berg list_for_each_entry_rcu(sdata, &local->interfaces, list) { 71804ecd257SJohannes Berg u8 needed_static, needed_dynamic; 71904ecd257SJohannes Berg 72004ecd257SJohannes Berg if (!ieee80211_sdata_running(sdata)) 72104ecd257SJohannes Berg continue; 72204ecd257SJohannes Berg 72304ecd257SJohannes Berg if (rcu_access_pointer(sdata->vif.chanctx_conf) != 72404ecd257SJohannes Berg &chanctx->conf) 72504ecd257SJohannes Berg continue; 72604ecd257SJohannes Berg 72704ecd257SJohannes Berg switch (sdata->vif.type) { 72804ecd257SJohannes Berg case NL80211_IFTYPE_P2P_DEVICE: 729cb3b7d87SAyala Beker case NL80211_IFTYPE_NAN: 73004ecd257SJohannes Berg continue; 73104ecd257SJohannes Berg case NL80211_IFTYPE_STATION: 73204ecd257SJohannes Berg if (!sdata->u.mgd.associated) 73304ecd257SJohannes Berg continue; 73404ecd257SJohannes Berg break; 73504ecd257SJohannes Berg case NL80211_IFTYPE_AP_VLAN: 73604ecd257SJohannes Berg continue; 73704ecd257SJohannes Berg case NL80211_IFTYPE_AP: 73804ecd257SJohannes Berg case NL80211_IFTYPE_ADHOC: 73904ecd257SJohannes Berg case NL80211_IFTYPE_WDS: 74004ecd257SJohannes Berg case NL80211_IFTYPE_MESH_POINT: 741239281f8SRostislav Lisovy case NL80211_IFTYPE_OCB: 74204ecd257SJohannes Berg break; 74304ecd257SJohannes Berg default: 74404ecd257SJohannes Berg WARN_ON_ONCE(1); 74504ecd257SJohannes Berg } 74604ecd257SJohannes Berg 74704ecd257SJohannes Berg switch (sdata->smps_mode) { 74804ecd257SJohannes Berg default: 74904ecd257SJohannes Berg WARN_ONCE(1, "Invalid SMPS mode %d\n", 75004ecd257SJohannes Berg sdata->smps_mode); 751fc0561dcSGustavo A. R. Silva fallthrough; 75204ecd257SJohannes Berg case IEEE80211_SMPS_OFF: 75304ecd257SJohannes Berg needed_static = sdata->needed_rx_chains; 75404ecd257SJohannes Berg needed_dynamic = sdata->needed_rx_chains; 75504ecd257SJohannes Berg break; 75604ecd257SJohannes Berg case IEEE80211_SMPS_DYNAMIC: 75704ecd257SJohannes Berg needed_static = 1; 75804ecd257SJohannes Berg needed_dynamic = sdata->needed_rx_chains; 75904ecd257SJohannes Berg break; 76004ecd257SJohannes Berg case IEEE80211_SMPS_STATIC: 76104ecd257SJohannes Berg needed_static = 1; 76204ecd257SJohannes Berg needed_dynamic = 1; 76304ecd257SJohannes Berg break; 76404ecd257SJohannes Berg } 76504ecd257SJohannes Berg 76604ecd257SJohannes Berg rx_chains_static = max(rx_chains_static, needed_static); 76704ecd257SJohannes Berg rx_chains_dynamic = max(rx_chains_dynamic, needed_dynamic); 76804ecd257SJohannes Berg } 7697b8a9cddSIdo Yariv 7707b8a9cddSIdo Yariv /* Disable SMPS for the monitor interface */ 7717b8a9cddSIdo Yariv sdata = rcu_dereference(local->monitor_sdata); 7727b8a9cddSIdo Yariv if (sdata && 7737b8a9cddSIdo Yariv rcu_access_pointer(sdata->vif.chanctx_conf) == &chanctx->conf) 7747b8a9cddSIdo Yariv rx_chains_dynamic = rx_chains_static = local->rx_chains; 7757b8a9cddSIdo Yariv 77604ecd257SJohannes Berg rcu_read_unlock(); 77704ecd257SJohannes Berg 77804ecd257SJohannes Berg if (!local->use_chanctx) { 77904ecd257SJohannes Berg if (rx_chains_static > 1) 78004ecd257SJohannes Berg local->smps_mode = IEEE80211_SMPS_OFF; 78104ecd257SJohannes Berg else if (rx_chains_dynamic > 1) 78204ecd257SJohannes Berg local->smps_mode = IEEE80211_SMPS_DYNAMIC; 78304ecd257SJohannes Berg else 78404ecd257SJohannes Berg local->smps_mode = IEEE80211_SMPS_STATIC; 78504ecd257SJohannes Berg ieee80211_hw_config(local, 0); 78604ecd257SJohannes Berg } 78704ecd257SJohannes Berg 78804ecd257SJohannes Berg if (rx_chains_static == chanctx->conf.rx_chains_static && 78904ecd257SJohannes Berg rx_chains_dynamic == chanctx->conf.rx_chains_dynamic) 79004ecd257SJohannes Berg return; 79104ecd257SJohannes Berg 79204ecd257SJohannes Berg chanctx->conf.rx_chains_static = rx_chains_static; 79304ecd257SJohannes Berg chanctx->conf.rx_chains_dynamic = rx_chains_dynamic; 79404ecd257SJohannes Berg drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RX_CHAINS); 79504ecd257SJohannes Berg } 79604ecd257SJohannes Berg 79711335a55SLuciano Coelho static void 79811335a55SLuciano Coelho __ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata, 79911335a55SLuciano Coelho bool clear) 80011335a55SLuciano Coelho { 80133926eb7SJohannes Berg struct ieee80211_local *local __maybe_unused = sdata->local; 80211335a55SLuciano Coelho struct ieee80211_sub_if_data *vlan; 80311335a55SLuciano Coelho struct ieee80211_chanctx_conf *conf; 80411335a55SLuciano Coelho 80511335a55SLuciano Coelho if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP)) 80611335a55SLuciano Coelho return; 80711335a55SLuciano Coelho 80811335a55SLuciano Coelho lockdep_assert_held(&local->mtx); 80911335a55SLuciano Coelho 81011335a55SLuciano Coelho /* Check that conf exists, even when clearing this function 81111335a55SLuciano Coelho * must be called with the AP's channel context still there 81211335a55SLuciano Coelho * as it would otherwise cause VLANs to have an invalid 81311335a55SLuciano Coelho * channel context pointer for a while, possibly pointing 81411335a55SLuciano Coelho * to a channel context that has already been freed. 81511335a55SLuciano Coelho */ 81611335a55SLuciano Coelho conf = rcu_dereference_protected(sdata->vif.chanctx_conf, 81711335a55SLuciano Coelho lockdep_is_held(&local->chanctx_mtx)); 81811335a55SLuciano Coelho WARN_ON(!conf); 81911335a55SLuciano Coelho 82011335a55SLuciano Coelho if (clear) 82111335a55SLuciano Coelho conf = NULL; 82211335a55SLuciano Coelho 82311335a55SLuciano Coelho list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) 82411335a55SLuciano Coelho rcu_assign_pointer(vlan->vif.chanctx_conf, conf); 82511335a55SLuciano Coelho } 82611335a55SLuciano Coelho 82711335a55SLuciano Coelho void ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata, 82811335a55SLuciano Coelho bool clear) 82911335a55SLuciano Coelho { 83011335a55SLuciano Coelho struct ieee80211_local *local = sdata->local; 83111335a55SLuciano Coelho 83211335a55SLuciano Coelho mutex_lock(&local->chanctx_mtx); 83311335a55SLuciano Coelho 83411335a55SLuciano Coelho __ieee80211_vif_copy_chanctx_to_vlans(sdata, clear); 83511335a55SLuciano Coelho 83611335a55SLuciano Coelho mutex_unlock(&local->chanctx_mtx); 83711335a55SLuciano Coelho } 83811335a55SLuciano Coelho 83911335a55SLuciano Coelho int ieee80211_vif_unreserve_chanctx(struct ieee80211_sub_if_data *sdata) 84011335a55SLuciano Coelho { 841e3afb920SMichal Kazior struct ieee80211_chanctx *ctx = sdata->reserved_chanctx; 842e3afb920SMichal Kazior 84311335a55SLuciano Coelho lockdep_assert_held(&sdata->local->chanctx_mtx); 84411335a55SLuciano Coelho 845e3afb920SMichal Kazior if (WARN_ON(!ctx)) 84611335a55SLuciano Coelho return -EINVAL; 84711335a55SLuciano Coelho 848e3afb920SMichal Kazior list_del(&sdata->reserved_chanctx_list); 84911335a55SLuciano Coelho sdata->reserved_chanctx = NULL; 85011335a55SLuciano Coelho 8515bcae31dSMichal Kazior if (ieee80211_chanctx_refcount(sdata->local, ctx) == 0) { 8525bcae31dSMichal Kazior if (ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER) { 8535bcae31dSMichal Kazior if (WARN_ON(!ctx->replace_ctx)) 8545bcae31dSMichal Kazior return -EINVAL; 8555bcae31dSMichal Kazior 8565bcae31dSMichal Kazior WARN_ON(ctx->replace_ctx->replace_state != 8575bcae31dSMichal Kazior IEEE80211_CHANCTX_WILL_BE_REPLACED); 8585bcae31dSMichal Kazior WARN_ON(ctx->replace_ctx->replace_ctx != ctx); 8595bcae31dSMichal Kazior 8605bcae31dSMichal Kazior ctx->replace_ctx->replace_ctx = NULL; 8615bcae31dSMichal Kazior ctx->replace_ctx->replace_state = 8625bcae31dSMichal Kazior IEEE80211_CHANCTX_REPLACE_NONE; 8635bcae31dSMichal Kazior 8645bcae31dSMichal Kazior list_del_rcu(&ctx->list); 8655bcae31dSMichal Kazior kfree_rcu(ctx, rcu_head); 8665bcae31dSMichal Kazior } else { 867e3afb920SMichal Kazior ieee80211_free_chanctx(sdata->local, ctx); 8685bcae31dSMichal Kazior } 8695bcae31dSMichal Kazior } 870e3afb920SMichal Kazior 87111335a55SLuciano Coelho return 0; 87211335a55SLuciano Coelho } 87311335a55SLuciano Coelho 87411335a55SLuciano Coelho int ieee80211_vif_reserve_chanctx(struct ieee80211_sub_if_data *sdata, 87511335a55SLuciano Coelho const struct cfg80211_chan_def *chandef, 87609332481SMichal Kazior enum ieee80211_chanctx_mode mode, 87709332481SMichal Kazior bool radar_required) 87811335a55SLuciano Coelho { 87911335a55SLuciano Coelho struct ieee80211_local *local = sdata->local; 8805bcae31dSMichal Kazior struct ieee80211_chanctx *new_ctx, *curr_ctx, *ctx; 88111335a55SLuciano Coelho 8825bcae31dSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 88311335a55SLuciano Coelho 8845bcae31dSMichal Kazior curr_ctx = ieee80211_vif_get_chanctx(sdata); 8855bcae31dSMichal Kazior if (curr_ctx && local->use_chanctx && !local->ops->switch_vif_chanctx) 8865bcae31dSMichal Kazior return -ENOTSUPP; 88711335a55SLuciano Coelho 88813f348a8SMichal Kazior new_ctx = ieee80211_find_reservation_chanctx(local, chandef, mode); 88911335a55SLuciano Coelho if (!new_ctx) { 8905bcae31dSMichal Kazior if (ieee80211_can_create_new_chanctx(local)) { 89111335a55SLuciano Coelho new_ctx = ieee80211_new_chanctx(local, chandef, mode); 8925bcae31dSMichal Kazior if (IS_ERR(new_ctx)) 8935bcae31dSMichal Kazior return PTR_ERR(new_ctx); 894c2b90ad8SMichal Kazior } else { 8955bcae31dSMichal Kazior if (!curr_ctx || 8965bcae31dSMichal Kazior (curr_ctx->replace_state == 8975bcae31dSMichal Kazior IEEE80211_CHANCTX_WILL_BE_REPLACED) || 8985bcae31dSMichal Kazior !list_empty(&curr_ctx->reserved_vifs)) { 8995bcae31dSMichal Kazior /* 9005bcae31dSMichal Kazior * Another vif already requested this context 9015bcae31dSMichal Kazior * for a reservation. Find another one hoping 9025bcae31dSMichal Kazior * all vifs assigned to it will also switch 9035bcae31dSMichal Kazior * soon enough. 9045bcae31dSMichal Kazior * 9055bcae31dSMichal Kazior * TODO: This needs a little more work as some 9065bcae31dSMichal Kazior * cases (more than 2 chanctx capable devices) 9075bcae31dSMichal Kazior * may fail which could otherwise succeed 9085bcae31dSMichal Kazior * provided some channel context juggling was 9095bcae31dSMichal Kazior * performed. 9105bcae31dSMichal Kazior * 9115bcae31dSMichal Kazior * Consider ctx1..3, vif1..6, each ctx has 2 9125bcae31dSMichal Kazior * vifs. vif1 and vif2 from ctx1 request new 9135bcae31dSMichal Kazior * different chandefs starting 2 in-place 9145bcae31dSMichal Kazior * reserations with ctx4 and ctx5 replacing 9155bcae31dSMichal Kazior * ctx1 and ctx2 respectively. Next vif5 and 9165bcae31dSMichal Kazior * vif6 from ctx3 reserve ctx4. If vif3 and 9175bcae31dSMichal Kazior * vif4 remain on ctx2 as they are then this 9185bcae31dSMichal Kazior * fails unless `replace_ctx` from ctx5 is 9195bcae31dSMichal Kazior * replaced with ctx3. 9205bcae31dSMichal Kazior */ 9215bcae31dSMichal Kazior list_for_each_entry(ctx, &local->chanctx_list, 9225bcae31dSMichal Kazior list) { 9235bcae31dSMichal Kazior if (ctx->replace_state != 9245bcae31dSMichal Kazior IEEE80211_CHANCTX_REPLACE_NONE) 9255bcae31dSMichal Kazior continue; 9265bcae31dSMichal Kazior 9275bcae31dSMichal Kazior if (!list_empty(&ctx->reserved_vifs)) 9285bcae31dSMichal Kazior continue; 9295bcae31dSMichal Kazior 9305bcae31dSMichal Kazior curr_ctx = ctx; 9315bcae31dSMichal Kazior break; 9325bcae31dSMichal Kazior } 9335bcae31dSMichal Kazior } 9345bcae31dSMichal Kazior 9355bcae31dSMichal Kazior /* 9365bcae31dSMichal Kazior * If that's true then all available contexts already 9375bcae31dSMichal Kazior * have reservations and cannot be used. 9385bcae31dSMichal Kazior */ 9395bcae31dSMichal Kazior if (!curr_ctx || 9405bcae31dSMichal Kazior (curr_ctx->replace_state == 9415bcae31dSMichal Kazior IEEE80211_CHANCTX_WILL_BE_REPLACED) || 9425bcae31dSMichal Kazior !list_empty(&curr_ctx->reserved_vifs)) 9435bcae31dSMichal Kazior return -EBUSY; 9445bcae31dSMichal Kazior 9455bcae31dSMichal Kazior new_ctx = ieee80211_alloc_chanctx(local, chandef, mode); 9465bcae31dSMichal Kazior if (!new_ctx) 9475bcae31dSMichal Kazior return -ENOMEM; 9485bcae31dSMichal Kazior 9495bcae31dSMichal Kazior new_ctx->replace_ctx = curr_ctx; 9505bcae31dSMichal Kazior new_ctx->replace_state = 9515bcae31dSMichal Kazior IEEE80211_CHANCTX_REPLACES_OTHER; 9525bcae31dSMichal Kazior 9535bcae31dSMichal Kazior curr_ctx->replace_ctx = new_ctx; 9545bcae31dSMichal Kazior curr_ctx->replace_state = 9555bcae31dSMichal Kazior IEEE80211_CHANCTX_WILL_BE_REPLACED; 9565bcae31dSMichal Kazior 9575bcae31dSMichal Kazior list_add_rcu(&new_ctx->list, &local->chanctx_list); 95811335a55SLuciano Coelho } 9595d52ee81SLuciano Coelho } 96011335a55SLuciano Coelho 961e3afb920SMichal Kazior list_add(&sdata->reserved_chanctx_list, &new_ctx->reserved_vifs); 96211335a55SLuciano Coelho sdata->reserved_chanctx = new_ctx; 96311335a55SLuciano Coelho sdata->reserved_chandef = *chandef; 96409332481SMichal Kazior sdata->reserved_radar_required = radar_required; 9655bcae31dSMichal Kazior sdata->reserved_ready = false; 9665bcae31dSMichal Kazior 9675bcae31dSMichal Kazior return 0; 96811335a55SLuciano Coelho } 96911335a55SLuciano Coelho 97003078de4SMichal Kazior static void 97103078de4SMichal Kazior ieee80211_vif_chanctx_reservation_complete(struct ieee80211_sub_if_data *sdata) 97203078de4SMichal Kazior { 97303078de4SMichal Kazior switch (sdata->vif.type) { 97403078de4SMichal Kazior case NL80211_IFTYPE_ADHOC: 97503078de4SMichal Kazior case NL80211_IFTYPE_AP: 97603078de4SMichal Kazior case NL80211_IFTYPE_MESH_POINT: 9776e0bd6c3SRostislav Lisovy case NL80211_IFTYPE_OCB: 97803078de4SMichal Kazior ieee80211_queue_work(&sdata->local->hw, 97903078de4SMichal Kazior &sdata->csa_finalize_work); 98003078de4SMichal Kazior break; 98103078de4SMichal Kazior case NL80211_IFTYPE_STATION: 9824c3ebc56SMichal Kazior ieee80211_queue_work(&sdata->local->hw, 9834c3ebc56SMichal Kazior &sdata->u.mgd.chswitch_work); 9844c3ebc56SMichal Kazior break; 9854c3ebc56SMichal Kazior case NL80211_IFTYPE_UNSPECIFIED: 98603078de4SMichal Kazior case NL80211_IFTYPE_AP_VLAN: 98703078de4SMichal Kazior case NL80211_IFTYPE_WDS: 98803078de4SMichal Kazior case NL80211_IFTYPE_MONITOR: 98903078de4SMichal Kazior case NL80211_IFTYPE_P2P_CLIENT: 99003078de4SMichal Kazior case NL80211_IFTYPE_P2P_GO: 99103078de4SMichal Kazior case NL80211_IFTYPE_P2P_DEVICE: 992cb3b7d87SAyala Beker case NL80211_IFTYPE_NAN: 99303078de4SMichal Kazior case NUM_NL80211_IFTYPES: 99403078de4SMichal Kazior WARN_ON(1); 99503078de4SMichal Kazior break; 99603078de4SMichal Kazior } 99703078de4SMichal Kazior } 99803078de4SMichal Kazior 9992967e031SFelix Fietkau static void 10002967e031SFelix Fietkau ieee80211_vif_update_chandef(struct ieee80211_sub_if_data *sdata, 10012967e031SFelix Fietkau const struct cfg80211_chan_def *chandef) 10022967e031SFelix Fietkau { 10032967e031SFelix Fietkau struct ieee80211_sub_if_data *vlan; 10042967e031SFelix Fietkau 10052967e031SFelix Fietkau sdata->vif.bss_conf.chandef = *chandef; 10062967e031SFelix Fietkau 10072967e031SFelix Fietkau if (sdata->vif.type != NL80211_IFTYPE_AP) 10082967e031SFelix Fietkau return; 10092967e031SFelix Fietkau 10102967e031SFelix Fietkau list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) 10112967e031SFelix Fietkau vlan->vif.bss_conf.chandef = *chandef; 10122967e031SFelix Fietkau } 10132967e031SFelix Fietkau 10145bcae31dSMichal Kazior static int 10155bcae31dSMichal Kazior ieee80211_vif_use_reserved_reassign(struct ieee80211_sub_if_data *sdata) 101611335a55SLuciano Coelho { 101711335a55SLuciano Coelho struct ieee80211_local *local = sdata->local; 10185bcae31dSMichal Kazior struct ieee80211_vif_chanctx_switch vif_chsw[1] = {}; 10195bcae31dSMichal Kazior struct ieee80211_chanctx *old_ctx, *new_ctx; 10205bcae31dSMichal Kazior const struct cfg80211_chan_def *chandef; 10215bcae31dSMichal Kazior u32 changed = 0; 10225bcae31dSMichal Kazior int err; 102311335a55SLuciano Coelho 102411335a55SLuciano Coelho lockdep_assert_held(&local->mtx); 10255bcae31dSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 102611335a55SLuciano Coelho 10275bcae31dSMichal Kazior new_ctx = sdata->reserved_chanctx; 10285bcae31dSMichal Kazior old_ctx = ieee80211_vif_get_chanctx(sdata); 102911335a55SLuciano Coelho 10305bcae31dSMichal Kazior if (WARN_ON(!sdata->reserved_ready)) 10315bcae31dSMichal Kazior return -EBUSY; 103211335a55SLuciano Coelho 10335bcae31dSMichal Kazior if (WARN_ON(!new_ctx)) 10345bcae31dSMichal Kazior return -EINVAL; 103511335a55SLuciano Coelho 10365bcae31dSMichal Kazior if (WARN_ON(!old_ctx)) 10375bcae31dSMichal Kazior return -EINVAL; 103811335a55SLuciano Coelho 10395bcae31dSMichal Kazior if (WARN_ON(new_ctx->replace_state == 10405bcae31dSMichal Kazior IEEE80211_CHANCTX_REPLACES_OTHER)) 10415bcae31dSMichal Kazior return -EINVAL; 104211335a55SLuciano Coelho 10435bcae31dSMichal Kazior chandef = ieee80211_chanctx_non_reserved_chandef(local, new_ctx, 10445bcae31dSMichal Kazior &sdata->reserved_chandef); 10455bcae31dSMichal Kazior if (WARN_ON(!chandef)) 10465bcae31dSMichal Kazior return -EINVAL; 104711335a55SLuciano Coelho 1048bf45a242SAndrei Otcheretianski ieee80211_change_chanctx(local, new_ctx, chandef); 1049bf45a242SAndrei Otcheretianski 10505bcae31dSMichal Kazior vif_chsw[0].vif = &sdata->vif; 10515bcae31dSMichal Kazior vif_chsw[0].old_ctx = &old_ctx->conf; 10525bcae31dSMichal Kazior vif_chsw[0].new_ctx = &new_ctx->conf; 10535bcae31dSMichal Kazior 1054e3afb920SMichal Kazior list_del(&sdata->reserved_chanctx_list); 10555bcae31dSMichal Kazior sdata->reserved_chanctx = NULL; 105611335a55SLuciano Coelho 10575bcae31dSMichal Kazior err = drv_switch_vif_chanctx(local, vif_chsw, 1, 10585bcae31dSMichal Kazior CHANCTX_SWMODE_REASSIGN_VIF); 10595bcae31dSMichal Kazior if (err) { 10605bcae31dSMichal Kazior if (ieee80211_chanctx_refcount(local, new_ctx) == 0) 10615bcae31dSMichal Kazior ieee80211_free_chanctx(local, new_ctx); 10625bcae31dSMichal Kazior 106303078de4SMichal Kazior goto out; 106411335a55SLuciano Coelho } 106511335a55SLuciano Coelho 10665bcae31dSMichal Kazior list_move(&sdata->assigned_chanctx_list, &new_ctx->assigned_vifs); 10675bcae31dSMichal Kazior rcu_assign_pointer(sdata->vif.chanctx_conf, &new_ctx->conf); 10685bcae31dSMichal Kazior 106911335a55SLuciano Coelho if (sdata->vif.type == NL80211_IFTYPE_AP) 107011335a55SLuciano Coelho __ieee80211_vif_copy_chanctx_to_vlans(sdata, false); 10715bcae31dSMichal Kazior 107217c18bf8SJohannes Berg ieee80211_check_fast_xmit_iface(sdata); 107317c18bf8SJohannes Berg 10745bcae31dSMichal Kazior if (ieee80211_chanctx_refcount(local, old_ctx) == 0) 10755bcae31dSMichal Kazior ieee80211_free_chanctx(local, old_ctx); 10765bcae31dSMichal Kazior 10775bcae31dSMichal Kazior if (sdata->vif.bss_conf.chandef.width != sdata->reserved_chandef.width) 10785bcae31dSMichal Kazior changed = BSS_CHANGED_BANDWIDTH; 10795bcae31dSMichal Kazior 10802967e031SFelix Fietkau ieee80211_vif_update_chandef(sdata, &sdata->reserved_chandef); 10815bcae31dSMichal Kazior 1082722ddb0dSEmmanuel Grumbach ieee80211_recalc_smps_chanctx(local, new_ctx); 1083722ddb0dSEmmanuel Grumbach ieee80211_recalc_radar_chanctx(local, new_ctx); 1084722ddb0dSEmmanuel Grumbach ieee80211_recalc_chanctx_min_def(local, new_ctx); 1085722ddb0dSEmmanuel Grumbach 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 1123bf45a242SAndrei Otcheretianski ieee80211_change_chanctx(local, new_ctx, chandef); 1124bf45a242SAndrei Otcheretianski 11255bcae31dSMichal Kazior list_del(&sdata->reserved_chanctx_list); 11265bcae31dSMichal Kazior sdata->reserved_chanctx = NULL; 11275bcae31dSMichal Kazior 11285bcae31dSMichal Kazior err = ieee80211_assign_vif_chanctx(sdata, new_ctx); 11295bcae31dSMichal Kazior if (err) { 11305bcae31dSMichal Kazior if (ieee80211_chanctx_refcount(local, new_ctx) == 0) 11315bcae31dSMichal Kazior ieee80211_free_chanctx(local, new_ctx); 11325bcae31dSMichal Kazior 11335bcae31dSMichal Kazior goto out; 11345bcae31dSMichal Kazior } 11355bcae31dSMichal Kazior 11365bcae31dSMichal Kazior out: 113703078de4SMichal Kazior ieee80211_vif_chanctx_reservation_complete(sdata); 11385bcae31dSMichal Kazior return err; 11395bcae31dSMichal Kazior } 11405bcae31dSMichal Kazior 11415bcae31dSMichal Kazior static bool 11425bcae31dSMichal Kazior ieee80211_vif_has_in_place_reservation(struct ieee80211_sub_if_data *sdata) 11435bcae31dSMichal Kazior { 11445bcae31dSMichal Kazior struct ieee80211_chanctx *old_ctx, *new_ctx; 11455bcae31dSMichal Kazior 11465bcae31dSMichal Kazior lockdep_assert_held(&sdata->local->chanctx_mtx); 11475bcae31dSMichal Kazior 11485bcae31dSMichal Kazior new_ctx = sdata->reserved_chanctx; 11495bcae31dSMichal Kazior old_ctx = ieee80211_vif_get_chanctx(sdata); 11505bcae31dSMichal Kazior 11515bcae31dSMichal Kazior if (!old_ctx) 11525bcae31dSMichal Kazior return false; 11535bcae31dSMichal Kazior 11545bcae31dSMichal Kazior if (WARN_ON(!new_ctx)) 11555bcae31dSMichal Kazior return false; 11565bcae31dSMichal Kazior 11575bcae31dSMichal Kazior if (old_ctx->replace_state != IEEE80211_CHANCTX_WILL_BE_REPLACED) 11585bcae31dSMichal Kazior return false; 11595bcae31dSMichal Kazior 11605bcae31dSMichal Kazior if (new_ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) 11615bcae31dSMichal Kazior return false; 11625bcae31dSMichal Kazior 11635bcae31dSMichal Kazior return true; 11645bcae31dSMichal Kazior } 11655bcae31dSMichal Kazior 11665bcae31dSMichal Kazior static int ieee80211_chsw_switch_hwconf(struct ieee80211_local *local, 11675bcae31dSMichal Kazior struct ieee80211_chanctx *new_ctx) 11685bcae31dSMichal Kazior { 11695bcae31dSMichal Kazior const struct cfg80211_chan_def *chandef; 11705bcae31dSMichal Kazior 11715bcae31dSMichal Kazior lockdep_assert_held(&local->mtx); 11725bcae31dSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 11735bcae31dSMichal Kazior 11745bcae31dSMichal Kazior chandef = ieee80211_chanctx_reserved_chandef(local, new_ctx, NULL); 11755bcae31dSMichal Kazior if (WARN_ON(!chandef)) 11765bcae31dSMichal Kazior return -EINVAL; 11775bcae31dSMichal Kazior 11785bcae31dSMichal Kazior local->hw.conf.radar_enabled = new_ctx->conf.radar_enabled; 11795bcae31dSMichal Kazior local->_oper_chandef = *chandef; 11805bcae31dSMichal Kazior ieee80211_hw_config(local, 0); 11815bcae31dSMichal Kazior 11825bcae31dSMichal Kazior return 0; 11835bcae31dSMichal Kazior } 11845bcae31dSMichal Kazior 11855bcae31dSMichal Kazior static int ieee80211_chsw_switch_vifs(struct ieee80211_local *local, 11865bcae31dSMichal Kazior int n_vifs) 11875bcae31dSMichal Kazior { 11885bcae31dSMichal Kazior struct ieee80211_vif_chanctx_switch *vif_chsw; 11895bcae31dSMichal Kazior struct ieee80211_sub_if_data *sdata; 11905bcae31dSMichal Kazior struct ieee80211_chanctx *ctx, *old_ctx; 11915bcae31dSMichal Kazior int i, err; 11925bcae31dSMichal Kazior 11935bcae31dSMichal Kazior lockdep_assert_held(&local->mtx); 11945bcae31dSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 11955bcae31dSMichal Kazior 11966396bb22SKees Cook vif_chsw = kcalloc(n_vifs, sizeof(vif_chsw[0]), GFP_KERNEL); 11975bcae31dSMichal Kazior if (!vif_chsw) 11985bcae31dSMichal Kazior return -ENOMEM; 11995bcae31dSMichal Kazior 12005bcae31dSMichal Kazior i = 0; 12015bcae31dSMichal Kazior list_for_each_entry(ctx, &local->chanctx_list, list) { 12025bcae31dSMichal Kazior if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) 12035bcae31dSMichal Kazior continue; 12045bcae31dSMichal Kazior 12055bcae31dSMichal Kazior if (WARN_ON(!ctx->replace_ctx)) { 12065bcae31dSMichal Kazior err = -EINVAL; 12075bcae31dSMichal Kazior goto out; 12085bcae31dSMichal Kazior } 12095bcae31dSMichal Kazior 12105bcae31dSMichal Kazior list_for_each_entry(sdata, &ctx->reserved_vifs, 12115bcae31dSMichal Kazior reserved_chanctx_list) { 12125bcae31dSMichal Kazior if (!ieee80211_vif_has_in_place_reservation( 12135bcae31dSMichal Kazior sdata)) 12145bcae31dSMichal Kazior continue; 12155bcae31dSMichal Kazior 12165bcae31dSMichal Kazior old_ctx = ieee80211_vif_get_chanctx(sdata); 12175bcae31dSMichal Kazior vif_chsw[i].vif = &sdata->vif; 12185bcae31dSMichal Kazior vif_chsw[i].old_ctx = &old_ctx->conf; 12195bcae31dSMichal Kazior vif_chsw[i].new_ctx = &ctx->conf; 12205bcae31dSMichal Kazior 12215bcae31dSMichal Kazior i++; 12225bcae31dSMichal Kazior } 12235bcae31dSMichal Kazior } 12245bcae31dSMichal Kazior 12255bcae31dSMichal Kazior err = drv_switch_vif_chanctx(local, vif_chsw, n_vifs, 12265bcae31dSMichal Kazior CHANCTX_SWMODE_SWAP_CONTEXTS); 12275bcae31dSMichal Kazior 12285bcae31dSMichal Kazior out: 12295bcae31dSMichal Kazior kfree(vif_chsw); 12305bcae31dSMichal Kazior return err; 12315bcae31dSMichal Kazior } 12325bcae31dSMichal Kazior 12335bcae31dSMichal Kazior static int ieee80211_chsw_switch_ctxs(struct ieee80211_local *local) 12345bcae31dSMichal Kazior { 12355bcae31dSMichal Kazior struct ieee80211_chanctx *ctx; 12365bcae31dSMichal Kazior int err; 12375bcae31dSMichal Kazior 12385bcae31dSMichal Kazior lockdep_assert_held(&local->mtx); 12395bcae31dSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 12405bcae31dSMichal Kazior 12415bcae31dSMichal Kazior list_for_each_entry(ctx, &local->chanctx_list, list) { 12425bcae31dSMichal Kazior if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) 12435bcae31dSMichal Kazior continue; 12445bcae31dSMichal Kazior 12455bcae31dSMichal Kazior if (!list_empty(&ctx->replace_ctx->assigned_vifs)) 12465bcae31dSMichal Kazior continue; 12475bcae31dSMichal Kazior 12485bcae31dSMichal Kazior ieee80211_del_chanctx(local, ctx->replace_ctx); 12495bcae31dSMichal Kazior err = ieee80211_add_chanctx(local, ctx); 12505bcae31dSMichal Kazior if (err) 12515bcae31dSMichal Kazior goto err; 12525bcae31dSMichal Kazior } 12535bcae31dSMichal Kazior 12545bcae31dSMichal Kazior return 0; 12555bcae31dSMichal Kazior 12565bcae31dSMichal Kazior err: 12575bcae31dSMichal Kazior WARN_ON(ieee80211_add_chanctx(local, ctx)); 12585bcae31dSMichal Kazior list_for_each_entry_continue_reverse(ctx, &local->chanctx_list, list) { 12595bcae31dSMichal Kazior if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) 12605bcae31dSMichal Kazior continue; 12615bcae31dSMichal Kazior 12625bcae31dSMichal Kazior if (!list_empty(&ctx->replace_ctx->assigned_vifs)) 12635bcae31dSMichal Kazior continue; 12645bcae31dSMichal Kazior 12655bcae31dSMichal Kazior ieee80211_del_chanctx(local, ctx); 12665bcae31dSMichal Kazior WARN_ON(ieee80211_add_chanctx(local, ctx->replace_ctx)); 12675bcae31dSMichal Kazior } 12685bcae31dSMichal Kazior 12695bcae31dSMichal Kazior return err; 12705bcae31dSMichal Kazior } 12715bcae31dSMichal Kazior 1272649b2a4dSJohannes Berg static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local) 12735bcae31dSMichal Kazior { 12745bcae31dSMichal Kazior struct ieee80211_sub_if_data *sdata, *sdata_tmp; 12755bcae31dSMichal Kazior struct ieee80211_chanctx *ctx, *ctx_tmp, *old_ctx; 12765bcae31dSMichal Kazior struct ieee80211_chanctx *new_ctx = NULL; 1277b7f2405cSKirtika Ruchandani int err, n_assigned, n_reserved, n_ready; 12785bcae31dSMichal Kazior int n_ctx = 0, n_vifs_switch = 0, n_vifs_assign = 0, n_vifs_ctxless = 0; 12795bcae31dSMichal Kazior 12805bcae31dSMichal Kazior lockdep_assert_held(&local->mtx); 12815bcae31dSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 12825bcae31dSMichal Kazior 12835bcae31dSMichal Kazior /* 12845bcae31dSMichal Kazior * If there are 2 independent pairs of channel contexts performing 12855bcae31dSMichal Kazior * cross-switch of their vifs this code will still wait until both are 12865bcae31dSMichal Kazior * ready even though it could be possible to switch one before the 12875bcae31dSMichal Kazior * other is ready. 12885bcae31dSMichal Kazior * 12895bcae31dSMichal Kazior * For practical reasons and code simplicity just do a single huge 12905bcae31dSMichal Kazior * switch. 12915bcae31dSMichal Kazior */ 12925bcae31dSMichal Kazior 12935bcae31dSMichal Kazior /* 12945bcae31dSMichal Kazior * Verify if the reservation is still feasible. 12955bcae31dSMichal Kazior * - if it's not then disconnect 12965bcae31dSMichal Kazior * - if it is but not all vifs necessary are ready then defer 12975bcae31dSMichal Kazior */ 12985bcae31dSMichal Kazior 12995bcae31dSMichal Kazior list_for_each_entry(ctx, &local->chanctx_list, list) { 13005bcae31dSMichal Kazior if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) 13015bcae31dSMichal Kazior continue; 13025bcae31dSMichal Kazior 13035bcae31dSMichal Kazior if (WARN_ON(!ctx->replace_ctx)) { 13045bcae31dSMichal Kazior err = -EINVAL; 13055bcae31dSMichal Kazior goto err; 13065bcae31dSMichal Kazior } 13075bcae31dSMichal Kazior 13085bcae31dSMichal Kazior if (!local->use_chanctx) 13095bcae31dSMichal Kazior new_ctx = ctx; 13105bcae31dSMichal Kazior 13115bcae31dSMichal Kazior n_ctx++; 13125bcae31dSMichal Kazior 13135bcae31dSMichal Kazior n_assigned = 0; 13145bcae31dSMichal Kazior n_reserved = 0; 13155bcae31dSMichal Kazior n_ready = 0; 13165bcae31dSMichal Kazior 13175bcae31dSMichal Kazior list_for_each_entry(sdata, &ctx->replace_ctx->assigned_vifs, 13185bcae31dSMichal Kazior assigned_chanctx_list) { 13195bcae31dSMichal Kazior n_assigned++; 13205bcae31dSMichal Kazior if (sdata->reserved_chanctx) { 13215bcae31dSMichal Kazior n_reserved++; 13225bcae31dSMichal Kazior if (sdata->reserved_ready) 13235bcae31dSMichal Kazior n_ready++; 13245bcae31dSMichal Kazior } 13255bcae31dSMichal Kazior } 13265bcae31dSMichal Kazior 13275bcae31dSMichal Kazior if (n_assigned != n_reserved) { 13285bcae31dSMichal Kazior if (n_ready == n_reserved) { 13295bcae31dSMichal Kazior wiphy_info(local->hw.wiphy, 13305bcae31dSMichal Kazior "channel context reservation cannot be finalized because some interfaces aren't switching\n"); 13315bcae31dSMichal Kazior err = -EBUSY; 13325bcae31dSMichal Kazior goto err; 13335bcae31dSMichal Kazior } 13345bcae31dSMichal Kazior 13355bcae31dSMichal Kazior return -EAGAIN; 13365bcae31dSMichal Kazior } 13375bcae31dSMichal Kazior 13385bcae31dSMichal Kazior ctx->conf.radar_enabled = false; 13395bcae31dSMichal Kazior list_for_each_entry(sdata, &ctx->reserved_vifs, 13405bcae31dSMichal Kazior reserved_chanctx_list) { 13415bcae31dSMichal Kazior if (ieee80211_vif_has_in_place_reservation(sdata) && 13425bcae31dSMichal Kazior !sdata->reserved_ready) 13435bcae31dSMichal Kazior return -EAGAIN; 13445bcae31dSMichal Kazior 13455bcae31dSMichal Kazior old_ctx = ieee80211_vif_get_chanctx(sdata); 13465bcae31dSMichal Kazior if (old_ctx) { 13475bcae31dSMichal Kazior if (old_ctx->replace_state == 13485bcae31dSMichal Kazior IEEE80211_CHANCTX_WILL_BE_REPLACED) 13495bcae31dSMichal Kazior n_vifs_switch++; 13505bcae31dSMichal Kazior else 13515bcae31dSMichal Kazior n_vifs_assign++; 13525bcae31dSMichal Kazior } else { 13535bcae31dSMichal Kazior n_vifs_ctxless++; 13545bcae31dSMichal Kazior } 13555bcae31dSMichal Kazior 13565bcae31dSMichal Kazior if (sdata->reserved_radar_required) 13575bcae31dSMichal Kazior ctx->conf.radar_enabled = true; 13585bcae31dSMichal Kazior } 13595bcae31dSMichal Kazior } 13605bcae31dSMichal Kazior 13615bcae31dSMichal Kazior if (WARN_ON(n_ctx == 0) || 13625bcae31dSMichal Kazior WARN_ON(n_vifs_switch == 0 && 13635bcae31dSMichal Kazior n_vifs_assign == 0 && 13645bcae31dSMichal Kazior n_vifs_ctxless == 0) || 13655bcae31dSMichal Kazior WARN_ON(n_ctx > 1 && !local->use_chanctx) || 13665bcae31dSMichal Kazior WARN_ON(!new_ctx && !local->use_chanctx)) { 13675bcae31dSMichal Kazior err = -EINVAL; 13685bcae31dSMichal Kazior goto err; 13695bcae31dSMichal Kazior } 13705bcae31dSMichal Kazior 13715bcae31dSMichal Kazior /* 13725bcae31dSMichal Kazior * All necessary vifs are ready. Perform the switch now depending on 13735bcae31dSMichal Kazior * reservations and driver capabilities. 13745bcae31dSMichal Kazior */ 13755bcae31dSMichal Kazior 13765bcae31dSMichal Kazior if (local->use_chanctx) { 13775bcae31dSMichal Kazior if (n_vifs_switch > 0) { 13785bcae31dSMichal Kazior err = ieee80211_chsw_switch_vifs(local, n_vifs_switch); 13795bcae31dSMichal Kazior if (err) 13805bcae31dSMichal Kazior goto err; 13815bcae31dSMichal Kazior } 13825bcae31dSMichal Kazior 13835bcae31dSMichal Kazior if (n_vifs_assign > 0 || n_vifs_ctxless > 0) { 13845bcae31dSMichal Kazior err = ieee80211_chsw_switch_ctxs(local); 13855bcae31dSMichal Kazior if (err) 13865bcae31dSMichal Kazior goto err; 13875bcae31dSMichal Kazior } 13885bcae31dSMichal Kazior } else { 13895bcae31dSMichal Kazior err = ieee80211_chsw_switch_hwconf(local, new_ctx); 13905bcae31dSMichal Kazior if (err) 13915bcae31dSMichal Kazior goto err; 13925bcae31dSMichal Kazior } 13935bcae31dSMichal Kazior 13945bcae31dSMichal Kazior /* 13955bcae31dSMichal Kazior * Update all structures, values and pointers to point to new channel 13965bcae31dSMichal Kazior * context(s). 13975bcae31dSMichal Kazior */ 13985bcae31dSMichal Kazior list_for_each_entry(ctx, &local->chanctx_list, list) { 13995bcae31dSMichal Kazior if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) 14005bcae31dSMichal Kazior continue; 14015bcae31dSMichal Kazior 14025bcae31dSMichal Kazior if (WARN_ON(!ctx->replace_ctx)) { 14035bcae31dSMichal Kazior err = -EINVAL; 14045bcae31dSMichal Kazior goto err; 14055bcae31dSMichal Kazior } 14065bcae31dSMichal Kazior 14075bcae31dSMichal Kazior list_for_each_entry(sdata, &ctx->reserved_vifs, 14085bcae31dSMichal Kazior reserved_chanctx_list) { 14095bcae31dSMichal Kazior u32 changed = 0; 14105bcae31dSMichal Kazior 14115bcae31dSMichal Kazior if (!ieee80211_vif_has_in_place_reservation(sdata)) 14125bcae31dSMichal Kazior continue; 14135bcae31dSMichal Kazior 14145bcae31dSMichal Kazior rcu_assign_pointer(sdata->vif.chanctx_conf, &ctx->conf); 14155bcae31dSMichal Kazior 14165bcae31dSMichal Kazior if (sdata->vif.type == NL80211_IFTYPE_AP) 14175bcae31dSMichal Kazior __ieee80211_vif_copy_chanctx_to_vlans(sdata, 14185bcae31dSMichal Kazior false); 14195bcae31dSMichal Kazior 142017c18bf8SJohannes Berg ieee80211_check_fast_xmit_iface(sdata); 142117c18bf8SJohannes Berg 14225bcae31dSMichal Kazior sdata->radar_required = sdata->reserved_radar_required; 14235bcae31dSMichal Kazior 14245bcae31dSMichal Kazior if (sdata->vif.bss_conf.chandef.width != 14255bcae31dSMichal Kazior sdata->reserved_chandef.width) 14265bcae31dSMichal Kazior changed = BSS_CHANGED_BANDWIDTH; 14275bcae31dSMichal Kazior 14282967e031SFelix Fietkau ieee80211_vif_update_chandef(sdata, &sdata->reserved_chandef); 14295bcae31dSMichal Kazior if (changed) 14305bcae31dSMichal Kazior ieee80211_bss_info_change_notify(sdata, 14315bcae31dSMichal Kazior changed); 14325bcae31dSMichal Kazior 1433db82d8a9SLorenzo Bianconi ieee80211_recalc_txpower(sdata, false); 14345bcae31dSMichal Kazior } 143511335a55SLuciano Coelho 143611335a55SLuciano Coelho ieee80211_recalc_chanctx_chantype(local, ctx); 143711335a55SLuciano Coelho ieee80211_recalc_smps_chanctx(local, ctx); 143811335a55SLuciano Coelho ieee80211_recalc_radar_chanctx(local, ctx); 143911335a55SLuciano Coelho ieee80211_recalc_chanctx_min_def(local, ctx); 14405bcae31dSMichal Kazior 14415bcae31dSMichal Kazior list_for_each_entry_safe(sdata, sdata_tmp, &ctx->reserved_vifs, 14425bcae31dSMichal Kazior reserved_chanctx_list) { 14435bcae31dSMichal Kazior if (ieee80211_vif_get_chanctx(sdata) != ctx) 14445bcae31dSMichal Kazior continue; 14455bcae31dSMichal Kazior 14465bcae31dSMichal Kazior list_del(&sdata->reserved_chanctx_list); 14475bcae31dSMichal Kazior list_move(&sdata->assigned_chanctx_list, 144847e4df94SMichal Kazior &ctx->assigned_vifs); 14495bcae31dSMichal Kazior sdata->reserved_chanctx = NULL; 145003078de4SMichal Kazior 145103078de4SMichal Kazior ieee80211_vif_chanctx_reservation_complete(sdata); 14525bcae31dSMichal Kazior } 14535bcae31dSMichal Kazior 14545bcae31dSMichal Kazior /* 14555bcae31dSMichal Kazior * This context might have been a dependency for an already 14565bcae31dSMichal Kazior * ready re-assign reservation interface that was deferred. Do 14575bcae31dSMichal Kazior * not propagate error to the caller though. The in-place 14585bcae31dSMichal Kazior * reservation for originally requested interface has already 14595bcae31dSMichal Kazior * succeeded at this point. 14605bcae31dSMichal Kazior */ 14615bcae31dSMichal Kazior list_for_each_entry_safe(sdata, sdata_tmp, &ctx->reserved_vifs, 14625bcae31dSMichal Kazior reserved_chanctx_list) { 14635bcae31dSMichal Kazior if (WARN_ON(ieee80211_vif_has_in_place_reservation( 14645bcae31dSMichal Kazior sdata))) 14655bcae31dSMichal Kazior continue; 14665bcae31dSMichal Kazior 14675bcae31dSMichal Kazior if (WARN_ON(sdata->reserved_chanctx != ctx)) 14685bcae31dSMichal Kazior continue; 14695bcae31dSMichal Kazior 14705bcae31dSMichal Kazior if (!sdata->reserved_ready) 14715bcae31dSMichal Kazior continue; 14725bcae31dSMichal Kazior 14735bcae31dSMichal Kazior if (ieee80211_vif_get_chanctx(sdata)) 14745bcae31dSMichal Kazior err = ieee80211_vif_use_reserved_reassign( 14755bcae31dSMichal Kazior sdata); 14765bcae31dSMichal Kazior else 14775bcae31dSMichal Kazior err = ieee80211_vif_use_reserved_assign(sdata); 14785bcae31dSMichal Kazior 14795bcae31dSMichal Kazior if (err) { 14805bcae31dSMichal Kazior sdata_info(sdata, 14815bcae31dSMichal Kazior "failed to finalize (re-)assign reservation (err=%d)\n", 14825bcae31dSMichal Kazior err); 14835bcae31dSMichal Kazior ieee80211_vif_unreserve_chanctx(sdata); 14845bcae31dSMichal Kazior cfg80211_stop_iface(local->hw.wiphy, 14855bcae31dSMichal Kazior &sdata->wdev, 14865bcae31dSMichal Kazior GFP_KERNEL); 14875bcae31dSMichal Kazior } 14885bcae31dSMichal Kazior } 14895bcae31dSMichal Kazior } 14905bcae31dSMichal Kazior 14915bcae31dSMichal Kazior /* 14925bcae31dSMichal Kazior * Finally free old contexts 14935bcae31dSMichal Kazior */ 14945bcae31dSMichal Kazior 14955bcae31dSMichal Kazior list_for_each_entry_safe(ctx, ctx_tmp, &local->chanctx_list, list) { 14965bcae31dSMichal Kazior if (ctx->replace_state != IEEE80211_CHANCTX_WILL_BE_REPLACED) 14975bcae31dSMichal Kazior continue; 14985bcae31dSMichal Kazior 14995bcae31dSMichal Kazior ctx->replace_ctx->replace_ctx = NULL; 15005bcae31dSMichal Kazior ctx->replace_ctx->replace_state = 15015bcae31dSMichal Kazior IEEE80211_CHANCTX_REPLACE_NONE; 15025bcae31dSMichal Kazior 15035bcae31dSMichal Kazior list_del_rcu(&ctx->list); 15045bcae31dSMichal Kazior kfree_rcu(ctx, rcu_head); 15055bcae31dSMichal Kazior } 15065bcae31dSMichal Kazior 15075bcae31dSMichal Kazior return 0; 15085bcae31dSMichal Kazior 15095bcae31dSMichal Kazior err: 15105bcae31dSMichal Kazior list_for_each_entry(ctx, &local->chanctx_list, list) { 15115bcae31dSMichal Kazior if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) 15125bcae31dSMichal Kazior continue; 15135bcae31dSMichal Kazior 15145bcae31dSMichal Kazior list_for_each_entry_safe(sdata, sdata_tmp, &ctx->reserved_vifs, 151503078de4SMichal Kazior reserved_chanctx_list) { 15165bcae31dSMichal Kazior ieee80211_vif_unreserve_chanctx(sdata); 151703078de4SMichal Kazior ieee80211_vif_chanctx_reservation_complete(sdata); 151803078de4SMichal Kazior } 15195bcae31dSMichal Kazior } 15205bcae31dSMichal Kazior 15215bcae31dSMichal Kazior return err; 15225bcae31dSMichal Kazior } 15235bcae31dSMichal Kazior 1524649b2a4dSJohannes Berg static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata) 1525649b2a4dSJohannes Berg { 1526649b2a4dSJohannes Berg struct ieee80211_local *local = sdata->local; 1527649b2a4dSJohannes Berg struct ieee80211_chanctx_conf *conf; 1528649b2a4dSJohannes Berg struct ieee80211_chanctx *ctx; 1529649b2a4dSJohannes Berg bool use_reserved_switch = false; 1530649b2a4dSJohannes Berg 1531649b2a4dSJohannes Berg lockdep_assert_held(&local->chanctx_mtx); 1532649b2a4dSJohannes Berg 1533649b2a4dSJohannes Berg conf = rcu_dereference_protected(sdata->vif.chanctx_conf, 1534649b2a4dSJohannes Berg lockdep_is_held(&local->chanctx_mtx)); 1535649b2a4dSJohannes Berg if (!conf) 1536649b2a4dSJohannes Berg return; 1537649b2a4dSJohannes Berg 1538649b2a4dSJohannes Berg ctx = container_of(conf, struct ieee80211_chanctx, conf); 1539649b2a4dSJohannes Berg 1540649b2a4dSJohannes Berg if (sdata->reserved_chanctx) { 1541649b2a4dSJohannes Berg if (sdata->reserved_chanctx->replace_state == 1542649b2a4dSJohannes Berg IEEE80211_CHANCTX_REPLACES_OTHER && 1543649b2a4dSJohannes Berg ieee80211_chanctx_num_reserved(local, 1544649b2a4dSJohannes Berg sdata->reserved_chanctx) > 1) 1545649b2a4dSJohannes Berg use_reserved_switch = true; 1546649b2a4dSJohannes Berg 1547649b2a4dSJohannes Berg ieee80211_vif_unreserve_chanctx(sdata); 1548649b2a4dSJohannes Berg } 1549649b2a4dSJohannes Berg 1550649b2a4dSJohannes Berg ieee80211_assign_vif_chanctx(sdata, NULL); 1551649b2a4dSJohannes Berg if (ieee80211_chanctx_refcount(local, ctx) == 0) 1552649b2a4dSJohannes Berg ieee80211_free_chanctx(local, ctx); 1553649b2a4dSJohannes Berg 1554104f5a62SEliad Peller sdata->radar_required = false; 1555104f5a62SEliad Peller 1556649b2a4dSJohannes Berg /* Unreserving may ready an in-place reservation. */ 1557649b2a4dSJohannes Berg if (use_reserved_switch) 1558649b2a4dSJohannes Berg ieee80211_vif_use_reserved_switch(local); 1559649b2a4dSJohannes Berg } 1560649b2a4dSJohannes Berg 1561649b2a4dSJohannes Berg int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata, 1562649b2a4dSJohannes Berg const struct cfg80211_chan_def *chandef, 1563649b2a4dSJohannes Berg enum ieee80211_chanctx_mode mode) 1564649b2a4dSJohannes Berg { 1565649b2a4dSJohannes Berg struct ieee80211_local *local = sdata->local; 1566649b2a4dSJohannes Berg struct ieee80211_chanctx *ctx; 1567649b2a4dSJohannes Berg u8 radar_detect_width = 0; 1568649b2a4dSJohannes Berg int ret; 1569649b2a4dSJohannes Berg 1570649b2a4dSJohannes Berg lockdep_assert_held(&local->mtx); 1571649b2a4dSJohannes Berg 1572649b2a4dSJohannes Berg WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev)); 1573649b2a4dSJohannes Berg 1574649b2a4dSJohannes Berg mutex_lock(&local->chanctx_mtx); 1575649b2a4dSJohannes Berg 1576649b2a4dSJohannes Berg ret = cfg80211_chandef_dfs_required(local->hw.wiphy, 1577649b2a4dSJohannes Berg chandef, 1578649b2a4dSJohannes Berg sdata->wdev.iftype); 1579649b2a4dSJohannes Berg if (ret < 0) 1580649b2a4dSJohannes Berg goto out; 1581649b2a4dSJohannes Berg if (ret > 0) 1582649b2a4dSJohannes Berg radar_detect_width = BIT(chandef->width); 1583649b2a4dSJohannes Berg 1584649b2a4dSJohannes Berg sdata->radar_required = ret; 1585649b2a4dSJohannes Berg 1586649b2a4dSJohannes Berg ret = ieee80211_check_combinations(sdata, chandef, mode, 1587649b2a4dSJohannes Berg radar_detect_width); 1588649b2a4dSJohannes Berg if (ret < 0) 1589649b2a4dSJohannes Berg goto out; 1590649b2a4dSJohannes Berg 1591649b2a4dSJohannes Berg __ieee80211_vif_release_channel(sdata); 1592649b2a4dSJohannes Berg 1593649b2a4dSJohannes Berg ctx = ieee80211_find_chanctx(local, chandef, mode); 1594649b2a4dSJohannes Berg if (!ctx) 1595649b2a4dSJohannes Berg ctx = ieee80211_new_chanctx(local, chandef, mode); 1596649b2a4dSJohannes Berg if (IS_ERR(ctx)) { 1597649b2a4dSJohannes Berg ret = PTR_ERR(ctx); 1598649b2a4dSJohannes Berg goto out; 1599649b2a4dSJohannes Berg } 1600649b2a4dSJohannes Berg 16012967e031SFelix Fietkau ieee80211_vif_update_chandef(sdata, chandef); 1602649b2a4dSJohannes Berg 1603649b2a4dSJohannes Berg ret = ieee80211_assign_vif_chanctx(sdata, ctx); 1604649b2a4dSJohannes Berg if (ret) { 1605649b2a4dSJohannes Berg /* if assign fails refcount stays the same */ 1606649b2a4dSJohannes Berg if (ieee80211_chanctx_refcount(local, ctx) == 0) 1607649b2a4dSJohannes Berg ieee80211_free_chanctx(local, ctx); 1608649b2a4dSJohannes Berg goto out; 1609649b2a4dSJohannes Berg } 1610649b2a4dSJohannes Berg 1611649b2a4dSJohannes Berg ieee80211_recalc_smps_chanctx(local, ctx); 1612649b2a4dSJohannes Berg ieee80211_recalc_radar_chanctx(local, ctx); 1613649b2a4dSJohannes Berg out: 1614104f5a62SEliad Peller if (ret) 1615104f5a62SEliad Peller sdata->radar_required = false; 1616104f5a62SEliad Peller 1617649b2a4dSJohannes Berg mutex_unlock(&local->chanctx_mtx); 1618649b2a4dSJohannes Berg return ret; 1619649b2a4dSJohannes Berg } 1620649b2a4dSJohannes Berg 16215bcae31dSMichal Kazior int ieee80211_vif_use_reserved_context(struct ieee80211_sub_if_data *sdata) 16225bcae31dSMichal Kazior { 16235bcae31dSMichal Kazior struct ieee80211_local *local = sdata->local; 16245bcae31dSMichal Kazior struct ieee80211_chanctx *new_ctx; 16255bcae31dSMichal Kazior struct ieee80211_chanctx *old_ctx; 16265bcae31dSMichal Kazior int err; 16275bcae31dSMichal Kazior 16285bcae31dSMichal Kazior lockdep_assert_held(&local->mtx); 16295bcae31dSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 16305bcae31dSMichal Kazior 16315bcae31dSMichal Kazior new_ctx = sdata->reserved_chanctx; 16325bcae31dSMichal Kazior old_ctx = ieee80211_vif_get_chanctx(sdata); 16335bcae31dSMichal Kazior 16345bcae31dSMichal Kazior if (WARN_ON(!new_ctx)) 16355bcae31dSMichal Kazior return -EINVAL; 16365bcae31dSMichal Kazior 16375bcae31dSMichal Kazior if (WARN_ON(new_ctx->replace_state == 16385bcae31dSMichal Kazior IEEE80211_CHANCTX_WILL_BE_REPLACED)) 16395bcae31dSMichal Kazior return -EINVAL; 16405bcae31dSMichal Kazior 16415bcae31dSMichal Kazior if (WARN_ON(sdata->reserved_ready)) 16425bcae31dSMichal Kazior return -EINVAL; 16435bcae31dSMichal Kazior 16445bcae31dSMichal Kazior sdata->reserved_ready = true; 16455bcae31dSMichal Kazior 16465bcae31dSMichal Kazior if (new_ctx->replace_state == IEEE80211_CHANCTX_REPLACE_NONE) { 16475bcae31dSMichal Kazior if (old_ctx) 16485bcae31dSMichal Kazior err = ieee80211_vif_use_reserved_reassign(sdata); 16495bcae31dSMichal Kazior else 16505bcae31dSMichal Kazior err = ieee80211_vif_use_reserved_assign(sdata); 16515bcae31dSMichal Kazior 16525bcae31dSMichal Kazior if (err) 16535bcae31dSMichal Kazior return err; 16545bcae31dSMichal Kazior } 16555bcae31dSMichal Kazior 16565bcae31dSMichal Kazior /* 16575bcae31dSMichal Kazior * In-place reservation may need to be finalized now either if: 16585bcae31dSMichal Kazior * a) sdata is taking part in the swapping itself and is the last one 16595bcae31dSMichal Kazior * b) sdata has switched with a re-assign reservation to an existing 16605bcae31dSMichal Kazior * context readying in-place switching of old_ctx 16615bcae31dSMichal Kazior * 16625bcae31dSMichal Kazior * In case of (b) do not propagate the error up because the requested 16635bcae31dSMichal Kazior * sdata already switched successfully. Just spill an extra warning. 16645bcae31dSMichal Kazior * The ieee80211_vif_use_reserved_switch() already stops all necessary 16655bcae31dSMichal Kazior * interfaces upon failure. 16665bcae31dSMichal Kazior */ 16675bcae31dSMichal Kazior if ((old_ctx && 16685bcae31dSMichal Kazior old_ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED) || 16695bcae31dSMichal Kazior new_ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER) { 16705bcae31dSMichal Kazior err = ieee80211_vif_use_reserved_switch(local); 16715bcae31dSMichal Kazior if (err && err != -EAGAIN) { 16725bcae31dSMichal Kazior if (new_ctx->replace_state == 16735bcae31dSMichal Kazior IEEE80211_CHANCTX_REPLACES_OTHER) 16745bcae31dSMichal Kazior return err; 16755bcae31dSMichal Kazior 16765bcae31dSMichal Kazior wiphy_info(local->hw.wiphy, 16775bcae31dSMichal Kazior "depending in-place reservation failed (err=%d)\n", 16785bcae31dSMichal Kazior err); 16795bcae31dSMichal Kazior } 16805bcae31dSMichal Kazior } 16815bcae31dSMichal Kazior 16825bcae31dSMichal Kazior return 0; 168373da7d5bSSimon Wunderlich } 168473da7d5bSSimon Wunderlich 16852c9b7359SJohannes Berg int ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata, 16862c9b7359SJohannes Berg const struct cfg80211_chan_def *chandef, 16872c9b7359SJohannes Berg u32 *changed) 16882c9b7359SJohannes Berg { 16892c9b7359SJohannes Berg struct ieee80211_local *local = sdata->local; 16902c9b7359SJohannes Berg struct ieee80211_chanctx_conf *conf; 16912c9b7359SJohannes Berg struct ieee80211_chanctx *ctx; 16925bcae31dSMichal Kazior const struct cfg80211_chan_def *compat; 16932c9b7359SJohannes Berg int ret; 16942c9b7359SJohannes Berg 16952c9b7359SJohannes Berg if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef, 16962c9b7359SJohannes Berg IEEE80211_CHAN_DISABLED)) 16972c9b7359SJohannes Berg return -EINVAL; 16982c9b7359SJohannes Berg 16992c9b7359SJohannes Berg mutex_lock(&local->chanctx_mtx); 17002c9b7359SJohannes Berg if (cfg80211_chandef_identical(chandef, &sdata->vif.bss_conf.chandef)) { 17012c9b7359SJohannes Berg ret = 0; 17022c9b7359SJohannes Berg goto out; 17032c9b7359SJohannes Berg } 17042c9b7359SJohannes Berg 17052c9b7359SJohannes Berg if (chandef->width == NL80211_CHAN_WIDTH_20_NOHT || 17062c9b7359SJohannes Berg sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT) { 17072c9b7359SJohannes Berg ret = -EINVAL; 17082c9b7359SJohannes Berg goto out; 17092c9b7359SJohannes Berg } 17102c9b7359SJohannes Berg 17112c9b7359SJohannes Berg conf = rcu_dereference_protected(sdata->vif.chanctx_conf, 17122c9b7359SJohannes Berg lockdep_is_held(&local->chanctx_mtx)); 17132c9b7359SJohannes Berg if (!conf) { 17142c9b7359SJohannes Berg ret = -EINVAL; 17152c9b7359SJohannes Berg goto out; 17162c9b7359SJohannes Berg } 17172c9b7359SJohannes Berg 17182c9b7359SJohannes Berg ctx = container_of(conf, struct ieee80211_chanctx, conf); 17195bcae31dSMichal Kazior 17205bcae31dSMichal Kazior compat = cfg80211_chandef_compatible(&conf->def, chandef); 17215bcae31dSMichal Kazior if (!compat) { 17222c9b7359SJohannes Berg ret = -EINVAL; 17232c9b7359SJohannes Berg goto out; 17242c9b7359SJohannes Berg } 17252c9b7359SJohannes Berg 17265bcae31dSMichal Kazior switch (ctx->replace_state) { 17275bcae31dSMichal Kazior case IEEE80211_CHANCTX_REPLACE_NONE: 17285bcae31dSMichal Kazior if (!ieee80211_chanctx_reserved_chandef(local, ctx, compat)) { 17295bcae31dSMichal Kazior ret = -EBUSY; 17305bcae31dSMichal Kazior goto out; 17315bcae31dSMichal Kazior } 17325bcae31dSMichal Kazior break; 17335bcae31dSMichal Kazior case IEEE80211_CHANCTX_WILL_BE_REPLACED: 1734d070f913SStephen Hemminger /* TODO: Perhaps the bandwidth change could be treated as a 17355bcae31dSMichal Kazior * reservation itself? */ 17365bcae31dSMichal Kazior ret = -EBUSY; 17375bcae31dSMichal Kazior goto out; 17385bcae31dSMichal Kazior case IEEE80211_CHANCTX_REPLACES_OTHER: 17395bcae31dSMichal Kazior /* channel context that is going to replace another channel 17405bcae31dSMichal Kazior * context doesn't really exist and shouldn't be assigned 17415bcae31dSMichal Kazior * anywhere yet */ 17425bcae31dSMichal Kazior WARN_ON(1); 17435bcae31dSMichal Kazior break; 17445bcae31dSMichal Kazior } 17455bcae31dSMichal Kazior 17462967e031SFelix Fietkau ieee80211_vif_update_chandef(sdata, chandef); 17472c9b7359SJohannes Berg 17482c9b7359SJohannes Berg ieee80211_recalc_chanctx_chantype(local, ctx); 17492c9b7359SJohannes Berg 17502c9b7359SJohannes Berg *changed |= BSS_CHANGED_BANDWIDTH; 17512c9b7359SJohannes Berg ret = 0; 17522c9b7359SJohannes Berg out: 17532c9b7359SJohannes Berg mutex_unlock(&local->chanctx_mtx); 17542c9b7359SJohannes Berg return ret; 17552c9b7359SJohannes Berg } 17562c9b7359SJohannes Berg 1757d01a1e65SMichal Kazior void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata) 1758d01a1e65SMichal Kazior { 175955de908aSJohannes Berg WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev)); 176055de908aSJohannes Berg 176134a3740dSJohannes Berg lockdep_assert_held(&sdata->local->mtx); 176234a3740dSJohannes Berg 1763d01a1e65SMichal Kazior mutex_lock(&sdata->local->chanctx_mtx); 1764d01a1e65SMichal Kazior __ieee80211_vif_release_channel(sdata); 1765d01a1e65SMichal Kazior mutex_unlock(&sdata->local->chanctx_mtx); 1766d01a1e65SMichal Kazior } 17673448c005SJohannes Berg 17684d76d21bSJohannes Berg void ieee80211_vif_vlan_copy_chanctx(struct ieee80211_sub_if_data *sdata) 17694d76d21bSJohannes Berg { 17704d76d21bSJohannes Berg struct ieee80211_local *local = sdata->local; 17714d76d21bSJohannes Berg struct ieee80211_sub_if_data *ap; 17724d76d21bSJohannes Berg struct ieee80211_chanctx_conf *conf; 17734d76d21bSJohannes Berg 17744d76d21bSJohannes Berg if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP_VLAN || !sdata->bss)) 17754d76d21bSJohannes Berg return; 17764d76d21bSJohannes Berg 17774d76d21bSJohannes Berg ap = container_of(sdata->bss, struct ieee80211_sub_if_data, u.ap); 17784d76d21bSJohannes Berg 17794d76d21bSJohannes Berg mutex_lock(&local->chanctx_mtx); 17804d76d21bSJohannes Berg 17814d76d21bSJohannes Berg conf = rcu_dereference_protected(ap->vif.chanctx_conf, 17824d76d21bSJohannes Berg lockdep_is_held(&local->chanctx_mtx)); 17834d76d21bSJohannes Berg rcu_assign_pointer(sdata->vif.chanctx_conf, conf); 17844d76d21bSJohannes Berg mutex_unlock(&local->chanctx_mtx); 17854d76d21bSJohannes Berg } 17864d76d21bSJohannes Berg 17873448c005SJohannes Berg void ieee80211_iter_chan_contexts_atomic( 17883448c005SJohannes Berg struct ieee80211_hw *hw, 17893448c005SJohannes Berg void (*iter)(struct ieee80211_hw *hw, 17903448c005SJohannes Berg struct ieee80211_chanctx_conf *chanctx_conf, 17913448c005SJohannes Berg void *data), 17923448c005SJohannes Berg void *iter_data) 17933448c005SJohannes Berg { 17943448c005SJohannes Berg struct ieee80211_local *local = hw_to_local(hw); 17953448c005SJohannes Berg struct ieee80211_chanctx *ctx; 17963448c005SJohannes Berg 17973448c005SJohannes Berg rcu_read_lock(); 17983448c005SJohannes Berg list_for_each_entry_rcu(ctx, &local->chanctx_list, list) 17998a61af65SJohannes Berg if (ctx->driver_present) 18003448c005SJohannes Berg iter(hw, &ctx->conf, iter_data); 18013448c005SJohannes Berg rcu_read_unlock(); 18023448c005SJohannes Berg } 18033448c005SJohannes Berg EXPORT_SYMBOL_GPL(ieee80211_iter_chan_contexts_atomic); 1804