wifi: mac80211: track capability/opmode NSS separately
authorJohannes Berg <johannes.berg@intel.com>
Wed, 28 Feb 2024 11:01:57 +0000 (12:01 +0100)
committerJohannes Berg <johannes.berg@intel.com>
Mon, 4 Mar 2024 13:25:27 +0000 (14:25 +0100)
We're currently tracking rx_nss for each station, and that
is meant to be initialized to the capability NSS and later
reduced by the operating mode notification NSS.

However, we're mixing up capabilities and operating mode
NSS in the same variable. This forces us to recalculate
the NSS capability on operating mode notification RX,
which is a bit strange; due to the previous fix I had to
never keep rx_nss as zero, it also means that the capa is
never taken into account properly.

Fix all this by storing the capability value, that can be
recalculated unconditionally whenever needed, and storing
the operating mode notification NSS separately, taking it
into account when assigning the final rx_nss value.

Cc: stable@vger.kernel.org
Fixes: dd6c064cfc3f ("wifi: mac80211: set station RX-NSS on reconfig")
Reviewed-by: Miriam Rachel Korenblit <miriam.rachel.korenblit@intel.com>
Link: https://msgid.link/20240228120157.0e1c41924d1d.I0acaa234e0267227b7e3ef81a59117c8792116bc@changeid
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
net/mac80211/cfg.c
net/mac80211/ieee80211_i.h
net/mac80211/rate.c
net/mac80211/sta_info.h
net/mac80211/vht.c

index 0744113f3535dbb5a57c74db457692cd05164c65..3aa96898a250d224d22059af95f3dc9b9b10b9a6 100644 (file)
@@ -1867,7 +1867,7 @@ static int sta_link_apply_parameters(struct ieee80211_local *local,
                                              sband->band);
        }
 
-       ieee80211_sta_set_rx_nss(link_sta);
+       ieee80211_sta_init_nss(link_sta);
 
        return ret;
 }
index 4bec625a84d12606a556ceaa56cca9bd142e1ca3..32b5ac8947bdd7faaec5bc3ad6ccab7dc8cd3ac3 100644 (file)
@@ -2153,7 +2153,7 @@ enum ieee80211_sta_rx_bandwidth
 ieee80211_sta_cap_rx_bw(struct link_sta_info *link_sta);
 enum ieee80211_sta_rx_bandwidth
 ieee80211_sta_cur_vht_bw(struct link_sta_info *link_sta);
-void ieee80211_sta_set_rx_nss(struct link_sta_info *link_sta);
+void ieee80211_sta_init_nss(struct link_sta_info *link_sta);
 enum ieee80211_sta_rx_bandwidth
 ieee80211_chan_width_to_rx_bw(enum nl80211_chan_width width);
 enum nl80211_chan_width
index 34e03b9522c8a11b7e2725dd8ccfd59eabcd3044..192c5f8b9e0867f42581c0f70939cd5222500f87 100644 (file)
@@ -37,7 +37,7 @@ void rate_control_rate_init(struct sta_info *sta)
        struct ieee80211_supported_band *sband;
        struct ieee80211_chanctx_conf *chanctx_conf;
 
-       ieee80211_sta_set_rx_nss(&sta->deflink);
+       ieee80211_sta_init_nss(&sta->deflink);
 
        if (!ref)
                return;
index f03731a5bbee214db74a300eb93ef8d47d55c130..a52fb76386d017a67036f2e79c9ad6d1c0679d85 100644 (file)
@@ -3,7 +3,7 @@
  * Copyright 2002-2005, Devicescape Software, Inc.
  * Copyright 2013-2014  Intel Mobile Communications GmbH
  * Copyright(c) 2015-2017 Intel Deutschland GmbH
- * Copyright(c) 2020-2023 Intel Corporation
+ * Copyright(c) 2020-2024 Intel Corporation
  */
 
 #ifndef STA_INFO_H
