1457c8996SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2f444de05SJohannes Berg /* 3f444de05SJohannes Berg * mac80211 - channel management 4d0a9123eSJohannes Berg * Copyright 2020 - 2022 Intel Corporation 5f444de05SJohannes Berg */ 6f444de05SJohannes Berg 70aaffa9bSJohannes Berg #include <linux/nl80211.h> 83448c005SJohannes Berg #include <linux/export.h> 94d76d21bSJohannes Berg #include <linux/rtnetlink.h> 103117bbdbSPaul Stewart #include <net/cfg80211.h> 11f444de05SJohannes Berg #include "ieee80211_i.h" 1235f2fce9SMichal Kazior #include "driver-ops.h" 1344b72ca8SIlan Peer #include "rate.h" 14f444de05SJohannes Berg 15c0166da9SMichal Kazior static int ieee80211_chanctx_num_assigned(struct ieee80211_local *local, 16c0166da9SMichal Kazior struct ieee80211_chanctx *ctx) 17c0166da9SMichal Kazior { 18c0166da9SMichal Kazior struct ieee80211_sub_if_data *sdata; 19c0166da9SMichal Kazior int num = 0; 20c0166da9SMichal Kazior 21c0166da9SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 22c0166da9SMichal Kazior 23c0166da9SMichal Kazior list_for_each_entry(sdata, &ctx->assigned_vifs, assigned_chanctx_list) 24c0166da9SMichal Kazior num++; 25c0166da9SMichal Kazior 26c0166da9SMichal Kazior return num; 27c0166da9SMichal Kazior } 28c0166da9SMichal Kazior 29c0166da9SMichal Kazior static int ieee80211_chanctx_num_reserved(struct ieee80211_local *local, 30c0166da9SMichal Kazior struct ieee80211_chanctx *ctx) 31c0166da9SMichal Kazior { 32c0166da9SMichal Kazior struct ieee80211_sub_if_data *sdata; 33c0166da9SMichal Kazior int num = 0; 34c0166da9SMichal Kazior 35c0166da9SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 36c0166da9SMichal Kazior 37c0166da9SMichal Kazior list_for_each_entry(sdata, &ctx->reserved_vifs, reserved_chanctx_list) 38c0166da9SMichal Kazior num++; 39c0166da9SMichal Kazior 40c0166da9SMichal Kazior return num; 41c0166da9SMichal Kazior } 42c0166da9SMichal Kazior 43c0166da9SMichal Kazior int ieee80211_chanctx_refcount(struct ieee80211_local *local, 44c0166da9SMichal Kazior struct ieee80211_chanctx *ctx) 45c0166da9SMichal Kazior { 46c0166da9SMichal Kazior return ieee80211_chanctx_num_assigned(local, ctx) + 47c0166da9SMichal Kazior ieee80211_chanctx_num_reserved(local, ctx); 48c0166da9SMichal Kazior } 49c0166da9SMichal Kazior 50c2b90ad8SMichal Kazior static int ieee80211_num_chanctx(struct ieee80211_local *local) 51c2b90ad8SMichal Kazior { 52c2b90ad8SMichal Kazior struct ieee80211_chanctx *ctx; 53c2b90ad8SMichal Kazior int num = 0; 54c2b90ad8SMichal Kazior 55c2b90ad8SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 56c2b90ad8SMichal Kazior 57c2b90ad8SMichal Kazior list_for_each_entry(ctx, &local->chanctx_list, list) 58c2b90ad8SMichal Kazior num++; 59c2b90ad8SMichal Kazior 60c2b90ad8SMichal Kazior return num; 61c2b90ad8SMichal Kazior } 62c2b90ad8SMichal Kazior 63c2b90ad8SMichal Kazior static bool ieee80211_can_create_new_chanctx(struct ieee80211_local *local) 64c2b90ad8SMichal Kazior { 65c2b90ad8SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 66c2b90ad8SMichal Kazior return ieee80211_num_chanctx(local) < ieee80211_max_num_channels(local); 67c2b90ad8SMichal Kazior } 68c2b90ad8SMichal Kazior 695bcae31dSMichal Kazior static struct ieee80211_chanctx * 705bcae31dSMichal Kazior ieee80211_vif_get_chanctx(struct ieee80211_sub_if_data *sdata) 715bcae31dSMichal Kazior { 721d4cc30cSJohannes Berg struct ieee80211_local *local __maybe_unused = sdata->local; 735bcae31dSMichal Kazior struct ieee80211_chanctx_conf *conf; 745bcae31dSMichal Kazior 75d0a9123eSJohannes Berg conf = rcu_dereference_protected(sdata->vif.bss_conf.chanctx_conf, 765bcae31dSMichal Kazior lockdep_is_held(&local->chanctx_mtx)); 775bcae31dSMichal Kazior if (!conf) 785bcae31dSMichal Kazior return NULL; 795bcae31dSMichal Kazior 805bcae31dSMichal Kazior return container_of(conf, struct ieee80211_chanctx, conf); 815bcae31dSMichal Kazior } 825bcae31dSMichal Kazior 830288157bSMichal Kazior static const struct cfg80211_chan_def * 840288157bSMichal Kazior ieee80211_chanctx_reserved_chandef(struct ieee80211_local *local, 850288157bSMichal Kazior struct ieee80211_chanctx *ctx, 860288157bSMichal Kazior const struct cfg80211_chan_def *compat) 870288157bSMichal Kazior { 880288157bSMichal Kazior struct ieee80211_sub_if_data *sdata; 890288157bSMichal Kazior 900288157bSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 910288157bSMichal Kazior 920288157bSMichal Kazior list_for_each_entry(sdata, &ctx->reserved_vifs, 930288157bSMichal Kazior reserved_chanctx_list) { 940288157bSMichal Kazior if (!compat) 950288157bSMichal Kazior compat = &sdata->reserved_chandef; 960288157bSMichal Kazior 970288157bSMichal Kazior compat = cfg80211_chandef_compatible(&sdata->reserved_chandef, 980288157bSMichal Kazior compat); 990288157bSMichal Kazior if (!compat) 1000288157bSMichal Kazior break; 1010288157bSMichal Kazior } 1020288157bSMichal Kazior 1030288157bSMichal Kazior return compat; 1040288157bSMichal Kazior } 1050288157bSMichal Kazior 10613f348a8SMichal Kazior static const struct cfg80211_chan_def * 10713f348a8SMichal Kazior ieee80211_chanctx_non_reserved_chandef(struct ieee80211_local *local, 10813f348a8SMichal Kazior struct ieee80211_chanctx *ctx, 10913f348a8SMichal Kazior const struct cfg80211_chan_def *compat) 11013f348a8SMichal Kazior { 11113f348a8SMichal Kazior struct ieee80211_sub_if_data *sdata; 11213f348a8SMichal Kazior 11313f348a8SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 11413f348a8SMichal Kazior 11513f348a8SMichal Kazior list_for_each_entry(sdata, &ctx->assigned_vifs, 11613f348a8SMichal Kazior assigned_chanctx_list) { 11713f348a8SMichal Kazior if (sdata->reserved_chanctx != NULL) 11813f348a8SMichal Kazior continue; 11913f348a8SMichal Kazior 12013f348a8SMichal Kazior if (!compat) 12113f348a8SMichal Kazior compat = &sdata->vif.bss_conf.chandef; 12213f348a8SMichal Kazior 12313f348a8SMichal Kazior compat = cfg80211_chandef_compatible( 12413f348a8SMichal Kazior &sdata->vif.bss_conf.chandef, compat); 12513f348a8SMichal Kazior if (!compat) 12613f348a8SMichal Kazior break; 12713f348a8SMichal Kazior } 12813f348a8SMichal Kazior 12913f348a8SMichal Kazior return compat; 13013f348a8SMichal Kazior } 13113f348a8SMichal Kazior 13213f348a8SMichal Kazior static const struct cfg80211_chan_def * 13313f348a8SMichal Kazior ieee80211_chanctx_combined_chandef(struct ieee80211_local *local, 13413f348a8SMichal Kazior struct ieee80211_chanctx *ctx, 13513f348a8SMichal Kazior const struct cfg80211_chan_def *compat) 13613f348a8SMichal Kazior { 13713f348a8SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 13813f348a8SMichal Kazior 13913f348a8SMichal Kazior compat = ieee80211_chanctx_reserved_chandef(local, ctx, compat); 14013f348a8SMichal Kazior if (!compat) 14113f348a8SMichal Kazior return NULL; 14213f348a8SMichal Kazior 14313f348a8SMichal Kazior compat = ieee80211_chanctx_non_reserved_chandef(local, ctx, compat); 14413f348a8SMichal Kazior if (!compat) 14513f348a8SMichal Kazior return NULL; 14613f348a8SMichal Kazior 14713f348a8SMichal Kazior return compat; 14813f348a8SMichal Kazior } 14913f348a8SMichal Kazior 15013f348a8SMichal Kazior static bool 15113f348a8SMichal Kazior ieee80211_chanctx_can_reserve_chandef(struct ieee80211_local *local, 15213f348a8SMichal Kazior struct ieee80211_chanctx *ctx, 15313f348a8SMichal Kazior const struct cfg80211_chan_def *def) 15413f348a8SMichal Kazior { 15513f348a8SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 15613f348a8SMichal Kazior 15713f348a8SMichal Kazior if (ieee80211_chanctx_combined_chandef(local, ctx, def)) 15813f348a8SMichal Kazior return true; 15913f348a8SMichal Kazior 16013f348a8SMichal Kazior if (!list_empty(&ctx->reserved_vifs) && 16113f348a8SMichal Kazior ieee80211_chanctx_reserved_chandef(local, ctx, def)) 16213f348a8SMichal Kazior return true; 16313f348a8SMichal Kazior 16413f348a8SMichal Kazior return false; 16513f348a8SMichal Kazior } 16613f348a8SMichal Kazior 16713f348a8SMichal Kazior static struct ieee80211_chanctx * 16813f348a8SMichal Kazior ieee80211_find_reservation_chanctx(struct ieee80211_local *local, 16913f348a8SMichal Kazior const struct cfg80211_chan_def *chandef, 17013f348a8SMichal Kazior enum ieee80211_chanctx_mode mode) 17113f348a8SMichal Kazior { 17213f348a8SMichal Kazior struct ieee80211_chanctx *ctx; 17313f348a8SMichal Kazior 17413f348a8SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 17513f348a8SMichal Kazior 17613f348a8SMichal Kazior if (mode == IEEE80211_CHANCTX_EXCLUSIVE) 17713f348a8SMichal Kazior return NULL; 17813f348a8SMichal Kazior 17913f348a8SMichal Kazior list_for_each_entry(ctx, &local->chanctx_list, list) { 1805bcae31dSMichal Kazior if (ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED) 1815bcae31dSMichal Kazior continue; 1825bcae31dSMichal Kazior 18313f348a8SMichal Kazior if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE) 18413f348a8SMichal Kazior continue; 18513f348a8SMichal Kazior 18613f348a8SMichal Kazior if (!ieee80211_chanctx_can_reserve_chandef(local, ctx, 18713f348a8SMichal Kazior chandef)) 18813f348a8SMichal Kazior continue; 18913f348a8SMichal Kazior 19013f348a8SMichal Kazior return ctx; 19113f348a8SMichal Kazior } 19213f348a8SMichal Kazior 19313f348a8SMichal Kazior return NULL; 19413f348a8SMichal Kazior } 19513f348a8SMichal Kazior 196bbf31e88SIlan Peer static enum nl80211_chan_width ieee80211_get_sta_bw(struct sta_info *sta) 19721f659bfSEliad Peller { 198bbf31e88SIlan Peer enum ieee80211_sta_rx_bandwidth width = ieee80211_sta_cap_rx_bw(sta); 199bbf31e88SIlan Peer 200bbf31e88SIlan Peer switch (width) { 20121f659bfSEliad Peller case IEEE80211_STA_RX_BW_20: 202046d2e7cSSriram R if (sta->sta.deflink.ht_cap.ht_supported) 20321f659bfSEliad Peller return NL80211_CHAN_WIDTH_20; 20421f659bfSEliad Peller else 20521f659bfSEliad Peller return NL80211_CHAN_WIDTH_20_NOHT; 20621f659bfSEliad Peller case IEEE80211_STA_RX_BW_40: 20721f659bfSEliad Peller return NL80211_CHAN_WIDTH_40; 20821f659bfSEliad Peller case IEEE80211_STA_RX_BW_80: 20921f659bfSEliad Peller return NL80211_CHAN_WIDTH_80; 21021f659bfSEliad Peller case IEEE80211_STA_RX_BW_160: 21121f659bfSEliad Peller /* 21221f659bfSEliad Peller * This applied for both 160 and 80+80. since we use 21321f659bfSEliad Peller * the returned value to consider degradation of 21421f659bfSEliad Peller * ctx->conf.min_def, we have to make sure to take 21521f659bfSEliad Peller * the bigger one (NL80211_CHAN_WIDTH_160). 21621f659bfSEliad Peller * Otherwise we might try degrading even when not 21721f659bfSEliad Peller * needed, as the max required sta_bw returned (80+80) 21821f659bfSEliad Peller * might be smaller than the configured bw (160). 21921f659bfSEliad Peller */ 22021f659bfSEliad Peller return NL80211_CHAN_WIDTH_160; 2215dca295dSIlan Peer case IEEE80211_STA_RX_BW_320: 2225dca295dSIlan Peer return NL80211_CHAN_WIDTH_320; 22321f659bfSEliad Peller default: 22421f659bfSEliad Peller WARN_ON(1); 22521f659bfSEliad Peller return NL80211_CHAN_WIDTH_20; 22621f659bfSEliad Peller } 22721f659bfSEliad Peller } 22821f659bfSEliad Peller 22921f659bfSEliad Peller static enum nl80211_chan_width 23021f659bfSEliad Peller ieee80211_get_max_required_bw(struct ieee80211_sub_if_data *sdata) 23121f659bfSEliad Peller { 23221f659bfSEliad Peller enum nl80211_chan_width max_bw = NL80211_CHAN_WIDTH_20_NOHT; 23321f659bfSEliad Peller struct sta_info *sta; 23421f659bfSEliad Peller 23521f659bfSEliad Peller rcu_read_lock(); 23621f659bfSEliad Peller list_for_each_entry_rcu(sta, &sdata->local->sta_list, list) { 23721f659bfSEliad Peller if (sdata != sta->sdata && 23821f659bfSEliad Peller !(sta->sdata->bss && sta->sdata->bss == sdata->bss)) 23921f659bfSEliad Peller continue; 24021f659bfSEliad Peller 241bbf31e88SIlan Peer max_bw = max(max_bw, ieee80211_get_sta_bw(sta)); 24221f659bfSEliad Peller } 24321f659bfSEliad Peller rcu_read_unlock(); 24421f659bfSEliad Peller 24521f659bfSEliad Peller return max_bw; 24621f659bfSEliad Peller } 24721f659bfSEliad Peller 24821f659bfSEliad Peller static enum nl80211_chan_width 24921f659bfSEliad Peller ieee80211_get_chanctx_max_required_bw(struct ieee80211_local *local, 25021f659bfSEliad Peller struct ieee80211_chanctx_conf *conf) 25121f659bfSEliad Peller { 25221f659bfSEliad Peller struct ieee80211_sub_if_data *sdata; 25321f659bfSEliad Peller enum nl80211_chan_width max_bw = NL80211_CHAN_WIDTH_20_NOHT; 25421f659bfSEliad Peller 25521f659bfSEliad Peller rcu_read_lock(); 25621f659bfSEliad Peller list_for_each_entry_rcu(sdata, &local->interfaces, list) { 25721f659bfSEliad Peller struct ieee80211_vif *vif = &sdata->vif; 25821f659bfSEliad Peller enum nl80211_chan_width width = NL80211_CHAN_WIDTH_20_NOHT; 25921f659bfSEliad Peller 26021f659bfSEliad Peller if (!ieee80211_sdata_running(sdata)) 26121f659bfSEliad Peller continue; 26221f659bfSEliad Peller 263d0a9123eSJohannes Berg if (rcu_access_pointer(sdata->vif.bss_conf.chanctx_conf) != conf) 26421f659bfSEliad Peller continue; 26521f659bfSEliad Peller 26621f659bfSEliad Peller switch (vif->type) { 26721f659bfSEliad Peller case NL80211_IFTYPE_AP: 26821f659bfSEliad Peller case NL80211_IFTYPE_AP_VLAN: 26921f659bfSEliad Peller width = ieee80211_get_max_required_bw(sdata); 27021f659bfSEliad Peller break; 2710fabfaafSArik Nemtsov case NL80211_IFTYPE_STATION: 2720fabfaafSArik Nemtsov /* 2730fabfaafSArik Nemtsov * The ap's sta->bandwidth is not set yet at this 2740fabfaafSArik Nemtsov * point, so take the width from the chandef, but 2750fabfaafSArik Nemtsov * account also for TDLS peers 2760fabfaafSArik Nemtsov */ 2770fabfaafSArik Nemtsov width = max(vif->bss_conf.chandef.width, 2780fabfaafSArik Nemtsov ieee80211_get_max_required_bw(sdata)); 2790fabfaafSArik Nemtsov break; 28021f659bfSEliad Peller case NL80211_IFTYPE_P2P_DEVICE: 281cb3b7d87SAyala Beker case NL80211_IFTYPE_NAN: 28221f659bfSEliad Peller continue; 28321f659bfSEliad Peller case NL80211_IFTYPE_ADHOC: 28421f659bfSEliad Peller case NL80211_IFTYPE_MESH_POINT: 2856e0bd6c3SRostislav Lisovy case NL80211_IFTYPE_OCB: 28621f659bfSEliad Peller width = vif->bss_conf.chandef.width; 28721f659bfSEliad Peller break; 28870d9c599SJohannes Berg case NL80211_IFTYPE_WDS: 28921f659bfSEliad Peller case NL80211_IFTYPE_UNSPECIFIED: 29021f659bfSEliad Peller case NUM_NL80211_IFTYPES: 29121f659bfSEliad Peller case NL80211_IFTYPE_MONITOR: 29221f659bfSEliad Peller case NL80211_IFTYPE_P2P_CLIENT: 29321f659bfSEliad Peller case NL80211_IFTYPE_P2P_GO: 29421f659bfSEliad Peller WARN_ON_ONCE(1); 29521f659bfSEliad Peller } 29621f659bfSEliad Peller max_bw = max(max_bw, width); 29721f659bfSEliad Peller } 2981c37a72cSEliad Peller 2991c37a72cSEliad Peller /* use the configured bandwidth in case of monitor interface */ 3001c37a72cSEliad Peller sdata = rcu_dereference(local->monitor_sdata); 301d0a9123eSJohannes Berg if (sdata && rcu_access_pointer(sdata->vif.bss_conf.chanctx_conf) == conf) 3021c37a72cSEliad Peller max_bw = max(max_bw, conf->def.width); 3031c37a72cSEliad Peller 30421f659bfSEliad Peller rcu_read_unlock(); 30521f659bfSEliad Peller 30621f659bfSEliad Peller return max_bw; 30721f659bfSEliad Peller } 30821f659bfSEliad Peller 30921f659bfSEliad Peller /* 31021f659bfSEliad Peller * recalc the min required chan width of the channel context, which is 31121f659bfSEliad Peller * the max of min required widths of all the interfaces bound to this 31221f659bfSEliad Peller * channel context. 31321f659bfSEliad Peller */ 314d6c37509SMordechay Goodstein static u32 _ieee80211_recalc_chanctx_min_def(struct ieee80211_local *local, 31521f659bfSEliad Peller struct ieee80211_chanctx *ctx) 31621f659bfSEliad Peller { 31721f659bfSEliad Peller enum nl80211_chan_width max_bw; 31821f659bfSEliad Peller struct cfg80211_chan_def min_def; 31921f659bfSEliad Peller 32021f659bfSEliad Peller lockdep_assert_held(&local->chanctx_mtx); 32121f659bfSEliad Peller 322df78a0c0SThomas Pedersen /* don't optimize non-20MHz based and radar_enabled confs */ 32321f659bfSEliad Peller if (ctx->conf.def.width == NL80211_CHAN_WIDTH_5 || 32421f659bfSEliad Peller ctx->conf.def.width == NL80211_CHAN_WIDTH_10 || 325df78a0c0SThomas Pedersen ctx->conf.def.width == NL80211_CHAN_WIDTH_1 || 326df78a0c0SThomas Pedersen ctx->conf.def.width == NL80211_CHAN_WIDTH_2 || 327df78a0c0SThomas Pedersen ctx->conf.def.width == NL80211_CHAN_WIDTH_4 || 328df78a0c0SThomas Pedersen ctx->conf.def.width == NL80211_CHAN_WIDTH_8 || 329df78a0c0SThomas Pedersen ctx->conf.def.width == NL80211_CHAN_WIDTH_16 || 33021f659bfSEliad Peller ctx->conf.radar_enabled) { 33121f659bfSEliad Peller ctx->conf.min_def = ctx->conf.def; 332d6c37509SMordechay Goodstein return 0; 33321f659bfSEliad Peller } 33421f659bfSEliad Peller 33521f659bfSEliad Peller max_bw = ieee80211_get_chanctx_max_required_bw(local, &ctx->conf); 33621f659bfSEliad Peller 33721f659bfSEliad Peller /* downgrade chandef up to max_bw */ 33821f659bfSEliad Peller min_def = ctx->conf.def; 33921f659bfSEliad Peller while (min_def.width > max_bw) 34021f659bfSEliad Peller ieee80211_chandef_downgrade(&min_def); 34121f659bfSEliad Peller 34221f659bfSEliad Peller if (cfg80211_chandef_identical(&ctx->conf.min_def, &min_def)) 343d6c37509SMordechay Goodstein return 0; 34421f659bfSEliad Peller 34521f659bfSEliad Peller ctx->conf.min_def = min_def; 34621f659bfSEliad Peller if (!ctx->driver_present) 347d6c37509SMordechay Goodstein return 0; 34821f659bfSEliad Peller 349d6c37509SMordechay Goodstein return IEEE80211_CHANCTX_CHANGE_MIN_WIDTH; 35021f659bfSEliad Peller } 35121f659bfSEliad Peller 352d6c37509SMordechay Goodstein /* calling this function is assuming that station vif is updated to 353d6c37509SMordechay Goodstein * lates changes by calling ieee80211_vif_update_chandef 354d6c37509SMordechay Goodstein */ 35544b72ca8SIlan Peer static void ieee80211_chan_bw_change(struct ieee80211_local *local, 356d6c37509SMordechay Goodstein struct ieee80211_chanctx *ctx, 357d6c37509SMordechay Goodstein bool narrowed) 35844b72ca8SIlan Peer { 35944b72ca8SIlan Peer struct sta_info *sta; 36044b72ca8SIlan Peer struct ieee80211_supported_band *sband = 36144b72ca8SIlan Peer local->hw.wiphy->bands[ctx->conf.def.chan->band]; 36244b72ca8SIlan Peer 36344b72ca8SIlan Peer rcu_read_lock(); 36444b72ca8SIlan Peer list_for_each_entry_rcu(sta, &local->sta_list, 36544b72ca8SIlan Peer list) { 36644b72ca8SIlan Peer enum ieee80211_sta_rx_bandwidth new_sta_bw; 36744b72ca8SIlan Peer 36844b72ca8SIlan Peer if (!ieee80211_sdata_running(sta->sdata)) 36944b72ca8SIlan Peer continue; 37044b72ca8SIlan Peer 371d0a9123eSJohannes Berg if (rcu_access_pointer(sta->sdata->vif.bss_conf.chanctx_conf) != 37244b72ca8SIlan Peer &ctx->conf) 37344b72ca8SIlan Peer continue; 37444b72ca8SIlan Peer 37544b72ca8SIlan Peer new_sta_bw = ieee80211_sta_cur_vht_bw(sta); 376d6c37509SMordechay Goodstein 377d6c37509SMordechay Goodstein /* nothing change */ 378046d2e7cSSriram R if (new_sta_bw == sta->sta.deflink.bandwidth) 37944b72ca8SIlan Peer continue; 38044b72ca8SIlan Peer 381d6c37509SMordechay Goodstein /* vif changed to narrow BW and narrow BW for station wasn't 382d6c37509SMordechay Goodstein * requested or vise versa */ 383046d2e7cSSriram R if ((new_sta_bw < sta->sta.deflink.bandwidth) == !narrowed) 384d6c37509SMordechay Goodstein continue; 385d6c37509SMordechay Goodstein 386046d2e7cSSriram R sta->sta.deflink.bandwidth = new_sta_bw; 38744b72ca8SIlan Peer rate_control_rate_update(local, sband, sta, 38844b72ca8SIlan Peer IEEE80211_RC_BW_CHANGED); 38944b72ca8SIlan Peer } 39044b72ca8SIlan Peer rcu_read_unlock(); 39144b72ca8SIlan Peer } 39244b72ca8SIlan Peer 393d6c37509SMordechay Goodstein /* 394d6c37509SMordechay Goodstein * recalc the min required chan width of the channel context, which is 395d6c37509SMordechay Goodstein * the max of min required widths of all the interfaces bound to this 396d6c37509SMordechay Goodstein * channel context. 397d6c37509SMordechay Goodstein */ 398d6c37509SMordechay Goodstein void ieee80211_recalc_chanctx_min_def(struct ieee80211_local *local, 399d6c37509SMordechay Goodstein struct ieee80211_chanctx *ctx) 400e89a96f5SMichal Kazior { 401d6c37509SMordechay Goodstein u32 changed = _ieee80211_recalc_chanctx_min_def(local, ctx); 40244b72ca8SIlan Peer 403d6c37509SMordechay Goodstein if (!changed) 404e89a96f5SMichal Kazior return; 405d6c37509SMordechay Goodstein 406d6c37509SMordechay Goodstein /* check is BW narrowed */ 407d6c37509SMordechay Goodstein ieee80211_chan_bw_change(local, ctx, true); 408d6c37509SMordechay Goodstein 409d6c37509SMordechay Goodstein drv_change_chanctx(local, ctx, changed); 410d6c37509SMordechay Goodstein 411d6c37509SMordechay Goodstein /* check is BW wider */ 412d6c37509SMordechay Goodstein ieee80211_chan_bw_change(local, ctx, false); 413aa507a7bSArik Nemtsov } 414e89a96f5SMichal Kazior 415d6c37509SMordechay Goodstein static void ieee80211_change_chanctx(struct ieee80211_local *local, 416d6c37509SMordechay Goodstein struct ieee80211_chanctx *ctx, 417d6c37509SMordechay Goodstein struct ieee80211_chanctx *old_ctx, 418d6c37509SMordechay Goodstein const struct cfg80211_chan_def *chandef) 419d6c37509SMordechay Goodstein { 420d6c37509SMordechay Goodstein u32 changed; 42144b72ca8SIlan Peer 4225dca295dSIlan Peer /* expected to handle only 20/40/80/160/320 channel widths */ 42344b72ca8SIlan Peer switch (chandef->width) { 42444b72ca8SIlan Peer case NL80211_CHAN_WIDTH_20_NOHT: 42544b72ca8SIlan Peer case NL80211_CHAN_WIDTH_20: 42644b72ca8SIlan Peer case NL80211_CHAN_WIDTH_40: 42744b72ca8SIlan Peer case NL80211_CHAN_WIDTH_80: 42844b72ca8SIlan Peer case NL80211_CHAN_WIDTH_80P80: 42944b72ca8SIlan Peer case NL80211_CHAN_WIDTH_160: 4305dca295dSIlan Peer case NL80211_CHAN_WIDTH_320: 43144b72ca8SIlan Peer break; 43244b72ca8SIlan Peer default: 43344b72ca8SIlan Peer WARN_ON(1); 43444b72ca8SIlan Peer } 43544b72ca8SIlan Peer 436d6c37509SMordechay Goodstein /* Check maybe BW narrowed - we do this _before_ calling recalc_chanctx_min_def 437d6c37509SMordechay Goodstein * due to maybe not returning from it, e.g in case new context was added 438d6c37509SMordechay Goodstein * first time with all parameters up to date. 439d6c37509SMordechay Goodstein */ 440d6c37509SMordechay Goodstein ieee80211_chan_bw_change(local, old_ctx, true); 44144b72ca8SIlan Peer 442d6c37509SMordechay Goodstein if (cfg80211_chandef_identical(&ctx->conf.def, chandef)) { 44321f659bfSEliad Peller ieee80211_recalc_chanctx_min_def(local, ctx); 444d6c37509SMordechay Goodstein return; 445d6c37509SMordechay Goodstein } 446d6c37509SMordechay Goodstein 447d6c37509SMordechay Goodstein WARN_ON(!cfg80211_chandef_compatible(&ctx->conf.def, chandef)); 448d6c37509SMordechay Goodstein 449d6c37509SMordechay Goodstein ctx->conf.def = *chandef; 450d6c37509SMordechay Goodstein 451d6c37509SMordechay Goodstein /* check if min chanctx also changed */ 452d6c37509SMordechay Goodstein changed = IEEE80211_CHANCTX_CHANGE_WIDTH | 453d6c37509SMordechay Goodstein _ieee80211_recalc_chanctx_min_def(local, ctx); 454d6c37509SMordechay Goodstein drv_change_chanctx(local, ctx, changed); 45555de908aSJohannes Berg 45655de908aSJohannes Berg if (!local->use_chanctx) { 457675a0b04SKarl Beldan local->_oper_chandef = *chandef; 45855de908aSJohannes Berg ieee80211_hw_config(local, 0); 45955de908aSJohannes Berg } 46044b72ca8SIlan Peer 461d6c37509SMordechay Goodstein /* check is BW wider */ 462d6c37509SMordechay Goodstein ieee80211_chan_bw_change(local, old_ctx, false); 4630aaffa9bSJohannes Berg } 464d01a1e65SMichal Kazior 465d01a1e65SMichal Kazior static struct ieee80211_chanctx * 466d01a1e65SMichal Kazior ieee80211_find_chanctx(struct ieee80211_local *local, 4674bf88530SJohannes Berg const struct cfg80211_chan_def *chandef, 468d01a1e65SMichal Kazior enum ieee80211_chanctx_mode mode) 469d01a1e65SMichal Kazior { 470d01a1e65SMichal Kazior struct ieee80211_chanctx *ctx; 471d01a1e65SMichal Kazior 472d01a1e65SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 473d01a1e65SMichal Kazior 474d01a1e65SMichal Kazior if (mode == IEEE80211_CHANCTX_EXCLUSIVE) 475d01a1e65SMichal Kazior return NULL; 476d01a1e65SMichal Kazior 477d01a1e65SMichal Kazior list_for_each_entry(ctx, &local->chanctx_list, list) { 4784bf88530SJohannes Berg const struct cfg80211_chan_def *compat; 479e89a96f5SMichal Kazior 4805bcae31dSMichal Kazior if (ctx->replace_state != IEEE80211_CHANCTX_REPLACE_NONE) 4815bcae31dSMichal Kazior continue; 4825bcae31dSMichal Kazior 483d01a1e65SMichal Kazior if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE) 484d01a1e65SMichal Kazior continue; 4854bf88530SJohannes Berg 4864bf88530SJohannes Berg compat = cfg80211_chandef_compatible(&ctx->conf.def, chandef); 4874bf88530SJohannes Berg if (!compat) 488d01a1e65SMichal Kazior continue; 489d01a1e65SMichal Kazior 4900288157bSMichal Kazior compat = ieee80211_chanctx_reserved_chandef(local, ctx, 4910288157bSMichal Kazior compat); 4920288157bSMichal Kazior if (!compat) 4930288157bSMichal Kazior continue; 4940288157bSMichal Kazior 495d6c37509SMordechay Goodstein ieee80211_change_chanctx(local, ctx, ctx, compat); 496e89a96f5SMichal Kazior 497d01a1e65SMichal Kazior return ctx; 498d01a1e65SMichal Kazior } 499d01a1e65SMichal Kazior 500d01a1e65SMichal Kazior return NULL; 501d01a1e65SMichal Kazior } 502d01a1e65SMichal Kazior 5035cbc95a7SEliad Peller bool ieee80211_is_radar_required(struct ieee80211_local *local) 504e4746851SSimon Wunderlich { 505e4746851SSimon Wunderlich struct ieee80211_sub_if_data *sdata; 506e4746851SSimon Wunderlich 507cc901de1SMichal Kazior lockdep_assert_held(&local->mtx); 508cc901de1SMichal Kazior 509e4746851SSimon Wunderlich rcu_read_lock(); 510e4746851SSimon Wunderlich list_for_each_entry_rcu(sdata, &local->interfaces, list) { 511e4746851SSimon Wunderlich if (sdata->radar_required) { 512e4746851SSimon Wunderlich rcu_read_unlock(); 513e4746851SSimon Wunderlich return true; 514e4746851SSimon Wunderlich } 515e4746851SSimon Wunderlich } 516e4746851SSimon Wunderlich rcu_read_unlock(); 517e4746851SSimon Wunderlich 518e4746851SSimon Wunderlich return false; 519e4746851SSimon Wunderlich } 520e4746851SSimon Wunderlich 521e7f2337aSEliad Peller static bool 522e7f2337aSEliad Peller ieee80211_chanctx_radar_required(struct ieee80211_local *local, 523e7f2337aSEliad Peller struct ieee80211_chanctx *ctx) 524e7f2337aSEliad Peller { 525e7f2337aSEliad Peller struct ieee80211_chanctx_conf *conf = &ctx->conf; 526e7f2337aSEliad Peller struct ieee80211_sub_if_data *sdata; 527e7f2337aSEliad Peller bool required = false; 528e7f2337aSEliad Peller 529e7f2337aSEliad Peller lockdep_assert_held(&local->chanctx_mtx); 530e7f2337aSEliad Peller lockdep_assert_held(&local->mtx); 531e7f2337aSEliad Peller 532e7f2337aSEliad Peller rcu_read_lock(); 533e7f2337aSEliad Peller list_for_each_entry_rcu(sdata, &local->interfaces, list) { 534e7f2337aSEliad Peller if (!ieee80211_sdata_running(sdata)) 535e7f2337aSEliad Peller continue; 536d0a9123eSJohannes Berg if (rcu_access_pointer(sdata->vif.bss_conf.chanctx_conf) != conf) 537e7f2337aSEliad Peller continue; 538e7f2337aSEliad Peller if (!sdata->radar_required) 539e7f2337aSEliad Peller continue; 540e7f2337aSEliad Peller 541e7f2337aSEliad Peller required = true; 542e7f2337aSEliad Peller break; 543e7f2337aSEliad Peller } 544e7f2337aSEliad Peller rcu_read_unlock(); 545e7f2337aSEliad Peller 546e7f2337aSEliad Peller return required; 547e7f2337aSEliad Peller } 548e7f2337aSEliad Peller 549d01a1e65SMichal Kazior static struct ieee80211_chanctx * 550ed68ebcaSMichal Kazior ieee80211_alloc_chanctx(struct ieee80211_local *local, 5514bf88530SJohannes Berg const struct cfg80211_chan_def *chandef, 552d01a1e65SMichal Kazior enum ieee80211_chanctx_mode mode) 553d01a1e65SMichal Kazior { 554d01a1e65SMichal Kazior struct ieee80211_chanctx *ctx; 555d01a1e65SMichal Kazior 556d01a1e65SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 557d01a1e65SMichal Kazior 558d01a1e65SMichal Kazior ctx = kzalloc(sizeof(*ctx) + local->hw.chanctx_data_size, GFP_KERNEL); 559d01a1e65SMichal Kazior if (!ctx) 560ed68ebcaSMichal Kazior return NULL; 561d01a1e65SMichal Kazior 562484298adSMichal Kazior INIT_LIST_HEAD(&ctx->assigned_vifs); 563e3afb920SMichal Kazior INIT_LIST_HEAD(&ctx->reserved_vifs); 5644bf88530SJohannes Berg ctx->conf.def = *chandef; 56504ecd257SJohannes Berg ctx->conf.rx_chains_static = 1; 56604ecd257SJohannes Berg ctx->conf.rx_chains_dynamic = 1; 567d01a1e65SMichal Kazior ctx->mode = mode; 568e7f2337aSEliad Peller ctx->conf.radar_enabled = false; 56921f659bfSEliad Peller ieee80211_recalc_chanctx_min_def(local, ctx); 570ed68ebcaSMichal Kazior 571ed68ebcaSMichal Kazior return ctx; 572ed68ebcaSMichal Kazior } 573ed68ebcaSMichal Kazior 574ed68ebcaSMichal Kazior static int ieee80211_add_chanctx(struct ieee80211_local *local, 575ed68ebcaSMichal Kazior struct ieee80211_chanctx *ctx) 576ed68ebcaSMichal Kazior { 577ed68ebcaSMichal Kazior u32 changed; 578ed68ebcaSMichal Kazior int err; 579ed68ebcaSMichal Kazior 580ed68ebcaSMichal Kazior lockdep_assert_held(&local->mtx); 581ed68ebcaSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 582ed68ebcaSMichal Kazior 583e4746851SSimon Wunderlich if (!local->use_chanctx) 584e4746851SSimon Wunderlich local->hw.conf.radar_enabled = ctx->conf.radar_enabled; 585d01a1e65SMichal Kazior 586382a103bSJohannes Berg /* turn idle off *before* setting channel -- some drivers need that */ 587382a103bSJohannes Berg changed = ieee80211_idle_off(local); 588382a103bSJohannes Berg if (changed) 589382a103bSJohannes Berg ieee80211_hw_config(local, changed); 590382a103bSJohannes Berg 59155de908aSJohannes Berg if (!local->use_chanctx) { 592ed68ebcaSMichal Kazior local->_oper_chandef = ctx->conf.def; 5939b4816f5SMichal Kazior ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); 59455de908aSJohannes Berg } else { 59535f2fce9SMichal Kazior err = drv_add_chanctx(local, ctx); 59635f2fce9SMichal Kazior if (err) { 597382a103bSJohannes Berg ieee80211_recalc_idle(local); 598ed68ebcaSMichal Kazior return err; 599ed68ebcaSMichal Kazior } 600ed68ebcaSMichal Kazior } 601ed68ebcaSMichal Kazior 602ed68ebcaSMichal Kazior return 0; 603ed68ebcaSMichal Kazior } 604ed68ebcaSMichal Kazior 605ed68ebcaSMichal Kazior static struct ieee80211_chanctx * 606ed68ebcaSMichal Kazior ieee80211_new_chanctx(struct ieee80211_local *local, 607ed68ebcaSMichal Kazior const struct cfg80211_chan_def *chandef, 608ed68ebcaSMichal Kazior enum ieee80211_chanctx_mode mode) 609ed68ebcaSMichal Kazior { 610ed68ebcaSMichal Kazior struct ieee80211_chanctx *ctx; 611ed68ebcaSMichal Kazior int err; 612ed68ebcaSMichal Kazior 613ed68ebcaSMichal Kazior lockdep_assert_held(&local->mtx); 614ed68ebcaSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 615ed68ebcaSMichal Kazior 616ed68ebcaSMichal Kazior ctx = ieee80211_alloc_chanctx(local, chandef, mode); 617ed68ebcaSMichal Kazior if (!ctx) 618ed68ebcaSMichal Kazior return ERR_PTR(-ENOMEM); 619ed68ebcaSMichal Kazior 620ed68ebcaSMichal Kazior err = ieee80211_add_chanctx(local, ctx); 621ed68ebcaSMichal Kazior if (err) { 622ed68ebcaSMichal Kazior kfree(ctx); 62334a3740dSJohannes Berg return ERR_PTR(err); 62435f2fce9SMichal Kazior } 62535f2fce9SMichal Kazior 6263448c005SJohannes Berg list_add_rcu(&ctx->list, &local->chanctx_list); 627d01a1e65SMichal Kazior return ctx; 628d01a1e65SMichal Kazior } 629d01a1e65SMichal Kazior 6301f0d54cdSMichal Kazior static void ieee80211_del_chanctx(struct ieee80211_local *local, 631d01a1e65SMichal Kazior struct ieee80211_chanctx *ctx) 632d01a1e65SMichal Kazior { 633d01a1e65SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 634d01a1e65SMichal Kazior 63555de908aSJohannes Berg if (!local->use_chanctx) { 636675a0b04SKarl Beldan struct cfg80211_chan_def *chandef = &local->_oper_chandef; 6375e480774SThomas Pedersen /* S1G doesn't have 20MHz, so get the correct width for the 6385e480774SThomas Pedersen * current channel. 6395e480774SThomas Pedersen */ 6405e480774SThomas Pedersen if (chandef->chan->band == NL80211_BAND_S1GHZ) 6415e480774SThomas Pedersen chandef->width = 6425e480774SThomas Pedersen ieee80211_s1g_channel_width(chandef->chan); 6435e480774SThomas Pedersen else 644675a0b04SKarl Beldan chandef->width = NL80211_CHAN_WIDTH_20_NOHT; 645675a0b04SKarl Beldan chandef->center_freq1 = chandef->chan->center_freq; 646b6011960SThomas Pedersen chandef->freq1_offset = chandef->chan->freq_offset; 647675a0b04SKarl Beldan chandef->center_freq2 = 0; 648e4746851SSimon Wunderlich 649e4746851SSimon Wunderlich /* NOTE: Disabling radar is only valid here for 650e4746851SSimon Wunderlich * single channel context. To be sure, check it ... 651e4746851SSimon Wunderlich */ 6521f0d54cdSMichal Kazior WARN_ON(local->hw.conf.radar_enabled && 6531f0d54cdSMichal Kazior !list_empty(&local->chanctx_list)); 6541f0d54cdSMichal Kazior 655e4746851SSimon Wunderlich local->hw.conf.radar_enabled = false; 656e4746851SSimon Wunderlich 6579b4816f5SMichal Kazior ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); 65855de908aSJohannes Berg } else { 65935f2fce9SMichal Kazior drv_remove_chanctx(local, ctx); 66055de908aSJohannes Berg } 66135f2fce9SMichal Kazior 662fd0f979aSJohannes Berg ieee80211_recalc_idle(local); 663d01a1e65SMichal Kazior } 664d01a1e65SMichal Kazior 6651f0d54cdSMichal Kazior static void ieee80211_free_chanctx(struct ieee80211_local *local, 666d01a1e65SMichal Kazior struct ieee80211_chanctx *ctx) 667d01a1e65SMichal Kazior { 668d01a1e65SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 669d01a1e65SMichal Kazior 670c0166da9SMichal Kazior WARN_ON_ONCE(ieee80211_chanctx_refcount(local, ctx) != 0); 67135f2fce9SMichal Kazior 6721f0d54cdSMichal Kazior list_del_rcu(&ctx->list); 6731f0d54cdSMichal Kazior ieee80211_del_chanctx(local, ctx); 6741f0d54cdSMichal Kazior kfree_rcu(ctx, rcu_head); 675d01a1e65SMichal Kazior } 676d01a1e65SMichal Kazior 6770fabfaafSArik Nemtsov void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local, 678e89a96f5SMichal Kazior struct ieee80211_chanctx *ctx) 679e89a96f5SMichal Kazior { 680e89a96f5SMichal Kazior struct ieee80211_chanctx_conf *conf = &ctx->conf; 681e89a96f5SMichal Kazior struct ieee80211_sub_if_data *sdata; 6824bf88530SJohannes Berg const struct cfg80211_chan_def *compat = NULL; 6830fabfaafSArik Nemtsov struct sta_info *sta; 684e89a96f5SMichal Kazior 685e89a96f5SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 686e89a96f5SMichal Kazior 687e89a96f5SMichal Kazior rcu_read_lock(); 688e89a96f5SMichal Kazior list_for_each_entry_rcu(sdata, &local->interfaces, list) { 6894bf88530SJohannes Berg 690e89a96f5SMichal Kazior if (!ieee80211_sdata_running(sdata)) 691e89a96f5SMichal Kazior continue; 692d0a9123eSJohannes Berg if (rcu_access_pointer(sdata->vif.bss_conf.chanctx_conf) != conf) 693e89a96f5SMichal Kazior continue; 6940e67c136SFelix Fietkau if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) 6950e67c136SFelix Fietkau continue; 696e89a96f5SMichal Kazior 6974bf88530SJohannes Berg if (!compat) 6984bf88530SJohannes Berg compat = &sdata->vif.bss_conf.chandef; 6994bf88530SJohannes Berg 7004bf88530SJohannes Berg compat = cfg80211_chandef_compatible( 7014bf88530SJohannes Berg &sdata->vif.bss_conf.chandef, compat); 702a00f4f6eSMichal Kazior if (WARN_ON_ONCE(!compat)) 7034bf88530SJohannes Berg break; 704e89a96f5SMichal Kazior } 7050fabfaafSArik Nemtsov 7060fabfaafSArik Nemtsov /* TDLS peers can sometimes affect the chandef width */ 7070fabfaafSArik Nemtsov list_for_each_entry_rcu(sta, &local->sta_list, list) { 7080fabfaafSArik Nemtsov if (!sta->uploaded || 7090fabfaafSArik Nemtsov !test_sta_flag(sta, WLAN_STA_TDLS_WIDER_BW) || 7100fabfaafSArik Nemtsov !test_sta_flag(sta, WLAN_STA_AUTHORIZED) || 7110fabfaafSArik Nemtsov !sta->tdls_chandef.chan) 7120fabfaafSArik Nemtsov continue; 7130fabfaafSArik Nemtsov 7140fabfaafSArik Nemtsov compat = cfg80211_chandef_compatible(&sta->tdls_chandef, 7150fabfaafSArik Nemtsov compat); 7160fabfaafSArik Nemtsov if (WARN_ON_ONCE(!compat)) 7170fabfaafSArik Nemtsov break; 7180fabfaafSArik Nemtsov } 719e89a96f5SMichal Kazior rcu_read_unlock(); 720e89a96f5SMichal Kazior 721a00f4f6eSMichal Kazior if (!compat) 7224bf88530SJohannes Berg return; 723e89a96f5SMichal Kazior 724d6c37509SMordechay Goodstein ieee80211_change_chanctx(local, ctx, ctx, compat); 725e89a96f5SMichal Kazior } 726e89a96f5SMichal Kazior 727367bbd10SJohannes Berg static void ieee80211_recalc_radar_chanctx(struct ieee80211_local *local, 728367bbd10SJohannes Berg struct ieee80211_chanctx *chanctx) 729367bbd10SJohannes Berg { 730367bbd10SJohannes Berg bool radar_enabled; 731367bbd10SJohannes Berg 732367bbd10SJohannes Berg lockdep_assert_held(&local->chanctx_mtx); 7335cbc95a7SEliad Peller /* for ieee80211_is_radar_required */ 73434a3740dSJohannes Berg lockdep_assert_held(&local->mtx); 735367bbd10SJohannes Berg 736e7f2337aSEliad Peller radar_enabled = ieee80211_chanctx_radar_required(local, chanctx); 737367bbd10SJohannes Berg 738367bbd10SJohannes Berg if (radar_enabled == chanctx->conf.radar_enabled) 739367bbd10SJohannes Berg return; 740367bbd10SJohannes Berg 741367bbd10SJohannes Berg chanctx->conf.radar_enabled = radar_enabled; 742367bbd10SJohannes Berg 743367bbd10SJohannes Berg if (!local->use_chanctx) { 744367bbd10SJohannes Berg local->hw.conf.radar_enabled = chanctx->conf.radar_enabled; 745367bbd10SJohannes Berg ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); 746367bbd10SJohannes Berg } 747367bbd10SJohannes Berg 748367bbd10SJohannes Berg drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RADAR); 749367bbd10SJohannes Berg } 750367bbd10SJohannes Berg 75177eeba97SLuciano Coelho static int ieee80211_assign_vif_chanctx(struct ieee80211_sub_if_data *sdata, 75277eeba97SLuciano Coelho struct ieee80211_chanctx *new_ctx) 753d01a1e65SMichal Kazior { 75435f2fce9SMichal Kazior struct ieee80211_local *local = sdata->local; 75577eeba97SLuciano Coelho struct ieee80211_chanctx_conf *conf; 75677eeba97SLuciano Coelho struct ieee80211_chanctx *curr_ctx = NULL; 75777eeba97SLuciano Coelho int ret = 0; 758d01a1e65SMichal Kazior 759708d50edSAyala Beker if (WARN_ON(sdata->vif.type == NL80211_IFTYPE_NAN)) 760708d50edSAyala Beker return -ENOTSUPP; 761708d50edSAyala Beker 762d0a9123eSJohannes Berg conf = rcu_dereference_protected(sdata->vif.bss_conf.chanctx_conf, 76377eeba97SLuciano Coelho lockdep_is_held(&local->chanctx_mtx)); 764d01a1e65SMichal Kazior 76577eeba97SLuciano Coelho if (conf) { 76677eeba97SLuciano Coelho curr_ctx = container_of(conf, struct ieee80211_chanctx, conf); 76735f2fce9SMichal Kazior 76877eeba97SLuciano Coelho drv_unassign_vif_chanctx(local, sdata, curr_ctx); 76977eeba97SLuciano Coelho conf = NULL; 770484298adSMichal Kazior list_del(&sdata->assigned_chanctx_list); 77177eeba97SLuciano Coelho } 77277eeba97SLuciano Coelho 77377eeba97SLuciano Coelho if (new_ctx) { 77477eeba97SLuciano Coelho ret = drv_assign_vif_chanctx(local, sdata, new_ctx); 77577eeba97SLuciano Coelho if (ret) 77677eeba97SLuciano Coelho goto out; 77777eeba97SLuciano Coelho 77877eeba97SLuciano Coelho conf = &new_ctx->conf; 779484298adSMichal Kazior list_add(&sdata->assigned_chanctx_list, 780484298adSMichal Kazior &new_ctx->assigned_vifs); 78177eeba97SLuciano Coelho } 78277eeba97SLuciano Coelho 78377eeba97SLuciano Coelho out: 784d0a9123eSJohannes Berg rcu_assign_pointer(sdata->vif.bss_conf.chanctx_conf, conf); 78577eeba97SLuciano Coelho 786*f276e20bSJohannes Berg sdata->vif.cfg.idle = !conf; 78777eeba97SLuciano Coelho 788c0166da9SMichal Kazior if (curr_ctx && ieee80211_chanctx_num_assigned(local, curr_ctx) > 0) { 78977eeba97SLuciano Coelho ieee80211_recalc_chanctx_chantype(local, curr_ctx); 79077eeba97SLuciano Coelho ieee80211_recalc_smps_chanctx(local, curr_ctx); 79177eeba97SLuciano Coelho ieee80211_recalc_radar_chanctx(local, curr_ctx); 79277eeba97SLuciano Coelho ieee80211_recalc_chanctx_min_def(local, curr_ctx); 79377eeba97SLuciano Coelho } 79477eeba97SLuciano Coelho 795c0166da9SMichal Kazior if (new_ctx && ieee80211_chanctx_num_assigned(local, new_ctx) > 0) { 796db82d8a9SLorenzo Bianconi ieee80211_recalc_txpower(sdata, false); 79777eeba97SLuciano Coelho ieee80211_recalc_chanctx_min_def(local, new_ctx); 79877eeba97SLuciano Coelho } 7995bbe754dSJohannes Berg 8005bbe754dSJohannes Berg if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE && 8015bbe754dSJohannes Berg sdata->vif.type != NL80211_IFTYPE_MONITOR) 80277eeba97SLuciano Coelho ieee80211_bss_info_change_notify(sdata, 80377eeba97SLuciano Coelho BSS_CHANGED_IDLE); 804fd0f979aSJohannes Berg 80517c18bf8SJohannes Berg ieee80211_check_fast_xmit_iface(sdata); 80617c18bf8SJohannes Berg 80777eeba97SLuciano Coelho return ret; 808d01a1e65SMichal Kazior } 809d01a1e65SMichal Kazior 81004ecd257SJohannes Berg void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local, 81104ecd257SJohannes Berg struct ieee80211_chanctx *chanctx) 81204ecd257SJohannes Berg { 81304ecd257SJohannes Berg struct ieee80211_sub_if_data *sdata; 81404ecd257SJohannes Berg u8 rx_chains_static, rx_chains_dynamic; 81504ecd257SJohannes Berg 81604ecd257SJohannes Berg lockdep_assert_held(&local->chanctx_mtx); 81704ecd257SJohannes Berg 81804ecd257SJohannes Berg rx_chains_static = 1; 81904ecd257SJohannes Berg rx_chains_dynamic = 1; 82004ecd257SJohannes Berg 82104ecd257SJohannes Berg rcu_read_lock(); 82204ecd257SJohannes Berg list_for_each_entry_rcu(sdata, &local->interfaces, list) { 82304ecd257SJohannes Berg u8 needed_static, needed_dynamic; 82404ecd257SJohannes Berg 82504ecd257SJohannes Berg if (!ieee80211_sdata_running(sdata)) 82604ecd257SJohannes Berg continue; 82704ecd257SJohannes Berg 828d0a9123eSJohannes Berg if (rcu_access_pointer(sdata->vif.bss_conf.chanctx_conf) != 82904ecd257SJohannes Berg &chanctx->conf) 83004ecd257SJohannes Berg continue; 83104ecd257SJohannes Berg 83204ecd257SJohannes Berg switch (sdata->vif.type) { 83304ecd257SJohannes Berg case NL80211_IFTYPE_P2P_DEVICE: 834cb3b7d87SAyala Beker case NL80211_IFTYPE_NAN: 83504ecd257SJohannes Berg continue; 83604ecd257SJohannes Berg case NL80211_IFTYPE_STATION: 83704ecd257SJohannes Berg if (!sdata->u.mgd.associated) 83804ecd257SJohannes Berg continue; 83904ecd257SJohannes Berg break; 84004ecd257SJohannes Berg case NL80211_IFTYPE_AP_VLAN: 84104ecd257SJohannes Berg continue; 84204ecd257SJohannes Berg case NL80211_IFTYPE_AP: 84304ecd257SJohannes Berg case NL80211_IFTYPE_ADHOC: 84404ecd257SJohannes Berg case NL80211_IFTYPE_MESH_POINT: 845239281f8SRostislav Lisovy case NL80211_IFTYPE_OCB: 84604ecd257SJohannes Berg break; 84704ecd257SJohannes Berg default: 84804ecd257SJohannes Berg WARN_ON_ONCE(1); 84904ecd257SJohannes Berg } 85004ecd257SJohannes Berg 85104ecd257SJohannes Berg switch (sdata->smps_mode) { 85204ecd257SJohannes Berg default: 85304ecd257SJohannes Berg WARN_ONCE(1, "Invalid SMPS mode %d\n", 85404ecd257SJohannes Berg sdata->smps_mode); 855fc0561dcSGustavo A. R. Silva fallthrough; 85604ecd257SJohannes Berg case IEEE80211_SMPS_OFF: 85704ecd257SJohannes Berg needed_static = sdata->needed_rx_chains; 85804ecd257SJohannes Berg needed_dynamic = sdata->needed_rx_chains; 85904ecd257SJohannes Berg break; 86004ecd257SJohannes Berg case IEEE80211_SMPS_DYNAMIC: 86104ecd257SJohannes Berg needed_static = 1; 86204ecd257SJohannes Berg needed_dynamic = sdata->needed_rx_chains; 86304ecd257SJohannes Berg break; 86404ecd257SJohannes Berg case IEEE80211_SMPS_STATIC: 86504ecd257SJohannes Berg needed_static = 1; 86604ecd257SJohannes Berg needed_dynamic = 1; 86704ecd257SJohannes Berg break; 86804ecd257SJohannes Berg } 86904ecd257SJohannes Berg 87004ecd257SJohannes Berg rx_chains_static = max(rx_chains_static, needed_static); 87104ecd257SJohannes Berg rx_chains_dynamic = max(rx_chains_dynamic, needed_dynamic); 87204ecd257SJohannes Berg } 8737b8a9cddSIdo Yariv 8747b8a9cddSIdo Yariv /* Disable SMPS for the monitor interface */ 8757b8a9cddSIdo Yariv sdata = rcu_dereference(local->monitor_sdata); 8767b8a9cddSIdo Yariv if (sdata && 877d0a9123eSJohannes Berg rcu_access_pointer(sdata->vif.bss_conf.chanctx_conf) == &chanctx->conf) 8787b8a9cddSIdo Yariv rx_chains_dynamic = rx_chains_static = local->rx_chains; 8797b8a9cddSIdo Yariv 88004ecd257SJohannes Berg rcu_read_unlock(); 88104ecd257SJohannes Berg 88204ecd257SJohannes Berg if (!local->use_chanctx) { 88304ecd257SJohannes Berg if (rx_chains_static > 1) 88404ecd257SJohannes Berg local->smps_mode = IEEE80211_SMPS_OFF; 88504ecd257SJohannes Berg else if (rx_chains_dynamic > 1) 88604ecd257SJohannes Berg local->smps_mode = IEEE80211_SMPS_DYNAMIC; 88704ecd257SJohannes Berg else 88804ecd257SJohannes Berg local->smps_mode = IEEE80211_SMPS_STATIC; 88904ecd257SJohannes Berg ieee80211_hw_config(local, 0); 89004ecd257SJohannes Berg } 89104ecd257SJohannes Berg 89204ecd257SJohannes Berg if (rx_chains_static == chanctx->conf.rx_chains_static && 89304ecd257SJohannes Berg rx_chains_dynamic == chanctx->conf.rx_chains_dynamic) 89404ecd257SJohannes Berg return; 89504ecd257SJohannes Berg 89604ecd257SJohannes Berg chanctx->conf.rx_chains_static = rx_chains_static; 89704ecd257SJohannes Berg chanctx->conf.rx_chains_dynamic = rx_chains_dynamic; 89804ecd257SJohannes Berg drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RX_CHAINS); 89904ecd257SJohannes Berg } 90004ecd257SJohannes Berg 90111335a55SLuciano Coelho static void 90211335a55SLuciano Coelho __ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata, 90311335a55SLuciano Coelho bool clear) 90411335a55SLuciano Coelho { 90533926eb7SJohannes Berg struct ieee80211_local *local __maybe_unused = sdata->local; 90611335a55SLuciano Coelho struct ieee80211_sub_if_data *vlan; 90711335a55SLuciano Coelho struct ieee80211_chanctx_conf *conf; 90811335a55SLuciano Coelho 90911335a55SLuciano Coelho if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP)) 91011335a55SLuciano Coelho return; 91111335a55SLuciano Coelho 91211335a55SLuciano Coelho lockdep_assert_held(&local->mtx); 91311335a55SLuciano Coelho 91411335a55SLuciano Coelho /* Check that conf exists, even when clearing this function 91511335a55SLuciano Coelho * must be called with the AP's channel context still there 91611335a55SLuciano Coelho * as it would otherwise cause VLANs to have an invalid 91711335a55SLuciano Coelho * channel context pointer for a while, possibly pointing 91811335a55SLuciano Coelho * to a channel context that has already been freed. 91911335a55SLuciano Coelho */ 920d0a9123eSJohannes Berg conf = rcu_dereference_protected(sdata->vif.bss_conf.chanctx_conf, 92111335a55SLuciano Coelho lockdep_is_held(&local->chanctx_mtx)); 92211335a55SLuciano Coelho WARN_ON(!conf); 92311335a55SLuciano Coelho 92411335a55SLuciano Coelho if (clear) 92511335a55SLuciano Coelho conf = NULL; 92611335a55SLuciano Coelho 92711335a55SLuciano Coelho list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) 928d0a9123eSJohannes Berg rcu_assign_pointer(vlan->vif.bss_conf.chanctx_conf, conf); 92911335a55SLuciano Coelho } 93011335a55SLuciano Coelho 93111335a55SLuciano Coelho void ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata, 93211335a55SLuciano Coelho bool clear) 93311335a55SLuciano Coelho { 93411335a55SLuciano Coelho struct ieee80211_local *local = sdata->local; 93511335a55SLuciano Coelho 93611335a55SLuciano Coelho mutex_lock(&local->chanctx_mtx); 93711335a55SLuciano Coelho 93811335a55SLuciano Coelho __ieee80211_vif_copy_chanctx_to_vlans(sdata, clear); 93911335a55SLuciano Coelho 94011335a55SLuciano Coelho mutex_unlock(&local->chanctx_mtx); 94111335a55SLuciano Coelho } 94211335a55SLuciano Coelho 94311335a55SLuciano Coelho int ieee80211_vif_unreserve_chanctx(struct ieee80211_sub_if_data *sdata) 94411335a55SLuciano Coelho { 945e3afb920SMichal Kazior struct ieee80211_chanctx *ctx = sdata->reserved_chanctx; 946e3afb920SMichal Kazior 94711335a55SLuciano Coelho lockdep_assert_held(&sdata->local->chanctx_mtx); 94811335a55SLuciano Coelho 949e3afb920SMichal Kazior if (WARN_ON(!ctx)) 95011335a55SLuciano Coelho return -EINVAL; 95111335a55SLuciano Coelho 952e3afb920SMichal Kazior list_del(&sdata->reserved_chanctx_list); 95311335a55SLuciano Coelho sdata->reserved_chanctx = NULL; 95411335a55SLuciano Coelho 9555bcae31dSMichal Kazior if (ieee80211_chanctx_refcount(sdata->local, ctx) == 0) { 9565bcae31dSMichal Kazior if (ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER) { 9575bcae31dSMichal Kazior if (WARN_ON(!ctx->replace_ctx)) 9585bcae31dSMichal Kazior return -EINVAL; 9595bcae31dSMichal Kazior 9605bcae31dSMichal Kazior WARN_ON(ctx->replace_ctx->replace_state != 9615bcae31dSMichal Kazior IEEE80211_CHANCTX_WILL_BE_REPLACED); 9625bcae31dSMichal Kazior WARN_ON(ctx->replace_ctx->replace_ctx != ctx); 9635bcae31dSMichal Kazior 9645bcae31dSMichal Kazior ctx->replace_ctx->replace_ctx = NULL; 9655bcae31dSMichal Kazior ctx->replace_ctx->replace_state = 9665bcae31dSMichal Kazior IEEE80211_CHANCTX_REPLACE_NONE; 9675bcae31dSMichal Kazior 9685bcae31dSMichal Kazior list_del_rcu(&ctx->list); 9695bcae31dSMichal Kazior kfree_rcu(ctx, rcu_head); 9705bcae31dSMichal Kazior } else { 971e3afb920SMichal Kazior ieee80211_free_chanctx(sdata->local, ctx); 9725bcae31dSMichal Kazior } 9735bcae31dSMichal Kazior } 974e3afb920SMichal Kazior 97511335a55SLuciano Coelho return 0; 97611335a55SLuciano Coelho } 97711335a55SLuciano Coelho 97811335a55SLuciano Coelho int ieee80211_vif_reserve_chanctx(struct ieee80211_sub_if_data *sdata, 97911335a55SLuciano Coelho const struct cfg80211_chan_def *chandef, 98009332481SMichal Kazior enum ieee80211_chanctx_mode mode, 98109332481SMichal Kazior bool radar_required) 98211335a55SLuciano Coelho { 98311335a55SLuciano Coelho struct ieee80211_local *local = sdata->local; 9845bcae31dSMichal Kazior struct ieee80211_chanctx *new_ctx, *curr_ctx, *ctx; 98511335a55SLuciano Coelho 9865bcae31dSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 98711335a55SLuciano Coelho 9885bcae31dSMichal Kazior curr_ctx = ieee80211_vif_get_chanctx(sdata); 9895bcae31dSMichal Kazior if (curr_ctx && local->use_chanctx && !local->ops->switch_vif_chanctx) 9905bcae31dSMichal Kazior return -ENOTSUPP; 99111335a55SLuciano Coelho 99213f348a8SMichal Kazior new_ctx = ieee80211_find_reservation_chanctx(local, chandef, mode); 99311335a55SLuciano Coelho if (!new_ctx) { 9945bcae31dSMichal Kazior if (ieee80211_can_create_new_chanctx(local)) { 99511335a55SLuciano Coelho new_ctx = ieee80211_new_chanctx(local, chandef, mode); 9965bcae31dSMichal Kazior if (IS_ERR(new_ctx)) 9975bcae31dSMichal Kazior return PTR_ERR(new_ctx); 998c2b90ad8SMichal Kazior } else { 9995bcae31dSMichal Kazior if (!curr_ctx || 10005bcae31dSMichal Kazior (curr_ctx->replace_state == 10015bcae31dSMichal Kazior IEEE80211_CHANCTX_WILL_BE_REPLACED) || 10025bcae31dSMichal Kazior !list_empty(&curr_ctx->reserved_vifs)) { 10035bcae31dSMichal Kazior /* 10045bcae31dSMichal Kazior * Another vif already requested this context 10055bcae31dSMichal Kazior * for a reservation. Find another one hoping 10065bcae31dSMichal Kazior * all vifs assigned to it will also switch 10075bcae31dSMichal Kazior * soon enough. 10085bcae31dSMichal Kazior * 10095bcae31dSMichal Kazior * TODO: This needs a little more work as some 10105bcae31dSMichal Kazior * cases (more than 2 chanctx capable devices) 10115bcae31dSMichal Kazior * may fail which could otherwise succeed 10125bcae31dSMichal Kazior * provided some channel context juggling was 10135bcae31dSMichal Kazior * performed. 10145bcae31dSMichal Kazior * 10155bcae31dSMichal Kazior * Consider ctx1..3, vif1..6, each ctx has 2 10165bcae31dSMichal Kazior * vifs. vif1 and vif2 from ctx1 request new 10175bcae31dSMichal Kazior * different chandefs starting 2 in-place 10185bcae31dSMichal Kazior * reserations with ctx4 and ctx5 replacing 10195bcae31dSMichal Kazior * ctx1 and ctx2 respectively. Next vif5 and 10205bcae31dSMichal Kazior * vif6 from ctx3 reserve ctx4. If vif3 and 10215bcae31dSMichal Kazior * vif4 remain on ctx2 as they are then this 10225bcae31dSMichal Kazior * fails unless `replace_ctx` from ctx5 is 10235bcae31dSMichal Kazior * replaced with ctx3. 10245bcae31dSMichal Kazior */ 10255bcae31dSMichal Kazior list_for_each_entry(ctx, &local->chanctx_list, 10265bcae31dSMichal Kazior list) { 10275bcae31dSMichal Kazior if (ctx->replace_state != 10285bcae31dSMichal Kazior IEEE80211_CHANCTX_REPLACE_NONE) 10295bcae31dSMichal Kazior continue; 10305bcae31dSMichal Kazior 10315bcae31dSMichal Kazior if (!list_empty(&ctx->reserved_vifs)) 10325bcae31dSMichal Kazior continue; 10335bcae31dSMichal Kazior 10345bcae31dSMichal Kazior curr_ctx = ctx; 10355bcae31dSMichal Kazior break; 10365bcae31dSMichal Kazior } 10375bcae31dSMichal Kazior } 10385bcae31dSMichal Kazior 10395bcae31dSMichal Kazior /* 10405bcae31dSMichal Kazior * If that's true then all available contexts already 10415bcae31dSMichal Kazior * have reservations and cannot be used. 10425bcae31dSMichal Kazior */ 10435bcae31dSMichal Kazior if (!curr_ctx || 10445bcae31dSMichal Kazior (curr_ctx->replace_state == 10455bcae31dSMichal Kazior IEEE80211_CHANCTX_WILL_BE_REPLACED) || 10465bcae31dSMichal Kazior !list_empty(&curr_ctx->reserved_vifs)) 10475bcae31dSMichal Kazior return -EBUSY; 10485bcae31dSMichal Kazior 10495bcae31dSMichal Kazior new_ctx = ieee80211_alloc_chanctx(local, chandef, mode); 10505bcae31dSMichal Kazior if (!new_ctx) 10515bcae31dSMichal Kazior return -ENOMEM; 10525bcae31dSMichal Kazior 10535bcae31dSMichal Kazior new_ctx->replace_ctx = curr_ctx; 10545bcae31dSMichal Kazior new_ctx->replace_state = 10555bcae31dSMichal Kazior IEEE80211_CHANCTX_REPLACES_OTHER; 10565bcae31dSMichal Kazior 10575bcae31dSMichal Kazior curr_ctx->replace_ctx = new_ctx; 10585bcae31dSMichal Kazior curr_ctx->replace_state = 10595bcae31dSMichal Kazior IEEE80211_CHANCTX_WILL_BE_REPLACED; 10605bcae31dSMichal Kazior 10615bcae31dSMichal Kazior list_add_rcu(&new_ctx->list, &local->chanctx_list); 106211335a55SLuciano Coelho } 10635d52ee81SLuciano Coelho } 106411335a55SLuciano Coelho 1065e3afb920SMichal Kazior list_add(&sdata->reserved_chanctx_list, &new_ctx->reserved_vifs); 106611335a55SLuciano Coelho sdata->reserved_chanctx = new_ctx; 106711335a55SLuciano Coelho sdata->reserved_chandef = *chandef; 106809332481SMichal Kazior sdata->reserved_radar_required = radar_required; 10695bcae31dSMichal Kazior sdata->reserved_ready = false; 10705bcae31dSMichal Kazior 10715bcae31dSMichal Kazior return 0; 107211335a55SLuciano Coelho } 107311335a55SLuciano Coelho 107403078de4SMichal Kazior static void 107503078de4SMichal Kazior ieee80211_vif_chanctx_reservation_complete(struct ieee80211_sub_if_data *sdata) 107603078de4SMichal Kazior { 107703078de4SMichal Kazior switch (sdata->vif.type) { 107803078de4SMichal Kazior case NL80211_IFTYPE_ADHOC: 107903078de4SMichal Kazior case NL80211_IFTYPE_AP: 108003078de4SMichal Kazior case NL80211_IFTYPE_MESH_POINT: 10816e0bd6c3SRostislav Lisovy case NL80211_IFTYPE_OCB: 108203078de4SMichal Kazior ieee80211_queue_work(&sdata->local->hw, 108303078de4SMichal Kazior &sdata->csa_finalize_work); 108403078de4SMichal Kazior break; 108503078de4SMichal Kazior case NL80211_IFTYPE_STATION: 10864c3ebc56SMichal Kazior ieee80211_queue_work(&sdata->local->hw, 10874c3ebc56SMichal Kazior &sdata->u.mgd.chswitch_work); 10884c3ebc56SMichal Kazior break; 10894c3ebc56SMichal Kazior case NL80211_IFTYPE_UNSPECIFIED: 109003078de4SMichal Kazior case NL80211_IFTYPE_AP_VLAN: 109103078de4SMichal Kazior case NL80211_IFTYPE_WDS: 109203078de4SMichal Kazior case NL80211_IFTYPE_MONITOR: 109303078de4SMichal Kazior case NL80211_IFTYPE_P2P_CLIENT: 109403078de4SMichal Kazior case NL80211_IFTYPE_P2P_GO: 109503078de4SMichal Kazior case NL80211_IFTYPE_P2P_DEVICE: 1096cb3b7d87SAyala Beker case NL80211_IFTYPE_NAN: 109703078de4SMichal Kazior case NUM_NL80211_IFTYPES: 109803078de4SMichal Kazior WARN_ON(1); 109903078de4SMichal Kazior break; 110003078de4SMichal Kazior } 110103078de4SMichal Kazior } 110203078de4SMichal Kazior 11032967e031SFelix Fietkau static void 11042967e031SFelix Fietkau ieee80211_vif_update_chandef(struct ieee80211_sub_if_data *sdata, 11052967e031SFelix Fietkau const struct cfg80211_chan_def *chandef) 11062967e031SFelix Fietkau { 11072967e031SFelix Fietkau struct ieee80211_sub_if_data *vlan; 11082967e031SFelix Fietkau 11092967e031SFelix Fietkau sdata->vif.bss_conf.chandef = *chandef; 11102967e031SFelix Fietkau 11112967e031SFelix Fietkau if (sdata->vif.type != NL80211_IFTYPE_AP) 11122967e031SFelix Fietkau return; 11132967e031SFelix Fietkau 11142967e031SFelix Fietkau list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) 11152967e031SFelix Fietkau vlan->vif.bss_conf.chandef = *chandef; 11162967e031SFelix Fietkau } 11172967e031SFelix Fietkau 11185bcae31dSMichal Kazior static int 11195bcae31dSMichal Kazior ieee80211_vif_use_reserved_reassign(struct ieee80211_sub_if_data *sdata) 112011335a55SLuciano Coelho { 112111335a55SLuciano Coelho struct ieee80211_local *local = sdata->local; 11225bcae31dSMichal Kazior struct ieee80211_vif_chanctx_switch vif_chsw[1] = {}; 11235bcae31dSMichal Kazior struct ieee80211_chanctx *old_ctx, *new_ctx; 11245bcae31dSMichal Kazior const struct cfg80211_chan_def *chandef; 11255bcae31dSMichal Kazior u32 changed = 0; 11265bcae31dSMichal Kazior int err; 112711335a55SLuciano Coelho 112811335a55SLuciano Coelho lockdep_assert_held(&local->mtx); 11295bcae31dSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 113011335a55SLuciano Coelho 11315bcae31dSMichal Kazior new_ctx = sdata->reserved_chanctx; 11325bcae31dSMichal Kazior old_ctx = ieee80211_vif_get_chanctx(sdata); 113311335a55SLuciano Coelho 11345bcae31dSMichal Kazior if (WARN_ON(!sdata->reserved_ready)) 11355bcae31dSMichal Kazior return -EBUSY; 113611335a55SLuciano Coelho 11375bcae31dSMichal Kazior if (WARN_ON(!new_ctx)) 11385bcae31dSMichal Kazior return -EINVAL; 113911335a55SLuciano Coelho 11405bcae31dSMichal Kazior if (WARN_ON(!old_ctx)) 11415bcae31dSMichal Kazior return -EINVAL; 114211335a55SLuciano Coelho 11435bcae31dSMichal Kazior if (WARN_ON(new_ctx->replace_state == 11445bcae31dSMichal Kazior IEEE80211_CHANCTX_REPLACES_OTHER)) 11455bcae31dSMichal Kazior return -EINVAL; 114611335a55SLuciano Coelho 11475bcae31dSMichal Kazior chandef = ieee80211_chanctx_non_reserved_chandef(local, new_ctx, 11485bcae31dSMichal Kazior &sdata->reserved_chandef); 11495bcae31dSMichal Kazior if (WARN_ON(!chandef)) 11505bcae31dSMichal Kazior return -EINVAL; 115111335a55SLuciano Coelho 1152d6c37509SMordechay Goodstein if (sdata->vif.bss_conf.chandef.width != sdata->reserved_chandef.width) 1153d6c37509SMordechay Goodstein changed = BSS_CHANGED_BANDWIDTH; 115444b72ca8SIlan Peer 1155d6c37509SMordechay Goodstein ieee80211_vif_update_chandef(sdata, &sdata->reserved_chandef); 1156bf45a242SAndrei Otcheretianski 1157d6c37509SMordechay Goodstein ieee80211_change_chanctx(local, new_ctx, old_ctx, chandef); 115844b72ca8SIlan Peer 11595bcae31dSMichal Kazior vif_chsw[0].vif = &sdata->vif; 11605bcae31dSMichal Kazior vif_chsw[0].old_ctx = &old_ctx->conf; 11615bcae31dSMichal Kazior vif_chsw[0].new_ctx = &new_ctx->conf; 11625bcae31dSMichal Kazior 1163e3afb920SMichal Kazior list_del(&sdata->reserved_chanctx_list); 11645bcae31dSMichal Kazior sdata->reserved_chanctx = NULL; 116511335a55SLuciano Coelho 11665bcae31dSMichal Kazior err = drv_switch_vif_chanctx(local, vif_chsw, 1, 11675bcae31dSMichal Kazior CHANCTX_SWMODE_REASSIGN_VIF); 11685bcae31dSMichal Kazior if (err) { 11695bcae31dSMichal Kazior if (ieee80211_chanctx_refcount(local, new_ctx) == 0) 11705bcae31dSMichal Kazior ieee80211_free_chanctx(local, new_ctx); 11715bcae31dSMichal Kazior 117203078de4SMichal Kazior goto out; 117311335a55SLuciano Coelho } 117411335a55SLuciano Coelho 11755bcae31dSMichal Kazior list_move(&sdata->assigned_chanctx_list, &new_ctx->assigned_vifs); 1176d0a9123eSJohannes Berg rcu_assign_pointer(sdata->vif.bss_conf.chanctx_conf, &new_ctx->conf); 11775bcae31dSMichal Kazior 117811335a55SLuciano Coelho if (sdata->vif.type == NL80211_IFTYPE_AP) 117911335a55SLuciano Coelho __ieee80211_vif_copy_chanctx_to_vlans(sdata, false); 11805bcae31dSMichal Kazior 118117c18bf8SJohannes Berg ieee80211_check_fast_xmit_iface(sdata); 118217c18bf8SJohannes Berg 11835bcae31dSMichal Kazior if (ieee80211_chanctx_refcount(local, old_ctx) == 0) 11845bcae31dSMichal Kazior ieee80211_free_chanctx(local, old_ctx); 11855bcae31dSMichal Kazior 1186d6c37509SMordechay Goodstein ieee80211_recalc_chanctx_min_def(local, new_ctx); 1187722ddb0dSEmmanuel Grumbach ieee80211_recalc_smps_chanctx(local, new_ctx); 1188722ddb0dSEmmanuel Grumbach ieee80211_recalc_radar_chanctx(local, new_ctx); 1189722ddb0dSEmmanuel Grumbach 11905bcae31dSMichal Kazior if (changed) 11915bcae31dSMichal Kazior ieee80211_bss_info_change_notify(sdata, changed); 11925bcae31dSMichal Kazior 119303078de4SMichal Kazior out: 119403078de4SMichal Kazior ieee80211_vif_chanctx_reservation_complete(sdata); 11955bcae31dSMichal Kazior return err; 11965d52ee81SLuciano Coelho } 119711335a55SLuciano Coelho 11985bcae31dSMichal Kazior static int 11995bcae31dSMichal Kazior ieee80211_vif_use_reserved_assign(struct ieee80211_sub_if_data *sdata) 12005bcae31dSMichal Kazior { 12015bcae31dSMichal Kazior struct ieee80211_local *local = sdata->local; 12025bcae31dSMichal Kazior struct ieee80211_chanctx *old_ctx, *new_ctx; 12035bcae31dSMichal Kazior const struct cfg80211_chan_def *chandef; 12045bcae31dSMichal Kazior int err; 12055bcae31dSMichal Kazior 12065bcae31dSMichal Kazior old_ctx = ieee80211_vif_get_chanctx(sdata); 12075bcae31dSMichal Kazior new_ctx = sdata->reserved_chanctx; 12085bcae31dSMichal Kazior 12095bcae31dSMichal Kazior if (WARN_ON(!sdata->reserved_ready)) 12105bcae31dSMichal Kazior return -EINVAL; 12115bcae31dSMichal Kazior 12125bcae31dSMichal Kazior if (WARN_ON(old_ctx)) 12135bcae31dSMichal Kazior return -EINVAL; 12145bcae31dSMichal Kazior 12155bcae31dSMichal Kazior if (WARN_ON(!new_ctx)) 12165bcae31dSMichal Kazior return -EINVAL; 12175bcae31dSMichal Kazior 12185bcae31dSMichal Kazior if (WARN_ON(new_ctx->replace_state == 12195bcae31dSMichal Kazior IEEE80211_CHANCTX_REPLACES_OTHER)) 12205bcae31dSMichal Kazior return -EINVAL; 12215bcae31dSMichal Kazior 12225bcae31dSMichal Kazior chandef = ieee80211_chanctx_non_reserved_chandef(local, new_ctx, 12235bcae31dSMichal Kazior &sdata->reserved_chandef); 12245bcae31dSMichal Kazior if (WARN_ON(!chandef)) 12255bcae31dSMichal Kazior return -EINVAL; 12265bcae31dSMichal Kazior 1227d6c37509SMordechay Goodstein ieee80211_change_chanctx(local, new_ctx, new_ctx, chandef); 1228bf45a242SAndrei Otcheretianski 12295bcae31dSMichal Kazior list_del(&sdata->reserved_chanctx_list); 12305bcae31dSMichal Kazior sdata->reserved_chanctx = NULL; 12315bcae31dSMichal Kazior 12325bcae31dSMichal Kazior err = ieee80211_assign_vif_chanctx(sdata, new_ctx); 12335bcae31dSMichal Kazior if (err) { 12345bcae31dSMichal Kazior if (ieee80211_chanctx_refcount(local, new_ctx) == 0) 12355bcae31dSMichal Kazior ieee80211_free_chanctx(local, new_ctx); 12365bcae31dSMichal Kazior 12375bcae31dSMichal Kazior goto out; 12385bcae31dSMichal Kazior } 12395bcae31dSMichal Kazior 12405bcae31dSMichal Kazior out: 124103078de4SMichal Kazior ieee80211_vif_chanctx_reservation_complete(sdata); 12425bcae31dSMichal Kazior return err; 12435bcae31dSMichal Kazior } 12445bcae31dSMichal Kazior 12455bcae31dSMichal Kazior static bool 12465bcae31dSMichal Kazior ieee80211_vif_has_in_place_reservation(struct ieee80211_sub_if_data *sdata) 12475bcae31dSMichal Kazior { 12485bcae31dSMichal Kazior struct ieee80211_chanctx *old_ctx, *new_ctx; 12495bcae31dSMichal Kazior 12505bcae31dSMichal Kazior lockdep_assert_held(&sdata->local->chanctx_mtx); 12515bcae31dSMichal Kazior 12525bcae31dSMichal Kazior new_ctx = sdata->reserved_chanctx; 12535bcae31dSMichal Kazior old_ctx = ieee80211_vif_get_chanctx(sdata); 12545bcae31dSMichal Kazior 12555bcae31dSMichal Kazior if (!old_ctx) 12565bcae31dSMichal Kazior return false; 12575bcae31dSMichal Kazior 12585bcae31dSMichal Kazior if (WARN_ON(!new_ctx)) 12595bcae31dSMichal Kazior return false; 12605bcae31dSMichal Kazior 12615bcae31dSMichal Kazior if (old_ctx->replace_state != IEEE80211_CHANCTX_WILL_BE_REPLACED) 12625bcae31dSMichal Kazior return false; 12635bcae31dSMichal Kazior 12645bcae31dSMichal Kazior if (new_ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) 12655bcae31dSMichal Kazior return false; 12665bcae31dSMichal Kazior 12675bcae31dSMichal Kazior return true; 12685bcae31dSMichal Kazior } 12695bcae31dSMichal Kazior 12705bcae31dSMichal Kazior static int ieee80211_chsw_switch_hwconf(struct ieee80211_local *local, 12715bcae31dSMichal Kazior struct ieee80211_chanctx *new_ctx) 12725bcae31dSMichal Kazior { 12735bcae31dSMichal Kazior const struct cfg80211_chan_def *chandef; 12745bcae31dSMichal Kazior 12755bcae31dSMichal Kazior lockdep_assert_held(&local->mtx); 12765bcae31dSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 12775bcae31dSMichal Kazior 12785bcae31dSMichal Kazior chandef = ieee80211_chanctx_reserved_chandef(local, new_ctx, NULL); 12795bcae31dSMichal Kazior if (WARN_ON(!chandef)) 12805bcae31dSMichal Kazior return -EINVAL; 12815bcae31dSMichal Kazior 12825bcae31dSMichal Kazior local->hw.conf.radar_enabled = new_ctx->conf.radar_enabled; 12835bcae31dSMichal Kazior local->_oper_chandef = *chandef; 12845bcae31dSMichal Kazior ieee80211_hw_config(local, 0); 12855bcae31dSMichal Kazior 12865bcae31dSMichal Kazior return 0; 12875bcae31dSMichal Kazior } 12885bcae31dSMichal Kazior 12895bcae31dSMichal Kazior static int ieee80211_chsw_switch_vifs(struct ieee80211_local *local, 12905bcae31dSMichal Kazior int n_vifs) 12915bcae31dSMichal Kazior { 12925bcae31dSMichal Kazior struct ieee80211_vif_chanctx_switch *vif_chsw; 12935bcae31dSMichal Kazior struct ieee80211_sub_if_data *sdata; 12945bcae31dSMichal Kazior struct ieee80211_chanctx *ctx, *old_ctx; 12955bcae31dSMichal Kazior int i, err; 12965bcae31dSMichal Kazior 12975bcae31dSMichal Kazior lockdep_assert_held(&local->mtx); 12985bcae31dSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 12995bcae31dSMichal Kazior 13006396bb22SKees Cook vif_chsw = kcalloc(n_vifs, sizeof(vif_chsw[0]), GFP_KERNEL); 13015bcae31dSMichal Kazior if (!vif_chsw) 13025bcae31dSMichal Kazior return -ENOMEM; 13035bcae31dSMichal Kazior 13045bcae31dSMichal Kazior i = 0; 13055bcae31dSMichal Kazior list_for_each_entry(ctx, &local->chanctx_list, list) { 13065bcae31dSMichal Kazior if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) 13075bcae31dSMichal Kazior continue; 13085bcae31dSMichal Kazior 13095bcae31dSMichal Kazior if (WARN_ON(!ctx->replace_ctx)) { 13105bcae31dSMichal Kazior err = -EINVAL; 13115bcae31dSMichal Kazior goto out; 13125bcae31dSMichal Kazior } 13135bcae31dSMichal Kazior 13145bcae31dSMichal Kazior list_for_each_entry(sdata, &ctx->reserved_vifs, 13155bcae31dSMichal Kazior reserved_chanctx_list) { 13165bcae31dSMichal Kazior if (!ieee80211_vif_has_in_place_reservation( 13175bcae31dSMichal Kazior sdata)) 13185bcae31dSMichal Kazior continue; 13195bcae31dSMichal Kazior 13205bcae31dSMichal Kazior old_ctx = ieee80211_vif_get_chanctx(sdata); 13215bcae31dSMichal Kazior vif_chsw[i].vif = &sdata->vif; 13225bcae31dSMichal Kazior vif_chsw[i].old_ctx = &old_ctx->conf; 13235bcae31dSMichal Kazior vif_chsw[i].new_ctx = &ctx->conf; 13245bcae31dSMichal Kazior 13255bcae31dSMichal Kazior i++; 13265bcae31dSMichal Kazior } 13275bcae31dSMichal Kazior } 13285bcae31dSMichal Kazior 13295bcae31dSMichal Kazior err = drv_switch_vif_chanctx(local, vif_chsw, n_vifs, 13305bcae31dSMichal Kazior CHANCTX_SWMODE_SWAP_CONTEXTS); 13315bcae31dSMichal Kazior 13325bcae31dSMichal Kazior out: 13335bcae31dSMichal Kazior kfree(vif_chsw); 13345bcae31dSMichal Kazior return err; 13355bcae31dSMichal Kazior } 13365bcae31dSMichal Kazior 13375bcae31dSMichal Kazior static int ieee80211_chsw_switch_ctxs(struct ieee80211_local *local) 13385bcae31dSMichal Kazior { 13395bcae31dSMichal Kazior struct ieee80211_chanctx *ctx; 13405bcae31dSMichal Kazior int err; 13415bcae31dSMichal Kazior 13425bcae31dSMichal Kazior lockdep_assert_held(&local->mtx); 13435bcae31dSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 13445bcae31dSMichal Kazior 13455bcae31dSMichal Kazior list_for_each_entry(ctx, &local->chanctx_list, list) { 13465bcae31dSMichal Kazior if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) 13475bcae31dSMichal Kazior continue; 13485bcae31dSMichal Kazior 13495bcae31dSMichal Kazior if (!list_empty(&ctx->replace_ctx->assigned_vifs)) 13505bcae31dSMichal Kazior continue; 13515bcae31dSMichal Kazior 13525bcae31dSMichal Kazior ieee80211_del_chanctx(local, ctx->replace_ctx); 13535bcae31dSMichal Kazior err = ieee80211_add_chanctx(local, ctx); 13545bcae31dSMichal Kazior if (err) 13555bcae31dSMichal Kazior goto err; 13565bcae31dSMichal Kazior } 13575bcae31dSMichal Kazior 13585bcae31dSMichal Kazior return 0; 13595bcae31dSMichal Kazior 13605bcae31dSMichal Kazior err: 13615bcae31dSMichal Kazior WARN_ON(ieee80211_add_chanctx(local, ctx)); 13625bcae31dSMichal Kazior list_for_each_entry_continue_reverse(ctx, &local->chanctx_list, list) { 13635bcae31dSMichal Kazior if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) 13645bcae31dSMichal Kazior continue; 13655bcae31dSMichal Kazior 13665bcae31dSMichal Kazior if (!list_empty(&ctx->replace_ctx->assigned_vifs)) 13675bcae31dSMichal Kazior continue; 13685bcae31dSMichal Kazior 13695bcae31dSMichal Kazior ieee80211_del_chanctx(local, ctx); 13705bcae31dSMichal Kazior WARN_ON(ieee80211_add_chanctx(local, ctx->replace_ctx)); 13715bcae31dSMichal Kazior } 13725bcae31dSMichal Kazior 13735bcae31dSMichal Kazior return err; 13745bcae31dSMichal Kazior } 13755bcae31dSMichal Kazior 1376649b2a4dSJohannes Berg static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local) 13775bcae31dSMichal Kazior { 13785bcae31dSMichal Kazior struct ieee80211_sub_if_data *sdata, *sdata_tmp; 13795bcae31dSMichal Kazior struct ieee80211_chanctx *ctx, *ctx_tmp, *old_ctx; 13805bcae31dSMichal Kazior struct ieee80211_chanctx *new_ctx = NULL; 1381b7f2405cSKirtika Ruchandani int err, n_assigned, n_reserved, n_ready; 13825bcae31dSMichal Kazior int n_ctx = 0, n_vifs_switch = 0, n_vifs_assign = 0, n_vifs_ctxless = 0; 13835bcae31dSMichal Kazior 13845bcae31dSMichal Kazior lockdep_assert_held(&local->mtx); 13855bcae31dSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 13865bcae31dSMichal Kazior 13875bcae31dSMichal Kazior /* 13885bcae31dSMichal Kazior * If there are 2 independent pairs of channel contexts performing 13895bcae31dSMichal Kazior * cross-switch of their vifs this code will still wait until both are 13905bcae31dSMichal Kazior * ready even though it could be possible to switch one before the 13915bcae31dSMichal Kazior * other is ready. 13925bcae31dSMichal Kazior * 13935bcae31dSMichal Kazior * For practical reasons and code simplicity just do a single huge 13945bcae31dSMichal Kazior * switch. 13955bcae31dSMichal Kazior */ 13965bcae31dSMichal Kazior 13975bcae31dSMichal Kazior /* 13985bcae31dSMichal Kazior * Verify if the reservation is still feasible. 13995bcae31dSMichal Kazior * - if it's not then disconnect 14005bcae31dSMichal Kazior * - if it is but not all vifs necessary are ready then defer 14015bcae31dSMichal Kazior */ 14025bcae31dSMichal Kazior 14035bcae31dSMichal Kazior list_for_each_entry(ctx, &local->chanctx_list, list) { 14045bcae31dSMichal Kazior if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) 14055bcae31dSMichal Kazior continue; 14065bcae31dSMichal Kazior 14075bcae31dSMichal Kazior if (WARN_ON(!ctx->replace_ctx)) { 14085bcae31dSMichal Kazior err = -EINVAL; 14095bcae31dSMichal Kazior goto err; 14105bcae31dSMichal Kazior } 14115bcae31dSMichal Kazior 14125bcae31dSMichal Kazior if (!local->use_chanctx) 14135bcae31dSMichal Kazior new_ctx = ctx; 14145bcae31dSMichal Kazior 14155bcae31dSMichal Kazior n_ctx++; 14165bcae31dSMichal Kazior 14175bcae31dSMichal Kazior n_assigned = 0; 14185bcae31dSMichal Kazior n_reserved = 0; 14195bcae31dSMichal Kazior n_ready = 0; 14205bcae31dSMichal Kazior 14215bcae31dSMichal Kazior list_for_each_entry(sdata, &ctx->replace_ctx->assigned_vifs, 14225bcae31dSMichal Kazior assigned_chanctx_list) { 14235bcae31dSMichal Kazior n_assigned++; 14245bcae31dSMichal Kazior if (sdata->reserved_chanctx) { 14255bcae31dSMichal Kazior n_reserved++; 14265bcae31dSMichal Kazior if (sdata->reserved_ready) 14275bcae31dSMichal Kazior n_ready++; 14285bcae31dSMichal Kazior } 14295bcae31dSMichal Kazior } 14305bcae31dSMichal Kazior 14315bcae31dSMichal Kazior if (n_assigned != n_reserved) { 14325bcae31dSMichal Kazior if (n_ready == n_reserved) { 14335bcae31dSMichal Kazior wiphy_info(local->hw.wiphy, 14345bcae31dSMichal Kazior "channel context reservation cannot be finalized because some interfaces aren't switching\n"); 14355bcae31dSMichal Kazior err = -EBUSY; 14365bcae31dSMichal Kazior goto err; 14375bcae31dSMichal Kazior } 14385bcae31dSMichal Kazior 14395bcae31dSMichal Kazior return -EAGAIN; 14405bcae31dSMichal Kazior } 14415bcae31dSMichal Kazior 14425bcae31dSMichal Kazior ctx->conf.radar_enabled = false; 14435bcae31dSMichal Kazior list_for_each_entry(sdata, &ctx->reserved_vifs, 14445bcae31dSMichal Kazior reserved_chanctx_list) { 14455bcae31dSMichal Kazior if (ieee80211_vif_has_in_place_reservation(sdata) && 14465bcae31dSMichal Kazior !sdata->reserved_ready) 14475bcae31dSMichal Kazior return -EAGAIN; 14485bcae31dSMichal Kazior 14495bcae31dSMichal Kazior old_ctx = ieee80211_vif_get_chanctx(sdata); 14505bcae31dSMichal Kazior if (old_ctx) { 14515bcae31dSMichal Kazior if (old_ctx->replace_state == 14525bcae31dSMichal Kazior IEEE80211_CHANCTX_WILL_BE_REPLACED) 14535bcae31dSMichal Kazior n_vifs_switch++; 14545bcae31dSMichal Kazior else 14555bcae31dSMichal Kazior n_vifs_assign++; 14565bcae31dSMichal Kazior } else { 14575bcae31dSMichal Kazior n_vifs_ctxless++; 14585bcae31dSMichal Kazior } 14595bcae31dSMichal Kazior 14605bcae31dSMichal Kazior if (sdata->reserved_radar_required) 14615bcae31dSMichal Kazior ctx->conf.radar_enabled = true; 14625bcae31dSMichal Kazior } 14635bcae31dSMichal Kazior } 14645bcae31dSMichal Kazior 14655bcae31dSMichal Kazior if (WARN_ON(n_ctx == 0) || 14665bcae31dSMichal Kazior WARN_ON(n_vifs_switch == 0 && 14675bcae31dSMichal Kazior n_vifs_assign == 0 && 14685bcae31dSMichal Kazior n_vifs_ctxless == 0) || 14695bcae31dSMichal Kazior WARN_ON(n_ctx > 1 && !local->use_chanctx) || 14705bcae31dSMichal Kazior WARN_ON(!new_ctx && !local->use_chanctx)) { 14715bcae31dSMichal Kazior err = -EINVAL; 14725bcae31dSMichal Kazior goto err; 14735bcae31dSMichal Kazior } 14745bcae31dSMichal Kazior 14755bcae31dSMichal Kazior /* 14765bcae31dSMichal Kazior * All necessary vifs are ready. Perform the switch now depending on 14775bcae31dSMichal Kazior * reservations and driver capabilities. 14785bcae31dSMichal Kazior */ 14795bcae31dSMichal Kazior 14805bcae31dSMichal Kazior if (local->use_chanctx) { 14815bcae31dSMichal Kazior if (n_vifs_switch > 0) { 14825bcae31dSMichal Kazior err = ieee80211_chsw_switch_vifs(local, n_vifs_switch); 14835bcae31dSMichal Kazior if (err) 14845bcae31dSMichal Kazior goto err; 14855bcae31dSMichal Kazior } 14865bcae31dSMichal Kazior 14875bcae31dSMichal Kazior if (n_vifs_assign > 0 || n_vifs_ctxless > 0) { 14885bcae31dSMichal Kazior err = ieee80211_chsw_switch_ctxs(local); 14895bcae31dSMichal Kazior if (err) 14905bcae31dSMichal Kazior goto err; 14915bcae31dSMichal Kazior } 14925bcae31dSMichal Kazior } else { 14935bcae31dSMichal Kazior err = ieee80211_chsw_switch_hwconf(local, new_ctx); 14945bcae31dSMichal Kazior if (err) 14955bcae31dSMichal Kazior goto err; 14965bcae31dSMichal Kazior } 14975bcae31dSMichal Kazior 14985bcae31dSMichal Kazior /* 14995bcae31dSMichal Kazior * Update all structures, values and pointers to point to new channel 15005bcae31dSMichal Kazior * context(s). 15015bcae31dSMichal Kazior */ 15025bcae31dSMichal Kazior list_for_each_entry(ctx, &local->chanctx_list, list) { 15035bcae31dSMichal Kazior if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) 15045bcae31dSMichal Kazior continue; 15055bcae31dSMichal Kazior 15065bcae31dSMichal Kazior if (WARN_ON(!ctx->replace_ctx)) { 15075bcae31dSMichal Kazior err = -EINVAL; 15085bcae31dSMichal Kazior goto err; 15095bcae31dSMichal Kazior } 15105bcae31dSMichal Kazior 15115bcae31dSMichal Kazior list_for_each_entry(sdata, &ctx->reserved_vifs, 15125bcae31dSMichal Kazior reserved_chanctx_list) { 15135bcae31dSMichal Kazior u32 changed = 0; 15145bcae31dSMichal Kazior 15155bcae31dSMichal Kazior if (!ieee80211_vif_has_in_place_reservation(sdata)) 15165bcae31dSMichal Kazior continue; 15175bcae31dSMichal Kazior 1518d0a9123eSJohannes Berg rcu_assign_pointer(sdata->vif.bss_conf.chanctx_conf, 1519d0a9123eSJohannes Berg &ctx->conf); 15205bcae31dSMichal Kazior 15215bcae31dSMichal Kazior if (sdata->vif.type == NL80211_IFTYPE_AP) 15225bcae31dSMichal Kazior __ieee80211_vif_copy_chanctx_to_vlans(sdata, 15235bcae31dSMichal Kazior false); 15245bcae31dSMichal Kazior 152517c18bf8SJohannes Berg ieee80211_check_fast_xmit_iface(sdata); 152617c18bf8SJohannes Berg 15275bcae31dSMichal Kazior sdata->radar_required = sdata->reserved_radar_required; 15285bcae31dSMichal Kazior 15295bcae31dSMichal Kazior if (sdata->vif.bss_conf.chandef.width != 15305bcae31dSMichal Kazior sdata->reserved_chandef.width) 15315bcae31dSMichal Kazior changed = BSS_CHANGED_BANDWIDTH; 15325bcae31dSMichal Kazior 15332967e031SFelix Fietkau ieee80211_vif_update_chandef(sdata, &sdata->reserved_chandef); 15345bcae31dSMichal Kazior if (changed) 15355bcae31dSMichal Kazior ieee80211_bss_info_change_notify(sdata, 15365bcae31dSMichal Kazior changed); 15375bcae31dSMichal Kazior 1538db82d8a9SLorenzo Bianconi ieee80211_recalc_txpower(sdata, false); 15395bcae31dSMichal Kazior } 154011335a55SLuciano Coelho 154111335a55SLuciano Coelho ieee80211_recalc_chanctx_chantype(local, ctx); 154211335a55SLuciano Coelho ieee80211_recalc_smps_chanctx(local, ctx); 154311335a55SLuciano Coelho ieee80211_recalc_radar_chanctx(local, ctx); 154411335a55SLuciano Coelho ieee80211_recalc_chanctx_min_def(local, ctx); 15455bcae31dSMichal Kazior 15465bcae31dSMichal Kazior list_for_each_entry_safe(sdata, sdata_tmp, &ctx->reserved_vifs, 15475bcae31dSMichal Kazior reserved_chanctx_list) { 15485bcae31dSMichal Kazior if (ieee80211_vif_get_chanctx(sdata) != ctx) 15495bcae31dSMichal Kazior continue; 15505bcae31dSMichal Kazior 15515bcae31dSMichal Kazior list_del(&sdata->reserved_chanctx_list); 15525bcae31dSMichal Kazior list_move(&sdata->assigned_chanctx_list, 155347e4df94SMichal Kazior &ctx->assigned_vifs); 15545bcae31dSMichal Kazior sdata->reserved_chanctx = NULL; 155503078de4SMichal Kazior 155603078de4SMichal Kazior ieee80211_vif_chanctx_reservation_complete(sdata); 15575bcae31dSMichal Kazior } 15585bcae31dSMichal Kazior 15595bcae31dSMichal Kazior /* 15605bcae31dSMichal Kazior * This context might have been a dependency for an already 15615bcae31dSMichal Kazior * ready re-assign reservation interface that was deferred. Do 15625bcae31dSMichal Kazior * not propagate error to the caller though. The in-place 15635bcae31dSMichal Kazior * reservation for originally requested interface has already 15645bcae31dSMichal Kazior * succeeded at this point. 15655bcae31dSMichal Kazior */ 15665bcae31dSMichal Kazior list_for_each_entry_safe(sdata, sdata_tmp, &ctx->reserved_vifs, 15675bcae31dSMichal Kazior reserved_chanctx_list) { 15685bcae31dSMichal Kazior if (WARN_ON(ieee80211_vif_has_in_place_reservation( 15695bcae31dSMichal Kazior sdata))) 15705bcae31dSMichal Kazior continue; 15715bcae31dSMichal Kazior 15725bcae31dSMichal Kazior if (WARN_ON(sdata->reserved_chanctx != ctx)) 15735bcae31dSMichal Kazior continue; 15745bcae31dSMichal Kazior 15755bcae31dSMichal Kazior if (!sdata->reserved_ready) 15765bcae31dSMichal Kazior continue; 15775bcae31dSMichal Kazior 15785bcae31dSMichal Kazior if (ieee80211_vif_get_chanctx(sdata)) 15795bcae31dSMichal Kazior err = ieee80211_vif_use_reserved_reassign( 15805bcae31dSMichal Kazior sdata); 15815bcae31dSMichal Kazior else 15825bcae31dSMichal Kazior err = ieee80211_vif_use_reserved_assign(sdata); 15835bcae31dSMichal Kazior 15845bcae31dSMichal Kazior if (err) { 15855bcae31dSMichal Kazior sdata_info(sdata, 15865bcae31dSMichal Kazior "failed to finalize (re-)assign reservation (err=%d)\n", 15875bcae31dSMichal Kazior err); 15885bcae31dSMichal Kazior ieee80211_vif_unreserve_chanctx(sdata); 15895bcae31dSMichal Kazior cfg80211_stop_iface(local->hw.wiphy, 15905bcae31dSMichal Kazior &sdata->wdev, 15915bcae31dSMichal Kazior GFP_KERNEL); 15925bcae31dSMichal Kazior } 15935bcae31dSMichal Kazior } 15945bcae31dSMichal Kazior } 15955bcae31dSMichal Kazior 15965bcae31dSMichal Kazior /* 15975bcae31dSMichal Kazior * Finally free old contexts 15985bcae31dSMichal Kazior */ 15995bcae31dSMichal Kazior 16005bcae31dSMichal Kazior list_for_each_entry_safe(ctx, ctx_tmp, &local->chanctx_list, list) { 16015bcae31dSMichal Kazior if (ctx->replace_state != IEEE80211_CHANCTX_WILL_BE_REPLACED) 16025bcae31dSMichal Kazior continue; 16035bcae31dSMichal Kazior 16045bcae31dSMichal Kazior ctx->replace_ctx->replace_ctx = NULL; 16055bcae31dSMichal Kazior ctx->replace_ctx->replace_state = 16065bcae31dSMichal Kazior IEEE80211_CHANCTX_REPLACE_NONE; 16075bcae31dSMichal Kazior 16085bcae31dSMichal Kazior list_del_rcu(&ctx->list); 16095bcae31dSMichal Kazior kfree_rcu(ctx, rcu_head); 16105bcae31dSMichal Kazior } 16115bcae31dSMichal Kazior 16125bcae31dSMichal Kazior return 0; 16135bcae31dSMichal Kazior 16145bcae31dSMichal Kazior err: 16155bcae31dSMichal Kazior list_for_each_entry(ctx, &local->chanctx_list, list) { 16165bcae31dSMichal Kazior if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) 16175bcae31dSMichal Kazior continue; 16185bcae31dSMichal Kazior 16195bcae31dSMichal Kazior list_for_each_entry_safe(sdata, sdata_tmp, &ctx->reserved_vifs, 162003078de4SMichal Kazior reserved_chanctx_list) { 16215bcae31dSMichal Kazior ieee80211_vif_unreserve_chanctx(sdata); 162203078de4SMichal Kazior ieee80211_vif_chanctx_reservation_complete(sdata); 162303078de4SMichal Kazior } 16245bcae31dSMichal Kazior } 16255bcae31dSMichal Kazior 16265bcae31dSMichal Kazior return err; 16275bcae31dSMichal Kazior } 16285bcae31dSMichal Kazior 1629649b2a4dSJohannes Berg static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata) 1630649b2a4dSJohannes Berg { 1631649b2a4dSJohannes Berg struct ieee80211_local *local = sdata->local; 1632649b2a4dSJohannes Berg struct ieee80211_chanctx_conf *conf; 1633649b2a4dSJohannes Berg struct ieee80211_chanctx *ctx; 1634649b2a4dSJohannes Berg bool use_reserved_switch = false; 1635649b2a4dSJohannes Berg 1636649b2a4dSJohannes Berg lockdep_assert_held(&local->chanctx_mtx); 1637649b2a4dSJohannes Berg 1638d0a9123eSJohannes Berg conf = rcu_dereference_protected(sdata->vif.bss_conf.chanctx_conf, 1639649b2a4dSJohannes Berg lockdep_is_held(&local->chanctx_mtx)); 1640649b2a4dSJohannes Berg if (!conf) 1641649b2a4dSJohannes Berg return; 1642649b2a4dSJohannes Berg 1643649b2a4dSJohannes Berg ctx = container_of(conf, struct ieee80211_chanctx, conf); 1644649b2a4dSJohannes Berg 1645649b2a4dSJohannes Berg if (sdata->reserved_chanctx) { 1646649b2a4dSJohannes Berg if (sdata->reserved_chanctx->replace_state == 1647649b2a4dSJohannes Berg IEEE80211_CHANCTX_REPLACES_OTHER && 1648649b2a4dSJohannes Berg ieee80211_chanctx_num_reserved(local, 1649649b2a4dSJohannes Berg sdata->reserved_chanctx) > 1) 1650649b2a4dSJohannes Berg use_reserved_switch = true; 1651649b2a4dSJohannes Berg 1652649b2a4dSJohannes Berg ieee80211_vif_unreserve_chanctx(sdata); 1653649b2a4dSJohannes Berg } 1654649b2a4dSJohannes Berg 1655649b2a4dSJohannes Berg ieee80211_assign_vif_chanctx(sdata, NULL); 1656649b2a4dSJohannes Berg if (ieee80211_chanctx_refcount(local, ctx) == 0) 1657649b2a4dSJohannes Berg ieee80211_free_chanctx(local, ctx); 1658649b2a4dSJohannes Berg 1659104f5a62SEliad Peller sdata->radar_required = false; 1660104f5a62SEliad Peller 1661649b2a4dSJohannes Berg /* Unreserving may ready an in-place reservation. */ 1662649b2a4dSJohannes Berg if (use_reserved_switch) 1663649b2a4dSJohannes Berg ieee80211_vif_use_reserved_switch(local); 1664649b2a4dSJohannes Berg } 1665649b2a4dSJohannes Berg 1666649b2a4dSJohannes Berg int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata, 1667649b2a4dSJohannes Berg const struct cfg80211_chan_def *chandef, 1668649b2a4dSJohannes Berg enum ieee80211_chanctx_mode mode) 1669649b2a4dSJohannes Berg { 1670649b2a4dSJohannes Berg struct ieee80211_local *local = sdata->local; 1671649b2a4dSJohannes Berg struct ieee80211_chanctx *ctx; 1672649b2a4dSJohannes Berg u8 radar_detect_width = 0; 1673649b2a4dSJohannes Berg int ret; 1674649b2a4dSJohannes Berg 1675649b2a4dSJohannes Berg lockdep_assert_held(&local->mtx); 1676649b2a4dSJohannes Berg 1677649b2a4dSJohannes Berg WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev)); 1678649b2a4dSJohannes Berg 1679649b2a4dSJohannes Berg mutex_lock(&local->chanctx_mtx); 1680649b2a4dSJohannes Berg 1681649b2a4dSJohannes Berg ret = cfg80211_chandef_dfs_required(local->hw.wiphy, 1682649b2a4dSJohannes Berg chandef, 1683649b2a4dSJohannes Berg sdata->wdev.iftype); 1684649b2a4dSJohannes Berg if (ret < 0) 1685649b2a4dSJohannes Berg goto out; 1686649b2a4dSJohannes Berg if (ret > 0) 1687649b2a4dSJohannes Berg radar_detect_width = BIT(chandef->width); 1688649b2a4dSJohannes Berg 1689649b2a4dSJohannes Berg sdata->radar_required = ret; 1690649b2a4dSJohannes Berg 1691649b2a4dSJohannes Berg ret = ieee80211_check_combinations(sdata, chandef, mode, 1692649b2a4dSJohannes Berg radar_detect_width); 1693649b2a4dSJohannes Berg if (ret < 0) 1694649b2a4dSJohannes Berg goto out; 1695649b2a4dSJohannes Berg 1696649b2a4dSJohannes Berg __ieee80211_vif_release_channel(sdata); 1697649b2a4dSJohannes Berg 1698649b2a4dSJohannes Berg ctx = ieee80211_find_chanctx(local, chandef, mode); 1699649b2a4dSJohannes Berg if (!ctx) 1700649b2a4dSJohannes Berg ctx = ieee80211_new_chanctx(local, chandef, mode); 1701649b2a4dSJohannes Berg if (IS_ERR(ctx)) { 1702649b2a4dSJohannes Berg ret = PTR_ERR(ctx); 1703649b2a4dSJohannes Berg goto out; 1704649b2a4dSJohannes Berg } 1705649b2a4dSJohannes Berg 17062967e031SFelix Fietkau ieee80211_vif_update_chandef(sdata, chandef); 1707649b2a4dSJohannes Berg 1708649b2a4dSJohannes Berg ret = ieee80211_assign_vif_chanctx(sdata, ctx); 1709649b2a4dSJohannes Berg if (ret) { 1710649b2a4dSJohannes Berg /* if assign fails refcount stays the same */ 1711649b2a4dSJohannes Berg if (ieee80211_chanctx_refcount(local, ctx) == 0) 1712649b2a4dSJohannes Berg ieee80211_free_chanctx(local, ctx); 1713649b2a4dSJohannes Berg goto out; 1714649b2a4dSJohannes Berg } 1715649b2a4dSJohannes Berg 1716649b2a4dSJohannes Berg ieee80211_recalc_smps_chanctx(local, ctx); 1717649b2a4dSJohannes Berg ieee80211_recalc_radar_chanctx(local, ctx); 1718649b2a4dSJohannes Berg out: 1719104f5a62SEliad Peller if (ret) 1720104f5a62SEliad Peller sdata->radar_required = false; 1721104f5a62SEliad Peller 1722649b2a4dSJohannes Berg mutex_unlock(&local->chanctx_mtx); 1723649b2a4dSJohannes Berg return ret; 1724649b2a4dSJohannes Berg } 1725649b2a4dSJohannes Berg 17265bcae31dSMichal Kazior int ieee80211_vif_use_reserved_context(struct ieee80211_sub_if_data *sdata) 17275bcae31dSMichal Kazior { 17285bcae31dSMichal Kazior struct ieee80211_local *local = sdata->local; 17295bcae31dSMichal Kazior struct ieee80211_chanctx *new_ctx; 17305bcae31dSMichal Kazior struct ieee80211_chanctx *old_ctx; 17315bcae31dSMichal Kazior int err; 17325bcae31dSMichal Kazior 17335bcae31dSMichal Kazior lockdep_assert_held(&local->mtx); 17345bcae31dSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 17355bcae31dSMichal Kazior 17365bcae31dSMichal Kazior new_ctx = sdata->reserved_chanctx; 17375bcae31dSMichal Kazior old_ctx = ieee80211_vif_get_chanctx(sdata); 17385bcae31dSMichal Kazior 17395bcae31dSMichal Kazior if (WARN_ON(!new_ctx)) 17405bcae31dSMichal Kazior return -EINVAL; 17415bcae31dSMichal Kazior 17425bcae31dSMichal Kazior if (WARN_ON(new_ctx->replace_state == 17435bcae31dSMichal Kazior IEEE80211_CHANCTX_WILL_BE_REPLACED)) 17445bcae31dSMichal Kazior return -EINVAL; 17455bcae31dSMichal Kazior 17465bcae31dSMichal Kazior if (WARN_ON(sdata->reserved_ready)) 17475bcae31dSMichal Kazior return -EINVAL; 17485bcae31dSMichal Kazior 17495bcae31dSMichal Kazior sdata->reserved_ready = true; 17505bcae31dSMichal Kazior 17515bcae31dSMichal Kazior if (new_ctx->replace_state == IEEE80211_CHANCTX_REPLACE_NONE) { 17525bcae31dSMichal Kazior if (old_ctx) 17532965c4cdSJohannes Berg return ieee80211_vif_use_reserved_reassign(sdata); 17545bcae31dSMichal Kazior 17552965c4cdSJohannes Berg return ieee80211_vif_use_reserved_assign(sdata); 17565bcae31dSMichal Kazior } 17575bcae31dSMichal Kazior 17585bcae31dSMichal Kazior /* 17595bcae31dSMichal Kazior * In-place reservation may need to be finalized now either if: 17605bcae31dSMichal Kazior * a) sdata is taking part in the swapping itself and is the last one 17615bcae31dSMichal Kazior * b) sdata has switched with a re-assign reservation to an existing 17625bcae31dSMichal Kazior * context readying in-place switching of old_ctx 17635bcae31dSMichal Kazior * 17645bcae31dSMichal Kazior * In case of (b) do not propagate the error up because the requested 17655bcae31dSMichal Kazior * sdata already switched successfully. Just spill an extra warning. 17665bcae31dSMichal Kazior * The ieee80211_vif_use_reserved_switch() already stops all necessary 17675bcae31dSMichal Kazior * interfaces upon failure. 17685bcae31dSMichal Kazior */ 17695bcae31dSMichal Kazior if ((old_ctx && 17705bcae31dSMichal Kazior old_ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED) || 17715bcae31dSMichal Kazior new_ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER) { 17725bcae31dSMichal Kazior err = ieee80211_vif_use_reserved_switch(local); 17735bcae31dSMichal Kazior if (err && err != -EAGAIN) { 17745bcae31dSMichal Kazior if (new_ctx->replace_state == 17755bcae31dSMichal Kazior IEEE80211_CHANCTX_REPLACES_OTHER) 17765bcae31dSMichal Kazior return err; 17775bcae31dSMichal Kazior 17785bcae31dSMichal Kazior wiphy_info(local->hw.wiphy, 17795bcae31dSMichal Kazior "depending in-place reservation failed (err=%d)\n", 17805bcae31dSMichal Kazior err); 17815bcae31dSMichal Kazior } 17825bcae31dSMichal Kazior } 17835bcae31dSMichal Kazior 17845bcae31dSMichal Kazior return 0; 178573da7d5bSSimon Wunderlich } 178673da7d5bSSimon Wunderlich 17872c9b7359SJohannes Berg int ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata, 17882c9b7359SJohannes Berg const struct cfg80211_chan_def *chandef, 17892c9b7359SJohannes Berg u32 *changed) 17902c9b7359SJohannes Berg { 17912c9b7359SJohannes Berg struct ieee80211_local *local = sdata->local; 17922c9b7359SJohannes Berg struct ieee80211_chanctx_conf *conf; 17932c9b7359SJohannes Berg struct ieee80211_chanctx *ctx; 17945bcae31dSMichal Kazior const struct cfg80211_chan_def *compat; 17952c9b7359SJohannes Berg int ret; 17962c9b7359SJohannes Berg 17972c9b7359SJohannes Berg if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef, 17982c9b7359SJohannes Berg IEEE80211_CHAN_DISABLED)) 17992c9b7359SJohannes Berg return -EINVAL; 18002c9b7359SJohannes Berg 18012c9b7359SJohannes Berg mutex_lock(&local->chanctx_mtx); 18022c9b7359SJohannes Berg if (cfg80211_chandef_identical(chandef, &sdata->vif.bss_conf.chandef)) { 18032c9b7359SJohannes Berg ret = 0; 18042c9b7359SJohannes Berg goto out; 18052c9b7359SJohannes Berg } 18062c9b7359SJohannes Berg 18072c9b7359SJohannes Berg if (chandef->width == NL80211_CHAN_WIDTH_20_NOHT || 18082c9b7359SJohannes Berg sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT) { 18092c9b7359SJohannes Berg ret = -EINVAL; 18102c9b7359SJohannes Berg goto out; 18112c9b7359SJohannes Berg } 18122c9b7359SJohannes Berg 1813d0a9123eSJohannes Berg conf = rcu_dereference_protected(sdata->vif.bss_conf.chanctx_conf, 18142c9b7359SJohannes Berg lockdep_is_held(&local->chanctx_mtx)); 18152c9b7359SJohannes Berg if (!conf) { 18162c9b7359SJohannes Berg ret = -EINVAL; 18172c9b7359SJohannes Berg goto out; 18182c9b7359SJohannes Berg } 18192c9b7359SJohannes Berg 18202c9b7359SJohannes Berg ctx = container_of(conf, struct ieee80211_chanctx, conf); 18215bcae31dSMichal Kazior 18225bcae31dSMichal Kazior compat = cfg80211_chandef_compatible(&conf->def, chandef); 18235bcae31dSMichal Kazior if (!compat) { 18242c9b7359SJohannes Berg ret = -EINVAL; 18252c9b7359SJohannes Berg goto out; 18262c9b7359SJohannes Berg } 18272c9b7359SJohannes Berg 18285bcae31dSMichal Kazior switch (ctx->replace_state) { 18295bcae31dSMichal Kazior case IEEE80211_CHANCTX_REPLACE_NONE: 18305bcae31dSMichal Kazior if (!ieee80211_chanctx_reserved_chandef(local, ctx, compat)) { 18315bcae31dSMichal Kazior ret = -EBUSY; 18325bcae31dSMichal Kazior goto out; 18335bcae31dSMichal Kazior } 18345bcae31dSMichal Kazior break; 18355bcae31dSMichal Kazior case IEEE80211_CHANCTX_WILL_BE_REPLACED: 1836d070f913SStephen Hemminger /* TODO: Perhaps the bandwidth change could be treated as a 18375bcae31dSMichal Kazior * reservation itself? */ 18385bcae31dSMichal Kazior ret = -EBUSY; 18395bcae31dSMichal Kazior goto out; 18405bcae31dSMichal Kazior case IEEE80211_CHANCTX_REPLACES_OTHER: 18415bcae31dSMichal Kazior /* channel context that is going to replace another channel 18425bcae31dSMichal Kazior * context doesn't really exist and shouldn't be assigned 18435bcae31dSMichal Kazior * anywhere yet */ 18445bcae31dSMichal Kazior WARN_ON(1); 18455bcae31dSMichal Kazior break; 18465bcae31dSMichal Kazior } 18475bcae31dSMichal Kazior 18482967e031SFelix Fietkau ieee80211_vif_update_chandef(sdata, chandef); 18492c9b7359SJohannes Berg 18502c9b7359SJohannes Berg ieee80211_recalc_chanctx_chantype(local, ctx); 18512c9b7359SJohannes Berg 18522c9b7359SJohannes Berg *changed |= BSS_CHANGED_BANDWIDTH; 18532c9b7359SJohannes Berg ret = 0; 18542c9b7359SJohannes Berg out: 18552c9b7359SJohannes Berg mutex_unlock(&local->chanctx_mtx); 18562c9b7359SJohannes Berg return ret; 18572c9b7359SJohannes Berg } 18582c9b7359SJohannes Berg 1859d01a1e65SMichal Kazior void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata) 1860d01a1e65SMichal Kazior { 186155de908aSJohannes Berg WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev)); 186255de908aSJohannes Berg 186334a3740dSJohannes Berg lockdep_assert_held(&sdata->local->mtx); 186434a3740dSJohannes Berg 1865d01a1e65SMichal Kazior mutex_lock(&sdata->local->chanctx_mtx); 1866d01a1e65SMichal Kazior __ieee80211_vif_release_channel(sdata); 1867d01a1e65SMichal Kazior mutex_unlock(&sdata->local->chanctx_mtx); 1868d01a1e65SMichal Kazior } 18693448c005SJohannes Berg 18704d76d21bSJohannes Berg void ieee80211_vif_vlan_copy_chanctx(struct ieee80211_sub_if_data *sdata) 18714d76d21bSJohannes Berg { 18724d76d21bSJohannes Berg struct ieee80211_local *local = sdata->local; 18734d76d21bSJohannes Berg struct ieee80211_sub_if_data *ap; 18744d76d21bSJohannes Berg struct ieee80211_chanctx_conf *conf; 18754d76d21bSJohannes Berg 18764d76d21bSJohannes Berg if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP_VLAN || !sdata->bss)) 18774d76d21bSJohannes Berg return; 18784d76d21bSJohannes Berg 18794d76d21bSJohannes Berg ap = container_of(sdata->bss, struct ieee80211_sub_if_data, u.ap); 18804d76d21bSJohannes Berg 18814d76d21bSJohannes Berg mutex_lock(&local->chanctx_mtx); 18824d76d21bSJohannes Berg 1883d0a9123eSJohannes Berg conf = rcu_dereference_protected(ap->vif.bss_conf.chanctx_conf, 18844d76d21bSJohannes Berg lockdep_is_held(&local->chanctx_mtx)); 1885d0a9123eSJohannes Berg rcu_assign_pointer(sdata->vif.bss_conf.chanctx_conf, conf); 18864d76d21bSJohannes Berg mutex_unlock(&local->chanctx_mtx); 18874d76d21bSJohannes Berg } 18884d76d21bSJohannes Berg 18893448c005SJohannes Berg void ieee80211_iter_chan_contexts_atomic( 18903448c005SJohannes Berg struct ieee80211_hw *hw, 18913448c005SJohannes Berg void (*iter)(struct ieee80211_hw *hw, 18923448c005SJohannes Berg struct ieee80211_chanctx_conf *chanctx_conf, 18933448c005SJohannes Berg void *data), 18943448c005SJohannes Berg void *iter_data) 18953448c005SJohannes Berg { 18963448c005SJohannes Berg struct ieee80211_local *local = hw_to_local(hw); 18973448c005SJohannes Berg struct ieee80211_chanctx *ctx; 18983448c005SJohannes Berg 18993448c005SJohannes Berg rcu_read_lock(); 19003448c005SJohannes Berg list_for_each_entry_rcu(ctx, &local->chanctx_list, list) 19018a61af65SJohannes Berg if (ctx->driver_present) 19023448c005SJohannes Berg iter(hw, &ctx->conf, iter_data); 19033448c005SJohannes Berg rcu_read_unlock(); 19043448c005SJohannes Berg } 19053448c005SJohannes Berg EXPORT_SYMBOL_GPL(ieee80211_iter_chan_contexts_atomic); 1906