]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
wifi: mac80211: clean up STA NSS handling
authorJohannes Berg <johannes.berg@intel.com>
Wed, 15 Apr 2026 12:42:07 +0000 (14:42 +0200)
committerJohannes Berg <johannes.berg@intel.com>
Tue, 28 Apr 2026 07:27:46 +0000 (09:27 +0200)
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 <johannes.berg@intel.com>
Reviewed-on: https://gerritwcs.ir.intel.com/c/iwlwifi-stack-dev/+/273695
Tested-by: iil_jenkins iil_jenkins <EC.GER.UNIX.IIL.JENKINS@INTEL.COM>
tested: iil_jenkins iil_jenkins <EC.GER.UNIX.IIL.JENKINS@INTEL.COM>
Reviewed-by: Miriam Rachel Korenblit <miriam.rachel.korenblit@intel.com>
Link: https://patch.msgid.link/20260415144514.bef3d07beeb6.I97fb93ccc1b366110ab23de58fcd73676cdd85d6@changeid
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
net/mac80211/ibss.c
net/mac80211/ieee80211_i.h
net/mac80211/mesh_plink.c
net/mac80211/mlme.c
net/mac80211/ocb.c
net/mac80211/rate.c
net/mac80211/sta_info.c
net/mac80211/sta_info.h
net/mac80211/vht.c

index 97292ff51475fde04c0132192ca8b9dd4a74e4dd..96f040b4672eb2d3a5ed95c3441297fbfeb7a30f 100644 (file)
@@ -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);
index 3ab8368acaf9ac3dd756ec8db7c2c844e6f1cfca..76213b50fe577e716ce4a9b79385f11c5d5ebf51 100644 (file)
@@ -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);
index 7cbab90c8784f186faf8090e55b132c221fb5630..94201840677d9bf9990c09eee588bb68238c234d 100644 (file)
@@ -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;
 
index 160ae65a5c645819c437f8b70938e1055e547016..438cc2d4731df46672d6460d9d353ff55302a88d 100644 (file)
@@ -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;
 
index ebb4f4d88c237f7869069311b6c9a8ca9eed2a88..447c84235c1c481ed5c6d397dc708cd33821c42d 100644 (file)
@@ -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 <rostislav.lisovy@fel.cvut.cz>
  * 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);
 
index 31af7dd6aedc2815a4fc56ccd1b54c981da59fba..ba1a3aa3f5d4a641f8268bc1e517b4188830e53d 100644 (file)
@@ -4,7 +4,7 @@
  * Copyright 2005-2006, Devicescape Software, Inc.
  * Copyright (c) 2006 Jiri Benc <jbenc@suse.cz>
  * Copyright 2017      Intel Deutschland GmbH
- * Copyright (C) 2019, 2022-2025 Intel Corporation
+ * Copyright (C) 2019, 2022-2026 Intel Corporation
  */
 
 #include <linux/kernel.h>
@@ -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;
 
index e08db1b0cb3011e63ab97c66cc97b47f1534ce52..0f174a6a04a86257eda928f24916bd954b3ede5d 100644 (file)
@@ -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)
index 05a776dba3e908e4fb4ca4a2b0f9761826247ebd..d0987b546bb7a388775029ccd0b2050cb570ae5d 100644 (file)
@@ -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);
index 17f3f281abe1b42f7367dbce80ff3adde150afaf..b93d6a3200c8f77c598a23092dcca82a72ea671f 100644 (file)
@@ -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)