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 { 18b4f85443SJohannes Berg struct ieee80211_link_data *link; 19c0166da9SMichal Kazior int num = 0; 20c0166da9SMichal Kazior 21c0166da9SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 22c0166da9SMichal Kazior 23b4f85443SJohannes Berg list_for_each_entry(link, &ctx->assigned_links, 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 { 32b4f85443SJohannes Berg struct ieee80211_link_data *link; 33c0166da9SMichal Kazior int num = 0; 34c0166da9SMichal Kazior 35c0166da9SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 36c0166da9SMichal Kazior 37b4f85443SJohannes Berg list_for_each_entry(link, &ctx->reserved_links, 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 * 70d8675a63SJohannes Berg ieee80211_link_get_chanctx(struct ieee80211_link_data *link) 715bcae31dSMichal Kazior { 72d8675a63SJohannes Berg struct ieee80211_local *local __maybe_unused = link->sdata->local; 735bcae31dSMichal Kazior struct ieee80211_chanctx_conf *conf; 745bcae31dSMichal Kazior 75d8675a63SJohannes Berg conf = rcu_dereference_protected(link->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 { 88b4f85443SJohannes Berg struct ieee80211_link_data *link; 890288157bSMichal Kazior 900288157bSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 910288157bSMichal Kazior 92b4f85443SJohannes Berg list_for_each_entry(link, &ctx->reserved_links, 930288157bSMichal Kazior reserved_chanctx_list) { 940288157bSMichal Kazior if (!compat) 95b4f85443SJohannes Berg compat = &link->reserved_chandef; 960288157bSMichal Kazior 97b4f85443SJohannes Berg compat = cfg80211_chandef_compatible(&link->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 { 111b4f85443SJohannes Berg struct ieee80211_link_data *link; 11213f348a8SMichal Kazior 11313f348a8SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 11413f348a8SMichal Kazior 115b4f85443SJohannes Berg list_for_each_entry(link, &ctx->assigned_links, 11613f348a8SMichal Kazior assigned_chanctx_list) { 117d8675a63SJohannes Berg struct ieee80211_bss_conf *link_conf = link->conf; 118b4f85443SJohannes Berg 119b4f85443SJohannes Berg if (link->reserved_chanctx) 12013f348a8SMichal Kazior continue; 12113f348a8SMichal Kazior 12213f348a8SMichal Kazior if (!compat) 123b4f85443SJohannes Berg compat = &link_conf->chandef; 12413f348a8SMichal Kazior 12513f348a8SMichal Kazior compat = cfg80211_chandef_compatible( 126b4f85443SJohannes Berg &link_conf->chandef, compat); 12713f348a8SMichal Kazior if (!compat) 12813f348a8SMichal Kazior break; 12913f348a8SMichal Kazior } 13013f348a8SMichal Kazior 13113f348a8SMichal Kazior return compat; 13213f348a8SMichal Kazior } 13313f348a8SMichal Kazior 13413f348a8SMichal Kazior static const struct cfg80211_chan_def * 13513f348a8SMichal Kazior ieee80211_chanctx_combined_chandef(struct ieee80211_local *local, 13613f348a8SMichal Kazior struct ieee80211_chanctx *ctx, 13713f348a8SMichal Kazior const struct cfg80211_chan_def *compat) 13813f348a8SMichal Kazior { 13913f348a8SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 14013f348a8SMichal Kazior 14113f348a8SMichal Kazior compat = ieee80211_chanctx_reserved_chandef(local, ctx, compat); 14213f348a8SMichal Kazior if (!compat) 14313f348a8SMichal Kazior return NULL; 14413f348a8SMichal Kazior 14513f348a8SMichal Kazior compat = ieee80211_chanctx_non_reserved_chandef(local, ctx, compat); 14613f348a8SMichal Kazior if (!compat) 14713f348a8SMichal Kazior return NULL; 14813f348a8SMichal Kazior 14913f348a8SMichal Kazior return compat; 15013f348a8SMichal Kazior } 15113f348a8SMichal Kazior 15213f348a8SMichal Kazior static bool 15313f348a8SMichal Kazior ieee80211_chanctx_can_reserve_chandef(struct ieee80211_local *local, 15413f348a8SMichal Kazior struct ieee80211_chanctx *ctx, 15513f348a8SMichal Kazior const struct cfg80211_chan_def *def) 15613f348a8SMichal Kazior { 15713f348a8SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 15813f348a8SMichal Kazior 15913f348a8SMichal Kazior if (ieee80211_chanctx_combined_chandef(local, ctx, def)) 16013f348a8SMichal Kazior return true; 16113f348a8SMichal Kazior 162b4f85443SJohannes Berg if (!list_empty(&ctx->reserved_links) && 16313f348a8SMichal Kazior ieee80211_chanctx_reserved_chandef(local, ctx, def)) 16413f348a8SMichal Kazior return true; 16513f348a8SMichal Kazior 16613f348a8SMichal Kazior return false; 16713f348a8SMichal Kazior } 16813f348a8SMichal Kazior 16913f348a8SMichal Kazior static struct ieee80211_chanctx * 17013f348a8SMichal Kazior ieee80211_find_reservation_chanctx(struct ieee80211_local *local, 17113f348a8SMichal Kazior const struct cfg80211_chan_def *chandef, 17213f348a8SMichal Kazior enum ieee80211_chanctx_mode mode) 17313f348a8SMichal Kazior { 17413f348a8SMichal Kazior struct ieee80211_chanctx *ctx; 17513f348a8SMichal Kazior 17613f348a8SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 17713f348a8SMichal Kazior 17813f348a8SMichal Kazior if (mode == IEEE80211_CHANCTX_EXCLUSIVE) 17913f348a8SMichal Kazior return NULL; 18013f348a8SMichal Kazior 18113f348a8SMichal Kazior list_for_each_entry(ctx, &local->chanctx_list, list) { 1825bcae31dSMichal Kazior if (ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED) 1835bcae31dSMichal Kazior continue; 1845bcae31dSMichal Kazior 18513f348a8SMichal Kazior if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE) 18613f348a8SMichal Kazior continue; 18713f348a8SMichal Kazior 18813f348a8SMichal Kazior if (!ieee80211_chanctx_can_reserve_chandef(local, ctx, 18913f348a8SMichal Kazior chandef)) 19013f348a8SMichal Kazior continue; 19113f348a8SMichal Kazior 19213f348a8SMichal Kazior return ctx; 19313f348a8SMichal Kazior } 19413f348a8SMichal Kazior 19513f348a8SMichal Kazior return NULL; 19613f348a8SMichal Kazior } 19713f348a8SMichal Kazior 198b4f85443SJohannes Berg static enum nl80211_chan_width ieee80211_get_sta_bw(struct sta_info *sta, 199b4f85443SJohannes Berg unsigned int link_id) 20021f659bfSEliad Peller { 201c71420dbSJohannes Berg enum ieee80211_sta_rx_bandwidth width; 202c71420dbSJohannes Berg struct link_sta_info *link_sta; 203c71420dbSJohannes Berg 204c71420dbSJohannes Berg link_sta = rcu_dereference(sta->link[link_id]); 205b4f85443SJohannes Berg 206b4f85443SJohannes Berg /* no effect if this STA has no presence on this link */ 207c71420dbSJohannes Berg if (!link_sta) 208b4f85443SJohannes Berg return NL80211_CHAN_WIDTH_20_NOHT; 209bbf31e88SIlan Peer 210c71420dbSJohannes Berg width = ieee80211_sta_cap_rx_bw(link_sta); 211c71420dbSJohannes Berg 212bbf31e88SIlan Peer switch (width) { 21321f659bfSEliad Peller case IEEE80211_STA_RX_BW_20: 214c71420dbSJohannes Berg if (link_sta->pub->ht_cap.ht_supported) 21521f659bfSEliad Peller return NL80211_CHAN_WIDTH_20; 21621f659bfSEliad Peller else 21721f659bfSEliad Peller return NL80211_CHAN_WIDTH_20_NOHT; 21821f659bfSEliad Peller case IEEE80211_STA_RX_BW_40: 21921f659bfSEliad Peller return NL80211_CHAN_WIDTH_40; 22021f659bfSEliad Peller case IEEE80211_STA_RX_BW_80: 22121f659bfSEliad Peller return NL80211_CHAN_WIDTH_80; 22221f659bfSEliad Peller case IEEE80211_STA_RX_BW_160: 22321f659bfSEliad Peller /* 22421f659bfSEliad Peller * This applied for both 160 and 80+80. since we use 22521f659bfSEliad Peller * the returned value to consider degradation of 22621f659bfSEliad Peller * ctx->conf.min_def, we have to make sure to take 22721f659bfSEliad Peller * the bigger one (NL80211_CHAN_WIDTH_160). 22821f659bfSEliad Peller * Otherwise we might try degrading even when not 22921f659bfSEliad Peller * needed, as the max required sta_bw returned (80+80) 23021f659bfSEliad Peller * might be smaller than the configured bw (160). 23121f659bfSEliad Peller */ 23221f659bfSEliad Peller return NL80211_CHAN_WIDTH_160; 2335dca295dSIlan Peer case IEEE80211_STA_RX_BW_320: 2345dca295dSIlan Peer return NL80211_CHAN_WIDTH_320; 23521f659bfSEliad Peller default: 23621f659bfSEliad Peller WARN_ON(1); 23721f659bfSEliad Peller return NL80211_CHAN_WIDTH_20; 23821f659bfSEliad Peller } 23921f659bfSEliad Peller } 24021f659bfSEliad Peller 24121f659bfSEliad Peller static enum nl80211_chan_width 242b4f85443SJohannes Berg ieee80211_get_max_required_bw(struct ieee80211_sub_if_data *sdata, 243b4f85443SJohannes Berg unsigned int link_id) 24421f659bfSEliad Peller { 24521f659bfSEliad Peller enum nl80211_chan_width max_bw = NL80211_CHAN_WIDTH_20_NOHT; 24621f659bfSEliad Peller struct sta_info *sta; 24721f659bfSEliad Peller 24821f659bfSEliad Peller list_for_each_entry_rcu(sta, &sdata->local->sta_list, list) { 24921f659bfSEliad Peller if (sdata != sta->sdata && 25021f659bfSEliad Peller !(sta->sdata->bss && sta->sdata->bss == sdata->bss)) 25121f659bfSEliad Peller continue; 25221f659bfSEliad Peller 253b4f85443SJohannes Berg max_bw = max(max_bw, ieee80211_get_sta_bw(sta, link_id)); 25421f659bfSEliad Peller } 25521f659bfSEliad Peller 25621f659bfSEliad Peller return max_bw; 25721f659bfSEliad Peller } 25821f659bfSEliad Peller 25921f659bfSEliad Peller static enum nl80211_chan_width 260b4f85443SJohannes Berg ieee80211_get_chanctx_vif_max_required_bw(struct ieee80211_sub_if_data *sdata, 261b4f85443SJohannes Berg struct ieee80211_chanctx_conf *conf) 262b4f85443SJohannes Berg { 263b4f85443SJohannes Berg enum nl80211_chan_width max_bw = NL80211_CHAN_WIDTH_20_NOHT; 264b4f85443SJohannes Berg struct ieee80211_vif *vif = &sdata->vif; 265b4f85443SJohannes Berg int link_id; 266b4f85443SJohannes Berg 267d8675a63SJohannes Berg rcu_read_lock(); 268b4f85443SJohannes Berg for (link_id = 0; link_id < ARRAY_SIZE(sdata->link); link_id++) { 269b4f85443SJohannes Berg enum nl80211_chan_width width = NL80211_CHAN_WIDTH_20_NOHT; 270b4f85443SJohannes Berg struct ieee80211_bss_conf *link_conf = 271d8675a63SJohannes Berg rcu_dereference(sdata->vif.link_conf[link_id]); 272b4f85443SJohannes Berg 273b4f85443SJohannes Berg if (!link_conf) 274b4f85443SJohannes Berg continue; 275b4f85443SJohannes Berg 276b4f85443SJohannes Berg if (rcu_access_pointer(link_conf->chanctx_conf) != conf) 277b4f85443SJohannes Berg continue; 278b4f85443SJohannes Berg 279b4f85443SJohannes Berg switch (vif->type) { 280b4f85443SJohannes Berg case NL80211_IFTYPE_AP: 281b4f85443SJohannes Berg case NL80211_IFTYPE_AP_VLAN: 282b4f85443SJohannes Berg width = ieee80211_get_max_required_bw(sdata, link_id); 283b4f85443SJohannes Berg break; 284b4f85443SJohannes Berg case NL80211_IFTYPE_STATION: 285b4f85443SJohannes Berg /* 286b4f85443SJohannes Berg * The ap's sta->bandwidth is not set yet at this 287b4f85443SJohannes Berg * point, so take the width from the chandef, but 288b4f85443SJohannes Berg * account also for TDLS peers 289b4f85443SJohannes Berg */ 290b4f85443SJohannes Berg width = max(link_conf->chandef.width, 291b4f85443SJohannes Berg ieee80211_get_max_required_bw(sdata, link_id)); 292b4f85443SJohannes Berg break; 293b4f85443SJohannes Berg case NL80211_IFTYPE_P2P_DEVICE: 294b4f85443SJohannes Berg case NL80211_IFTYPE_NAN: 295b4f85443SJohannes Berg continue; 296b4f85443SJohannes Berg case NL80211_IFTYPE_ADHOC: 297b4f85443SJohannes Berg case NL80211_IFTYPE_MESH_POINT: 298b4f85443SJohannes Berg case NL80211_IFTYPE_OCB: 299b4f85443SJohannes Berg width = link_conf->chandef.width; 300b4f85443SJohannes Berg break; 301b4f85443SJohannes Berg case NL80211_IFTYPE_WDS: 302b4f85443SJohannes Berg case NL80211_IFTYPE_UNSPECIFIED: 303b4f85443SJohannes Berg case NUM_NL80211_IFTYPES: 304b4f85443SJohannes Berg case NL80211_IFTYPE_MONITOR: 305b4f85443SJohannes Berg case NL80211_IFTYPE_P2P_CLIENT: 306b4f85443SJohannes Berg case NL80211_IFTYPE_P2P_GO: 307b4f85443SJohannes Berg WARN_ON_ONCE(1); 308b4f85443SJohannes Berg } 309b4f85443SJohannes Berg 310b4f85443SJohannes Berg max_bw = max(max_bw, width); 311b4f85443SJohannes Berg } 312d8675a63SJohannes Berg rcu_read_unlock(); 313b4f85443SJohannes Berg 314b4f85443SJohannes Berg return max_bw; 315b4f85443SJohannes Berg } 316b4f85443SJohannes Berg 317b4f85443SJohannes Berg static enum nl80211_chan_width 31821f659bfSEliad Peller ieee80211_get_chanctx_max_required_bw(struct ieee80211_local *local, 31921f659bfSEliad Peller struct ieee80211_chanctx_conf *conf) 32021f659bfSEliad Peller { 32121f659bfSEliad Peller struct ieee80211_sub_if_data *sdata; 32221f659bfSEliad Peller enum nl80211_chan_width max_bw = NL80211_CHAN_WIDTH_20_NOHT; 32321f659bfSEliad Peller 32421f659bfSEliad Peller rcu_read_lock(); 32521f659bfSEliad Peller list_for_each_entry_rcu(sdata, &local->interfaces, list) { 326b4f85443SJohannes Berg enum nl80211_chan_width width; 32721f659bfSEliad Peller 32821f659bfSEliad Peller if (!ieee80211_sdata_running(sdata)) 32921f659bfSEliad Peller continue; 33021f659bfSEliad Peller 331b4f85443SJohannes Berg width = ieee80211_get_chanctx_vif_max_required_bw(sdata, conf); 33221f659bfSEliad Peller 33321f659bfSEliad Peller max_bw = max(max_bw, width); 33421f659bfSEliad Peller } 3351c37a72cSEliad Peller 3361c37a72cSEliad Peller /* use the configured bandwidth in case of monitor interface */ 3371c37a72cSEliad Peller sdata = rcu_dereference(local->monitor_sdata); 338b4f85443SJohannes Berg if (sdata && 339d8675a63SJohannes Berg rcu_access_pointer(sdata->vif.bss_conf.chanctx_conf) == conf) 3401c37a72cSEliad Peller max_bw = max(max_bw, conf->def.width); 3411c37a72cSEliad Peller 34221f659bfSEliad Peller rcu_read_unlock(); 34321f659bfSEliad Peller 34421f659bfSEliad Peller return max_bw; 34521f659bfSEliad Peller } 34621f659bfSEliad Peller 34721f659bfSEliad Peller /* 34821f659bfSEliad Peller * recalc the min required chan width of the channel context, which is 34921f659bfSEliad Peller * the max of min required widths of all the interfaces bound to this 35021f659bfSEliad Peller * channel context. 35121f659bfSEliad Peller */ 352d6c37509SMordechay Goodstein static u32 _ieee80211_recalc_chanctx_min_def(struct ieee80211_local *local, 35321f659bfSEliad Peller struct ieee80211_chanctx *ctx) 35421f659bfSEliad Peller { 35521f659bfSEliad Peller enum nl80211_chan_width max_bw; 35621f659bfSEliad Peller struct cfg80211_chan_def min_def; 35721f659bfSEliad Peller 35821f659bfSEliad Peller lockdep_assert_held(&local->chanctx_mtx); 35921f659bfSEliad Peller 360df78a0c0SThomas Pedersen /* don't optimize non-20MHz based and radar_enabled confs */ 36121f659bfSEliad Peller if (ctx->conf.def.width == NL80211_CHAN_WIDTH_5 || 36221f659bfSEliad Peller ctx->conf.def.width == NL80211_CHAN_WIDTH_10 || 363df78a0c0SThomas Pedersen ctx->conf.def.width == NL80211_CHAN_WIDTH_1 || 364df78a0c0SThomas Pedersen ctx->conf.def.width == NL80211_CHAN_WIDTH_2 || 365df78a0c0SThomas Pedersen ctx->conf.def.width == NL80211_CHAN_WIDTH_4 || 366df78a0c0SThomas Pedersen ctx->conf.def.width == NL80211_CHAN_WIDTH_8 || 367df78a0c0SThomas Pedersen ctx->conf.def.width == NL80211_CHAN_WIDTH_16 || 36821f659bfSEliad Peller ctx->conf.radar_enabled) { 36921f659bfSEliad Peller ctx->conf.min_def = ctx->conf.def; 370d6c37509SMordechay Goodstein return 0; 37121f659bfSEliad Peller } 37221f659bfSEliad Peller 37321f659bfSEliad Peller max_bw = ieee80211_get_chanctx_max_required_bw(local, &ctx->conf); 37421f659bfSEliad Peller 37521f659bfSEliad Peller /* downgrade chandef up to max_bw */ 37621f659bfSEliad Peller min_def = ctx->conf.def; 37721f659bfSEliad Peller while (min_def.width > max_bw) 37821f659bfSEliad Peller ieee80211_chandef_downgrade(&min_def); 37921f659bfSEliad Peller 38021f659bfSEliad Peller if (cfg80211_chandef_identical(&ctx->conf.min_def, &min_def)) 381d6c37509SMordechay Goodstein return 0; 38221f659bfSEliad Peller 38321f659bfSEliad Peller ctx->conf.min_def = min_def; 38421f659bfSEliad Peller if (!ctx->driver_present) 385d6c37509SMordechay Goodstein return 0; 38621f659bfSEliad Peller 387d6c37509SMordechay Goodstein return IEEE80211_CHANCTX_CHANGE_MIN_WIDTH; 38821f659bfSEliad Peller } 38921f659bfSEliad Peller 390d6c37509SMordechay Goodstein /* calling this function is assuming that station vif is updated to 391b4f85443SJohannes Berg * lates changes by calling ieee80211_link_update_chandef 392d6c37509SMordechay Goodstein */ 39344b72ca8SIlan Peer static void ieee80211_chan_bw_change(struct ieee80211_local *local, 394d6c37509SMordechay Goodstein struct ieee80211_chanctx *ctx, 395d6c37509SMordechay Goodstein bool narrowed) 39644b72ca8SIlan Peer { 39744b72ca8SIlan Peer struct sta_info *sta; 39844b72ca8SIlan Peer struct ieee80211_supported_band *sband = 39944b72ca8SIlan Peer local->hw.wiphy->bands[ctx->conf.def.chan->band]; 40044b72ca8SIlan Peer 40144b72ca8SIlan Peer rcu_read_lock(); 40244b72ca8SIlan Peer list_for_each_entry_rcu(sta, &local->sta_list, 40344b72ca8SIlan Peer list) { 404b4f85443SJohannes Berg struct ieee80211_sub_if_data *sdata = sta->sdata; 40544b72ca8SIlan Peer enum ieee80211_sta_rx_bandwidth new_sta_bw; 406b4f85443SJohannes Berg unsigned int link_id; 40744b72ca8SIlan Peer 40844b72ca8SIlan Peer if (!ieee80211_sdata_running(sta->sdata)) 40944b72ca8SIlan Peer continue; 41044b72ca8SIlan Peer 411b4f85443SJohannes Berg for (link_id = 0; link_id < ARRAY_SIZE(sta->sdata->link); link_id++) { 412b4f85443SJohannes Berg struct ieee80211_bss_conf *link_conf = 413d8675a63SJohannes Berg rcu_dereference(sdata->vif.link_conf[link_id]); 414c71420dbSJohannes Berg struct link_sta_info *link_sta; 415b4f85443SJohannes Berg 416b4f85443SJohannes Berg if (!link_conf) 41744b72ca8SIlan Peer continue; 41844b72ca8SIlan Peer 419b4f85443SJohannes Berg if (rcu_access_pointer(link_conf->chanctx_conf) != &ctx->conf) 420b4f85443SJohannes Berg continue; 421b4f85443SJohannes Berg 422c71420dbSJohannes Berg link_sta = rcu_dereference(sta->link[link_id]); 423c71420dbSJohannes Berg if (!link_sta) 424c71420dbSJohannes Berg continue; 425c71420dbSJohannes Berg 426c71420dbSJohannes Berg new_sta_bw = ieee80211_sta_cur_vht_bw(link_sta); 427d6c37509SMordechay Goodstein 428d6c37509SMordechay Goodstein /* nothing change */ 429c71420dbSJohannes Berg if (new_sta_bw == link_sta->pub->bandwidth) 43044b72ca8SIlan Peer continue; 43144b72ca8SIlan Peer 432d6c37509SMordechay Goodstein /* vif changed to narrow BW and narrow BW for station wasn't 433d6c37509SMordechay Goodstein * requested or vise versa */ 434c71420dbSJohannes Berg if ((new_sta_bw < link_sta->pub->bandwidth) == !narrowed) 435d6c37509SMordechay Goodstein continue; 436d6c37509SMordechay Goodstein 437c71420dbSJohannes Berg link_sta->pub->bandwidth = new_sta_bw; 438b4f85443SJohannes Berg rate_control_rate_update(local, sband, sta, link_id, 43944b72ca8SIlan Peer IEEE80211_RC_BW_CHANGED); 44044b72ca8SIlan Peer } 441b4f85443SJohannes Berg } 44244b72ca8SIlan Peer rcu_read_unlock(); 44344b72ca8SIlan Peer } 44444b72ca8SIlan Peer 445d6c37509SMordechay Goodstein /* 446d6c37509SMordechay Goodstein * recalc the min required chan width of the channel context, which is 447d6c37509SMordechay Goodstein * the max of min required widths of all the interfaces bound to this 448d6c37509SMordechay Goodstein * channel context. 449d6c37509SMordechay Goodstein */ 450d6c37509SMordechay Goodstein void ieee80211_recalc_chanctx_min_def(struct ieee80211_local *local, 451d6c37509SMordechay Goodstein struct ieee80211_chanctx *ctx) 452e89a96f5SMichal Kazior { 453d6c37509SMordechay Goodstein u32 changed = _ieee80211_recalc_chanctx_min_def(local, ctx); 45444b72ca8SIlan Peer 455d6c37509SMordechay Goodstein if (!changed) 456e89a96f5SMichal Kazior return; 457d6c37509SMordechay Goodstein 458d6c37509SMordechay Goodstein /* check is BW narrowed */ 459d6c37509SMordechay Goodstein ieee80211_chan_bw_change(local, ctx, true); 460d6c37509SMordechay Goodstein 461d6c37509SMordechay Goodstein drv_change_chanctx(local, ctx, changed); 462d6c37509SMordechay Goodstein 463d6c37509SMordechay Goodstein /* check is BW wider */ 464d6c37509SMordechay Goodstein ieee80211_chan_bw_change(local, ctx, false); 465aa507a7bSArik Nemtsov } 466e89a96f5SMichal Kazior 467d6c37509SMordechay Goodstein static void ieee80211_change_chanctx(struct ieee80211_local *local, 468d6c37509SMordechay Goodstein struct ieee80211_chanctx *ctx, 469d6c37509SMordechay Goodstein struct ieee80211_chanctx *old_ctx, 470d6c37509SMordechay Goodstein const struct cfg80211_chan_def *chandef) 471d6c37509SMordechay Goodstein { 472d6c37509SMordechay Goodstein u32 changed; 47344b72ca8SIlan Peer 4745dca295dSIlan Peer /* expected to handle only 20/40/80/160/320 channel widths */ 47544b72ca8SIlan Peer switch (chandef->width) { 47644b72ca8SIlan Peer case NL80211_CHAN_WIDTH_20_NOHT: 47744b72ca8SIlan Peer case NL80211_CHAN_WIDTH_20: 47844b72ca8SIlan Peer case NL80211_CHAN_WIDTH_40: 47944b72ca8SIlan Peer case NL80211_CHAN_WIDTH_80: 48044b72ca8SIlan Peer case NL80211_CHAN_WIDTH_80P80: 48144b72ca8SIlan Peer case NL80211_CHAN_WIDTH_160: 4825dca295dSIlan Peer case NL80211_CHAN_WIDTH_320: 48344b72ca8SIlan Peer break; 48444b72ca8SIlan Peer default: 48544b72ca8SIlan Peer WARN_ON(1); 48644b72ca8SIlan Peer } 48744b72ca8SIlan Peer 488d6c37509SMordechay Goodstein /* Check maybe BW narrowed - we do this _before_ calling recalc_chanctx_min_def 489d6c37509SMordechay Goodstein * due to maybe not returning from it, e.g in case new context was added 490d6c37509SMordechay Goodstein * first time with all parameters up to date. 491d6c37509SMordechay Goodstein */ 492d6c37509SMordechay Goodstein ieee80211_chan_bw_change(local, old_ctx, true); 49344b72ca8SIlan Peer 494d6c37509SMordechay Goodstein if (cfg80211_chandef_identical(&ctx->conf.def, chandef)) { 49521f659bfSEliad Peller ieee80211_recalc_chanctx_min_def(local, ctx); 496d6c37509SMordechay Goodstein return; 497d6c37509SMordechay Goodstein } 498d6c37509SMordechay Goodstein 499d6c37509SMordechay Goodstein WARN_ON(!cfg80211_chandef_compatible(&ctx->conf.def, chandef)); 500d6c37509SMordechay Goodstein 501d6c37509SMordechay Goodstein ctx->conf.def = *chandef; 502d6c37509SMordechay Goodstein 503d6c37509SMordechay Goodstein /* check if min chanctx also changed */ 504d6c37509SMordechay Goodstein changed = IEEE80211_CHANCTX_CHANGE_WIDTH | 505d6c37509SMordechay Goodstein _ieee80211_recalc_chanctx_min_def(local, ctx); 506d6c37509SMordechay Goodstein drv_change_chanctx(local, ctx, changed); 50755de908aSJohannes Berg 50855de908aSJohannes Berg if (!local->use_chanctx) { 509675a0b04SKarl Beldan local->_oper_chandef = *chandef; 51055de908aSJohannes Berg ieee80211_hw_config(local, 0); 51155de908aSJohannes Berg } 51244b72ca8SIlan Peer 513d6c37509SMordechay Goodstein /* check is BW wider */ 514d6c37509SMordechay Goodstein ieee80211_chan_bw_change(local, old_ctx, false); 5150aaffa9bSJohannes Berg } 516d01a1e65SMichal Kazior 517d01a1e65SMichal Kazior static struct ieee80211_chanctx * 518d01a1e65SMichal Kazior ieee80211_find_chanctx(struct ieee80211_local *local, 5194bf88530SJohannes Berg const struct cfg80211_chan_def *chandef, 520d01a1e65SMichal Kazior enum ieee80211_chanctx_mode mode) 521d01a1e65SMichal Kazior { 522d01a1e65SMichal Kazior struct ieee80211_chanctx *ctx; 523d01a1e65SMichal Kazior 524d01a1e65SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 525d01a1e65SMichal Kazior 526d01a1e65SMichal Kazior if (mode == IEEE80211_CHANCTX_EXCLUSIVE) 527d01a1e65SMichal Kazior return NULL; 528d01a1e65SMichal Kazior 529d01a1e65SMichal Kazior list_for_each_entry(ctx, &local->chanctx_list, list) { 5304bf88530SJohannes Berg const struct cfg80211_chan_def *compat; 531e89a96f5SMichal Kazior 5325bcae31dSMichal Kazior if (ctx->replace_state != IEEE80211_CHANCTX_REPLACE_NONE) 5335bcae31dSMichal Kazior continue; 5345bcae31dSMichal Kazior 535d01a1e65SMichal Kazior if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE) 536d01a1e65SMichal Kazior continue; 5374bf88530SJohannes Berg 5384bf88530SJohannes Berg compat = cfg80211_chandef_compatible(&ctx->conf.def, chandef); 5394bf88530SJohannes Berg if (!compat) 540d01a1e65SMichal Kazior continue; 541d01a1e65SMichal Kazior 5420288157bSMichal Kazior compat = ieee80211_chanctx_reserved_chandef(local, ctx, 5430288157bSMichal Kazior compat); 5440288157bSMichal Kazior if (!compat) 5450288157bSMichal Kazior continue; 5460288157bSMichal Kazior 547d6c37509SMordechay Goodstein ieee80211_change_chanctx(local, ctx, ctx, compat); 548e89a96f5SMichal Kazior 549d01a1e65SMichal Kazior return ctx; 550d01a1e65SMichal Kazior } 551d01a1e65SMichal Kazior 552d01a1e65SMichal Kazior return NULL; 553d01a1e65SMichal Kazior } 554d01a1e65SMichal Kazior 5555cbc95a7SEliad Peller bool ieee80211_is_radar_required(struct ieee80211_local *local) 556e4746851SSimon Wunderlich { 557e4746851SSimon Wunderlich struct ieee80211_sub_if_data *sdata; 558e4746851SSimon Wunderlich 559cc901de1SMichal Kazior lockdep_assert_held(&local->mtx); 560cc901de1SMichal Kazior 561e4746851SSimon Wunderlich rcu_read_lock(); 562e4746851SSimon Wunderlich list_for_each_entry_rcu(sdata, &local->interfaces, list) { 563b4f85443SJohannes Berg unsigned int link_id; 564b4f85443SJohannes Berg 565b4f85443SJohannes Berg for (link_id = 0; link_id < ARRAY_SIZE(sdata->link); link_id++) { 566d8675a63SJohannes Berg struct ieee80211_link_data *link; 567d8675a63SJohannes Berg 568d8675a63SJohannes Berg link = rcu_dereference(sdata->link[link_id]); 569d8675a63SJohannes Berg 570d8675a63SJohannes Berg if (link && link->radar_required) { 571e4746851SSimon Wunderlich rcu_read_unlock(); 572e4746851SSimon Wunderlich return true; 573e4746851SSimon Wunderlich } 574e4746851SSimon Wunderlich } 575b4f85443SJohannes Berg } 576e4746851SSimon Wunderlich rcu_read_unlock(); 577e4746851SSimon Wunderlich 578e4746851SSimon Wunderlich return false; 579e4746851SSimon Wunderlich } 580e4746851SSimon Wunderlich 581e7f2337aSEliad Peller static bool 582e7f2337aSEliad Peller ieee80211_chanctx_radar_required(struct ieee80211_local *local, 583e7f2337aSEliad Peller struct ieee80211_chanctx *ctx) 584e7f2337aSEliad Peller { 585e7f2337aSEliad Peller struct ieee80211_chanctx_conf *conf = &ctx->conf; 586e7f2337aSEliad Peller struct ieee80211_sub_if_data *sdata; 587e7f2337aSEliad Peller bool required = false; 588e7f2337aSEliad Peller 589e7f2337aSEliad Peller lockdep_assert_held(&local->chanctx_mtx); 590e7f2337aSEliad Peller lockdep_assert_held(&local->mtx); 591e7f2337aSEliad Peller 592e7f2337aSEliad Peller rcu_read_lock(); 593e7f2337aSEliad Peller list_for_each_entry_rcu(sdata, &local->interfaces, list) { 594b4f85443SJohannes Berg unsigned int link_id; 595b4f85443SJohannes Berg 596e7f2337aSEliad Peller if (!ieee80211_sdata_running(sdata)) 597e7f2337aSEliad Peller continue; 598b4f85443SJohannes Berg for (link_id = 0; link_id < ARRAY_SIZE(sdata->link); link_id++) { 599d8675a63SJohannes Berg struct ieee80211_link_data *link; 600b4f85443SJohannes Berg 601d8675a63SJohannes Berg link = rcu_dereference(sdata->link[link_id]); 602d8675a63SJohannes Berg if (!link) 603e7f2337aSEliad Peller continue; 604e7f2337aSEliad Peller 605d8675a63SJohannes Berg if (rcu_access_pointer(link->conf->chanctx_conf) != conf) 606b4f85443SJohannes Berg continue; 607d8675a63SJohannes Berg if (!link->radar_required) 608b4f85443SJohannes Berg continue; 609e7f2337aSEliad Peller required = true; 610e7f2337aSEliad Peller break; 611e7f2337aSEliad Peller } 612b4f85443SJohannes Berg 613b4f85443SJohannes Berg if (required) 614b4f85443SJohannes Berg break; 615b4f85443SJohannes Berg } 616e7f2337aSEliad Peller rcu_read_unlock(); 617e7f2337aSEliad Peller 618e7f2337aSEliad Peller return required; 619e7f2337aSEliad Peller } 620e7f2337aSEliad Peller 621d01a1e65SMichal Kazior static struct ieee80211_chanctx * 622ed68ebcaSMichal Kazior ieee80211_alloc_chanctx(struct ieee80211_local *local, 6234bf88530SJohannes Berg const struct cfg80211_chan_def *chandef, 624d01a1e65SMichal Kazior enum ieee80211_chanctx_mode mode) 625d01a1e65SMichal Kazior { 626d01a1e65SMichal Kazior struct ieee80211_chanctx *ctx; 627d01a1e65SMichal Kazior 628d01a1e65SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 629d01a1e65SMichal Kazior 630d01a1e65SMichal Kazior ctx = kzalloc(sizeof(*ctx) + local->hw.chanctx_data_size, GFP_KERNEL); 631d01a1e65SMichal Kazior if (!ctx) 632ed68ebcaSMichal Kazior return NULL; 633d01a1e65SMichal Kazior 634b4f85443SJohannes Berg INIT_LIST_HEAD(&ctx->assigned_links); 635b4f85443SJohannes Berg INIT_LIST_HEAD(&ctx->reserved_links); 6364bf88530SJohannes Berg ctx->conf.def = *chandef; 63704ecd257SJohannes Berg ctx->conf.rx_chains_static = 1; 63804ecd257SJohannes Berg ctx->conf.rx_chains_dynamic = 1; 639d01a1e65SMichal Kazior ctx->mode = mode; 640e7f2337aSEliad Peller ctx->conf.radar_enabled = false; 64121f659bfSEliad Peller ieee80211_recalc_chanctx_min_def(local, ctx); 642ed68ebcaSMichal Kazior 643ed68ebcaSMichal Kazior return ctx; 644ed68ebcaSMichal Kazior } 645ed68ebcaSMichal Kazior 646ed68ebcaSMichal Kazior static int ieee80211_add_chanctx(struct ieee80211_local *local, 647ed68ebcaSMichal Kazior struct ieee80211_chanctx *ctx) 648ed68ebcaSMichal Kazior { 649ed68ebcaSMichal Kazior u32 changed; 650ed68ebcaSMichal Kazior int err; 651ed68ebcaSMichal Kazior 652ed68ebcaSMichal Kazior lockdep_assert_held(&local->mtx); 653ed68ebcaSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 654ed68ebcaSMichal Kazior 655e4746851SSimon Wunderlich if (!local->use_chanctx) 656e4746851SSimon Wunderlich local->hw.conf.radar_enabled = ctx->conf.radar_enabled; 657d01a1e65SMichal Kazior 658382a103bSJohannes Berg /* turn idle off *before* setting channel -- some drivers need that */ 659382a103bSJohannes Berg changed = ieee80211_idle_off(local); 660382a103bSJohannes Berg if (changed) 661382a103bSJohannes Berg ieee80211_hw_config(local, changed); 662382a103bSJohannes Berg 66355de908aSJohannes Berg if (!local->use_chanctx) { 664ed68ebcaSMichal Kazior local->_oper_chandef = ctx->conf.def; 6659b4816f5SMichal Kazior ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); 66655de908aSJohannes Berg } else { 66735f2fce9SMichal Kazior err = drv_add_chanctx(local, ctx); 66835f2fce9SMichal Kazior if (err) { 669382a103bSJohannes Berg ieee80211_recalc_idle(local); 670ed68ebcaSMichal Kazior return err; 671ed68ebcaSMichal Kazior } 672ed68ebcaSMichal Kazior } 673ed68ebcaSMichal Kazior 674ed68ebcaSMichal Kazior return 0; 675ed68ebcaSMichal Kazior } 676ed68ebcaSMichal Kazior 677ed68ebcaSMichal Kazior static struct ieee80211_chanctx * 678ed68ebcaSMichal Kazior ieee80211_new_chanctx(struct ieee80211_local *local, 679ed68ebcaSMichal Kazior const struct cfg80211_chan_def *chandef, 680ed68ebcaSMichal Kazior enum ieee80211_chanctx_mode mode) 681ed68ebcaSMichal Kazior { 682ed68ebcaSMichal Kazior struct ieee80211_chanctx *ctx; 683ed68ebcaSMichal Kazior int err; 684ed68ebcaSMichal Kazior 685ed68ebcaSMichal Kazior lockdep_assert_held(&local->mtx); 686ed68ebcaSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 687ed68ebcaSMichal Kazior 688ed68ebcaSMichal Kazior ctx = ieee80211_alloc_chanctx(local, chandef, mode); 689ed68ebcaSMichal Kazior if (!ctx) 690ed68ebcaSMichal Kazior return ERR_PTR(-ENOMEM); 691ed68ebcaSMichal Kazior 692ed68ebcaSMichal Kazior err = ieee80211_add_chanctx(local, ctx); 693ed68ebcaSMichal Kazior if (err) { 694ed68ebcaSMichal Kazior kfree(ctx); 69534a3740dSJohannes Berg return ERR_PTR(err); 69635f2fce9SMichal Kazior } 69735f2fce9SMichal Kazior 6983448c005SJohannes Berg list_add_rcu(&ctx->list, &local->chanctx_list); 699d01a1e65SMichal Kazior return ctx; 700d01a1e65SMichal Kazior } 701d01a1e65SMichal Kazior 7021f0d54cdSMichal Kazior static void ieee80211_del_chanctx(struct ieee80211_local *local, 703d01a1e65SMichal Kazior struct ieee80211_chanctx *ctx) 704d01a1e65SMichal Kazior { 705d01a1e65SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 706d01a1e65SMichal Kazior 70755de908aSJohannes Berg if (!local->use_chanctx) { 708675a0b04SKarl Beldan struct cfg80211_chan_def *chandef = &local->_oper_chandef; 7095e480774SThomas Pedersen /* S1G doesn't have 20MHz, so get the correct width for the 7105e480774SThomas Pedersen * current channel. 7115e480774SThomas Pedersen */ 7125e480774SThomas Pedersen if (chandef->chan->band == NL80211_BAND_S1GHZ) 7135e480774SThomas Pedersen chandef->width = 7145e480774SThomas Pedersen ieee80211_s1g_channel_width(chandef->chan); 7155e480774SThomas Pedersen else 716675a0b04SKarl Beldan chandef->width = NL80211_CHAN_WIDTH_20_NOHT; 717675a0b04SKarl Beldan chandef->center_freq1 = chandef->chan->center_freq; 718b6011960SThomas Pedersen chandef->freq1_offset = chandef->chan->freq_offset; 719675a0b04SKarl Beldan chandef->center_freq2 = 0; 720e4746851SSimon Wunderlich 721e4746851SSimon Wunderlich /* NOTE: Disabling radar is only valid here for 722e4746851SSimon Wunderlich * single channel context. To be sure, check it ... 723e4746851SSimon Wunderlich */ 7241f0d54cdSMichal Kazior WARN_ON(local->hw.conf.radar_enabled && 7251f0d54cdSMichal Kazior !list_empty(&local->chanctx_list)); 7261f0d54cdSMichal Kazior 727e4746851SSimon Wunderlich local->hw.conf.radar_enabled = false; 728e4746851SSimon Wunderlich 7299b4816f5SMichal Kazior ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); 73055de908aSJohannes Berg } else { 73135f2fce9SMichal Kazior drv_remove_chanctx(local, ctx); 73255de908aSJohannes Berg } 73335f2fce9SMichal Kazior 734fd0f979aSJohannes Berg ieee80211_recalc_idle(local); 735d01a1e65SMichal Kazior } 736d01a1e65SMichal Kazior 7371f0d54cdSMichal Kazior static void ieee80211_free_chanctx(struct ieee80211_local *local, 738d01a1e65SMichal Kazior struct ieee80211_chanctx *ctx) 739d01a1e65SMichal Kazior { 740d01a1e65SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 741d01a1e65SMichal Kazior 742c0166da9SMichal Kazior WARN_ON_ONCE(ieee80211_chanctx_refcount(local, ctx) != 0); 74335f2fce9SMichal Kazior 7441f0d54cdSMichal Kazior list_del_rcu(&ctx->list); 7451f0d54cdSMichal Kazior ieee80211_del_chanctx(local, ctx); 7461f0d54cdSMichal Kazior kfree_rcu(ctx, rcu_head); 747d01a1e65SMichal Kazior } 748d01a1e65SMichal Kazior 7490fabfaafSArik Nemtsov void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local, 750e89a96f5SMichal Kazior struct ieee80211_chanctx *ctx) 751e89a96f5SMichal Kazior { 752e89a96f5SMichal Kazior struct ieee80211_chanctx_conf *conf = &ctx->conf; 753e89a96f5SMichal Kazior struct ieee80211_sub_if_data *sdata; 7544bf88530SJohannes Berg const struct cfg80211_chan_def *compat = NULL; 7550fabfaafSArik Nemtsov struct sta_info *sta; 756e89a96f5SMichal Kazior 757e89a96f5SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 758e89a96f5SMichal Kazior 759e89a96f5SMichal Kazior rcu_read_lock(); 760e89a96f5SMichal Kazior list_for_each_entry_rcu(sdata, &local->interfaces, list) { 761b4f85443SJohannes Berg int link_id; 7624bf88530SJohannes Berg 763e89a96f5SMichal Kazior if (!ieee80211_sdata_running(sdata)) 764e89a96f5SMichal Kazior continue; 765b4f85443SJohannes Berg 7660e67c136SFelix Fietkau if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) 7670e67c136SFelix Fietkau continue; 768e89a96f5SMichal Kazior 769b4f85443SJohannes Berg for (link_id = 0; link_id < ARRAY_SIZE(sdata->link); link_id++) { 770b4f85443SJohannes Berg struct ieee80211_bss_conf *link_conf = 771d8675a63SJohannes Berg rcu_dereference(sdata->vif.link_conf[link_id]); 7724bf88530SJohannes Berg 773b4f85443SJohannes Berg if (!link_conf) 774b4f85443SJohannes Berg continue; 775b4f85443SJohannes Berg 776b4f85443SJohannes Berg if (rcu_access_pointer(link_conf->chanctx_conf) != conf) 777b4f85443SJohannes Berg continue; 778b4f85443SJohannes Berg 779b4f85443SJohannes Berg if (!compat) 780b4f85443SJohannes Berg compat = &link_conf->chandef; 781b4f85443SJohannes Berg 782b4f85443SJohannes Berg compat = cfg80211_chandef_compatible(&link_conf->chandef, 783b4f85443SJohannes Berg compat); 784a00f4f6eSMichal Kazior if (WARN_ON_ONCE(!compat)) 7854bf88530SJohannes Berg break; 786e89a96f5SMichal Kazior } 787b4f85443SJohannes Berg } 7880fabfaafSArik Nemtsov 7890fabfaafSArik Nemtsov /* TDLS peers can sometimes affect the chandef width */ 7900fabfaafSArik Nemtsov list_for_each_entry_rcu(sta, &local->sta_list, list) { 7910fabfaafSArik Nemtsov if (!sta->uploaded || 7920fabfaafSArik Nemtsov !test_sta_flag(sta, WLAN_STA_TDLS_WIDER_BW) || 7930fabfaafSArik Nemtsov !test_sta_flag(sta, WLAN_STA_AUTHORIZED) || 7940fabfaafSArik Nemtsov !sta->tdls_chandef.chan) 7950fabfaafSArik Nemtsov continue; 7960fabfaafSArik Nemtsov 7970fabfaafSArik Nemtsov compat = cfg80211_chandef_compatible(&sta->tdls_chandef, 7980fabfaafSArik Nemtsov compat); 7990fabfaafSArik Nemtsov if (WARN_ON_ONCE(!compat)) 8000fabfaafSArik Nemtsov break; 8010fabfaafSArik Nemtsov } 802e89a96f5SMichal Kazior rcu_read_unlock(); 803e89a96f5SMichal Kazior 804a00f4f6eSMichal Kazior if (!compat) 8054bf88530SJohannes Berg return; 806e89a96f5SMichal Kazior 807d6c37509SMordechay Goodstein ieee80211_change_chanctx(local, ctx, ctx, compat); 808e89a96f5SMichal Kazior } 809e89a96f5SMichal Kazior 810367bbd10SJohannes Berg static void ieee80211_recalc_radar_chanctx(struct ieee80211_local *local, 811367bbd10SJohannes Berg struct ieee80211_chanctx *chanctx) 812367bbd10SJohannes Berg { 813367bbd10SJohannes Berg bool radar_enabled; 814367bbd10SJohannes Berg 815367bbd10SJohannes Berg lockdep_assert_held(&local->chanctx_mtx); 8165cbc95a7SEliad Peller /* for ieee80211_is_radar_required */ 81734a3740dSJohannes Berg lockdep_assert_held(&local->mtx); 818367bbd10SJohannes Berg 819e7f2337aSEliad Peller radar_enabled = ieee80211_chanctx_radar_required(local, chanctx); 820367bbd10SJohannes Berg 821367bbd10SJohannes Berg if (radar_enabled == chanctx->conf.radar_enabled) 822367bbd10SJohannes Berg return; 823367bbd10SJohannes Berg 824367bbd10SJohannes Berg chanctx->conf.radar_enabled = radar_enabled; 825367bbd10SJohannes Berg 826367bbd10SJohannes Berg if (!local->use_chanctx) { 827367bbd10SJohannes Berg local->hw.conf.radar_enabled = chanctx->conf.radar_enabled; 828367bbd10SJohannes Berg ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); 829367bbd10SJohannes Berg } 830367bbd10SJohannes Berg 831367bbd10SJohannes Berg drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RADAR); 832367bbd10SJohannes Berg } 833367bbd10SJohannes Berg 834b4f85443SJohannes Berg static int ieee80211_assign_link_chanctx(struct ieee80211_link_data *link, 83577eeba97SLuciano Coelho struct ieee80211_chanctx *new_ctx) 836d01a1e65SMichal Kazior { 837b4f85443SJohannes Berg struct ieee80211_sub_if_data *sdata = link->sdata; 83835f2fce9SMichal Kazior struct ieee80211_local *local = sdata->local; 83977eeba97SLuciano Coelho struct ieee80211_chanctx_conf *conf; 84077eeba97SLuciano Coelho struct ieee80211_chanctx *curr_ctx = NULL; 84177eeba97SLuciano Coelho int ret = 0; 842d01a1e65SMichal Kazior 843708d50edSAyala Beker if (WARN_ON(sdata->vif.type == NL80211_IFTYPE_NAN)) 844708d50edSAyala Beker return -ENOTSUPP; 845708d50edSAyala Beker 846d8675a63SJohannes Berg conf = rcu_dereference_protected(link->conf->chanctx_conf, 84777eeba97SLuciano Coelho lockdep_is_held(&local->chanctx_mtx)); 848d01a1e65SMichal Kazior 84977eeba97SLuciano Coelho if (conf) { 85077eeba97SLuciano Coelho curr_ctx = container_of(conf, struct ieee80211_chanctx, conf); 85135f2fce9SMichal Kazior 852*727eff4dSGregory Greenman drv_unassign_vif_chanctx(local, sdata, link->conf, curr_ctx); 85377eeba97SLuciano Coelho conf = NULL; 854b4f85443SJohannes Berg list_del(&link->assigned_chanctx_list); 85577eeba97SLuciano Coelho } 85677eeba97SLuciano Coelho 85777eeba97SLuciano Coelho if (new_ctx) { 858*727eff4dSGregory Greenman ret = drv_assign_vif_chanctx(local, sdata, link->conf, new_ctx); 85977eeba97SLuciano Coelho if (ret) 86077eeba97SLuciano Coelho goto out; 86177eeba97SLuciano Coelho 86277eeba97SLuciano Coelho conf = &new_ctx->conf; 863b4f85443SJohannes Berg list_add(&link->assigned_chanctx_list, 864b4f85443SJohannes Berg &new_ctx->assigned_links); 86577eeba97SLuciano Coelho } 86677eeba97SLuciano Coelho 86777eeba97SLuciano Coelho out: 868d8675a63SJohannes Berg rcu_assign_pointer(link->conf->chanctx_conf, conf); 86977eeba97SLuciano Coelho 870f276e20bSJohannes Berg sdata->vif.cfg.idle = !conf; 87177eeba97SLuciano Coelho 872c0166da9SMichal Kazior if (curr_ctx && ieee80211_chanctx_num_assigned(local, curr_ctx) > 0) { 87377eeba97SLuciano Coelho ieee80211_recalc_chanctx_chantype(local, curr_ctx); 87477eeba97SLuciano Coelho ieee80211_recalc_smps_chanctx(local, curr_ctx); 87577eeba97SLuciano Coelho ieee80211_recalc_radar_chanctx(local, curr_ctx); 87677eeba97SLuciano Coelho ieee80211_recalc_chanctx_min_def(local, curr_ctx); 87777eeba97SLuciano Coelho } 87877eeba97SLuciano Coelho 879c0166da9SMichal Kazior if (new_ctx && ieee80211_chanctx_num_assigned(local, new_ctx) > 0) { 880db82d8a9SLorenzo Bianconi ieee80211_recalc_txpower(sdata, false); 88177eeba97SLuciano Coelho ieee80211_recalc_chanctx_min_def(local, new_ctx); 88277eeba97SLuciano Coelho } 8835bbe754dSJohannes Berg 8845bbe754dSJohannes Berg if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE && 8855bbe754dSJohannes Berg sdata->vif.type != NL80211_IFTYPE_MONITOR) 8867b7090b4SJohannes Berg ieee80211_vif_cfg_change_notify(sdata, BSS_CHANGED_IDLE); 887fd0f979aSJohannes Berg 88817c18bf8SJohannes Berg ieee80211_check_fast_xmit_iface(sdata); 88917c18bf8SJohannes Berg 89077eeba97SLuciano Coelho return ret; 891d01a1e65SMichal Kazior } 892d01a1e65SMichal Kazior 89304ecd257SJohannes Berg void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local, 89404ecd257SJohannes Berg struct ieee80211_chanctx *chanctx) 89504ecd257SJohannes Berg { 89604ecd257SJohannes Berg struct ieee80211_sub_if_data *sdata; 89704ecd257SJohannes Berg u8 rx_chains_static, rx_chains_dynamic; 89804ecd257SJohannes Berg 89904ecd257SJohannes Berg lockdep_assert_held(&local->chanctx_mtx); 90004ecd257SJohannes Berg 90104ecd257SJohannes Berg rx_chains_static = 1; 90204ecd257SJohannes Berg rx_chains_dynamic = 1; 90304ecd257SJohannes Berg 90404ecd257SJohannes Berg rcu_read_lock(); 90504ecd257SJohannes Berg list_for_each_entry_rcu(sdata, &local->interfaces, list) { 90604ecd257SJohannes Berg u8 needed_static, needed_dynamic; 907b4f85443SJohannes Berg unsigned int link_id; 90804ecd257SJohannes Berg 90904ecd257SJohannes Berg if (!ieee80211_sdata_running(sdata)) 91004ecd257SJohannes Berg continue; 91104ecd257SJohannes Berg 91204ecd257SJohannes Berg switch (sdata->vif.type) { 91304ecd257SJohannes Berg case NL80211_IFTYPE_STATION: 91404ecd257SJohannes Berg if (!sdata->u.mgd.associated) 91504ecd257SJohannes Berg continue; 91604ecd257SJohannes Berg break; 91704ecd257SJohannes Berg case NL80211_IFTYPE_AP: 91804ecd257SJohannes Berg case NL80211_IFTYPE_ADHOC: 91904ecd257SJohannes Berg case NL80211_IFTYPE_MESH_POINT: 920239281f8SRostislav Lisovy case NL80211_IFTYPE_OCB: 92104ecd257SJohannes Berg break; 92204ecd257SJohannes Berg default: 923b4f85443SJohannes Berg continue; 92404ecd257SJohannes Berg } 92504ecd257SJohannes Berg 926b4f85443SJohannes Berg for (link_id = 0; link_id < ARRAY_SIZE(sdata->link); link_id++) { 927d8675a63SJohannes Berg struct ieee80211_link_data *link; 928b4f85443SJohannes Berg 929d8675a63SJohannes Berg link = rcu_dereference(sdata->link[link_id]); 930d8675a63SJohannes Berg 931d8675a63SJohannes Berg if (!link) 932b4f85443SJohannes Berg continue; 933b4f85443SJohannes Berg 934d8675a63SJohannes Berg if (rcu_access_pointer(link->conf->chanctx_conf) != &chanctx->conf) 935b4f85443SJohannes Berg continue; 936b4f85443SJohannes Berg 937b4f85443SJohannes Berg switch (link->smps_mode) { 93804ecd257SJohannes Berg default: 93904ecd257SJohannes Berg WARN_ONCE(1, "Invalid SMPS mode %d\n", 940b4f85443SJohannes Berg link->smps_mode); 941fc0561dcSGustavo A. R. Silva fallthrough; 94204ecd257SJohannes Berg case IEEE80211_SMPS_OFF: 943b4f85443SJohannes Berg needed_static = link->needed_rx_chains; 944b4f85443SJohannes Berg needed_dynamic = link->needed_rx_chains; 94504ecd257SJohannes Berg break; 94604ecd257SJohannes Berg case IEEE80211_SMPS_DYNAMIC: 94704ecd257SJohannes Berg needed_static = 1; 948b4f85443SJohannes Berg needed_dynamic = link->needed_rx_chains; 94904ecd257SJohannes Berg break; 95004ecd257SJohannes Berg case IEEE80211_SMPS_STATIC: 95104ecd257SJohannes Berg needed_static = 1; 95204ecd257SJohannes Berg needed_dynamic = 1; 95304ecd257SJohannes Berg break; 95404ecd257SJohannes Berg } 95504ecd257SJohannes Berg 95604ecd257SJohannes Berg rx_chains_static = max(rx_chains_static, needed_static); 95704ecd257SJohannes Berg rx_chains_dynamic = max(rx_chains_dynamic, needed_dynamic); 95804ecd257SJohannes Berg } 959b4f85443SJohannes Berg } 9607b8a9cddSIdo Yariv 9617b8a9cddSIdo Yariv /* Disable SMPS for the monitor interface */ 9627b8a9cddSIdo Yariv sdata = rcu_dereference(local->monitor_sdata); 9637b8a9cddSIdo Yariv if (sdata && 964d8675a63SJohannes Berg rcu_access_pointer(sdata->vif.bss_conf.chanctx_conf) == &chanctx->conf) 9657b8a9cddSIdo Yariv rx_chains_dynamic = rx_chains_static = local->rx_chains; 9667b8a9cddSIdo Yariv 96704ecd257SJohannes Berg rcu_read_unlock(); 96804ecd257SJohannes Berg 96904ecd257SJohannes Berg if (!local->use_chanctx) { 97004ecd257SJohannes Berg if (rx_chains_static > 1) 97104ecd257SJohannes Berg local->smps_mode = IEEE80211_SMPS_OFF; 97204ecd257SJohannes Berg else if (rx_chains_dynamic > 1) 97304ecd257SJohannes Berg local->smps_mode = IEEE80211_SMPS_DYNAMIC; 97404ecd257SJohannes Berg else 97504ecd257SJohannes Berg local->smps_mode = IEEE80211_SMPS_STATIC; 97604ecd257SJohannes Berg ieee80211_hw_config(local, 0); 97704ecd257SJohannes Berg } 97804ecd257SJohannes Berg 97904ecd257SJohannes Berg if (rx_chains_static == chanctx->conf.rx_chains_static && 98004ecd257SJohannes Berg rx_chains_dynamic == chanctx->conf.rx_chains_dynamic) 98104ecd257SJohannes Berg return; 98204ecd257SJohannes Berg 98304ecd257SJohannes Berg chanctx->conf.rx_chains_static = rx_chains_static; 98404ecd257SJohannes Berg chanctx->conf.rx_chains_dynamic = rx_chains_dynamic; 98504ecd257SJohannes Berg drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RX_CHAINS); 98604ecd257SJohannes Berg } 98704ecd257SJohannes Berg 98811335a55SLuciano Coelho static void 989b4f85443SJohannes Berg __ieee80211_link_copy_chanctx_to_vlans(struct ieee80211_link_data *link, 99011335a55SLuciano Coelho bool clear) 99111335a55SLuciano Coelho { 992b4f85443SJohannes Berg struct ieee80211_sub_if_data *sdata = link->sdata; 993b4f85443SJohannes Berg unsigned int link_id = link->link_id; 994d8675a63SJohannes Berg struct ieee80211_bss_conf *link_conf = link->conf; 99533926eb7SJohannes Berg struct ieee80211_local *local __maybe_unused = sdata->local; 99611335a55SLuciano Coelho struct ieee80211_sub_if_data *vlan; 99711335a55SLuciano Coelho struct ieee80211_chanctx_conf *conf; 99811335a55SLuciano Coelho 99911335a55SLuciano Coelho if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP)) 100011335a55SLuciano Coelho return; 100111335a55SLuciano Coelho 100211335a55SLuciano Coelho lockdep_assert_held(&local->mtx); 100311335a55SLuciano Coelho 100411335a55SLuciano Coelho /* Check that conf exists, even when clearing this function 100511335a55SLuciano Coelho * must be called with the AP's channel context still there 100611335a55SLuciano Coelho * as it would otherwise cause VLANs to have an invalid 100711335a55SLuciano Coelho * channel context pointer for a while, possibly pointing 100811335a55SLuciano Coelho * to a channel context that has already been freed. 100911335a55SLuciano Coelho */ 1010b4f85443SJohannes Berg conf = rcu_dereference_protected(link_conf->chanctx_conf, 101111335a55SLuciano Coelho lockdep_is_held(&local->chanctx_mtx)); 101211335a55SLuciano Coelho WARN_ON(!conf); 101311335a55SLuciano Coelho 101411335a55SLuciano Coelho if (clear) 101511335a55SLuciano Coelho conf = NULL; 101611335a55SLuciano Coelho 1017d8675a63SJohannes Berg rcu_read_lock(); 1018d8675a63SJohannes Berg list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) { 1019d8675a63SJohannes Berg struct ieee80211_bss_conf *vlan_conf; 1020d8675a63SJohannes Berg 1021d8675a63SJohannes Berg vlan_conf = rcu_dereference(vlan->vif.link_conf[link_id]); 1022d8675a63SJohannes Berg if (WARN_ON(!vlan_conf)) 1023d8675a63SJohannes Berg continue; 1024d8675a63SJohannes Berg 1025d8675a63SJohannes Berg rcu_assign_pointer(vlan_conf->chanctx_conf, conf); 1026d8675a63SJohannes Berg } 1027d8675a63SJohannes Berg rcu_read_unlock(); 102811335a55SLuciano Coelho } 102911335a55SLuciano Coelho 1030b4f85443SJohannes Berg void ieee80211_link_copy_chanctx_to_vlans(struct ieee80211_link_data *link, 103111335a55SLuciano Coelho bool clear) 103211335a55SLuciano Coelho { 1033b4f85443SJohannes Berg struct ieee80211_local *local = link->sdata->local; 103411335a55SLuciano Coelho 103511335a55SLuciano Coelho mutex_lock(&local->chanctx_mtx); 103611335a55SLuciano Coelho 1037b4f85443SJohannes Berg __ieee80211_link_copy_chanctx_to_vlans(link, clear); 103811335a55SLuciano Coelho 103911335a55SLuciano Coelho mutex_unlock(&local->chanctx_mtx); 104011335a55SLuciano Coelho } 104111335a55SLuciano Coelho 1042b4f85443SJohannes Berg int ieee80211_link_unreserve_chanctx(struct ieee80211_link_data *link) 104311335a55SLuciano Coelho { 1044b4f85443SJohannes Berg struct ieee80211_sub_if_data *sdata = link->sdata; 1045b4f85443SJohannes Berg struct ieee80211_chanctx *ctx = link->reserved_chanctx; 1046e3afb920SMichal Kazior 104711335a55SLuciano Coelho lockdep_assert_held(&sdata->local->chanctx_mtx); 104811335a55SLuciano Coelho 1049e3afb920SMichal Kazior if (WARN_ON(!ctx)) 105011335a55SLuciano Coelho return -EINVAL; 105111335a55SLuciano Coelho 1052b4f85443SJohannes Berg list_del(&link->reserved_chanctx_list); 1053b4f85443SJohannes Berg link->reserved_chanctx = NULL; 105411335a55SLuciano Coelho 10555bcae31dSMichal Kazior if (ieee80211_chanctx_refcount(sdata->local, ctx) == 0) { 10565bcae31dSMichal Kazior if (ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER) { 10575bcae31dSMichal Kazior if (WARN_ON(!ctx->replace_ctx)) 10585bcae31dSMichal Kazior return -EINVAL; 10595bcae31dSMichal Kazior 10605bcae31dSMichal Kazior WARN_ON(ctx->replace_ctx->replace_state != 10615bcae31dSMichal Kazior IEEE80211_CHANCTX_WILL_BE_REPLACED); 10625bcae31dSMichal Kazior WARN_ON(ctx->replace_ctx->replace_ctx != ctx); 10635bcae31dSMichal Kazior 10645bcae31dSMichal Kazior ctx->replace_ctx->replace_ctx = NULL; 10655bcae31dSMichal Kazior ctx->replace_ctx->replace_state = 10665bcae31dSMichal Kazior IEEE80211_CHANCTX_REPLACE_NONE; 10675bcae31dSMichal Kazior 10685bcae31dSMichal Kazior list_del_rcu(&ctx->list); 10695bcae31dSMichal Kazior kfree_rcu(ctx, rcu_head); 10705bcae31dSMichal Kazior } else { 1071e3afb920SMichal Kazior ieee80211_free_chanctx(sdata->local, ctx); 10725bcae31dSMichal Kazior } 10735bcae31dSMichal Kazior } 1074e3afb920SMichal Kazior 107511335a55SLuciano Coelho return 0; 107611335a55SLuciano Coelho } 107711335a55SLuciano Coelho 1078b4f85443SJohannes Berg int ieee80211_link_reserve_chanctx(struct ieee80211_link_data *link, 107911335a55SLuciano Coelho const struct cfg80211_chan_def *chandef, 108009332481SMichal Kazior enum ieee80211_chanctx_mode mode, 108109332481SMichal Kazior bool radar_required) 108211335a55SLuciano Coelho { 1083b4f85443SJohannes Berg struct ieee80211_sub_if_data *sdata = link->sdata; 108411335a55SLuciano Coelho struct ieee80211_local *local = sdata->local; 10855bcae31dSMichal Kazior struct ieee80211_chanctx *new_ctx, *curr_ctx, *ctx; 108611335a55SLuciano Coelho 10875bcae31dSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 108811335a55SLuciano Coelho 1089b4f85443SJohannes Berg curr_ctx = ieee80211_link_get_chanctx(link); 10905bcae31dSMichal Kazior if (curr_ctx && local->use_chanctx && !local->ops->switch_vif_chanctx) 10915bcae31dSMichal Kazior return -ENOTSUPP; 109211335a55SLuciano Coelho 109313f348a8SMichal Kazior new_ctx = ieee80211_find_reservation_chanctx(local, chandef, mode); 109411335a55SLuciano Coelho if (!new_ctx) { 10955bcae31dSMichal Kazior if (ieee80211_can_create_new_chanctx(local)) { 109611335a55SLuciano Coelho new_ctx = ieee80211_new_chanctx(local, chandef, mode); 10975bcae31dSMichal Kazior if (IS_ERR(new_ctx)) 10985bcae31dSMichal Kazior return PTR_ERR(new_ctx); 1099c2b90ad8SMichal Kazior } else { 11005bcae31dSMichal Kazior if (!curr_ctx || 11015bcae31dSMichal Kazior (curr_ctx->replace_state == 11025bcae31dSMichal Kazior IEEE80211_CHANCTX_WILL_BE_REPLACED) || 1103b4f85443SJohannes Berg !list_empty(&curr_ctx->reserved_links)) { 11045bcae31dSMichal Kazior /* 1105b4f85443SJohannes Berg * Another link already requested this context 11065bcae31dSMichal Kazior * for a reservation. Find another one hoping 1107b4f85443SJohannes Berg * all links assigned to it will also switch 11085bcae31dSMichal Kazior * soon enough. 11095bcae31dSMichal Kazior * 11105bcae31dSMichal Kazior * TODO: This needs a little more work as some 11115bcae31dSMichal Kazior * cases (more than 2 chanctx capable devices) 11125bcae31dSMichal Kazior * may fail which could otherwise succeed 11135bcae31dSMichal Kazior * provided some channel context juggling was 11145bcae31dSMichal Kazior * performed. 11155bcae31dSMichal Kazior * 1116b4f85443SJohannes Berg * Consider ctx1..3, link1..6, each ctx has 2 1117b4f85443SJohannes Berg * links. link1 and link2 from ctx1 request new 11185bcae31dSMichal Kazior * different chandefs starting 2 in-place 11195bcae31dSMichal Kazior * reserations with ctx4 and ctx5 replacing 1120b4f85443SJohannes Berg * ctx1 and ctx2 respectively. Next link5 and 1121b4f85443SJohannes Berg * link6 from ctx3 reserve ctx4. If link3 and 1122b4f85443SJohannes Berg * link4 remain on ctx2 as they are then this 11235bcae31dSMichal Kazior * fails unless `replace_ctx` from ctx5 is 11245bcae31dSMichal Kazior * replaced with ctx3. 11255bcae31dSMichal Kazior */ 11265bcae31dSMichal Kazior list_for_each_entry(ctx, &local->chanctx_list, 11275bcae31dSMichal Kazior list) { 11285bcae31dSMichal Kazior if (ctx->replace_state != 11295bcae31dSMichal Kazior IEEE80211_CHANCTX_REPLACE_NONE) 11305bcae31dSMichal Kazior continue; 11315bcae31dSMichal Kazior 1132b4f85443SJohannes Berg if (!list_empty(&ctx->reserved_links)) 11335bcae31dSMichal Kazior continue; 11345bcae31dSMichal Kazior 11355bcae31dSMichal Kazior curr_ctx = ctx; 11365bcae31dSMichal Kazior break; 11375bcae31dSMichal Kazior } 11385bcae31dSMichal Kazior } 11395bcae31dSMichal Kazior 11405bcae31dSMichal Kazior /* 11415bcae31dSMichal Kazior * If that's true then all available contexts already 11425bcae31dSMichal Kazior * have reservations and cannot be used. 11435bcae31dSMichal Kazior */ 11445bcae31dSMichal Kazior if (!curr_ctx || 11455bcae31dSMichal Kazior (curr_ctx->replace_state == 11465bcae31dSMichal Kazior IEEE80211_CHANCTX_WILL_BE_REPLACED) || 1147b4f85443SJohannes Berg !list_empty(&curr_ctx->reserved_links)) 11485bcae31dSMichal Kazior return -EBUSY; 11495bcae31dSMichal Kazior 11505bcae31dSMichal Kazior new_ctx = ieee80211_alloc_chanctx(local, chandef, mode); 11515bcae31dSMichal Kazior if (!new_ctx) 11525bcae31dSMichal Kazior return -ENOMEM; 11535bcae31dSMichal Kazior 11545bcae31dSMichal Kazior new_ctx->replace_ctx = curr_ctx; 11555bcae31dSMichal Kazior new_ctx->replace_state = 11565bcae31dSMichal Kazior IEEE80211_CHANCTX_REPLACES_OTHER; 11575bcae31dSMichal Kazior 11585bcae31dSMichal Kazior curr_ctx->replace_ctx = new_ctx; 11595bcae31dSMichal Kazior curr_ctx->replace_state = 11605bcae31dSMichal Kazior IEEE80211_CHANCTX_WILL_BE_REPLACED; 11615bcae31dSMichal Kazior 11625bcae31dSMichal Kazior list_add_rcu(&new_ctx->list, &local->chanctx_list); 116311335a55SLuciano Coelho } 11645d52ee81SLuciano Coelho } 116511335a55SLuciano Coelho 1166b4f85443SJohannes Berg list_add(&link->reserved_chanctx_list, &new_ctx->reserved_links); 1167b4f85443SJohannes Berg link->reserved_chanctx = new_ctx; 1168b4f85443SJohannes Berg link->reserved_chandef = *chandef; 1169b4f85443SJohannes Berg link->reserved_radar_required = radar_required; 1170b4f85443SJohannes Berg link->reserved_ready = false; 11715bcae31dSMichal Kazior 11725bcae31dSMichal Kazior return 0; 117311335a55SLuciano Coelho } 117411335a55SLuciano Coelho 117503078de4SMichal Kazior static void 1176b4f85443SJohannes Berg ieee80211_link_chanctx_reservation_complete(struct ieee80211_link_data *link) 117703078de4SMichal Kazior { 1178b4f85443SJohannes Berg struct ieee80211_sub_if_data *sdata = link->sdata; 1179b4f85443SJohannes Berg 118003078de4SMichal Kazior switch (sdata->vif.type) { 118103078de4SMichal Kazior case NL80211_IFTYPE_ADHOC: 118203078de4SMichal Kazior case NL80211_IFTYPE_AP: 118303078de4SMichal Kazior case NL80211_IFTYPE_MESH_POINT: 11846e0bd6c3SRostislav Lisovy case NL80211_IFTYPE_OCB: 118503078de4SMichal Kazior ieee80211_queue_work(&sdata->local->hw, 1186b4f85443SJohannes Berg &link->csa_finalize_work); 118703078de4SMichal Kazior break; 118803078de4SMichal Kazior case NL80211_IFTYPE_STATION: 11894c3ebc56SMichal Kazior ieee80211_queue_work(&sdata->local->hw, 11905bd5666dSJohannes Berg &link->u.mgd.chswitch_work); 11914c3ebc56SMichal Kazior break; 11924c3ebc56SMichal Kazior case NL80211_IFTYPE_UNSPECIFIED: 119303078de4SMichal Kazior case NL80211_IFTYPE_AP_VLAN: 119403078de4SMichal Kazior case NL80211_IFTYPE_WDS: 119503078de4SMichal Kazior case NL80211_IFTYPE_MONITOR: 119603078de4SMichal Kazior case NL80211_IFTYPE_P2P_CLIENT: 119703078de4SMichal Kazior case NL80211_IFTYPE_P2P_GO: 119803078de4SMichal Kazior case NL80211_IFTYPE_P2P_DEVICE: 1199cb3b7d87SAyala Beker case NL80211_IFTYPE_NAN: 120003078de4SMichal Kazior case NUM_NL80211_IFTYPES: 120103078de4SMichal Kazior WARN_ON(1); 120203078de4SMichal Kazior break; 120303078de4SMichal Kazior } 120403078de4SMichal Kazior } 120503078de4SMichal Kazior 12062967e031SFelix Fietkau static void 1207b4f85443SJohannes Berg ieee80211_link_update_chandef(struct ieee80211_link_data *link, 12082967e031SFelix Fietkau const struct cfg80211_chan_def *chandef) 12092967e031SFelix Fietkau { 1210b4f85443SJohannes Berg struct ieee80211_sub_if_data *sdata = link->sdata; 1211b4f85443SJohannes Berg unsigned int link_id = link->link_id; 12122967e031SFelix Fietkau struct ieee80211_sub_if_data *vlan; 12132967e031SFelix Fietkau 1214d8675a63SJohannes Berg link->conf->chandef = *chandef; 12152967e031SFelix Fietkau 12162967e031SFelix Fietkau if (sdata->vif.type != NL80211_IFTYPE_AP) 12172967e031SFelix Fietkau return; 12182967e031SFelix Fietkau 1219d8675a63SJohannes Berg rcu_read_lock(); 1220d8675a63SJohannes Berg list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) { 1221d8675a63SJohannes Berg struct ieee80211_bss_conf *vlan_conf; 1222d8675a63SJohannes Berg 1223d8675a63SJohannes Berg vlan_conf = rcu_dereference(vlan->vif.link_conf[link_id]); 1224d8675a63SJohannes Berg if (WARN_ON(!vlan_conf)) 1225d8675a63SJohannes Berg continue; 1226d8675a63SJohannes Berg 1227d8675a63SJohannes Berg vlan_conf->chandef = *chandef; 1228d8675a63SJohannes Berg } 1229d8675a63SJohannes Berg rcu_read_unlock(); 12302967e031SFelix Fietkau } 12312967e031SFelix Fietkau 12325bcae31dSMichal Kazior static int 1233b4f85443SJohannes Berg ieee80211_link_use_reserved_reassign(struct ieee80211_link_data *link) 123411335a55SLuciano Coelho { 1235b4f85443SJohannes Berg struct ieee80211_sub_if_data *sdata = link->sdata; 1236d8675a63SJohannes Berg struct ieee80211_bss_conf *link_conf = link->conf; 123711335a55SLuciano Coelho struct ieee80211_local *local = sdata->local; 12385bcae31dSMichal Kazior struct ieee80211_vif_chanctx_switch vif_chsw[1] = {}; 12395bcae31dSMichal Kazior struct ieee80211_chanctx *old_ctx, *new_ctx; 12405bcae31dSMichal Kazior const struct cfg80211_chan_def *chandef; 12415bcae31dSMichal Kazior u32 changed = 0; 12425bcae31dSMichal Kazior int err; 124311335a55SLuciano Coelho 124411335a55SLuciano Coelho lockdep_assert_held(&local->mtx); 12455bcae31dSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 124611335a55SLuciano Coelho 1247b4f85443SJohannes Berg new_ctx = link->reserved_chanctx; 1248b4f85443SJohannes Berg old_ctx = ieee80211_link_get_chanctx(link); 124911335a55SLuciano Coelho 1250b4f85443SJohannes Berg if (WARN_ON(!link->reserved_ready)) 12515bcae31dSMichal Kazior return -EBUSY; 125211335a55SLuciano Coelho 12535bcae31dSMichal Kazior if (WARN_ON(!new_ctx)) 12545bcae31dSMichal Kazior return -EINVAL; 125511335a55SLuciano Coelho 12565bcae31dSMichal Kazior if (WARN_ON(!old_ctx)) 12575bcae31dSMichal Kazior return -EINVAL; 125811335a55SLuciano Coelho 12595bcae31dSMichal Kazior if (WARN_ON(new_ctx->replace_state == 12605bcae31dSMichal Kazior IEEE80211_CHANCTX_REPLACES_OTHER)) 12615bcae31dSMichal Kazior return -EINVAL; 126211335a55SLuciano Coelho 12635bcae31dSMichal Kazior chandef = ieee80211_chanctx_non_reserved_chandef(local, new_ctx, 1264b4f85443SJohannes Berg &link->reserved_chandef); 12655bcae31dSMichal Kazior if (WARN_ON(!chandef)) 12665bcae31dSMichal Kazior return -EINVAL; 126711335a55SLuciano Coelho 1268b4f85443SJohannes Berg if (link_conf->chandef.width != link->reserved_chandef.width) 1269d6c37509SMordechay Goodstein changed = BSS_CHANGED_BANDWIDTH; 127044b72ca8SIlan Peer 1271b4f85443SJohannes Berg ieee80211_link_update_chandef(link, &link->reserved_chandef); 1272bf45a242SAndrei Otcheretianski 1273d6c37509SMordechay Goodstein ieee80211_change_chanctx(local, new_ctx, old_ctx, chandef); 127444b72ca8SIlan Peer 12755bcae31dSMichal Kazior vif_chsw[0].vif = &sdata->vif; 12765bcae31dSMichal Kazior vif_chsw[0].old_ctx = &old_ctx->conf; 12775bcae31dSMichal Kazior vif_chsw[0].new_ctx = &new_ctx->conf; 1278*727eff4dSGregory Greenman vif_chsw[0].link_conf = link->conf; 12795bcae31dSMichal Kazior 1280b4f85443SJohannes Berg list_del(&link->reserved_chanctx_list); 1281b4f85443SJohannes Berg link->reserved_chanctx = NULL; 128211335a55SLuciano Coelho 12835bcae31dSMichal Kazior err = drv_switch_vif_chanctx(local, vif_chsw, 1, 12845bcae31dSMichal Kazior CHANCTX_SWMODE_REASSIGN_VIF); 12855bcae31dSMichal Kazior if (err) { 12865bcae31dSMichal Kazior if (ieee80211_chanctx_refcount(local, new_ctx) == 0) 12875bcae31dSMichal Kazior ieee80211_free_chanctx(local, new_ctx); 12885bcae31dSMichal Kazior 128903078de4SMichal Kazior goto out; 129011335a55SLuciano Coelho } 129111335a55SLuciano Coelho 1292b4f85443SJohannes Berg list_move(&link->assigned_chanctx_list, &new_ctx->assigned_links); 1293b4f85443SJohannes Berg rcu_assign_pointer(link_conf->chanctx_conf, &new_ctx->conf); 12945bcae31dSMichal Kazior 129511335a55SLuciano Coelho if (sdata->vif.type == NL80211_IFTYPE_AP) 1296b4f85443SJohannes Berg __ieee80211_link_copy_chanctx_to_vlans(link, false); 12975bcae31dSMichal Kazior 129817c18bf8SJohannes Berg ieee80211_check_fast_xmit_iface(sdata); 129917c18bf8SJohannes Berg 13005bcae31dSMichal Kazior if (ieee80211_chanctx_refcount(local, old_ctx) == 0) 13015bcae31dSMichal Kazior ieee80211_free_chanctx(local, old_ctx); 13025bcae31dSMichal Kazior 1303d6c37509SMordechay Goodstein ieee80211_recalc_chanctx_min_def(local, new_ctx); 1304722ddb0dSEmmanuel Grumbach ieee80211_recalc_smps_chanctx(local, new_ctx); 1305722ddb0dSEmmanuel Grumbach ieee80211_recalc_radar_chanctx(local, new_ctx); 1306722ddb0dSEmmanuel Grumbach 13075bcae31dSMichal Kazior if (changed) 1308d8675a63SJohannes Berg ieee80211_link_info_change_notify(sdata, link, changed); 13095bcae31dSMichal Kazior 131003078de4SMichal Kazior out: 1311b4f85443SJohannes Berg ieee80211_link_chanctx_reservation_complete(link); 13125bcae31dSMichal Kazior return err; 13135d52ee81SLuciano Coelho } 131411335a55SLuciano Coelho 13155bcae31dSMichal Kazior static int 1316b4f85443SJohannes Berg ieee80211_link_use_reserved_assign(struct ieee80211_link_data *link) 13175bcae31dSMichal Kazior { 1318b4f85443SJohannes Berg struct ieee80211_sub_if_data *sdata = link->sdata; 13195bcae31dSMichal Kazior struct ieee80211_local *local = sdata->local; 13205bcae31dSMichal Kazior struct ieee80211_chanctx *old_ctx, *new_ctx; 13215bcae31dSMichal Kazior const struct cfg80211_chan_def *chandef; 13225bcae31dSMichal Kazior int err; 13235bcae31dSMichal Kazior 1324d8675a63SJohannes Berg old_ctx = ieee80211_link_get_chanctx(link); 1325b4f85443SJohannes Berg new_ctx = link->reserved_chanctx; 13265bcae31dSMichal Kazior 1327b4f85443SJohannes Berg if (WARN_ON(!link->reserved_ready)) 13285bcae31dSMichal Kazior return -EINVAL; 13295bcae31dSMichal Kazior 13305bcae31dSMichal Kazior if (WARN_ON(old_ctx)) 13315bcae31dSMichal Kazior return -EINVAL; 13325bcae31dSMichal Kazior 13335bcae31dSMichal Kazior if (WARN_ON(!new_ctx)) 13345bcae31dSMichal Kazior return -EINVAL; 13355bcae31dSMichal Kazior 13365bcae31dSMichal Kazior if (WARN_ON(new_ctx->replace_state == 13375bcae31dSMichal Kazior IEEE80211_CHANCTX_REPLACES_OTHER)) 13385bcae31dSMichal Kazior return -EINVAL; 13395bcae31dSMichal Kazior 13405bcae31dSMichal Kazior chandef = ieee80211_chanctx_non_reserved_chandef(local, new_ctx, 1341b4f85443SJohannes Berg &link->reserved_chandef); 13425bcae31dSMichal Kazior if (WARN_ON(!chandef)) 13435bcae31dSMichal Kazior return -EINVAL; 13445bcae31dSMichal Kazior 1345d6c37509SMordechay Goodstein ieee80211_change_chanctx(local, new_ctx, new_ctx, chandef); 1346bf45a242SAndrei Otcheretianski 1347b4f85443SJohannes Berg list_del(&link->reserved_chanctx_list); 1348b4f85443SJohannes Berg link->reserved_chanctx = NULL; 13495bcae31dSMichal Kazior 1350b4f85443SJohannes Berg err = ieee80211_assign_link_chanctx(link, new_ctx); 13515bcae31dSMichal Kazior if (err) { 13525bcae31dSMichal Kazior if (ieee80211_chanctx_refcount(local, new_ctx) == 0) 13535bcae31dSMichal Kazior ieee80211_free_chanctx(local, new_ctx); 13545bcae31dSMichal Kazior 13555bcae31dSMichal Kazior goto out; 13565bcae31dSMichal Kazior } 13575bcae31dSMichal Kazior 13585bcae31dSMichal Kazior out: 1359b4f85443SJohannes Berg ieee80211_link_chanctx_reservation_complete(link); 13605bcae31dSMichal Kazior return err; 13615bcae31dSMichal Kazior } 13625bcae31dSMichal Kazior 13635bcae31dSMichal Kazior static bool 1364b4f85443SJohannes Berg ieee80211_link_has_in_place_reservation(struct ieee80211_link_data *link) 13655bcae31dSMichal Kazior { 1366b4f85443SJohannes Berg struct ieee80211_sub_if_data *sdata = link->sdata; 13675bcae31dSMichal Kazior struct ieee80211_chanctx *old_ctx, *new_ctx; 13685bcae31dSMichal Kazior 13695bcae31dSMichal Kazior lockdep_assert_held(&sdata->local->chanctx_mtx); 13705bcae31dSMichal Kazior 1371b4f85443SJohannes Berg new_ctx = link->reserved_chanctx; 1372b4f85443SJohannes Berg old_ctx = ieee80211_link_get_chanctx(link); 13735bcae31dSMichal Kazior 13745bcae31dSMichal Kazior if (!old_ctx) 13755bcae31dSMichal Kazior return false; 13765bcae31dSMichal Kazior 13775bcae31dSMichal Kazior if (WARN_ON(!new_ctx)) 13785bcae31dSMichal Kazior return false; 13795bcae31dSMichal Kazior 13805bcae31dSMichal Kazior if (old_ctx->replace_state != IEEE80211_CHANCTX_WILL_BE_REPLACED) 13815bcae31dSMichal Kazior return false; 13825bcae31dSMichal Kazior 13835bcae31dSMichal Kazior if (new_ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) 13845bcae31dSMichal Kazior return false; 13855bcae31dSMichal Kazior 13865bcae31dSMichal Kazior return true; 13875bcae31dSMichal Kazior } 13885bcae31dSMichal Kazior 13895bcae31dSMichal Kazior static int ieee80211_chsw_switch_hwconf(struct ieee80211_local *local, 13905bcae31dSMichal Kazior struct ieee80211_chanctx *new_ctx) 13915bcae31dSMichal Kazior { 13925bcae31dSMichal Kazior const struct cfg80211_chan_def *chandef; 13935bcae31dSMichal Kazior 13945bcae31dSMichal Kazior lockdep_assert_held(&local->mtx); 13955bcae31dSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 13965bcae31dSMichal Kazior 13975bcae31dSMichal Kazior chandef = ieee80211_chanctx_reserved_chandef(local, new_ctx, NULL); 13985bcae31dSMichal Kazior if (WARN_ON(!chandef)) 13995bcae31dSMichal Kazior return -EINVAL; 14005bcae31dSMichal Kazior 14015bcae31dSMichal Kazior local->hw.conf.radar_enabled = new_ctx->conf.radar_enabled; 14025bcae31dSMichal Kazior local->_oper_chandef = *chandef; 14035bcae31dSMichal Kazior ieee80211_hw_config(local, 0); 14045bcae31dSMichal Kazior 14055bcae31dSMichal Kazior return 0; 14065bcae31dSMichal Kazior } 14075bcae31dSMichal Kazior 14085bcae31dSMichal Kazior static int ieee80211_chsw_switch_vifs(struct ieee80211_local *local, 14095bcae31dSMichal Kazior int n_vifs) 14105bcae31dSMichal Kazior { 14115bcae31dSMichal Kazior struct ieee80211_vif_chanctx_switch *vif_chsw; 1412b4f85443SJohannes Berg struct ieee80211_link_data *link; 14135bcae31dSMichal Kazior struct ieee80211_chanctx *ctx, *old_ctx; 14145bcae31dSMichal Kazior int i, err; 14155bcae31dSMichal Kazior 14165bcae31dSMichal Kazior lockdep_assert_held(&local->mtx); 14175bcae31dSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 14185bcae31dSMichal Kazior 14196396bb22SKees Cook vif_chsw = kcalloc(n_vifs, sizeof(vif_chsw[0]), GFP_KERNEL); 14205bcae31dSMichal Kazior if (!vif_chsw) 14215bcae31dSMichal Kazior return -ENOMEM; 14225bcae31dSMichal Kazior 14235bcae31dSMichal Kazior i = 0; 14245bcae31dSMichal Kazior list_for_each_entry(ctx, &local->chanctx_list, list) { 14255bcae31dSMichal Kazior if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) 14265bcae31dSMichal Kazior continue; 14275bcae31dSMichal Kazior 14285bcae31dSMichal Kazior if (WARN_ON(!ctx->replace_ctx)) { 14295bcae31dSMichal Kazior err = -EINVAL; 14305bcae31dSMichal Kazior goto out; 14315bcae31dSMichal Kazior } 14325bcae31dSMichal Kazior 1433b4f85443SJohannes Berg list_for_each_entry(link, &ctx->reserved_links, 14345bcae31dSMichal Kazior reserved_chanctx_list) { 1435b4f85443SJohannes Berg if (!ieee80211_link_has_in_place_reservation(link)) 14365bcae31dSMichal Kazior continue; 14375bcae31dSMichal Kazior 1438b4f85443SJohannes Berg old_ctx = ieee80211_link_get_chanctx(link); 1439b4f85443SJohannes Berg vif_chsw[i].vif = &link->sdata->vif; 14405bcae31dSMichal Kazior vif_chsw[i].old_ctx = &old_ctx->conf; 14415bcae31dSMichal Kazior vif_chsw[i].new_ctx = &ctx->conf; 1442*727eff4dSGregory Greenman vif_chsw[i].link_conf = link->conf; 14435bcae31dSMichal Kazior 14445bcae31dSMichal Kazior i++; 14455bcae31dSMichal Kazior } 14465bcae31dSMichal Kazior } 14475bcae31dSMichal Kazior 14485bcae31dSMichal Kazior err = drv_switch_vif_chanctx(local, vif_chsw, n_vifs, 14495bcae31dSMichal Kazior CHANCTX_SWMODE_SWAP_CONTEXTS); 14505bcae31dSMichal Kazior 14515bcae31dSMichal Kazior out: 14525bcae31dSMichal Kazior kfree(vif_chsw); 14535bcae31dSMichal Kazior return err; 14545bcae31dSMichal Kazior } 14555bcae31dSMichal Kazior 14565bcae31dSMichal Kazior static int ieee80211_chsw_switch_ctxs(struct ieee80211_local *local) 14575bcae31dSMichal Kazior { 14585bcae31dSMichal Kazior struct ieee80211_chanctx *ctx; 14595bcae31dSMichal Kazior int err; 14605bcae31dSMichal Kazior 14615bcae31dSMichal Kazior lockdep_assert_held(&local->mtx); 14625bcae31dSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 14635bcae31dSMichal Kazior 14645bcae31dSMichal Kazior list_for_each_entry(ctx, &local->chanctx_list, list) { 14655bcae31dSMichal Kazior if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) 14665bcae31dSMichal Kazior continue; 14675bcae31dSMichal Kazior 1468b4f85443SJohannes Berg if (!list_empty(&ctx->replace_ctx->assigned_links)) 14695bcae31dSMichal Kazior continue; 14705bcae31dSMichal Kazior 14715bcae31dSMichal Kazior ieee80211_del_chanctx(local, ctx->replace_ctx); 14725bcae31dSMichal Kazior err = ieee80211_add_chanctx(local, ctx); 14735bcae31dSMichal Kazior if (err) 14745bcae31dSMichal Kazior goto err; 14755bcae31dSMichal Kazior } 14765bcae31dSMichal Kazior 14775bcae31dSMichal Kazior return 0; 14785bcae31dSMichal Kazior 14795bcae31dSMichal Kazior err: 14805bcae31dSMichal Kazior WARN_ON(ieee80211_add_chanctx(local, ctx)); 14815bcae31dSMichal Kazior list_for_each_entry_continue_reverse(ctx, &local->chanctx_list, list) { 14825bcae31dSMichal Kazior if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) 14835bcae31dSMichal Kazior continue; 14845bcae31dSMichal Kazior 1485b4f85443SJohannes Berg if (!list_empty(&ctx->replace_ctx->assigned_links)) 14865bcae31dSMichal Kazior continue; 14875bcae31dSMichal Kazior 14885bcae31dSMichal Kazior ieee80211_del_chanctx(local, ctx); 14895bcae31dSMichal Kazior WARN_ON(ieee80211_add_chanctx(local, ctx->replace_ctx)); 14905bcae31dSMichal Kazior } 14915bcae31dSMichal Kazior 14925bcae31dSMichal Kazior return err; 14935bcae31dSMichal Kazior } 14945bcae31dSMichal Kazior 1495649b2a4dSJohannes Berg static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local) 14965bcae31dSMichal Kazior { 14975bcae31dSMichal Kazior struct ieee80211_chanctx *ctx, *ctx_tmp, *old_ctx; 14985bcae31dSMichal Kazior struct ieee80211_chanctx *new_ctx = NULL; 1499b7f2405cSKirtika Ruchandani int err, n_assigned, n_reserved, n_ready; 15005bcae31dSMichal Kazior int n_ctx = 0, n_vifs_switch = 0, n_vifs_assign = 0, n_vifs_ctxless = 0; 15015bcae31dSMichal Kazior 15025bcae31dSMichal Kazior lockdep_assert_held(&local->mtx); 15035bcae31dSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 15045bcae31dSMichal Kazior 15055bcae31dSMichal Kazior /* 15065bcae31dSMichal Kazior * If there are 2 independent pairs of channel contexts performing 15075bcae31dSMichal Kazior * cross-switch of their vifs this code will still wait until both are 15085bcae31dSMichal Kazior * ready even though it could be possible to switch one before the 15095bcae31dSMichal Kazior * other is ready. 15105bcae31dSMichal Kazior * 15115bcae31dSMichal Kazior * For practical reasons and code simplicity just do a single huge 15125bcae31dSMichal Kazior * switch. 15135bcae31dSMichal Kazior */ 15145bcae31dSMichal Kazior 15155bcae31dSMichal Kazior /* 15165bcae31dSMichal Kazior * Verify if the reservation is still feasible. 15175bcae31dSMichal Kazior * - if it's not then disconnect 15185bcae31dSMichal Kazior * - if it is but not all vifs necessary are ready then defer 15195bcae31dSMichal Kazior */ 15205bcae31dSMichal Kazior 15215bcae31dSMichal Kazior list_for_each_entry(ctx, &local->chanctx_list, list) { 1522b4f85443SJohannes Berg struct ieee80211_link_data *link; 1523b4f85443SJohannes Berg 15245bcae31dSMichal Kazior if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) 15255bcae31dSMichal Kazior continue; 15265bcae31dSMichal Kazior 15275bcae31dSMichal Kazior if (WARN_ON(!ctx->replace_ctx)) { 15285bcae31dSMichal Kazior err = -EINVAL; 15295bcae31dSMichal Kazior goto err; 15305bcae31dSMichal Kazior } 15315bcae31dSMichal Kazior 15325bcae31dSMichal Kazior if (!local->use_chanctx) 15335bcae31dSMichal Kazior new_ctx = ctx; 15345bcae31dSMichal Kazior 15355bcae31dSMichal Kazior n_ctx++; 15365bcae31dSMichal Kazior 15375bcae31dSMichal Kazior n_assigned = 0; 15385bcae31dSMichal Kazior n_reserved = 0; 15395bcae31dSMichal Kazior n_ready = 0; 15405bcae31dSMichal Kazior 1541b4f85443SJohannes Berg list_for_each_entry(link, &ctx->replace_ctx->assigned_links, 15425bcae31dSMichal Kazior assigned_chanctx_list) { 15435bcae31dSMichal Kazior n_assigned++; 1544b4f85443SJohannes Berg if (link->reserved_chanctx) { 15455bcae31dSMichal Kazior n_reserved++; 1546b4f85443SJohannes Berg if (link->reserved_ready) 15475bcae31dSMichal Kazior n_ready++; 15485bcae31dSMichal Kazior } 15495bcae31dSMichal Kazior } 15505bcae31dSMichal Kazior 15515bcae31dSMichal Kazior if (n_assigned != n_reserved) { 15525bcae31dSMichal Kazior if (n_ready == n_reserved) { 15535bcae31dSMichal Kazior wiphy_info(local->hw.wiphy, 15545bcae31dSMichal Kazior "channel context reservation cannot be finalized because some interfaces aren't switching\n"); 15555bcae31dSMichal Kazior err = -EBUSY; 15565bcae31dSMichal Kazior goto err; 15575bcae31dSMichal Kazior } 15585bcae31dSMichal Kazior 15595bcae31dSMichal Kazior return -EAGAIN; 15605bcae31dSMichal Kazior } 15615bcae31dSMichal Kazior 15625bcae31dSMichal Kazior ctx->conf.radar_enabled = false; 1563b4f85443SJohannes Berg list_for_each_entry(link, &ctx->reserved_links, 15645bcae31dSMichal Kazior reserved_chanctx_list) { 1565b4f85443SJohannes Berg if (ieee80211_link_has_in_place_reservation(link) && 1566b4f85443SJohannes Berg !link->reserved_ready) 15675bcae31dSMichal Kazior return -EAGAIN; 15685bcae31dSMichal Kazior 1569b4f85443SJohannes Berg old_ctx = ieee80211_link_get_chanctx(link); 15705bcae31dSMichal Kazior if (old_ctx) { 15715bcae31dSMichal Kazior if (old_ctx->replace_state == 15725bcae31dSMichal Kazior IEEE80211_CHANCTX_WILL_BE_REPLACED) 15735bcae31dSMichal Kazior n_vifs_switch++; 15745bcae31dSMichal Kazior else 15755bcae31dSMichal Kazior n_vifs_assign++; 15765bcae31dSMichal Kazior } else { 15775bcae31dSMichal Kazior n_vifs_ctxless++; 15785bcae31dSMichal Kazior } 15795bcae31dSMichal Kazior 1580b4f85443SJohannes Berg if (link->reserved_radar_required) 15815bcae31dSMichal Kazior ctx->conf.radar_enabled = true; 15825bcae31dSMichal Kazior } 15835bcae31dSMichal Kazior } 15845bcae31dSMichal Kazior 15855bcae31dSMichal Kazior if (WARN_ON(n_ctx == 0) || 15865bcae31dSMichal Kazior WARN_ON(n_vifs_switch == 0 && 15875bcae31dSMichal Kazior n_vifs_assign == 0 && 15885bcae31dSMichal Kazior n_vifs_ctxless == 0) || 15895bcae31dSMichal Kazior WARN_ON(n_ctx > 1 && !local->use_chanctx) || 15905bcae31dSMichal Kazior WARN_ON(!new_ctx && !local->use_chanctx)) { 15915bcae31dSMichal Kazior err = -EINVAL; 15925bcae31dSMichal Kazior goto err; 15935bcae31dSMichal Kazior } 15945bcae31dSMichal Kazior 15955bcae31dSMichal Kazior /* 15965bcae31dSMichal Kazior * All necessary vifs are ready. Perform the switch now depending on 15975bcae31dSMichal Kazior * reservations and driver capabilities. 15985bcae31dSMichal Kazior */ 15995bcae31dSMichal Kazior 16005bcae31dSMichal Kazior if (local->use_chanctx) { 16015bcae31dSMichal Kazior if (n_vifs_switch > 0) { 16025bcae31dSMichal Kazior err = ieee80211_chsw_switch_vifs(local, n_vifs_switch); 16035bcae31dSMichal Kazior if (err) 16045bcae31dSMichal Kazior goto err; 16055bcae31dSMichal Kazior } 16065bcae31dSMichal Kazior 16075bcae31dSMichal Kazior if (n_vifs_assign > 0 || n_vifs_ctxless > 0) { 16085bcae31dSMichal Kazior err = ieee80211_chsw_switch_ctxs(local); 16095bcae31dSMichal Kazior if (err) 16105bcae31dSMichal Kazior goto err; 16115bcae31dSMichal Kazior } 16125bcae31dSMichal Kazior } else { 16135bcae31dSMichal Kazior err = ieee80211_chsw_switch_hwconf(local, new_ctx); 16145bcae31dSMichal Kazior if (err) 16155bcae31dSMichal Kazior goto err; 16165bcae31dSMichal Kazior } 16175bcae31dSMichal Kazior 16185bcae31dSMichal Kazior /* 16195bcae31dSMichal Kazior * Update all structures, values and pointers to point to new channel 16205bcae31dSMichal Kazior * context(s). 16215bcae31dSMichal Kazior */ 16225bcae31dSMichal Kazior list_for_each_entry(ctx, &local->chanctx_list, list) { 1623b4f85443SJohannes Berg struct ieee80211_link_data *link, *link_tmp; 1624b4f85443SJohannes Berg 16255bcae31dSMichal Kazior if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) 16265bcae31dSMichal Kazior continue; 16275bcae31dSMichal Kazior 16285bcae31dSMichal Kazior if (WARN_ON(!ctx->replace_ctx)) { 16295bcae31dSMichal Kazior err = -EINVAL; 16305bcae31dSMichal Kazior goto err; 16315bcae31dSMichal Kazior } 16325bcae31dSMichal Kazior 1633b4f85443SJohannes Berg list_for_each_entry(link, &ctx->reserved_links, 16345bcae31dSMichal Kazior reserved_chanctx_list) { 1635b4f85443SJohannes Berg struct ieee80211_sub_if_data *sdata = link->sdata; 1636d8675a63SJohannes Berg struct ieee80211_bss_conf *link_conf = link->conf; 16375bcae31dSMichal Kazior u32 changed = 0; 16385bcae31dSMichal Kazior 1639b4f85443SJohannes Berg if (!ieee80211_link_has_in_place_reservation(link)) 16405bcae31dSMichal Kazior continue; 16415bcae31dSMichal Kazior 1642b4f85443SJohannes Berg rcu_assign_pointer(link_conf->chanctx_conf, 1643d0a9123eSJohannes Berg &ctx->conf); 16445bcae31dSMichal Kazior 16455bcae31dSMichal Kazior if (sdata->vif.type == NL80211_IFTYPE_AP) 1646b4f85443SJohannes Berg __ieee80211_link_copy_chanctx_to_vlans(link, 16475bcae31dSMichal Kazior false); 16485bcae31dSMichal Kazior 164917c18bf8SJohannes Berg ieee80211_check_fast_xmit_iface(sdata); 165017c18bf8SJohannes Berg 1651b4f85443SJohannes Berg link->radar_required = link->reserved_radar_required; 16525bcae31dSMichal Kazior 1653b4f85443SJohannes Berg if (link_conf->chandef.width != link->reserved_chandef.width) 16545bcae31dSMichal Kazior changed = BSS_CHANGED_BANDWIDTH; 16555bcae31dSMichal Kazior 1656b4f85443SJohannes Berg ieee80211_link_update_chandef(link, &link->reserved_chandef); 16575bcae31dSMichal Kazior if (changed) 1658b4f85443SJohannes Berg ieee80211_link_info_change_notify(sdata, 1659d8675a63SJohannes Berg link, 16605bcae31dSMichal Kazior changed); 16615bcae31dSMichal Kazior 1662db82d8a9SLorenzo Bianconi ieee80211_recalc_txpower(sdata, false); 16635bcae31dSMichal Kazior } 166411335a55SLuciano Coelho 166511335a55SLuciano Coelho ieee80211_recalc_chanctx_chantype(local, ctx); 166611335a55SLuciano Coelho ieee80211_recalc_smps_chanctx(local, ctx); 166711335a55SLuciano Coelho ieee80211_recalc_radar_chanctx(local, ctx); 166811335a55SLuciano Coelho ieee80211_recalc_chanctx_min_def(local, ctx); 16695bcae31dSMichal Kazior 1670b4f85443SJohannes Berg list_for_each_entry_safe(link, link_tmp, &ctx->reserved_links, 16715bcae31dSMichal Kazior reserved_chanctx_list) { 1672b4f85443SJohannes Berg if (ieee80211_link_get_chanctx(link) != ctx) 16735bcae31dSMichal Kazior continue; 16745bcae31dSMichal Kazior 1675b4f85443SJohannes Berg list_del(&link->reserved_chanctx_list); 1676b4f85443SJohannes Berg list_move(&link->assigned_chanctx_list, 1677b4f85443SJohannes Berg &ctx->assigned_links); 1678b4f85443SJohannes Berg link->reserved_chanctx = NULL; 167903078de4SMichal Kazior 1680b4f85443SJohannes Berg ieee80211_link_chanctx_reservation_complete(link); 16815bcae31dSMichal Kazior } 16825bcae31dSMichal Kazior 16835bcae31dSMichal Kazior /* 16845bcae31dSMichal Kazior * This context might have been a dependency for an already 16855bcae31dSMichal Kazior * ready re-assign reservation interface that was deferred. Do 16865bcae31dSMichal Kazior * not propagate error to the caller though. The in-place 16875bcae31dSMichal Kazior * reservation for originally requested interface has already 16885bcae31dSMichal Kazior * succeeded at this point. 16895bcae31dSMichal Kazior */ 1690b4f85443SJohannes Berg list_for_each_entry_safe(link, link_tmp, &ctx->reserved_links, 16915bcae31dSMichal Kazior reserved_chanctx_list) { 1692b4f85443SJohannes Berg if (WARN_ON(ieee80211_link_has_in_place_reservation(link))) 16935bcae31dSMichal Kazior continue; 16945bcae31dSMichal Kazior 1695b4f85443SJohannes Berg if (WARN_ON(link->reserved_chanctx != ctx)) 16965bcae31dSMichal Kazior continue; 16975bcae31dSMichal Kazior 1698b4f85443SJohannes Berg if (!link->reserved_ready) 16995bcae31dSMichal Kazior continue; 17005bcae31dSMichal Kazior 1701b4f85443SJohannes Berg if (ieee80211_link_get_chanctx(link)) 1702b4f85443SJohannes Berg err = ieee80211_link_use_reserved_reassign(link); 17035bcae31dSMichal Kazior else 1704b4f85443SJohannes Berg err = ieee80211_link_use_reserved_assign(link); 17055bcae31dSMichal Kazior 17065bcae31dSMichal Kazior if (err) { 1707b4f85443SJohannes Berg link_info(link, 17085bcae31dSMichal Kazior "failed to finalize (re-)assign reservation (err=%d)\n", 17095bcae31dSMichal Kazior err); 1710b4f85443SJohannes Berg ieee80211_link_unreserve_chanctx(link); 17115bcae31dSMichal Kazior cfg80211_stop_iface(local->hw.wiphy, 1712b4f85443SJohannes Berg &link->sdata->wdev, 17135bcae31dSMichal Kazior GFP_KERNEL); 17145bcae31dSMichal Kazior } 17155bcae31dSMichal Kazior } 17165bcae31dSMichal Kazior } 17175bcae31dSMichal Kazior 17185bcae31dSMichal Kazior /* 17195bcae31dSMichal Kazior * Finally free old contexts 17205bcae31dSMichal Kazior */ 17215bcae31dSMichal Kazior 17225bcae31dSMichal Kazior list_for_each_entry_safe(ctx, ctx_tmp, &local->chanctx_list, list) { 17235bcae31dSMichal Kazior if (ctx->replace_state != IEEE80211_CHANCTX_WILL_BE_REPLACED) 17245bcae31dSMichal Kazior continue; 17255bcae31dSMichal Kazior 17265bcae31dSMichal Kazior ctx->replace_ctx->replace_ctx = NULL; 17275bcae31dSMichal Kazior ctx->replace_ctx->replace_state = 17285bcae31dSMichal Kazior IEEE80211_CHANCTX_REPLACE_NONE; 17295bcae31dSMichal Kazior 17305bcae31dSMichal Kazior list_del_rcu(&ctx->list); 17315bcae31dSMichal Kazior kfree_rcu(ctx, rcu_head); 17325bcae31dSMichal Kazior } 17335bcae31dSMichal Kazior 17345bcae31dSMichal Kazior return 0; 17355bcae31dSMichal Kazior 17365bcae31dSMichal Kazior err: 17375bcae31dSMichal Kazior list_for_each_entry(ctx, &local->chanctx_list, list) { 1738b4f85443SJohannes Berg struct ieee80211_link_data *link, *link_tmp; 1739b4f85443SJohannes Berg 17405bcae31dSMichal Kazior if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) 17415bcae31dSMichal Kazior continue; 17425bcae31dSMichal Kazior 1743b4f85443SJohannes Berg list_for_each_entry_safe(link, link_tmp, &ctx->reserved_links, 174403078de4SMichal Kazior reserved_chanctx_list) { 1745b4f85443SJohannes Berg ieee80211_link_unreserve_chanctx(link); 1746b4f85443SJohannes Berg ieee80211_link_chanctx_reservation_complete(link); 174703078de4SMichal Kazior } 17485bcae31dSMichal Kazior } 17495bcae31dSMichal Kazior 17505bcae31dSMichal Kazior return err; 17515bcae31dSMichal Kazior } 17525bcae31dSMichal Kazior 1753b4f85443SJohannes Berg static void __ieee80211_link_release_channel(struct ieee80211_link_data *link) 1754649b2a4dSJohannes Berg { 1755b4f85443SJohannes Berg struct ieee80211_sub_if_data *sdata = link->sdata; 1756d8675a63SJohannes Berg struct ieee80211_bss_conf *link_conf = link->conf; 1757649b2a4dSJohannes Berg struct ieee80211_local *local = sdata->local; 1758649b2a4dSJohannes Berg struct ieee80211_chanctx_conf *conf; 1759649b2a4dSJohannes Berg struct ieee80211_chanctx *ctx; 1760649b2a4dSJohannes Berg bool use_reserved_switch = false; 1761649b2a4dSJohannes Berg 1762649b2a4dSJohannes Berg lockdep_assert_held(&local->chanctx_mtx); 1763649b2a4dSJohannes Berg 1764b4f85443SJohannes Berg conf = rcu_dereference_protected(link_conf->chanctx_conf, 1765649b2a4dSJohannes Berg lockdep_is_held(&local->chanctx_mtx)); 1766649b2a4dSJohannes Berg if (!conf) 1767649b2a4dSJohannes Berg return; 1768649b2a4dSJohannes Berg 1769649b2a4dSJohannes Berg ctx = container_of(conf, struct ieee80211_chanctx, conf); 1770649b2a4dSJohannes Berg 1771b4f85443SJohannes Berg if (link->reserved_chanctx) { 1772b4f85443SJohannes Berg if (link->reserved_chanctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER && 1773b4f85443SJohannes Berg ieee80211_chanctx_num_reserved(local, link->reserved_chanctx) > 1) 1774649b2a4dSJohannes Berg use_reserved_switch = true; 1775649b2a4dSJohannes Berg 1776b4f85443SJohannes Berg ieee80211_link_unreserve_chanctx(link); 1777649b2a4dSJohannes Berg } 1778649b2a4dSJohannes Berg 1779b4f85443SJohannes Berg ieee80211_assign_link_chanctx(link, NULL); 1780649b2a4dSJohannes Berg if (ieee80211_chanctx_refcount(local, ctx) == 0) 1781649b2a4dSJohannes Berg ieee80211_free_chanctx(local, ctx); 1782649b2a4dSJohannes Berg 1783b4f85443SJohannes Berg link->radar_required = false; 1784104f5a62SEliad Peller 1785649b2a4dSJohannes Berg /* Unreserving may ready an in-place reservation. */ 1786649b2a4dSJohannes Berg if (use_reserved_switch) 1787649b2a4dSJohannes Berg ieee80211_vif_use_reserved_switch(local); 1788649b2a4dSJohannes Berg } 1789649b2a4dSJohannes Berg 1790b4f85443SJohannes Berg int ieee80211_link_use_channel(struct ieee80211_link_data *link, 1791649b2a4dSJohannes Berg const struct cfg80211_chan_def *chandef, 1792649b2a4dSJohannes Berg enum ieee80211_chanctx_mode mode) 1793649b2a4dSJohannes Berg { 1794b4f85443SJohannes Berg struct ieee80211_sub_if_data *sdata = link->sdata; 1795649b2a4dSJohannes Berg struct ieee80211_local *local = sdata->local; 1796649b2a4dSJohannes Berg struct ieee80211_chanctx *ctx; 1797649b2a4dSJohannes Berg u8 radar_detect_width = 0; 1798649b2a4dSJohannes Berg int ret; 1799649b2a4dSJohannes Berg 1800649b2a4dSJohannes Berg lockdep_assert_held(&local->mtx); 1801649b2a4dSJohannes Berg 1802649b2a4dSJohannes Berg mutex_lock(&local->chanctx_mtx); 1803649b2a4dSJohannes Berg 1804649b2a4dSJohannes Berg ret = cfg80211_chandef_dfs_required(local->hw.wiphy, 1805649b2a4dSJohannes Berg chandef, 1806649b2a4dSJohannes Berg sdata->wdev.iftype); 1807649b2a4dSJohannes Berg if (ret < 0) 1808649b2a4dSJohannes Berg goto out; 1809649b2a4dSJohannes Berg if (ret > 0) 1810649b2a4dSJohannes Berg radar_detect_width = BIT(chandef->width); 1811649b2a4dSJohannes Berg 1812d8675a63SJohannes Berg link->radar_required = ret; 1813649b2a4dSJohannes Berg 1814649b2a4dSJohannes Berg ret = ieee80211_check_combinations(sdata, chandef, mode, 1815649b2a4dSJohannes Berg radar_detect_width); 1816649b2a4dSJohannes Berg if (ret < 0) 1817649b2a4dSJohannes Berg goto out; 1818649b2a4dSJohannes Berg 1819b4f85443SJohannes Berg __ieee80211_link_release_channel(link); 1820649b2a4dSJohannes Berg 1821649b2a4dSJohannes Berg ctx = ieee80211_find_chanctx(local, chandef, mode); 1822649b2a4dSJohannes Berg if (!ctx) 1823649b2a4dSJohannes Berg ctx = ieee80211_new_chanctx(local, chandef, mode); 1824649b2a4dSJohannes Berg if (IS_ERR(ctx)) { 1825649b2a4dSJohannes Berg ret = PTR_ERR(ctx); 1826649b2a4dSJohannes Berg goto out; 1827649b2a4dSJohannes Berg } 1828649b2a4dSJohannes Berg 1829b4f85443SJohannes Berg ieee80211_link_update_chandef(link, chandef); 1830649b2a4dSJohannes Berg 1831b4f85443SJohannes Berg ret = ieee80211_assign_link_chanctx(link, ctx); 1832649b2a4dSJohannes Berg if (ret) { 1833649b2a4dSJohannes Berg /* if assign fails refcount stays the same */ 1834649b2a4dSJohannes Berg if (ieee80211_chanctx_refcount(local, ctx) == 0) 1835649b2a4dSJohannes Berg ieee80211_free_chanctx(local, ctx); 1836649b2a4dSJohannes Berg goto out; 1837649b2a4dSJohannes Berg } 1838649b2a4dSJohannes Berg 1839649b2a4dSJohannes Berg ieee80211_recalc_smps_chanctx(local, ctx); 1840649b2a4dSJohannes Berg ieee80211_recalc_radar_chanctx(local, ctx); 1841649b2a4dSJohannes Berg out: 1842104f5a62SEliad Peller if (ret) 1843b4f85443SJohannes Berg link->radar_required = false; 1844104f5a62SEliad Peller 1845649b2a4dSJohannes Berg mutex_unlock(&local->chanctx_mtx); 1846649b2a4dSJohannes Berg return ret; 1847649b2a4dSJohannes Berg } 1848649b2a4dSJohannes Berg 1849b4f85443SJohannes Berg int ieee80211_link_use_reserved_context(struct ieee80211_link_data *link) 18505bcae31dSMichal Kazior { 1851b4f85443SJohannes Berg struct ieee80211_sub_if_data *sdata = link->sdata; 18525bcae31dSMichal Kazior struct ieee80211_local *local = sdata->local; 18535bcae31dSMichal Kazior struct ieee80211_chanctx *new_ctx; 18545bcae31dSMichal Kazior struct ieee80211_chanctx *old_ctx; 18555bcae31dSMichal Kazior int err; 18565bcae31dSMichal Kazior 18575bcae31dSMichal Kazior lockdep_assert_held(&local->mtx); 18585bcae31dSMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 18595bcae31dSMichal Kazior 1860b4f85443SJohannes Berg new_ctx = link->reserved_chanctx; 1861b4f85443SJohannes Berg old_ctx = ieee80211_link_get_chanctx(link); 18625bcae31dSMichal Kazior 18635bcae31dSMichal Kazior if (WARN_ON(!new_ctx)) 18645bcae31dSMichal Kazior return -EINVAL; 18655bcae31dSMichal Kazior 18665bcae31dSMichal Kazior if (WARN_ON(new_ctx->replace_state == 18675bcae31dSMichal Kazior IEEE80211_CHANCTX_WILL_BE_REPLACED)) 18685bcae31dSMichal Kazior return -EINVAL; 18695bcae31dSMichal Kazior 1870b4f85443SJohannes Berg if (WARN_ON(link->reserved_ready)) 18715bcae31dSMichal Kazior return -EINVAL; 18725bcae31dSMichal Kazior 1873b4f85443SJohannes Berg link->reserved_ready = true; 18745bcae31dSMichal Kazior 18755bcae31dSMichal Kazior if (new_ctx->replace_state == IEEE80211_CHANCTX_REPLACE_NONE) { 18765bcae31dSMichal Kazior if (old_ctx) 1877b4f85443SJohannes Berg return ieee80211_link_use_reserved_reassign(link); 18785bcae31dSMichal Kazior 1879b4f85443SJohannes Berg return ieee80211_link_use_reserved_assign(link); 18805bcae31dSMichal Kazior } 18815bcae31dSMichal Kazior 18825bcae31dSMichal Kazior /* 18835bcae31dSMichal Kazior * In-place reservation may need to be finalized now either if: 18845bcae31dSMichal Kazior * a) sdata is taking part in the swapping itself and is the last one 18855bcae31dSMichal Kazior * b) sdata has switched with a re-assign reservation to an existing 18865bcae31dSMichal Kazior * context readying in-place switching of old_ctx 18875bcae31dSMichal Kazior * 18885bcae31dSMichal Kazior * In case of (b) do not propagate the error up because the requested 18895bcae31dSMichal Kazior * sdata already switched successfully. Just spill an extra warning. 18905bcae31dSMichal Kazior * The ieee80211_vif_use_reserved_switch() already stops all necessary 18915bcae31dSMichal Kazior * interfaces upon failure. 18925bcae31dSMichal Kazior */ 18935bcae31dSMichal Kazior if ((old_ctx && 18945bcae31dSMichal Kazior old_ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED) || 18955bcae31dSMichal Kazior new_ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER) { 18965bcae31dSMichal Kazior err = ieee80211_vif_use_reserved_switch(local); 18975bcae31dSMichal Kazior if (err && err != -EAGAIN) { 18985bcae31dSMichal Kazior if (new_ctx->replace_state == 18995bcae31dSMichal Kazior IEEE80211_CHANCTX_REPLACES_OTHER) 19005bcae31dSMichal Kazior return err; 19015bcae31dSMichal Kazior 19025bcae31dSMichal Kazior wiphy_info(local->hw.wiphy, 19035bcae31dSMichal Kazior "depending in-place reservation failed (err=%d)\n", 19045bcae31dSMichal Kazior err); 19055bcae31dSMichal Kazior } 19065bcae31dSMichal Kazior } 19075bcae31dSMichal Kazior 19085bcae31dSMichal Kazior return 0; 190973da7d5bSSimon Wunderlich } 191073da7d5bSSimon Wunderlich 1911b4f85443SJohannes Berg int ieee80211_link_change_bandwidth(struct ieee80211_link_data *link, 19122c9b7359SJohannes Berg const struct cfg80211_chan_def *chandef, 19132c9b7359SJohannes Berg u32 *changed) 19142c9b7359SJohannes Berg { 1915b4f85443SJohannes Berg struct ieee80211_sub_if_data *sdata = link->sdata; 1916d8675a63SJohannes Berg struct ieee80211_bss_conf *link_conf = link->conf; 19172c9b7359SJohannes Berg struct ieee80211_local *local = sdata->local; 19182c9b7359SJohannes Berg struct ieee80211_chanctx_conf *conf; 19192c9b7359SJohannes Berg struct ieee80211_chanctx *ctx; 19205bcae31dSMichal Kazior const struct cfg80211_chan_def *compat; 19212c9b7359SJohannes Berg int ret; 19222c9b7359SJohannes Berg 19232c9b7359SJohannes Berg if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef, 19242c9b7359SJohannes Berg IEEE80211_CHAN_DISABLED)) 19252c9b7359SJohannes Berg return -EINVAL; 19262c9b7359SJohannes Berg 19272c9b7359SJohannes Berg mutex_lock(&local->chanctx_mtx); 1928b4f85443SJohannes Berg if (cfg80211_chandef_identical(chandef, &link_conf->chandef)) { 19292c9b7359SJohannes Berg ret = 0; 19302c9b7359SJohannes Berg goto out; 19312c9b7359SJohannes Berg } 19322c9b7359SJohannes Berg 19332c9b7359SJohannes Berg if (chandef->width == NL80211_CHAN_WIDTH_20_NOHT || 1934b4f85443SJohannes Berg link_conf->chandef.width == NL80211_CHAN_WIDTH_20_NOHT) { 19352c9b7359SJohannes Berg ret = -EINVAL; 19362c9b7359SJohannes Berg goto out; 19372c9b7359SJohannes Berg } 19382c9b7359SJohannes Berg 1939b4f85443SJohannes Berg conf = rcu_dereference_protected(link_conf->chanctx_conf, 19402c9b7359SJohannes Berg lockdep_is_held(&local->chanctx_mtx)); 19412c9b7359SJohannes Berg if (!conf) { 19422c9b7359SJohannes Berg ret = -EINVAL; 19432c9b7359SJohannes Berg goto out; 19442c9b7359SJohannes Berg } 19452c9b7359SJohannes Berg 19462c9b7359SJohannes Berg ctx = container_of(conf, struct ieee80211_chanctx, conf); 19475bcae31dSMichal Kazior 19485bcae31dSMichal Kazior compat = cfg80211_chandef_compatible(&conf->def, chandef); 19495bcae31dSMichal Kazior if (!compat) { 19502c9b7359SJohannes Berg ret = -EINVAL; 19512c9b7359SJohannes Berg goto out; 19522c9b7359SJohannes Berg } 19532c9b7359SJohannes Berg 19545bcae31dSMichal Kazior switch (ctx->replace_state) { 19555bcae31dSMichal Kazior case IEEE80211_CHANCTX_REPLACE_NONE: 19565bcae31dSMichal Kazior if (!ieee80211_chanctx_reserved_chandef(local, ctx, compat)) { 19575bcae31dSMichal Kazior ret = -EBUSY; 19585bcae31dSMichal Kazior goto out; 19595bcae31dSMichal Kazior } 19605bcae31dSMichal Kazior break; 19615bcae31dSMichal Kazior case IEEE80211_CHANCTX_WILL_BE_REPLACED: 1962d070f913SStephen Hemminger /* TODO: Perhaps the bandwidth change could be treated as a 19635bcae31dSMichal Kazior * reservation itself? */ 19645bcae31dSMichal Kazior ret = -EBUSY; 19655bcae31dSMichal Kazior goto out; 19665bcae31dSMichal Kazior case IEEE80211_CHANCTX_REPLACES_OTHER: 19675bcae31dSMichal Kazior /* channel context that is going to replace another channel 19685bcae31dSMichal Kazior * context doesn't really exist and shouldn't be assigned 19695bcae31dSMichal Kazior * anywhere yet */ 19705bcae31dSMichal Kazior WARN_ON(1); 19715bcae31dSMichal Kazior break; 19725bcae31dSMichal Kazior } 19735bcae31dSMichal Kazior 1974b4f85443SJohannes Berg ieee80211_link_update_chandef(link, chandef); 19752c9b7359SJohannes Berg 19762c9b7359SJohannes Berg ieee80211_recalc_chanctx_chantype(local, ctx); 19772c9b7359SJohannes Berg 19782c9b7359SJohannes Berg *changed |= BSS_CHANGED_BANDWIDTH; 19792c9b7359SJohannes Berg ret = 0; 19802c9b7359SJohannes Berg out: 19812c9b7359SJohannes Berg mutex_unlock(&local->chanctx_mtx); 19822c9b7359SJohannes Berg return ret; 19832c9b7359SJohannes Berg } 19842c9b7359SJohannes Berg 1985b4f85443SJohannes Berg void ieee80211_link_release_channel(struct ieee80211_link_data *link) 1986d01a1e65SMichal Kazior { 1987b4f85443SJohannes Berg struct ieee80211_sub_if_data *sdata = link->sdata; 1988b4f85443SJohannes Berg 1989d01a1e65SMichal Kazior mutex_lock(&sdata->local->chanctx_mtx); 1990939c4c7eSJohannes Berg if (rcu_access_pointer(link->conf->chanctx_conf)) { 1991939c4c7eSJohannes Berg lockdep_assert_held(&sdata->local->mtx); 1992b4f85443SJohannes Berg __ieee80211_link_release_channel(link); 1993939c4c7eSJohannes Berg } 1994d01a1e65SMichal Kazior mutex_unlock(&sdata->local->chanctx_mtx); 1995d01a1e65SMichal Kazior } 19963448c005SJohannes Berg 1997b4f85443SJohannes Berg void ieee80211_link_vlan_copy_chanctx(struct ieee80211_link_data *link) 19984d76d21bSJohannes Berg { 1999b4f85443SJohannes Berg struct ieee80211_sub_if_data *sdata = link->sdata; 2000b4f85443SJohannes Berg unsigned int link_id = link->link_id; 2001d8675a63SJohannes Berg struct ieee80211_bss_conf *link_conf = link->conf; 2002d8675a63SJohannes Berg struct ieee80211_bss_conf *ap_conf; 20034d76d21bSJohannes Berg struct ieee80211_local *local = sdata->local; 20044d76d21bSJohannes Berg struct ieee80211_sub_if_data *ap; 20054d76d21bSJohannes Berg struct ieee80211_chanctx_conf *conf; 20064d76d21bSJohannes Berg 20074d76d21bSJohannes Berg if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP_VLAN || !sdata->bss)) 20084d76d21bSJohannes Berg return; 20094d76d21bSJohannes Berg 20104d76d21bSJohannes Berg ap = container_of(sdata->bss, struct ieee80211_sub_if_data, u.ap); 20114d76d21bSJohannes Berg 20124d76d21bSJohannes Berg mutex_lock(&local->chanctx_mtx); 20134d76d21bSJohannes Berg 2014d8675a63SJohannes Berg rcu_read_lock(); 2015d8675a63SJohannes Berg ap_conf = rcu_dereference(ap->vif.link_conf[link_id]); 2016d8675a63SJohannes Berg conf = rcu_dereference_protected(ap_conf->chanctx_conf, 20174d76d21bSJohannes Berg lockdep_is_held(&local->chanctx_mtx)); 2018b4f85443SJohannes Berg rcu_assign_pointer(link_conf->chanctx_conf, conf); 2019d8675a63SJohannes Berg rcu_read_unlock(); 20204d76d21bSJohannes Berg mutex_unlock(&local->chanctx_mtx); 20214d76d21bSJohannes Berg } 20224d76d21bSJohannes Berg 20233448c005SJohannes Berg void ieee80211_iter_chan_contexts_atomic( 20243448c005SJohannes Berg struct ieee80211_hw *hw, 20253448c005SJohannes Berg void (*iter)(struct ieee80211_hw *hw, 20263448c005SJohannes Berg struct ieee80211_chanctx_conf *chanctx_conf, 20273448c005SJohannes Berg void *data), 20283448c005SJohannes Berg void *iter_data) 20293448c005SJohannes Berg { 20303448c005SJohannes Berg struct ieee80211_local *local = hw_to_local(hw); 20313448c005SJohannes Berg struct ieee80211_chanctx *ctx; 20323448c005SJohannes Berg 20333448c005SJohannes Berg rcu_read_lock(); 20343448c005SJohannes Berg list_for_each_entry_rcu(ctx, &local->chanctx_list, list) 20358a61af65SJohannes Berg if (ctx->driver_present) 20363448c005SJohannes Berg iter(hw, &ctx->conf, iter_data); 20373448c005SJohannes Berg rcu_read_unlock(); 20383448c005SJohannes Berg } 20393448c005SJohannes Berg EXPORT_SYMBOL_GPL(ieee80211_iter_chan_contexts_atomic); 2040