@@ -482,6 +482,8 @@ struct ieee80211_fragment_cache {
  *     same for non-MLD STA. This is used as key for searching link STA
  * @link_id: Link ID uniquely identifying the link STA. This is 0 for non-MLD
  *     and set to the corresponding vif LinkId for MLD STA
+ * @op_mode_nss: NSS limit as set by operating mode notification, or 0
+ * @capa_nss: NSS limit as determined by local and peer capabilities
  * @link_hash_node: hash node for rhashtable
  * @sta: Points to the STA info
  * @gtk: group keys negotiated with this station, if any
@@ -518,6 +520,8 @@ struct link_sta_info {
        u8 addr[ETH_ALEN];
        u8 link_id;
 
+       u8 op_mode_nss, capa_nss;
+
        struct rhlist_head link_hash_node;
 
        struct sta_info *sta;
index 2c475c439ba9b34631e881285fe6110147455e93..642891cafbaf24b5348d528ea36cc5d888159d7c 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Portions of this file
  * Copyright(c) 2015 - 2016 Intel Deutschland GmbH
- * Copyright (C) 2018 - 2023 Intel Corporation
+ * Copyright (C) 2018 - 2024 Intel Corporation
  */
 
 #include <linux/ieee80211.h>
@@ -541,15 +541,11 @@ ieee80211_sta_cur_vht_bw(struct link_sta_info *link_sta)
        return bw;
 }
 
-void ieee80211_sta_set_rx_nss(struct link_sta_info *link_sta)
+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 we received a notification already don't overwrite it */
-       if (link_sta->pub->rx_nss)
-               return;
-
        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;
@@ -627,7 +623,15 @@ void ieee80211_sta_set_rx_nss(struct link_sta_info *link_sta)
        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);
-       link_sta->pub->rx_nss = max_t(u8, 1, 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,
@@ -637,7 +641,7 @@ u32 __ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata,
        enum ieee80211_sta_rx_bandwidth new_bw;
        struct sta_opmode_info sta_opmode = {};
        u32 changed = 0;
-       u8 nss, cur_nss;
+       u8 nss;
 
        /* ignore - no support for BF yet */
        if (opmode & IEEE80211_OPMODE_NOTIF_RX_NSS_TYPE_BF)
@@ -647,23 +651,17 @@ u32 __ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata,
        nss >>= IEEE80211_OPMODE_NOTIF_RX_NSS_SHIFT;
        nss += 1;
 
-       if (link_sta->pub->rx_nss != nss) {
-               cur_nss = link_sta->pub->rx_nss;
-               /* Reset rx_nss and call ieee80211_sta_set_rx_nss() which
-                * will set the same to max nss value calculated based on capability.
-                */
-               link_sta->pub->rx_nss = 0;
-               ieee80211_sta_set_rx_nss(link_sta);
-               /* Do not allow an nss change to rx_nss greater than max_nss
-                * negotiated and capped to APs capability during association.
-                */
-               if (nss <= link_sta->pub->rx_nss) {
-                       link_sta->pub->rx_nss = nss;
-                       sta_opmode.rx_nss = nss;
-                       changed |= IEEE80211_RC_NSS_CHANGED;
-                       sta_opmode.changed |= STA_OPMODE_N_SS_CHANGED;
+       if (link_sta->op_mode_nss != nss) {
+               if (nss <= link_sta->capa_nss) {
+                       link_sta->op_mode_nss = nss;
+
+                       if (nss != link_sta->pub->rx_nss) {
+                               link_sta->pub->rx_nss = nss;
+                               changed |= IEEE80211_RC_NSS_CHANGED;
+                               sta_opmode.rx_nss = link_sta->pub->rx_nss;
+                               sta_opmode.changed |= STA_OPMODE_N_SS_CHANGED;
+                       }
                } else {
-                       link_sta->pub->rx_nss = cur_nss;
                        pr_warn_ratelimited("Ignoring NSS change in VHT Operating Mode Notification from %pM with invalid nss %d",
                                            link_sta->pub->addr, nss);
                }