From: Johannes Berg Date: Wed, 15 Apr 2026 12:42:07 +0000 (+0200) Subject: wifi: mac80211: clean up STA NSS handling X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e5ad38a9b2614d627bc5af7f9f522027aa26e890;p=thirdparty%2Fkernel%2Flinux.git wifi: mac80211: clean up STA NSS handling Move ieee80211_sta_init_nss() from VHT code to station code, and disentangle it from rate control. This way, it becomes clearer when 'rx_nss' is set up. While doing this, fix the client side code to set up link_sta->op_mode_nss instead of link_sta->pub->rx_nss for the opmode element in association response, and remove the (now wrong) comment about handling that in the function. type=cleanup ticket=none Signed-off-by: Johannes Berg Reviewed-on: https://gerritwcs.ir.intel.com/c/iwlwifi-stack-dev/+/273695 Tested-by: iil_jenkins iil_jenkins tested: iil_jenkins iil_jenkins Reviewed-by: Miriam Rachel Korenblit Link: https://patch.msgid.link/20260415144514.bef3d07beeb6.I97fb93ccc1b366110ab23de58fcd73676cdd85d6@changeid Signed-off-by: Johannes Berg --- diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 97292ff51475f..96f040b4672eb 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -553,6 +553,8 @@ static struct sta_info *ieee80211_ibss_finish_sta(struct sta_info *sta) memcpy(addr, sta->sta.addr, ETH_ALEN); + ieee80211_sta_init_nss(&sta->deflink); + ibss_dbg(sdata, "Adding new IBSS station %pM\n", addr); sta_info_pre_move_state(sta, IEEE80211_STA_AUTH); diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 3ab8368acaf9a..76213b50fe577 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -2315,7 +2315,7 @@ ieee80211_sta_cur_vht_bw(struct link_sta_info *link_sta) { return _ieee80211_sta_cur_vht_bw(link_sta, NULL); } -void ieee80211_sta_init_nss(struct link_sta_info *link_sta); + void ieee80211_process_mu_groups(struct ieee80211_sub_if_data *sdata, struct ieee80211_link_data *link, struct ieee80211_mgmt *mgmt); diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index 7cbab90c8784f..94201840677d9 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -470,6 +470,8 @@ static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata, elems->eht_cap, elems->eht_cap_len, &sta->deflink); + ieee80211_sta_init_nss(&sta->deflink); + if (bw != sta->sta.deflink.bandwidth) changed |= IEEE80211_RC_BW_CHANGED; diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 160ae65a5c645..438cc2d4731df 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -5749,9 +5749,9 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link, */ /* - * If an operating mode notification IE is present, override the - * NSS calculation (that would be done in rate_control_rate_init()) - * and use the # of streams from that element. + * If an operating mode notification element is present, set the opmode + * NSS override to correct for the current number of spatial streams, + * overriding the capabilities. ieee80211_sta_init_nss() uses this. */ if (elems->opmode_notif && !(*elems->opmode_notif & IEEE80211_OPMODE_NOTIF_RX_NSS_TYPE_BF)) { @@ -5760,9 +5760,11 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link, nss = *elems->opmode_notif & IEEE80211_OPMODE_NOTIF_RX_NSS_MASK; nss >>= IEEE80211_OPMODE_NOTIF_RX_NSS_SHIFT; nss += 1; - link_sta->pub->rx_nss = nss; + link_sta->op_mode_nss = nss; } + ieee80211_sta_init_nss(link_sta); + /* * Always handle WMM once after association regardless * of the first value the AP uses. Setting -1 here has @@ -10617,7 +10619,6 @@ void ieee80211_process_ml_reconf_resp(struct ieee80211_sub_if_data *sdata, if (add_links_data->link[link_id].status != WLAN_STATUS_SUCCESS) goto disconnect; - ieee80211_sta_init_nss(link_sta); if (ieee80211_sta_activate_link(sta, link_id)) goto disconnect; diff --git a/net/mac80211/ocb.c b/net/mac80211/ocb.c index ebb4f4d88c237..447c84235c1c4 100644 --- a/net/mac80211/ocb.c +++ b/net/mac80211/ocb.c @@ -4,7 +4,7 @@ * * Copyright: (c) 2014 Czech Technical University in Prague * (c) 2014 Volkswagen Group Research - * Copyright (C) 2022 - 2024 Intel Corporation + * Copyright (C) 2022 - 2024, 2026 Intel Corporation * Author: Rostislav Lisovy * Funded by: Volkswagen Group Research */ @@ -92,6 +92,8 @@ static struct sta_info *ieee80211_ocb_finish_sta(struct sta_info *sta) memcpy(addr, sta->sta.addr, ETH_ALEN); + ieee80211_sta_init_nss(&sta->deflink); + ocb_dbg(sdata, "Adding new IBSS station %pM (dev=%s)\n", addr, sdata->name); diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c index 31af7dd6aedc2..ba1a3aa3f5d4a 100644 --- a/net/mac80211/rate.c +++ b/net/mac80211/rate.c @@ -4,7 +4,7 @@ * Copyright 2005-2006, Devicescape Software, Inc. * Copyright (c) 2006 Jiri Benc * Copyright 2017 Intel Deutschland GmbH - * Copyright (C) 2019, 2022-2025 Intel Corporation + * Copyright (C) 2019, 2022-2026 Intel Corporation */ #include @@ -38,8 +38,6 @@ void rate_control_rate_init(struct link_sta_info *link_sta) struct ieee80211_supported_band *sband; struct ieee80211_chanctx_conf *chanctx_conf; - ieee80211_sta_init_nss(link_sta); - if (!ref) return; diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index e08db1b0cb301..0f174a6a04a86 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -3426,6 +3426,98 @@ void ieee80211_sta_remove_link(struct sta_info *sta, unsigned int link_id) sta_remove_link(sta, link_id, true); } +void ieee80211_sta_init_nss(struct link_sta_info *link_sta) +{ + u8 ht_rx_nss = 0, vht_rx_nss = 0, he_rx_nss = 0, eht_rx_nss = 0, rx_nss; + bool support_160; + + if (link_sta->pub->eht_cap.has_eht) { + int i; + const u8 *rx_nss_mcs = (void *)&link_sta->pub->eht_cap.eht_mcs_nss_supp; + + /* get the max nss for EHT over all possible bandwidths and mcs */ + for (i = 0; i < sizeof(struct ieee80211_eht_mcs_nss_supp); i++) + eht_rx_nss = max_t(u8, eht_rx_nss, + u8_get_bits(rx_nss_mcs[i], + IEEE80211_EHT_MCS_NSS_RX)); + } + + if (link_sta->pub->he_cap.has_he) { + int i; + u8 rx_mcs_80 = 0, rx_mcs_160 = 0; + const struct ieee80211_sta_he_cap *he_cap = &link_sta->pub->he_cap; + u16 mcs_160_map = + le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_160); + u16 mcs_80_map = le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_80); + + for (i = 7; i >= 0; i--) { + u8 mcs_160 = (mcs_160_map >> (2 * i)) & 3; + + if (mcs_160 != IEEE80211_HE_MCS_NOT_SUPPORTED) { + rx_mcs_160 = i + 1; + break; + } + } + for (i = 7; i >= 0; i--) { + u8 mcs_80 = (mcs_80_map >> (2 * i)) & 3; + + if (mcs_80 != IEEE80211_HE_MCS_NOT_SUPPORTED) { + rx_mcs_80 = i + 1; + break; + } + } + + support_160 = he_cap->he_cap_elem.phy_cap_info[0] & + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G; + + if (support_160) + he_rx_nss = min(rx_mcs_80, rx_mcs_160); + else + he_rx_nss = rx_mcs_80; + } + + if (link_sta->pub->ht_cap.ht_supported) { + if (link_sta->pub->ht_cap.mcs.rx_mask[0]) + ht_rx_nss++; + if (link_sta->pub->ht_cap.mcs.rx_mask[1]) + ht_rx_nss++; + if (link_sta->pub->ht_cap.mcs.rx_mask[2]) + ht_rx_nss++; + if (link_sta->pub->ht_cap.mcs.rx_mask[3]) + ht_rx_nss++; + /* FIXME: consider rx_highest? */ + } + + if (link_sta->pub->vht_cap.vht_supported) { + int i; + u16 rx_mcs_map; + + rx_mcs_map = le16_to_cpu(link_sta->pub->vht_cap.vht_mcs.rx_mcs_map); + + for (i = 7; i >= 0; i--) { + u8 mcs = (rx_mcs_map >> (2 * i)) & 3; + + if (mcs != IEEE80211_VHT_MCS_NOT_SUPPORTED) { + vht_rx_nss = i + 1; + break; + } + } + /* FIXME: consider rx_highest? */ + } + + rx_nss = max(vht_rx_nss, ht_rx_nss); + rx_nss = max(he_rx_nss, rx_nss); + rx_nss = max(eht_rx_nss, rx_nss); + rx_nss = max_t(u8, 1, rx_nss); + link_sta->capa_nss = rx_nss; + + if (link_sta->op_mode_nss) + link_sta->pub->rx_nss = + min_t(u8, rx_nss, link_sta->op_mode_nss); + else + link_sta->pub->rx_nss = rx_nss; +} + void ieee80211_sta_set_max_amsdu_subframes(struct sta_info *sta, const u8 *ext_capab, unsigned int ext_capab_len) diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 05a776dba3e90..d0987b546bb7a 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -997,6 +997,7 @@ void ieee80211_sta_ps_deliver_uapsd(struct sta_info *sta); unsigned long ieee80211_sta_last_active(struct sta_info *sta, int link_id); +void ieee80211_sta_init_nss(struct link_sta_info *link_sta); void ieee80211_sta_set_max_amsdu_subframes(struct sta_info *sta, const u8 *ext_capab, unsigned int ext_capab_len); diff --git a/net/mac80211/vht.c b/net/mac80211/vht.c index 17f3f281abe1b..b93d6a3200c8f 100644 --- a/net/mac80211/vht.c +++ b/net/mac80211/vht.c @@ -488,99 +488,6 @@ _ieee80211_sta_cur_vht_bw(struct link_sta_info *link_sta, return bw; } -void ieee80211_sta_init_nss(struct link_sta_info *link_sta) -{ - u8 ht_rx_nss = 0, vht_rx_nss = 0, he_rx_nss = 0, eht_rx_nss = 0, rx_nss; - bool support_160; - - if (link_sta->pub->eht_cap.has_eht) { - int i; - const u8 *rx_nss_mcs = (void *)&link_sta->pub->eht_cap.eht_mcs_nss_supp; - - /* get the max nss for EHT over all possible bandwidths and mcs */ - for (i = 0; i < sizeof(struct ieee80211_eht_mcs_nss_supp); i++) - eht_rx_nss = max_t(u8, eht_rx_nss, - u8_get_bits(rx_nss_mcs[i], - IEEE80211_EHT_MCS_NSS_RX)); - } - - if (link_sta->pub->he_cap.has_he) { - int i; - u8 rx_mcs_80 = 0, rx_mcs_160 = 0; - const struct ieee80211_sta_he_cap *he_cap = &link_sta->pub->he_cap; - u16 mcs_160_map = - le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_160); - u16 mcs_80_map = le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_80); - - for (i = 7; i >= 0; i--) { - u8 mcs_160 = (mcs_160_map >> (2 * i)) & 3; - - if (mcs_160 != IEEE80211_HE_MCS_NOT_SUPPORTED) { - rx_mcs_160 = i + 1; - break; - } - } - for (i = 7; i >= 0; i--) { - u8 mcs_80 = (mcs_80_map >> (2 * i)) & 3; - - if (mcs_80 != IEEE80211_HE_MCS_NOT_SUPPORTED) { - rx_mcs_80 = i + 1; - break; - } - } - - support_160 = he_cap->he_cap_elem.phy_cap_info[0] & - IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G; - - if (support_160) - he_rx_nss = min(rx_mcs_80, rx_mcs_160); - else - he_rx_nss = rx_mcs_80; - } - - if (link_sta->pub->ht_cap.ht_supported) { - if (link_sta->pub->ht_cap.mcs.rx_mask[0]) - ht_rx_nss++; - if (link_sta->pub->ht_cap.mcs.rx_mask[1]) - ht_rx_nss++; - if (link_sta->pub->ht_cap.mcs.rx_mask[2]) - ht_rx_nss++; - if (link_sta->pub->ht_cap.mcs.rx_mask[3]) - ht_rx_nss++; - /* FIXME: consider rx_highest? */ - } - - if (link_sta->pub->vht_cap.vht_supported) { - int i; - u16 rx_mcs_map; - - rx_mcs_map = le16_to_cpu(link_sta->pub->vht_cap.vht_mcs.rx_mcs_map); - - for (i = 7; i >= 0; i--) { - u8 mcs = (rx_mcs_map >> (2 * i)) & 3; - - if (mcs != IEEE80211_VHT_MCS_NOT_SUPPORTED) { - vht_rx_nss = i + 1; - break; - } - } - /* FIXME: consider rx_highest? */ - } - - rx_nss = max(vht_rx_nss, ht_rx_nss); - rx_nss = max(he_rx_nss, rx_nss); - rx_nss = max(eht_rx_nss, rx_nss); - rx_nss = max_t(u8, 1, rx_nss); - link_sta->capa_nss = rx_nss; - - /* that shouldn't be set yet, but we can handle it anyway */ - if (link_sta->op_mode_nss) - link_sta->pub->rx_nss = - min_t(u8, rx_nss, link_sta->op_mode_nss); - else - link_sta->pub->rx_nss = rx_nss; -} - u32 __ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata, struct link_sta_info *link_sta, u8 opmode, enum nl80211_band band)