]> Git Repo - linux.git/commitdiff
Merge wireless into wireless-next
authorJohannes Berg <[email protected]>
Thu, 5 Oct 2023 20:57:34 +0000 (22:57 +0200)
committerKalle Valo <[email protected]>
Fri, 6 Oct 2023 14:08:47 +0000 (17:08 +0300)
Resolve several conflicts, mostly between changes/fixes in
wireless and the locking rework in wireless-next. One of
the conflicts actually shows a bug in wireless that we'll
want to fix separately.

Signed-off-by: Johannes Berg <[email protected]>
Signed-off-by: Kalle Valo <[email protected]>
17 files changed:
1  2 
drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
drivers/net/wireless/intel/iwlwifi/mvm/fw.c
drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c
drivers/net/wireless/intel/iwlwifi/mvm/tx.c
include/net/cfg80211.h
net/mac80211/cfg.c
net/mac80211/ibss.c
net/mac80211/ieee80211_i.h
net/mac80211/key.c
net/mac80211/mesh.c
net/mac80211/mlme.c
net/mac80211/tx.c
net/wireless/core.c
net/wireless/core.h
net/wireless/mlme.c
net/wireless/nl80211.c
net/wireless/scan.c

index 6eef6bc430e2439152fdf9ebf5c14abcb43e5205,611d1a6aabb9e5f9cf6787e23434665df0819e67..9d248ba1c0b2bca1d48f39bd942baa17ea056ef8
@@@ -442,7 -442,12 +442,12 @@@ struct brcmf_scan_params_v2_le 
                                 * fixed parameter portion is assumed, otherwise
                                 * ssid in the fixed portion is ignored
                                 */
-       __le16 channel_list[1]; /* list of chanspecs */
+       union {
+               __le16 padding; /* Reserve space for at least 1 entry for abort
+                                * which uses an on stack brcmf_scan_params_v2_le
+                                */
+               DECLARE_FLEX_ARRAY(__le16, channel_list);       /* chanspecs */
+       };
  };
  
  struct brcmf_scan_results {
@@@ -702,7 -707,7 +707,7 @@@ struct brcmf_sta_info_le 
  
  struct brcmf_chanspec_list {
        __le32  count;          /* # of entries */
-       __le32  element[1];     /* variable length uint32 list */
+       __le32  element[];      /* variable length uint32 list */
  };
  
  /*
@@@ -1209,7 -1214,7 +1214,7 @@@ struct brcmf_gscan_config 
        u8 count_of_channel_buckets;
        u8 retry_threshold;
        __le16  lost_ap_window;
 -      struct brcmf_gscan_bucket_config bucket[];
 +      struct brcmf_gscan_bucket_config bucket[] __counted_by(count_of_channel_buckets);
  };
  
  /**
index 5c719636c9bd30d6efd6c81f29beb0290355a8f3,1d5ee4330f29f334475886c6b6e466dff2f65c44..a5348b01531021573f3f62a881b1a01488fb81a7
@@@ -583,7 -583,6 +583,7 @@@ static int iwl_run_unified_mvm_ucode(st
        static const u16 init_complete[] = {
                INIT_COMPLETE_NOTIF,
        };
 +      u32 sb_cfg;
        int ret;
  
        if (mvm->trans->cfg->tx_with_siso_diversity)
  
        mvm->rfkill_safe_init_done = false;
  
 +      if (mvm->trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_AX210) {
 +              sb_cfg = iwl_read_umac_prph(mvm->trans, SB_MODIFY_CFG_FLAG);
 +              /* if needed, we'll reset this on our way out later */
 +              mvm->pldr_sync = !(sb_cfg & SB_CFG_RESIDES_IN_OTP_MASK);
 +              if (mvm->pldr_sync && iwl_mei_pldr_req())
 +                      return -EBUSY;
 +      }
 +
        iwl_init_notification_wait(&mvm->notif_wait,
                                   &init_wait,
                                   init_complete,
        ret = iwl_mvm_load_ucode_wait_alive(mvm, IWL_UCODE_REGULAR);
        if (ret) {
                IWL_ERR(mvm, "Failed to start RT ucode: %d\n", ret);
 +
 +              /* if we needed reset then fail here, but notify and remove */
 +              if (mvm->pldr_sync) {
 +                      iwl_mei_alive_notif(false);
 +                      iwl_trans_pcie_remove(mvm->trans, true);
 +              }
 +
                goto error;
        }
        iwl_dbg_tlv_time_point(&mvm->fwrt, IWL_FW_INI_TIME_POINT_AFTER_ALIVE,
  
        /* Read the NVM only at driver load time, no need to do this twice */
        if (!IWL_MVM_PARSE_NVM && !mvm->nvm_data) {
 -              mvm->nvm_data = iwl_get_nvm(mvm->trans, mvm->fw);
 +              mvm->nvm_data = iwl_get_nvm(mvm->trans, mvm->fw,
 +                                          mvm->set_tx_ant, mvm->set_rx_ant);
                if (IS_ERR(mvm->nvm_data)) {
                        ret = PTR_ERR(mvm->nvm_data);
                        mvm->nvm_data = NULL;
@@@ -819,7 -802,7 +819,7 @@@ out
                mvm->nvm_data->bands[0].n_channels = 1;
                mvm->nvm_data->bands[0].n_bitrates = 1;
                mvm->nvm_data->bands[0].bitrates =
-                       (void *)((u8 *)mvm->nvm_data->channels + 1);
+                       (void *)(mvm->nvm_data->channels + 1);
                mvm->nvm_data->bands[0].bitrates->hw_value = 10;
        }
  
@@@ -1519,6 -1502,7 +1519,6 @@@ int iwl_mvm_up(struct iwl_mvm *mvm
        struct ieee80211_channel *chan;
        struct cfg80211_chan_def chandef;
        struct ieee80211_supported_band *sband = NULL;
 -      u32 sb_cfg;
  
        lockdep_assert_held(&mvm->mutex);
  
        if (ret)
                return ret;
  
 -      sb_cfg = iwl_read_umac_prph(mvm->trans, SB_MODIFY_CFG_FLAG);
 -      mvm->pldr_sync = !(sb_cfg & SB_CFG_RESIDES_IN_OTP_MASK);
 -      if (mvm->pldr_sync && iwl_mei_pldr_req())
 -              return -EBUSY;
 -
        ret = iwl_mvm_load_rt_fw(mvm);
        if (ret) {
                IWL_ERR(mvm, "Failed to start RT ucode: %d\n", ret);
        /* FW loaded successfully */
        mvm->pldr_sync = false;
  
 +      iwl_fw_disable_dbg_asserts(&mvm->fwrt);
        iwl_get_shared_mem_conf(&mvm->fwrt);
  
        ret = iwl_mvm_sf_update(mvm, NULL, false);
index 5449deb3c2d6933096b5ce254ab3dc576aefb804,b719843e94576e35e7ce2270136c15d5756ce4e4..46e207211f21d19b186599207f5b9c1dc40cb7c2
@@@ -731,73 -731,78 +731,78 @@@ static void iwl_mvm_mld_vif_cfg_changed
  
        mvmvif->associated = vif->cfg.assoc;
  
-       if (!(changes & BSS_CHANGED_ASSOC))
-               return;
-       if (vif->cfg.assoc) {
-               /* clear statistics to get clean beacon counter */
-               iwl_mvm_request_statistics(mvm, true);
-               iwl_mvm_sf_update(mvm, vif, false);
-               iwl_mvm_power_vif_assoc(mvm, vif);
-               for_each_mvm_vif_valid_link(mvmvif, i) {
-                       memset(&mvmvif->link[i]->beacon_stats, 0,
-                              sizeof(mvmvif->link[i]->beacon_stats));
+       if (changes & BSS_CHANGED_ASSOC) {
+               if (vif->cfg.assoc) {
+                       /* clear statistics to get clean beacon counter */
+                       iwl_mvm_request_statistics(mvm, true);
+                       iwl_mvm_sf_update(mvm, vif, false);
+                       iwl_mvm_power_vif_assoc(mvm, vif);
+                       for_each_mvm_vif_valid_link(mvmvif, i) {
+                               memset(&mvmvif->link[i]->beacon_stats, 0,
+                                      sizeof(mvmvif->link[i]->beacon_stats));
+                               if (vif->p2p) {
+                                       iwl_mvm_update_smps(mvm, vif,
+                                                           IWL_MVM_SMPS_REQ_PROT,
+                                                           IEEE80211_SMPS_DYNAMIC, i);
+                               }
+                               rcu_read_lock();
+                               link_conf = rcu_dereference(vif->link_conf[i]);
+                               if (link_conf && !link_conf->dtim_period)
+                                       protect = true;
+                               rcu_read_unlock();
+                       }
  
-                       if (vif->p2p) {
-                               iwl_mvm_update_smps(mvm, vif,
-                                                   IWL_MVM_SMPS_REQ_PROT,
-                                                   IEEE80211_SMPS_DYNAMIC, i);
+                       if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) &&
+                           protect) {
+                               /* If we're not restarting and still haven't
+                                * heard a beacon (dtim period unknown) then
+                                * make sure we still have enough minimum time
+                                * remaining in the time event, since the auth
+                                * might actually have taken quite a while
+                                * (especially for SAE) and so the remaining
+                                * time could be small without us having heard
+                                * a beacon yet.
+                                */
+                               iwl_mvm_protect_assoc(mvm, vif, 0);
                        }
  
-                       rcu_read_lock();
-                       link_conf = rcu_dereference(vif->link_conf[i]);
-                       if (link_conf && !link_conf->dtim_period)
-                               protect = true;
-                       rcu_read_unlock();
-               }
+                       iwl_mvm_sf_update(mvm, vif, false);
+                       /* FIXME: need to decide about misbehaving AP handling */
+                       iwl_mvm_power_vif_assoc(mvm, vif);
+               } else if (iwl_mvm_mld_vif_have_valid_ap_sta(mvmvif)) {
+                       iwl_mvm_mei_host_disassociated(mvm);
  
-               if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) &&
-                   protect) {
-                       /* If we're not restarting and still haven't
-                        * heard a beacon (dtim period unknown) then
-                        * make sure we still have enough minimum time
-                        * remaining in the time event, since the auth
-                        * might actually have taken quite a while
-                        * (especially for SAE) and so the remaining
-                        * time could be small without us having heard
-                        * a beacon yet.
+                       /* If update fails - SF might be running in associated
+                        * mode while disassociated - which is forbidden.
                         */
-                       iwl_mvm_protect_assoc(mvm, vif, 0);
+                       ret = iwl_mvm_sf_update(mvm, vif, false);
+                       WARN_ONCE(ret &&
+                                 !test_bit(IWL_MVM_STATUS_HW_RESTART_REQUESTED,
+                                           &mvm->status),
+                                 "Failed to update SF upon disassociation\n");
+                       /* If we get an assert during the connection (after the
+                        * station has been added, but before the vif is set
+                        * to associated), mac80211 will re-add the station and
+                        * then configure the vif. Since the vif is not
+                        * associated, we would remove the station here and
+                        * this would fail the recovery.
+                        */
+                       iwl_mvm_mld_vif_delete_all_stas(mvm, vif);
                }
  
-               iwl_mvm_sf_update(mvm, vif, false);
-               /* FIXME: need to decide about misbehaving AP handling */
-               iwl_mvm_power_vif_assoc(mvm, vif);
-       } else if (iwl_mvm_mld_vif_have_valid_ap_sta(mvmvif)) {
-               iwl_mvm_mei_host_disassociated(mvm);
-               /* If update fails - SF might be running in associated
-                * mode while disassociated - which is forbidden.
-                */
-               ret = iwl_mvm_sf_update(mvm, vif, false);
-               WARN_ONCE(ret &&
-                         !test_bit(IWL_MVM_STATUS_HW_RESTART_REQUESTED,
-                                   &mvm->status),
-                         "Failed to update SF upon disassociation\n");
-               /* If we get an assert during the connection (after the
-                * station has been added, but before the vif is set
-                * to associated), mac80211 will re-add the station and
-                * then configure the vif. Since the vif is not
-                * associated, we would remove the station here and
-                * this would fail the recovery.
-                */
-               iwl_mvm_mld_vif_delete_all_stas(mvm, vif);
+               iwl_mvm_bss_info_changed_station_assoc(mvm, vif, changes);
        }
  
-       iwl_mvm_bss_info_changed_station_assoc(mvm, vif, changes);
+       if (changes & BSS_CHANGED_PS) {
+               ret = iwl_mvm_power_update_mac(mvm);
+               if (ret)
+                       IWL_ERR(mvm, "failed to update power mode\n");
+       }
  }
  
  static void
@@@ -1084,6 -1089,9 +1089,6 @@@ iwl_mvm_mld_change_vif_links(struct iee
                }
        }
  
 -      if (err)
 -              goto out_err;
 -
        err = 0;
        if (new_links == 0) {
                mvmvif->link[0] = &mvmvif->deflink;
@@@ -1121,7 -1129,6 +1126,7 @@@ const struct ieee80211_ops iwl_mvm_mld_
        .wake_tx_queue = iwl_mvm_mac_wake_tx_queue,
        .ampdu_action = iwl_mvm_mac_ampdu_action,
        .get_antenna = iwl_mvm_op_get_antenna,
 +      .set_antenna = iwl_mvm_op_set_antenna,
        .start = iwl_mvm_mac_start,
        .reconfig_complete = iwl_mvm_mac_reconfig_complete,
        .stop = iwl_mvm_mac_stop,
index ec18e348553d709649eaa46b0ec32b1971f0fe5d,898dca3936435cb162a61aa776e6999eed7f112f..674ddf951b79165f868669626041f243776cd468
@@@ -262,42 -262,8 +262,42 @@@ static u32 iwl_mvm_get_tx_ant(struct iw
        return BIT(mvm->mgmt_last_antenna_idx) << RATE_MCS_ANT_POS;
  }
  
 +static u32 iwl_mvm_convert_rate_idx(struct iwl_mvm *mvm,
 +                                  struct ieee80211_tx_info *info,
 +                                  int rate_idx)
 +{
 +      u32 rate_flags = 0;
 +      u8 rate_plcp;
 +      bool is_cck;
 +
 +      /* if the rate isn't a well known legacy rate, take the lowest one */
 +      if (rate_idx < 0 || rate_idx >= IWL_RATE_COUNT_LEGACY)
 +              rate_idx = iwl_mvm_mac_ctxt_get_lowest_rate(mvm,
 +                                                          info,
 +                                                          info->control.vif);
 +
 +      /* Get PLCP rate for tx_cmd->rate_n_flags */
 +      rate_plcp = iwl_mvm_mac80211_idx_to_hwrate(mvm->fw, rate_idx);
 +      is_cck = (rate_idx >= IWL_FIRST_CCK_RATE) &&
 +               (rate_idx <= IWL_LAST_CCK_RATE);
 +
 +      /* Set CCK or OFDM flag */
 +      if (iwl_fw_lookup_cmd_ver(mvm->fw, TX_CMD, 0) > 8) {
 +              if (!is_cck)
 +                      rate_flags |= RATE_MCS_LEGACY_OFDM_MSK;
 +              else
 +                      rate_flags |= RATE_MCS_CCK_MSK;
 +      } else if (is_cck) {
 +              rate_flags |= RATE_MCS_CCK_MSK_V1;
 +      }
 +
 +      return (u32)rate_plcp | rate_flags;
 +}
 +
  static u32 iwl_mvm_get_inject_tx_rate(struct iwl_mvm *mvm,
 -                                    struct ieee80211_tx_info *info)
 +                                    struct ieee80211_tx_info *info,
 +                                    struct ieee80211_sta *sta,
 +                                    __le16 fc)
  {
        struct ieee80211_tx_rate *rate = &info->control.rates[0];
        u32 result;
                        result |= u32_encode_bits(2, RATE_MCS_CHAN_WIDTH_MSK_V1);
                else if (rate->flags & IEEE80211_TX_RC_160_MHZ_WIDTH)
                        result |= u32_encode_bits(3, RATE_MCS_CHAN_WIDTH_MSK_V1);
 +
 +              if (iwl_fw_lookup_notif_ver(mvm->fw, LONG_GROUP, TX_CMD, 0) > 6)
 +                      result = iwl_new_rate_from_v1(result);
        } else if (rate->flags & IEEE80211_TX_RC_MCS) {
                result = RATE_MCS_HT_MSK_V1;
                result |= u32_encode_bits(rate->idx,
                        result |= RATE_MCS_LDPC_MSK_V1;
                if (u32_get_bits(info->flags, IEEE80211_TX_CTL_STBC))
                        result |= RATE_MCS_STBC_MSK;
 +
 +              if (iwl_fw_lookup_notif_ver(mvm->fw, LONG_GROUP, TX_CMD, 0) > 6)
 +                      result = iwl_new_rate_from_v1(result);
        } else {
 -              return 0;
 +              int rate_idx = info->control.rates[0].idx;
 +
 +              result = iwl_mvm_convert_rate_idx(mvm, info, rate_idx);
        }
  
 -      if (iwl_fw_lookup_notif_ver(mvm->fw, LONG_GROUP, TX_CMD, 0) > 6)
 -              return iwl_new_rate_from_v1(result);
 +      if (info->control.antennas)
 +              result |= u32_encode_bits(info->control.antennas,
 +                                        RATE_MCS_ANT_AB_MSK);
 +      else
 +              result |= iwl_mvm_get_tx_ant(mvm, info, sta, fc);
 +
        return result;
  }
  
@@@ -361,8 -315,17 +361,8 @@@ static u32 iwl_mvm_get_tx_rate(struct i
                               struct ieee80211_sta *sta, __le16 fc)
  {
        int rate_idx = -1;
 -      u8 rate_plcp;
 -      u32 rate_flags = 0;
 -      bool is_cck;
  
 -      if (unlikely(info->control.flags & IEEE80211_TX_CTRL_RATE_INJECT)) {
 -              u32 result = iwl_mvm_get_inject_tx_rate(mvm, info);
 -
 -              if (result)
 -                      return result;
 -              rate_idx = info->control.rates[0].idx;
 -      } else if (!ieee80211_hw_check(mvm->hw, HAS_RATE_CONTROL)) {
 +      if (!ieee80211_hw_check(mvm->hw, HAS_RATE_CONTROL)) {
                /* info->control is only relevant for non HW rate control */
  
                /* HT rate doesn't make sense for a non data frame */
                BUILD_BUG_ON(IWL_FIRST_CCK_RATE != 0);
        }
  
 -      /* if the rate isn't a well known legacy rate, take the lowest one */
 -      if (rate_idx < 0 || rate_idx >= IWL_RATE_COUNT_LEGACY)
 -              rate_idx = iwl_mvm_mac_ctxt_get_lowest_rate(mvm,
 -                                                          info,
 -                                                          info->control.vif);
 -
 -      /* Get PLCP rate for tx_cmd->rate_n_flags */
 -      rate_plcp = iwl_mvm_mac80211_idx_to_hwrate(mvm->fw, rate_idx);
 -      is_cck = (rate_idx >= IWL_FIRST_CCK_RATE) && (rate_idx <= IWL_LAST_CCK_RATE);
 -
 -      /* Set CCK or OFDM flag */
 -      if (iwl_fw_lookup_cmd_ver(mvm->fw, TX_CMD, 0) > 8) {
 -              if (!is_cck)
 -                      rate_flags |= RATE_MCS_LEGACY_OFDM_MSK;
 -              else
 -                      rate_flags |= RATE_MCS_CCK_MSK;
 -      } else if (is_cck) {
 -              rate_flags |= RATE_MCS_CCK_MSK_V1;
 -      }
 -
 -      return (u32)rate_plcp | rate_flags;
 +      return iwl_mvm_convert_rate_idx(mvm, info, rate_idx);
  }
  
  static u32 iwl_mvm_get_tx_rate_n_flags(struct iwl_mvm *mvm,
                                       struct ieee80211_tx_info *info,
                                       struct ieee80211_sta *sta, __le16 fc)
  {
 +      if (unlikely(info->control.flags & IEEE80211_TX_CTRL_RATE_INJECT))
 +              return iwl_mvm_get_inject_tx_rate(mvm, info, sta, fc);
 +
        return iwl_mvm_get_tx_rate(mvm, info, sta, fc) |
                iwl_mvm_get_tx_ant(mvm, info, sta, fc);
  }
@@@ -556,20 -536,16 +556,20 @@@ iwl_mvm_set_tx_params(struct iwl_mvm *m
                        flags |= IWL_TX_FLAGS_ENCRYPT_DIS;
  
                /*
 -               * For data packets rate info comes from the fw. Only
 -               * set rate/antenna during connection establishment or in case
 -               * no station is given.
 +               * For data and mgmt packets rate info comes from the fw. Only
 +               * set rate/antenna for injected frames with fixed rate, or
 +               * when no sta is given.
                 */
 -              if (!sta || !ieee80211_is_data(hdr->frame_control) ||
 -                  mvmsta->sta_state < IEEE80211_STA_AUTHORIZED) {
 +              if (unlikely(!sta ||
 +                           info->control.flags & IEEE80211_TX_CTRL_RATE_INJECT)) {
                        flags |= IWL_TX_FLAGS_CMD_RATE;
                        rate_n_flags =
                                iwl_mvm_get_tx_rate_n_flags(mvm, info, sta,
                                                            hdr->frame_control);
 +              } else if (!ieee80211_is_data(hdr->frame_control) ||
 +                         mvmsta->sta_state < IEEE80211_STA_AUTHORIZED) {
 +                      /* These are important frames */
 +                      flags |= IWL_TX_FLAGS_HIGH_PRI;
                }
  
                if (mvm->trans->trans_cfg->device_family >=
@@@ -1636,6 -1612,7 +1636,7 @@@ static void iwl_mvm_rx_tx_cmd_single(st
                iwl_trans_free_tx_cmd(mvm->trans, info->driver_data[1]);
  
                memset(&info->status, 0, sizeof(info->status));
+               info->flags &= ~(IEEE80211_TX_STAT_ACK | IEEE80211_TX_STAT_TX_FILTERED);
  
                /* inform mac80211 about what happened with the frame */
                switch (status & TX_STATUS_MSK) {
@@@ -1988,6 -1965,8 +1989,8 @@@ static void iwl_mvm_tx_reclaim(struct i
                 */
                if (!is_flush)
                        info->flags |= IEEE80211_TX_STAT_ACK;
+               else
+                       info->flags &= ~IEEE80211_TX_STAT_ACK;
        }
  
        /*
diff --combined include/net/cfg80211.h
index 34c50f7273d1c8b8b815bf5c16ab6810ee1f3def,7192346e4a22dfd1f9f2a6c6067e92f4167b55ad..a9ef43b337486cbab98441e5a23ee6bd82627718
@@@ -76,8 -76,6 +76,8 @@@ struct wiphy
   * @IEEE80211_CHAN_DISABLED: This channel is disabled.
   * @IEEE80211_CHAN_NO_IR: do not initiate radiation, this includes
   *    sending probe requests or beaconing.
 + * @IEEE80211_CHAN_PSD: Power spectral density (in dBm) is set for this
 + *    channel.
   * @IEEE80211_CHAN_RADAR: Radar detection is required on this channel.
   * @IEEE80211_CHAN_NO_HT40PLUS: extension channel above this channel
   *    is not permitted.
  enum ieee80211_channel_flags {
        IEEE80211_CHAN_DISABLED         = 1<<0,
        IEEE80211_CHAN_NO_IR            = 1<<1,
 -      /* hole at 1<<2 */
 +      IEEE80211_CHAN_PSD              = 1<<2,
        IEEE80211_CHAN_RADAR            = 1<<3,
        IEEE80211_CHAN_NO_HT40PLUS      = 1<<4,
        IEEE80211_CHAN_NO_HT40MINUS     = 1<<5,
   *    on this channel.
   * @dfs_state_entered: timestamp (jiffies) when the dfs state was entered.
   * @dfs_cac_ms: DFS CAC time in milliseconds, this is valid for DFS channels.
 + * @psd: power spectral density (in dBm)
   */
  struct ieee80211_channel {
        enum nl80211_band band;
        enum nl80211_dfs_state dfs_state;
        unsigned long dfs_state_entered;
        unsigned int dfs_cac_ms;
 +      s8 psd;
  };
  
  /**
@@@ -414,19 -410,6 +414,19 @@@ struct ieee80211_sta_eht_cap 
        u8 eht_ppe_thres[IEEE80211_EHT_PPE_THRES_MAX_LEN];
  };
  
 +/* sparse defines __CHECKER__; see Documentation/dev-tools/sparse.rst */
 +#ifdef __CHECKER__
 +/*
 + * This is used to mark the sband->iftype_data pointer which is supposed
 + * to be an array with special access semantics (per iftype), but a lot
 + * of code got it wrong in the past, so with this marking sparse will be
 + * noisy when the pointer is used directly.
 + */
 +# define __iftd               __attribute__((noderef, address_space(__iftype_data)))
 +#else
 +# define __iftd
 +#endif /* __CHECKER__ */
 +
  /**
   * struct ieee80211_sband_iftype_data - sband data per interface type
   *
@@@ -560,47 -543,9 +560,47 @@@ struct ieee80211_supported_band 
        struct ieee80211_sta_s1g_cap s1g_cap;
        struct ieee80211_edmg edmg_cap;
        u16 n_iftype_data;
 -      const struct ieee80211_sband_iftype_data *iftype_data;
 +      const struct ieee80211_sband_iftype_data __iftd *iftype_data;
  };
  
 +/**
 + * _ieee80211_set_sband_iftype_data - set sband iftype data array
 + * @sband: the sband to initialize
 + * @iftd: the iftype data array pointer
 + * @n_iftd: the length of the iftype data array
 + *
 + * Set the sband iftype data array; use this where the length cannot
 + * be derived from the ARRAY_SIZE() of the argument, but prefer
 + * ieee80211_set_sband_iftype_data() where it can be used.
 + */
 +static inline void
 +_ieee80211_set_sband_iftype_data(struct ieee80211_supported_band *sband,
 +                               const struct ieee80211_sband_iftype_data *iftd,
 +                               u16 n_iftd)
 +{
 +      sband->iftype_data = (const void __iftd __force *)iftd;
 +      sband->n_iftype_data = n_iftd;
 +}
 +
 +/**
 + * ieee80211_set_sband_iftype_data - set sband iftype data array
 + * @sband: the sband to initialize
 + * @iftd: the iftype data array
 + */
 +#define ieee80211_set_sband_iftype_data(sband, iftd)  \
 +      _ieee80211_set_sband_iftype_data(sband, iftd, ARRAY_SIZE(iftd))
 +
 +/**
 + * for_each_sband_iftype_data - iterate sband iftype data entries
 + * @sband: the sband whose iftype_data array to iterate
 + * @i: iterator counter
 + * @iftd: iftype data pointer to set
 + */
 +#define for_each_sband_iftype_data(sband, i, iftd)                            \
 +      for (i = 0, iftd = (const void __force *)&(sband)->iftype_data[i];      \
 +           i < (sband)->n_iftype_data;                                        \
 +           i++, iftd = (const void __force *)&(sband)->iftype_data[i])
 +
  /**
   * ieee80211_get_sband_iftype_data - return sband data for a given iftype
   * @sband: the sband to search for the STA on
@@@ -612,7 -557,6 +612,7 @@@ static inline const struct ieee80211_sb
  ieee80211_get_sband_iftype_data(const struct ieee80211_supported_band *sband,
                                u8 iftype)
  {
 +      const struct ieee80211_sband_iftype_data *data;
        int i;
  
        if (WARN_ON(iftype >= NL80211_IFTYPE_MAX))
        if (iftype == NL80211_IFTYPE_AP_VLAN)
                iftype = NL80211_IFTYPE_AP;
  
 -      for (i = 0; i < sband->n_iftype_data; i++)  {
 -              const struct ieee80211_sband_iftype_data *data =
 -                      &sband->iftype_data[i];
 -
 +      for_each_sband_iftype_data(sband, i, data) {
                if (data->types_mask & BIT(iftype))
                        return data;
        }
@@@ -1006,30 -953,6 +1006,30 @@@ int cfg80211_chandef_dfs_required(struc
                                  const struct cfg80211_chan_def *chandef,
                                  enum nl80211_iftype iftype);
  
 +/**
 + * cfg80211_chandef_dfs_usable - checks if chandef is DFS usable and we
 + *                             can/need start CAC on such channel
 + * @wiphy: the wiphy to validate against
 + * @chandef: the channel definition to check
 + *
 + * Return: true if all channels available and at least
 + *       one channel requires CAC (NL80211_DFS_USABLE)
 + */
 +bool cfg80211_chandef_dfs_usable(struct wiphy *wiphy,
 +                               const struct cfg80211_chan_def *chandef);
 +
 +/**
 + * cfg80211_chandef_dfs_cac_time - get the DFS CAC time (in ms) for given
 + *                               channel definition
 + * @wiphy: the wiphy to validate against
 + * @chandef: the channel definition to check
 + *
 + * Returns: DFS CAC time (in ms) which applies for this channel definition
 + */
 +unsigned int
 +cfg80211_chandef_dfs_cac_time(struct wiphy *wiphy,
 +                            const struct cfg80211_chan_def *chandef);
 +
  /**
   * nl80211_send_chandef - sends the channel definition.
   * @msg: the msg to send channel definition
@@@ -1366,7 -1289,6 +1366,7 @@@ struct cfg80211_acl_data 
   * struct cfg80211_fils_discovery - FILS discovery parameters from
   * IEEE Std 802.11ai-2016, Annex C.3 MIB detail.
   *
 + * @update: Set to true if the feature configuration should be updated.
   * @min_interval: Minimum packet interval in TUs (0 - 10000)
   * @max_interval: Maximum packet interval in TUs (0 - 10000)
   * @tmpl_len: Template length
   *    frame headers.
   */
  struct cfg80211_fils_discovery {
 +      bool update;
        u32 min_interval;
        u32 max_interval;
        size_t tmpl_len;
   * struct cfg80211_unsol_bcast_probe_resp - Unsolicited broadcast probe
   *    response parameters in 6GHz.
   *
 + * @update: Set to true if the feature configuration should be updated.
   * @interval: Packet interval in TUs. Maximum allowed is 20 TU, as mentioned
   *    in IEEE P802.11ax/D6.0 26.17.2.3.2 - AP behavior for fast passive
   *    scanning
   * @tmpl: Template data for probe response
   */
  struct cfg80211_unsol_bcast_probe_resp {
 +      bool update;
        u32 interval;
        size_t tmpl_len;
        const u8 *tmpl;
@@@ -1480,22 -1399,6 +1480,22 @@@ struct cfg80211_ap_settings 
        u16 punct_bitmap;
  };
  
 +
 +/**
 + * struct cfg80211_ap_update - AP configuration update
 + *
 + * Subset of &struct cfg80211_ap_settings, for updating a running AP.
 + *
 + * @beacon: beacon data
 + * @fils_discovery: FILS discovery transmission parameters
 + * @unsol_bcast_probe_resp: Unsolicited broadcast probe response parameters
 + */
 +struct cfg80211_ap_update {
 +      struct cfg80211_beacon_data beacon;
 +      struct cfg80211_fils_discovery fils_discovery;
 +      struct cfg80211_unsol_bcast_probe_resp unsol_bcast_probe_resp;
 +};
 +
  /**
   * struct cfg80211_csa_settings - channel switch settings
   *
@@@ -2584,6 -2487,7 +2584,6 @@@ struct cfg80211_scan_6ghz_params 
   * @n_ssids: number of SSIDs
   * @channels: channels to scan on.
   * @n_channels: total number of channels to scan
 - * @scan_width: channel width for scanning
   * @ie: optional information element(s) to add into Probe Request or %NULL
   * @ie_len: length of ie in octets
   * @duration: how long to listen on each channel, in TUs. If
@@@ -2613,6 -2517,7 +2613,6 @@@ struct cfg80211_scan_request 
        struct cfg80211_ssid *ssids;
        int n_ssids;
        u32 n_channels;
 -      enum nl80211_bss_scan_width scan_width;
        const u8 *ie;
        size_t ie_len;
        u16 duration;
@@@ -2707,6 -2612,7 +2707,6 @@@ struct cfg80211_bss_select_adjust 
   * @ssids: SSIDs to scan for (passed in the probe_reqs in active scans)
   * @n_ssids: number of SSIDs
   * @n_channels: total number of channels to scan
 - * @scan_width: channel width for scanning
   * @ie: optional information element(s) to add into Probe Request or %NULL
   * @ie_len: length of ie in octets
   * @flags: control flags from &enum nl80211_scan_flags
@@@ -2754,6 -2660,7 +2754,6 @@@ struct cfg80211_sched_scan_request 
        struct cfg80211_ssid *ssids;
        int n_ssids;
        u32 n_channels;
 -      enum nl80211_bss_scan_width scan_width;
        const u8 *ie;
        size_t ie_len;
        u32 flags;
@@@ -2801,6 -2708,7 +2801,6 @@@ enum cfg80211_signal_type 
  /**
   * struct cfg80211_inform_bss - BSS inform data
   * @chan: channel the frame was received on
 - * @scan_width: scan width that was used
   * @signal: signal strength value, according to the wiphy's
   *    signal type
   * @boottime_ns: timestamp (CLOCK_BOOTTIME) when the information was
   */
  struct cfg80211_inform_bss {
        struct ieee80211_channel *chan;
 -      enum nl80211_bss_scan_width scan_width;
        s32 signal;
        u64 boottime_ns;
        u64 parent_tsf;
@@@ -2853,6 -2762,7 +2853,6 @@@ struct cfg80211_bss_ies 
   * for use in scan results and similar.
   *
   * @channel: channel this BSS is on
 - * @scan_width: width of the control channel
   * @bssid: BSSID of the BSS
   * @beacon_interval: the beacon interval as from the frame
   * @capability: the capability field in host byte order
   */
  struct cfg80211_bss {
        struct ieee80211_channel *channel;
 -      enum nl80211_bss_scan_width scan_width;
  
        const struct cfg80211_bss_ies __rcu *ies;
        const struct cfg80211_bss_ies __rcu *beacon_ies;
@@@ -2980,15 -2891,12 +2980,15 @@@ struct cfg80211_auth_request 
   * @elems_len: length of the elements
   * @disabled: If set this link should be included during association etc. but it
   *    should not be used until enabled by the AP MLD.
 + * @error: per-link error code, must be <= 0. If there is an error, then the
 + *    operation as a whole must fail.
   */
  struct cfg80211_assoc_link {
        struct cfg80211_bss *bss;
        const u8 *elems;
        size_t elems_len;
        bool disabled;
 +      int error;
  };
  
  /**
@@@ -4542,7 -4450,7 +4542,7 @@@ struct cfg80211_ops 
        int     (*start_ap)(struct wiphy *wiphy, struct net_device *dev,
                            struct cfg80211_ap_settings *settings);
        int     (*change_beacon)(struct wiphy *wiphy, struct net_device *dev,
 -                               struct cfg80211_beacon_data *info);
 +                               struct cfg80211_ap_update *info);
        int     (*stop_ap)(struct wiphy *wiphy, struct net_device *dev,
                           unsigned int link_id);
  
   * @WIPHY_FLAG_SUPPORTS_EXT_KCK_32: The device supports 32-byte KCK keys.
   * @WIPHY_FLAG_NOTIFY_REGDOM_BY_DRIVER: The device could handle reg notify for
   *    NL80211_REGDOM_SET_BY_DRIVER.
 + * @WIPHY_FLAG_CHANNEL_CHANGE_ON_BEACON: reg_call_notifier() is called if driver
 + *    set this flag to update channels on beacon hints.
   */
  enum wiphy_flags {
        WIPHY_FLAG_SUPPORTS_EXT_KEK_KCK         = BIT(0),
        WIPHY_FLAG_SUPPORTS_5_10_MHZ            = BIT(22),
        WIPHY_FLAG_HAS_CHANNEL_SWITCH           = BIT(23),
        WIPHY_FLAG_NOTIFY_REGDOM_BY_DRIVER      = BIT(24),
 +      WIPHY_FLAG_CHANNEL_CHANGE_ON_BEACON     = BIT(25),
  };
  
  /**
@@@ -5921,16 -5826,6 +5921,16 @@@ void wiphy_work_queue(struct wiphy *wip
   */
  void wiphy_work_cancel(struct wiphy *wiphy, struct wiphy_work *work);
  
 +/**
 + * wiphy_work_flush - flush previously queued work
 + * @wiphy: the wiphy, for debug purposes
 + * @work: the work to flush, this can be %NULL to flush all work
 + *
 + * Flush the work (i.e. run it if pending). This must be called
 + * under the wiphy mutex acquired by wiphy_lock().
 + */
 +void wiphy_work_flush(struct wiphy *wiphy, struct wiphy_work *work);
 +
  struct wiphy_delayed_work {
        struct wiphy_work work;
        struct wiphy *wiphy;
@@@ -5974,17 -5869,6 +5974,17 @@@ void wiphy_delayed_work_queue(struct wi
  void wiphy_delayed_work_cancel(struct wiphy *wiphy,
                               struct wiphy_delayed_work *dwork);
  
 +/**
 + * wiphy_delayed_work_flush - flush previously queued delayed work
 + * @wiphy: the wiphy, for debug purposes
 + * @dwork: the delayed work to flush
 + *
 + * Flush the work (i.e. run it if pending). This must be called
 + * under the wiphy mutex acquired by wiphy_lock().
 + */
 +void wiphy_delayed_work_flush(struct wiphy *wiphy,
 +                            struct wiphy_delayed_work *dwork);
 +
  /**
   * struct wireless_dev - wireless device state
   *
   * @mgmt_registrations: list of registrations for management frames
   * @mgmt_registrations_need_update: mgmt registrations were updated,
   *    need to propagate the update to the driver
 - * @mtx: mutex used to lock data in this struct, may be used by drivers
 - *    and some API functions require it held
   * @beacon_interval: beacon interval used on this device for transmitting
   *    beacons, 0 when not valid
   * @address: The address for this device, valid only if @netdev is %NULL
   * @event_lock: (private) lock for event list
   * @owner_nlportid: (private) owner socket port ID
   * @nl_owner_dead: (private) owner socket went away
+  * @cqm_rssi_work: (private) CQM RSSI reporting work
   * @cqm_config: (private) nl80211 RSSI monitor state
   * @pmsr_list: (private) peer measurement requests
   * @pmsr_lock: (private) peer measurements requests/results lock
@@@ -6078,6 -5965,8 +6079,6 @@@ struct wireless_dev 
        struct list_head mgmt_registrations;
        u8 mgmt_registrations_need_update:1;
  
 -      struct mutex mtx;
 -
        bool use_4addr, is_running, registered, registering;
  
        u8 address[ETH_ALEN] __aligned(sizeof(u16));
        } wext;
  #endif
  
-       struct cfg80211_cqm_config *cqm_config;
+       struct wiphy_work cqm_rssi_work;
+       struct cfg80211_cqm_config __rcu *cqm_config;
  
        struct list_head pmsr_list;
        spinlock_t pmsr_lock;
@@@ -6367,11 -6257,13 +6369,11 @@@ ieee80211_get_response_rate(struct ieee
  /**
   * ieee80211_mandatory_rates - get mandatory rates for a given band
   * @sband: the band to look for rates in
 - * @scan_width: width of the control channel
   *
   * This function returns a bitmap of the mandatory rates for the given
   * band, bits are set according to the rate position in the bitrates array.
   */
 -u32 ieee80211_mandatory_rates(struct ieee80211_supported_band *sband,
 -                            enum nl80211_bss_scan_width scan_width);
 +u32 ieee80211_mandatory_rates(struct ieee80211_supported_band *sband);
  
  /*
   * Radiotap parsing functions -- for controlled injection support
@@@ -7032,6 -6924,22 +7034,6 @@@ cfg80211_inform_bss_frame_data(struct w
                               struct ieee80211_mgmt *mgmt, size_t len,
                               gfp_t gfp);
  
 -static inline struct cfg80211_bss * __must_check
 -cfg80211_inform_bss_width_frame(struct wiphy *wiphy,
 -                              struct ieee80211_channel *rx_channel,
 -                              enum nl80211_bss_scan_width scan_width,
 -                              struct ieee80211_mgmt *mgmt, size_t len,
 -                              s32 signal, gfp_t gfp)
 -{
 -      struct cfg80211_inform_bss data = {
 -              .chan = rx_channel,
 -              .scan_width = scan_width,
 -              .signal = signal,
 -      };
 -
 -      return cfg80211_inform_bss_frame_data(wiphy, &data, mgmt, len, gfp);
 -}
 -
  static inline struct cfg80211_bss * __must_check
  cfg80211_inform_bss_frame(struct wiphy *wiphy,
                          struct ieee80211_channel *rx_channel,
  {
        struct cfg80211_inform_bss data = {
                .chan = rx_channel,
 -              .scan_width = NL80211_BSS_CHAN_WIDTH_20,
                .signal = signal,
        };
  
@@@ -7141,6 -7050,26 +7143,6 @@@ cfg80211_inform_bss_data(struct wiphy *
                         u16 beacon_interval, const u8 *ie, size_t ielen,
                         gfp_t gfp);
  
 -static inline struct cfg80211_bss * __must_check
 -cfg80211_inform_bss_width(struct wiphy *wiphy,
 -                        struct ieee80211_channel *rx_channel,
 -                        enum nl80211_bss_scan_width scan_width,
 -                        enum cfg80211_bss_frame_type ftype,
 -                        const u8 *bssid, u64 tsf, u16 capability,
 -                        u16 beacon_interval, const u8 *ie, size_t ielen,
 -                        s32 signal, gfp_t gfp)
 -{
 -      struct cfg80211_inform_bss data = {
 -              .chan = rx_channel,
 -              .scan_width = scan_width,
 -              .signal = signal,
 -      };
 -
 -      return cfg80211_inform_bss_data(wiphy, &data, ftype, bssid, tsf,
 -                                      capability, beacon_interval, ie, ielen,
 -                                      gfp);
 -}
 -
  static inline struct cfg80211_bss * __must_check
  cfg80211_inform_bss(struct wiphy *wiphy,
                    struct ieee80211_channel *rx_channel,
  {
        struct cfg80211_inform_bss data = {
                .chan = rx_channel,
 -              .scan_width = NL80211_BSS_CHAN_WIDTH_20,
                .signal = signal,
        };
  
@@@ -7235,6 -7165,19 +7237,6 @@@ void cfg80211_bss_iter(struct wiphy *wi
                                    void *data),
                       void *iter_data);
  
 -static inline enum nl80211_bss_scan_width
 -cfg80211_chandef_to_scan_width(const struct cfg80211_chan_def *chandef)
 -{
 -      switch (chandef->width) {
 -      case NL80211_CHAN_WIDTH_5:
 -              return NL80211_BSS_CHAN_WIDTH_5;
 -      case NL80211_CHAN_WIDTH_10:
 -              return NL80211_BSS_CHAN_WIDTH_10;
 -      default:
 -              return NL80211_BSS_CHAN_WIDTH_20;
 -      }
 -}
 -
  /**
   * cfg80211_rx_mlme_mgmt - notification of processed MLME management frame
   * @dev: network device
@@@ -7290,7 -7233,7 +7292,7 @@@ struct cfg80211_rx_assoc_resp 
        int uapsd_queues;
        const u8 *ap_mld_addr;
        struct {
-               const u8 *addr;
+               u8 addr[ETH_ALEN] __aligned(2);
                struct cfg80211_bss *bss;
                u16 status;
        } links[IEEE80211_MLD_MAX_NUM_LINKS];
@@@ -8627,7 -8570,7 +8629,7 @@@ bool cfg80211_reg_can_beacon_relax(stru
   * @link_id: the link ID for MLO, must be 0 for non-MLO
   * @punct_bitmap: the new puncturing bitmap
   *
 - * Caller must acquire wdev_lock, therefore must only be called from sleepable
 + * Caller must hold wiphy mutex, therefore must only be called from sleepable
   * driver context!
   */
  void cfg80211_ch_switch_notify(struct net_device *dev,
@@@ -8866,18 -8809,6 +8868,18 @@@ static inline size_t ieee80211_ie_split
        return ieee80211_ie_split_ric(ies, ielen, ids, n_ids, NULL, 0, offset);
  }
  
 +/**
 + * ieee80211_fragment_element - fragment the last element in skb
 + * @skb: The skbuf that the element was added to
 + * @len_pos: Pointer to length of the element to fragment
 + * @frag_id: The element ID to use for fragments
 + *
 + * This function fragments all data after @len_pos, adding fragmentation
 + * elements with the given ID as appropriate. The SKB will grow in size
 + * accordingly.
 + */
 +void ieee80211_fragment_element(struct sk_buff *skb, u8 *len_pos, u8 frag_id);
 +
  /**
   * cfg80211_report_wowlan_wakeup - report wakeup from WoWLAN
   * @wdev: the wireless device reporting the wakeup
diff --combined net/mac80211/cfg.c
index 5bc6b13294654d0de42311de1a6a0b75937e93f3,0e3a1753a51c6dcc904b004ec15aa2f9f2e1a413..5cec0c251e86ac0ba3f6c3737ccc70083e459824
@@@ -214,8 -214,6 +214,8 @@@ static int ieee80211_change_iface(struc
        struct sta_info *sta;
        int ret;
  
 +      lockdep_assert_wiphy(local->hw.wiphy);
 +
        ret = ieee80211_if_change_type(sdata, type);
        if (ret)
                return ret;
                if (!ifmgd->associated)
                        return 0;
  
 -              mutex_lock(&local->sta_mtx);
                sta = sta_info_get(sdata, sdata->deflink.u.mgd.bssid);
                if (sta)
                        drv_sta_set_4addr(local, sdata, &sta->sta,
                                          params->use_4addr);
 -              mutex_unlock(&local->sta_mtx);
  
                if (params->use_4addr)
                        ieee80211_send_4addr_nullfunc(local, sdata);
@@@ -261,9 -261,9 +261,9 @@@ static int ieee80211_start_p2p_device(s
        struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
        int ret;
  
 -      mutex_lock(&sdata->local->chanctx_mtx);
 +      lockdep_assert_wiphy(sdata->local->hw.wiphy);
 +
        ret = ieee80211_check_combinations(sdata, NULL, 0, 0);
 -      mutex_unlock(&sdata->local->chanctx_mtx);
        if (ret < 0)
                return ret;
  
@@@ -283,9 -283,9 +283,9 @@@ static int ieee80211_start_nan(struct w
        struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
        int ret;
  
 -      mutex_lock(&sdata->local->chanctx_mtx);
 +      lockdep_assert_wiphy(sdata->local->hw.wiphy);
 +
        ret = ieee80211_check_combinations(sdata, NULL, 0, 0);
 -      mutex_unlock(&sdata->local->chanctx_mtx);
        if (ret < 0)
                return ret;
  
@@@ -452,11 -452,13 +452,11 @@@ static int ieee80211_set_tx(struct ieee
        if (sta->ptk_idx == key_idx)
                return 0;
  
 -      mutex_lock(&local->key_mtx);
 -      key = key_mtx_dereference(local, sta->ptk[key_idx]);
 +      key = wiphy_dereference(local->hw.wiphy, sta->ptk[key_idx]);
  
        if (key && key->conf.flags & IEEE80211_KEY_FLAG_NO_AUTO_TX)
                ret = ieee80211_set_tx_key(key);
  
 -      mutex_unlock(&local->key_mtx);
        return ret;
  }
  
@@@ -470,9 -472,8 +470,10 @@@ static int ieee80211_add_key(struct wip
        struct ieee80211_local *local = sdata->local;
        struct sta_info *sta = NULL;
        struct ieee80211_key *key;
+       int err;
  
 +      lockdep_assert_wiphy(local->hw.wiphy);
 +
        if (!ieee80211_sdata_running(sdata))
                return -ENETDOWN;
  
        if (params->mode == NL80211_KEY_NO_TX)
                key->conf.flags |= IEEE80211_KEY_FLAG_NO_AUTO_TX;
  
 -      mutex_lock(&local->sta_mtx);
 -
        if (mac_addr) {
                sta = sta_info_get_bss(sdata, mac_addr);
                /*
                 */
                if (!sta || !test_sta_flag(sta, WLAN_STA_ASSOC)) {
                        ieee80211_key_free_unused(key);
 -                      err = -ENOENT;
 -                      goto out_unlock;
 +                      return -ENOENT;
                }
        }
  
                break;
        }
  
-       return ieee80211_key_link(key, link, sta);
+       err = ieee80211_key_link(key, link, sta);
+       /* KRACK protection, shouldn't happen but just silently accept key */
+       if (err == -EALREADY)
+               err = 0;
 - out_unlock:
 -      mutex_unlock(&local->sta_mtx);
 -
+       return err;
  }
  
  static struct ieee80211_key *
@@@ -573,7 -585,8 +579,7 @@@ ieee80211_lookup_key(struct ieee80211_s
        struct ieee80211_key *key;
  
        if (link_id >= 0) {
 -              link = rcu_dereference_check(sdata->link[link_id],
 -                                           lockdep_is_held(&sdata->wdev.mtx));
 +              link = sdata_dereference(sdata->link[link_id], sdata);
                if (!link)
                        return NULL;
        }
  
                if (link_id >= 0) {
                        link_sta = rcu_dereference_check(sta->link[link_id],
 -                                                       lockdep_is_held(&local->sta_mtx));
 +                                                       lockdep_is_held(&local->hw.wiphy->mtx));
                        if (!link_sta)
                                return NULL;
                } else {
                }
  
                if (pairwise && key_idx < NUM_DEFAULT_KEYS)
 -                      return rcu_dereference_check_key_mtx(local,
 -                                                           sta->ptk[key_idx]);
 +                      return wiphy_dereference(local->hw.wiphy,
 +                                               sta->ptk[key_idx]);
  
                if (!pairwise &&
                    key_idx < NUM_DEFAULT_KEYS +
                              NUM_DEFAULT_MGMT_KEYS +
                              NUM_DEFAULT_BEACON_KEYS)
 -                      return rcu_dereference_check_key_mtx(local,
 -                                                           link_sta->gtk[key_idx]);
 +                      return wiphy_dereference(local->hw.wiphy,
 +                                               link_sta->gtk[key_idx]);
  
                return NULL;
        }
  
        if (pairwise && key_idx < NUM_DEFAULT_KEYS)
 -              return rcu_dereference_check_key_mtx(local,
 -                                                   sdata->keys[key_idx]);
 +              return wiphy_dereference(local->hw.wiphy, sdata->keys[key_idx]);
  
 -      key = rcu_dereference_check_key_mtx(local, link->gtk[key_idx]);
 +      key = wiphy_dereference(local->hw.wiphy, link->gtk[key_idx]);
        if (key)
                return key;
  
        /* or maybe it was a WEP key */
        if (key_idx < NUM_DEFAULT_KEYS)
 -              return rcu_dereference_check_key_mtx(local, sdata->keys[key_idx]);
 +              return wiphy_dereference(local->hw.wiphy, sdata->keys[key_idx]);
  
        return NULL;
  }
@@@ -630,16 -644,25 +636,16 @@@ static int ieee80211_del_key(struct wip
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_key *key;
 -      int ret;
  
 -      mutex_lock(&local->sta_mtx);
 -      mutex_lock(&local->key_mtx);
 +      lockdep_assert_wiphy(local->hw.wiphy);
  
        key = ieee80211_lookup_key(sdata, link_id, key_idx, pairwise, mac_addr);
 -      if (!key) {
 -              ret = -ENOENT;
 -              goto out_unlock;
 -      }
 +      if (!key)
 +              return -ENOENT;
  
        ieee80211_key_free(key, sdata->vif.type == NL80211_IFTYPE_STATION);
  
 -      ret = 0;
 - out_unlock:
 -      mutex_unlock(&local->key_mtx);
 -      mutex_unlock(&local->sta_mtx);
 -
 -      return ret;
 +      return 0;
  }
  
  static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev,
@@@ -810,11 -833,15 +816,11 @@@ void sta_set_rate_info_tx(struct sta_in
                rinfo->nss = ieee80211_rate_get_vht_nss(rate);
        } else {
                struct ieee80211_supported_band *sband;
 -              int shift = ieee80211_vif_get_shift(&sta->sdata->vif);
 -              u16 brate;
  
                sband = ieee80211_get_sband(sta->sdata);
                WARN_ON_ONCE(sband && !sband->bitrates);
 -              if (sband && sband->bitrates) {
 -                      brate = sband->bitrates[rate->idx].bitrate;
 -                      rinfo->legacy = DIV_ROUND_UP(brate, 1 << shift);
 -              }
 +              if (sband && sband->bitrates)
 +                      rinfo->legacy = sband->bitrates[rate->idx].bitrate;
        }
        if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
                rinfo->bw = RATE_INFO_BW_40;
@@@ -836,7 -863,7 +842,7 @@@ static int ieee80211_dump_station(struc
        struct sta_info *sta;
        int ret = -ENOENT;
  
 -      mutex_lock(&local->sta_mtx);
 +      lockdep_assert_wiphy(local->hw.wiphy);
  
        sta = sta_info_get_by_idx(sdata, idx);
        if (sta) {
                sta_set_sinfo(sta, sinfo, true);
        }
  
 -      mutex_unlock(&local->sta_mtx);
 -
        return ret;
  }
  
@@@ -864,7 -893,7 +870,7 @@@ static int ieee80211_get_station(struc
        struct sta_info *sta;
        int ret = -ENOENT;
  
 -      mutex_lock(&local->sta_mtx);
 +      lockdep_assert_wiphy(local->hw.wiphy);
  
        sta = sta_info_get_bss(sdata, mac);
        if (sta) {
                sta_set_sinfo(sta, sinfo, true);
        }
  
 -      mutex_unlock(&local->sta_mtx);
 -
        return ret;
  }
  
@@@ -882,8 -913,6 +888,8 @@@ static int ieee80211_set_monitor_channe
        struct ieee80211_sub_if_data *sdata;
        int ret = 0;
  
 +      lockdep_assert_wiphy(local->hw.wiphy);
 +
        if (cfg80211_chandef_identical(&local->monitor_chandef, chandef))
                return 0;
  
                sdata = wiphy_dereference(local->hw.wiphy,
                                          local->monitor_sdata);
                if (sdata) {
 -                      sdata_lock(sdata);
 -                      mutex_lock(&local->mtx);
                        ieee80211_link_release_channel(&sdata->deflink);
                        ret = ieee80211_link_use_channel(&sdata->deflink,
                                                         chandef,
                                                         IEEE80211_CHANCTX_EXCLUSIVE);
 -                      mutex_unlock(&local->mtx);
 -                      sdata_unlock(sdata);
                }
        } else {
 -              mutex_lock(&local->mtx);
                if (local->open_count == local->monitors) {
                        local->_oper_chandef = *chandef;
                        ieee80211_hw_config(local, 0);
                }
 -              mutex_unlock(&local->mtx);
        }
  
        if (ret == 0)
@@@ -952,29 -987,25 +958,29 @@@ static int ieee80211_set_fils_discovery
        struct fils_discovery_data *new, *old = NULL;
        struct ieee80211_fils_discovery *fd;
  
 -      if (!params->tmpl || !params->tmpl_len)
 -              return -EINVAL;
 +      if (!params->update)
 +              return 0;
  
        fd = &link_conf->fils_discovery;
        fd->min_interval = params->min_interval;
        fd->max_interval = params->max_interval;
  
        old = sdata_dereference(link->u.ap.fils_discovery, sdata);
 -      new = kzalloc(sizeof(*new) + params->tmpl_len, GFP_KERNEL);
 -      if (!new)
 -              return -ENOMEM;
 -      new->len = params->tmpl_len;
 -      memcpy(new->data, params->tmpl, params->tmpl_len);
 -      rcu_assign_pointer(link->u.ap.fils_discovery, new);
 -
        if (old)
                kfree_rcu(old, rcu_head);
  
 -      return 0;
 +      if (params->tmpl && params->tmpl_len) {
 +              new = kzalloc(sizeof(*new) + params->tmpl_len, GFP_KERNEL);
 +              if (!new)
 +                      return -ENOMEM;
 +              new->len = params->tmpl_len;
 +              memcpy(new->data, params->tmpl, params->tmpl_len);
 +              rcu_assign_pointer(link->u.ap.fils_discovery, new);
 +      } else {
 +              RCU_INIT_POINTER(link->u.ap.fils_discovery, NULL);
 +      }
 +
 +      return BSS_CHANGED_FILS_DISCOVERY;
  }
  
  static int
@@@ -985,27 -1016,23 +991,27 @@@ ieee80211_set_unsol_bcast_probe_resp(st
  {
        struct unsol_bcast_probe_resp_data *new, *old = NULL;
  
 -      if (!params->tmpl || !params->tmpl_len)
 -              return -EINVAL;
 +      if (!params->update)
 +              return 0;
  
 -      old = sdata_dereference(link->u.ap.unsol_bcast_probe_resp, sdata);
 -      new = kzalloc(sizeof(*new) + params->tmpl_len, GFP_KERNEL);
 -      if (!new)
 -              return -ENOMEM;
 -      new->len = params->tmpl_len;
 -      memcpy(new->data, params->tmpl, params->tmpl_len);
 -      rcu_assign_pointer(link->u.ap.unsol_bcast_probe_resp, new);
 +      link_conf->unsol_bcast_probe_resp_interval = params->interval;
  
 +      old = sdata_dereference(link->u.ap.unsol_bcast_probe_resp, sdata);
        if (old)
                kfree_rcu(old, rcu_head);
  
 -      link_conf->unsol_bcast_probe_resp_interval = params->interval;
 +      if (params->tmpl && params->tmpl_len) {
 +              new = kzalloc(sizeof(*new) + params->tmpl_len, GFP_KERNEL);
 +              if (!new)
 +                      return -ENOMEM;
 +              new->len = params->tmpl_len;
 +              memcpy(new->data, params->tmpl, params->tmpl_len);
 +              rcu_assign_pointer(link->u.ap.unsol_bcast_probe_resp, new);
 +      } else {
 +              RCU_INIT_POINTER(link->u.ap.unsol_bcast_probe_resp, NULL);
 +      }
  
 -      return 0;
 +      return BSS_CHANGED_UNSOL_BCAST_PROBE_RESP;
  }
  
  static int ieee80211_set_ftm_responder_params(
@@@ -1251,8 -1278,6 +1257,8 @@@ static int ieee80211_start_ap(struct wi
        struct ieee80211_link_data *link;
        struct ieee80211_bss_conf *link_conf;
  
 +      lockdep_assert_wiphy(local->hw.wiphy);
 +
        link = sdata_dereference(sdata->link[link_id], sdata);
        if (!link)
                return -ENOLINK;
                        return err;
        }
  
 -      mutex_lock(&local->mtx);
        err = ieee80211_link_use_channel(link, &params->chandef,
                                         IEEE80211_CHANCTX_SHARED);
        if (!err)
                ieee80211_link_copy_chanctx_to_vlans(link, false);
 -      mutex_unlock(&local->mtx);
        if (err) {
                link_conf->beacon_int = prev_beacon_int;
                return err;
        if (err < 0)
                goto error;
  
 -      if (params->fils_discovery.max_interval) {
 -              err = ieee80211_set_fils_discovery(sdata,
 -                                                 &params->fils_discovery,
 -                                                 link, link_conf);
 -              if (err < 0)
 -                      goto error;
 -              changed |= BSS_CHANGED_FILS_DISCOVERY;
 -      }
 +      err = ieee80211_set_fils_discovery(sdata, &params->fils_discovery,
 +                                         link, link_conf);
 +      if (err < 0)
 +              goto error;
 +      changed |= err;
  
 -      if (params->unsol_bcast_probe_resp.interval) {
 -              err = ieee80211_set_unsol_bcast_probe_resp(sdata,
 -                                                         &params->unsol_bcast_probe_resp,
 -                                                         link, link_conf);
 -              if (err < 0)
 -                      goto error;
 -              changed |= BSS_CHANGED_UNSOL_BCAST_PROBE_RESP;
 -      }
 +      err = ieee80211_set_unsol_bcast_probe_resp(sdata,
 +                                                 &params->unsol_bcast_probe_resp,
 +                                                 link, link_conf);
 +      if (err < 0)
 +              goto error;
 +      changed |= err;
  
        err = drv_start_ap(sdata->local, sdata, link_conf);
        if (err) {
        return 0;
  
  error:
 -      mutex_lock(&local->mtx);
        ieee80211_link_release_channel(link);
 -      mutex_unlock(&local->mtx);
  
        return err;
  }
  
  static int ieee80211_change_beacon(struct wiphy *wiphy, struct net_device *dev,
 -                                 struct cfg80211_beacon_data *params)
 +                                 struct cfg80211_ap_update *params)
 +
  {
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
        struct ieee80211_link_data *link;
 +      struct cfg80211_beacon_data *beacon = &params->beacon;
        struct beacon_data *old;
        int err;
        struct ieee80211_bss_conf *link_conf;
        u64 changed = 0;
  
 -      sdata_assert_lock(sdata);
 +      lockdep_assert_wiphy(wiphy);
  
 -      link = sdata_dereference(sdata->link[params->link_id], sdata);
 +      link = sdata_dereference(sdata->link[beacon->link_id], sdata);
        if (!link)
                return -ENOLINK;
  
        if (!old)
                return -ENOENT;
  
 -      err = ieee80211_assign_beacon(sdata, link, params, NULL, NULL,
 +      err = ieee80211_assign_beacon(sdata, link, beacon, NULL, NULL,
                                      &changed);
        if (err < 0)
                return err;
  
 -      if (params->he_bss_color_valid &&
 -          params->he_bss_color.enabled != link_conf->he_bss_color.enabled) {
 -              link_conf->he_bss_color.enabled = params->he_bss_color.enabled;
 +      err = ieee80211_set_fils_discovery(sdata, &params->fils_discovery,
 +                                         link, link_conf);
 +      if (err < 0)
 +              return err;
 +      changed |= err;
 +
 +      err = ieee80211_set_unsol_bcast_probe_resp(sdata,
 +                                                 &params->unsol_bcast_probe_resp,
 +                                                 link, link_conf);
 +      if (err < 0)
 +              return err;
 +      changed |= err;
 +
 +      if (beacon->he_bss_color_valid &&
 +          beacon->he_bss_color.enabled != link_conf->he_bss_color.enabled) {
 +              link_conf->he_bss_color.enabled = beacon->he_bss_color.enabled;
                changed |= BSS_CHANGED_HE_BSS_COLOR;
        }
  
@@@ -1560,7 -1579,7 +1566,7 @@@ static int ieee80211_stop_ap(struct wip
                sdata_dereference(sdata->link[link_id], sdata);
        struct ieee80211_bss_conf *link_conf = link->conf;
  
 -      sdata_assert_lock(sdata);
 +      lockdep_assert_wiphy(local->hw.wiphy);
  
        old_beacon = sdata_dereference(link->u.ap.beacon, sdata);
        if (!old_beacon)
                                  sdata);
  
        /* abort any running channel switch or color change */
 -      mutex_lock(&local->mtx);
        link_conf->csa_active = false;
        link_conf->color_change_active = false;
        if (link->csa_block_tx) {
                link->csa_block_tx = false;
        }
  
 -      mutex_unlock(&local->mtx);
 -
        ieee80211_free_next_beacon(link);
  
        /* turn off carrier for this interface and dependent VLANs */
  
        if (sdata->wdev.cac_started) {
                chandef = link_conf->chandef;
 -              cancel_delayed_work_sync(&link->dfs_cac_timer_work);
 +              wiphy_delayed_work_cancel(wiphy, &link->dfs_cac_timer_work);
                cfg80211_cac_event(sdata->dev, &chandef,
                                   NL80211_RADAR_CAC_ABORTED,
                                   GFP_KERNEL);
        local->total_ps_buffered -= skb_queue_len(&sdata->u.ap.ps.bc_buf);
        ieee80211_purge_tx_queue(&local->hw, &sdata->u.ap.ps.bc_buf);
  
 -      mutex_lock(&local->mtx);
        ieee80211_link_copy_chanctx_to_vlans(link, true);
        ieee80211_link_release_channel(link);
 -      mutex_unlock(&local->mtx);
  
        return 0;
  }
@@@ -1779,7 -1803,7 +1785,7 @@@ static int sta_link_apply_parameters(st
                sdata_dereference(sdata->link[link_id], sdata);
        struct link_sta_info *link_sta =
                rcu_dereference_protected(sta->link[link_id],
 -                                        lockdep_is_held(&local->sta_mtx));
 +                                        lockdep_is_held(&local->hw.wiphy->mtx));
  
        /*
         * If there are no changes, then accept a link that doesn't exist,
        /* VHT can override some HT caps such as the A-MSDU max length */
        if (params->vht_capa)
                ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband,
-                                                   params->vht_capa, link_sta);
+                                                   params->vht_capa, NULL,
+                                                   link_sta);
  
        if (params->he_capa)
                ieee80211_he_cap_ie_to_sta_he_cap(sdata, sband,
@@@ -2013,8 -2038,6 +2020,8 @@@ static int ieee80211_add_station(struc
        struct ieee80211_sub_if_data *sdata;
        int err;
  
 +      lockdep_assert_wiphy(local->hw.wiphy);
 +
        if (params->vlan) {
                sdata = IEEE80211_DEV_TO_SUB_IF(params->vlan);
  
         * visible yet), sta_apply_parameters (and inner functions) require
         * the mutex due to other paths.
         */
 -      mutex_lock(&local->sta_mtx);
        err = sta_apply_parameters(local, sta, params);
 -      mutex_unlock(&local->sta_mtx);
        if (err) {
                sta_info_free(local, sta);
                return err;
@@@ -2101,11 -2126,13 +2108,11 @@@ static int ieee80211_change_station(str
        enum cfg80211_station_type statype;
        int err;
  
 -      mutex_lock(&local->sta_mtx);
 +      lockdep_assert_wiphy(local->hw.wiphy);
  
        sta = sta_info_get_bss(sdata, mac);
 -      if (!sta) {
 -              err = -ENOENT;
 -              goto out_err;
 -      }
 +      if (!sta)
 +              return -ENOENT;
  
        switch (sdata->vif.type) {
        case NL80211_IFTYPE_MESH_POINT:
                        statype = CFG80211_STA_AP_CLIENT_UNASSOC;
                break;
        default:
 -              err = -EOPNOTSUPP;
 -              goto out_err;
 +              return -EOPNOTSUPP;
        }
  
        err = cfg80211_check_station_change(wiphy, params, statype);
        if (err)
 -              goto out_err;
 +              return err;
  
        if (params->vlan && params->vlan != sta->sdata->dev) {
                vlansdata = IEEE80211_DEV_TO_SUB_IF(params->vlan);
  
                if (params->vlan->ieee80211_ptr->use_4addr) {
 -                      if (vlansdata->u.vlan.sta) {
 -                              err = -EBUSY;
 -                              goto out_err;
 -                      }
 +                      if (vlansdata->u.vlan.sta)
 +                              return -EBUSY;
  
                        rcu_assign_pointer(vlansdata->u.vlan.sta, sta);
                        __ieee80211_check_fast_rx_iface(vlansdata);
                }
        }
  
 -      /* we use sta_info_get_bss() so this might be different */
 -      if (sdata != sta->sdata) {
 -              mutex_lock_nested(&sta->sdata->wdev.mtx, 1);
 -              err = sta_apply_parameters(local, sta, params);
 -              mutex_unlock(&sta->sdata->wdev.mtx);
 -      } else {
 -              err = sta_apply_parameters(local, sta, params);
 -      }
 +      err = sta_apply_parameters(local, sta, params);
        if (err)
 -              goto out_err;
 -
 -      mutex_unlock(&local->sta_mtx);
 +              return err;
  
        if (sdata->vif.type == NL80211_IFTYPE_STATION &&
            params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)) {
        }
  
        return 0;
 -out_err:
 -      mutex_unlock(&local->sta_mtx);
 -      return err;
  }
  
  #ifdef CONFIG_MAC80211_MESH
@@@ -2596,8 -2638,6 +2603,8 @@@ static int ieee80211_join_mesh(struct w
        struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
        int err;
  
 +      lockdep_assert_wiphy(sdata->local->hw.wiphy);
 +
        memcpy(&ifmsh->mshcfg, conf, sizeof(struct mesh_config));
        err = copy_mesh_setup(ifmsh, setup);
        if (err)
        sdata->deflink.smps_mode = IEEE80211_SMPS_OFF;
        sdata->deflink.needed_rx_chains = sdata->local->rx_chains;
  
 -      mutex_lock(&sdata->local->mtx);
        err = ieee80211_link_use_channel(&sdata->deflink, &setup->chandef,
                                         IEEE80211_CHANCTX_SHARED);
 -      mutex_unlock(&sdata->local->mtx);
        if (err)
                return err;
  
@@@ -2621,11 -2663,11 +2628,11 @@@ static int ieee80211_leave_mesh(struct 
  {
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
  
 +      lockdep_assert_wiphy(sdata->local->hw.wiphy);
 +
        ieee80211_stop_mesh(sdata);
 -      mutex_lock(&sdata->local->mtx);
        ieee80211_link_release_channel(&sdata->deflink);
        kfree(sdata->u.mesh.ie);
 -      mutex_unlock(&sdata->local->mtx);
  
        return 0;
  }
@@@ -2983,8 -3025,6 +2990,8 @@@ static int ieee80211_set_tx_power(struc
        bool update_txp_type = false;
        bool has_monitor = false;
  
 +      lockdep_assert_wiphy(local->hw.wiphy);
 +
        if (wdev) {
                sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
  
                break;
        }
  
 -      mutex_lock(&local->iflist_mtx);
        list_for_each_entry(sdata, &local->interfaces, list) {
                if (sdata->vif.type == NL80211_IFTYPE_MONITOR) {
                        has_monitor = true;
                        continue;
                ieee80211_recalc_txpower(sdata, update_txp_type);
        }
 -      mutex_unlock(&local->iflist_mtx);
  
        if (has_monitor) {
                sdata = wiphy_dereference(local->hw.wiphy,
@@@ -3079,10 -3121,6 +3086,10 @@@ static int ieee80211_get_tx_power(struc
        else
                *dbm = sdata->vif.bss_conf.txpower;
  
 +      /* INT_MIN indicates no power level was set yet */
 +      if (*dbm == INT_MIN)
 +              return -EINVAL;
 +
        return 0;
  }
  
@@@ -3139,15 -3177,11 +3146,15 @@@ int __ieee80211_request_smps_mgd(struc
        struct sta_info *sta;
        bool tdls_peer_found = false;
  
 -      lockdep_assert_held(&sdata->wdev.mtx);
 +      lockdep_assert_wiphy(sdata->local->hw.wiphy);
  
        if (WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_STATION))
                return -EINVAL;
  
 +      if (ieee80211_vif_is_mld(&sdata->vif) &&
 +          !(sdata->vif.active_links & BIT(link->link_id)))
 +              return 0;
 +
        old_req = link->u.mgd.req_smps;
        link->u.mgd.req_smps = smps_mode;
  
            link->conf->chandef.width == NL80211_CHAN_WIDTH_20_NOHT)
                return 0;
  
 -      ap = link->u.mgd.bssid;
 +      ap = sdata->vif.cfg.ap_addr;
  
        rcu_read_lock();
        list_for_each_entry_rcu(sta, &sdata->local->sta_list, list) {
  
        /* send SM PS frame to AP */
        err = ieee80211_send_smps_action(sdata, smps_mode,
 -                                       ap, ap);
 +                                       ap, ap,
 +                                       ieee80211_vif_is_mld(&sdata->vif) ?
 +                                       link->link_id : -1);
        if (err)
                link->u.mgd.req_smps = old_req;
        else if (smps_mode != IEEE80211_SMPS_OFF && tdls_peer_found)
@@@ -3218,6 -3250,7 +3225,6 @@@ static int ieee80211_set_power_mgmt(str
        local->dynamic_ps_forced_timeout = timeout;
  
        /* no change, but if automatic follow powersave */
 -      sdata_lock(sdata);
        for (link_id = 0; link_id < ARRAY_SIZE(sdata->link); link_id++) {
                struct ieee80211_link_data *link;
  
                __ieee80211_request_smps_mgd(sdata, link,
                                             link->u.mgd.req_smps);
        }
 -      sdata_unlock(sdata);
  
        if (ieee80211_hw_check(&local->hw, SUPPORTS_DYNAMIC_PS))
                ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
@@@ -3373,8 -3407,7 +3380,8 @@@ static int ieee80211_start_radar_detect
        struct ieee80211_local *local = sdata->local;
        int err;
  
 -      mutex_lock(&local->mtx);
 +      lockdep_assert_wiphy(local->hw.wiphy);
 +
        if (!list_empty(&local->roc_list) || local->scanning) {
                err = -EBUSY;
                goto out_unlock;
        if (err)
                goto out_unlock;
  
 -      ieee80211_queue_delayed_work(&sdata->local->hw,
 -                                   &sdata->deflink.dfs_cac_timer_work,
 -                                   msecs_to_jiffies(cac_time_ms));
 +      wiphy_delayed_work_queue(wiphy, &sdata->deflink.dfs_cac_timer_work,
 +                               msecs_to_jiffies(cac_time_ms));
  
   out_unlock:
 -      mutex_unlock(&local->mtx);
        return err;
  }
  
@@@ -3402,21 -3437,20 +3409,21 @@@ static void ieee80211_end_cac(struct wi
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
        struct ieee80211_local *local = sdata->local;
  
 -      mutex_lock(&local->mtx);
 +      lockdep_assert_wiphy(local->hw.wiphy);
 +
        list_for_each_entry(sdata, &local->interfaces, list) {
                /* it might be waiting for the local->mtx, but then
                 * by the time it gets it, sdata->wdev.cac_started
                 * will no longer be true
                 */
 -              cancel_delayed_work(&sdata->deflink.dfs_cac_timer_work);
 +              wiphy_delayed_work_cancel(wiphy,
 +                                        &sdata->deflink.dfs_cac_timer_work);
  
                if (sdata->wdev.cac_started) {
                        ieee80211_link_release_channel(&sdata->deflink);
                        sdata->wdev.cac_started = false;
                }
        }
 -      mutex_unlock(&local->mtx);
  }
  
  static struct cfg80211_beacon_data *
@@@ -3548,11 -3582,11 +3555,11 @@@ void ieee80211_csa_finish(struct ieee80
                        if (iter == sdata || iter->vif.mbssid_tx_vif != vif)
                                continue;
  
 -                      ieee80211_queue_work(&iter->local->hw,
 -                                           &iter->deflink.csa_finalize_work);
 +                      wiphy_work_queue(iter->local->hw.wiphy,
 +                                       &iter->deflink.csa_finalize_work);
                }
        }
 -      ieee80211_queue_work(&local->hw, &sdata->deflink.csa_finalize_work);
 +      wiphy_work_queue(local->hw.wiphy, &sdata->deflink.csa_finalize_work);
  
        rcu_read_unlock();
  }
@@@ -3608,14 -3642,15 +3615,14 @@@ static int ieee80211_set_after_csa_beac
        return 0;
  }
  
 -static int __ieee80211_csa_finalize(struct ieee80211_sub_if_data *sdata)
 +static int __ieee80211_csa_finalize(struct ieee80211_link_data *link_data)
  {
 +      struct ieee80211_sub_if_data *sdata = link_data->sdata;
        struct ieee80211_local *local = sdata->local;
        u64 changed = 0;
        int err;
  
 -      sdata_assert_lock(sdata);
 -      lockdep_assert_held(&local->mtx);
 -      lockdep_assert_held(&local->chanctx_mtx);
 +      lockdep_assert_wiphy(local->hw.wiphy);
  
        /*
         * using reservation isn't immediate as it may be deferred until later
         * completed successfully
         */
  
 -      if (sdata->deflink.reserved_chanctx) {
 +      if (link_data->reserved_chanctx) {
                /*
                 * with multi-vif csa driver may call ieee80211_csa_finish()
                 * many times while waiting for other interfaces to use their
                 * reservations
                 */
 -              if (sdata->deflink.reserved_ready)
 +              if (link_data->reserved_ready)
                        return 0;
  
                return ieee80211_link_use_reserved_context(&sdata->deflink);
        }
  
 -      if (!cfg80211_chandef_identical(&sdata->vif.bss_conf.chandef,
 -                                      &sdata->deflink.csa_chandef))
 +      if (!cfg80211_chandef_identical(&link_data->conf->chandef,
 +                                      &link_data->csa_chandef))
                return -EINVAL;
  
        sdata->vif.bss_conf.csa_active = false;
                changed |= BSS_CHANGED_EHT_PUNCTURING;
        }
  
 -      ieee80211_link_info_change_notify(sdata, &sdata->deflink, changed);
 +      ieee80211_link_info_change_notify(sdata, link_data, changed);
  
 -      if (sdata->deflink.csa_block_tx) {
 +      if (link_data->csa_block_tx) {
                ieee80211_wake_vif_queues(local, sdata,
                                          IEEE80211_QUEUE_STOP_REASON_CSA);
 -              sdata->deflink.csa_block_tx = false;
 +              link_data->csa_block_tx = false;
        }
  
 -      err = drv_post_channel_switch(sdata);
 +      err = drv_post_channel_switch(link_data);
        if (err)
                return err;
  
 -      cfg80211_ch_switch_notify(sdata->dev, &sdata->deflink.csa_chandef, 0,
 -                                sdata->vif.bss_conf.eht_puncturing);
 +      cfg80211_ch_switch_notify(sdata->dev, &link_data->csa_chandef,
 +                                link_data->link_id,
 +                                link_data->conf->eht_puncturing);
  
        return 0;
  }
  
 -static void ieee80211_csa_finalize(struct ieee80211_sub_if_data *sdata)
 +static void ieee80211_csa_finalize(struct ieee80211_link_data *link_data)
  {
 -      if (__ieee80211_csa_finalize(sdata)) {
 +      struct ieee80211_sub_if_data *sdata = link_data->sdata;
 +
 +      if (__ieee80211_csa_finalize(link_data)) {
                sdata_info(sdata, "failed to finalize CSA, disconnecting\n");
                cfg80211_stop_iface(sdata->local->hw.wiphy, &sdata->wdev,
                                    GFP_KERNEL);
        }
  }
  
 -void ieee80211_csa_finalize_work(struct work_struct *work)
 +void ieee80211_csa_finalize_work(struct wiphy *wiphy, struct wiphy_work *work)
  {
 -      struct ieee80211_sub_if_data *sdata =
 -              container_of(work, struct ieee80211_sub_if_data,
 -                           deflink.csa_finalize_work);
 +      struct ieee80211_link_data *link =
 +              container_of(work, struct ieee80211_link_data, csa_finalize_work);
 +      struct ieee80211_sub_if_data *sdata = link->sdata;
        struct ieee80211_local *local = sdata->local;
  
 -      sdata_lock(sdata);
 -      mutex_lock(&local->mtx);
 -      mutex_lock(&local->chanctx_mtx);
 +      lockdep_assert_wiphy(local->hw.wiphy);
  
        /* AP might have been stopped while waiting for the lock. */
 -      if (!sdata->vif.bss_conf.csa_active)
 -              goto unlock;
 +      if (!link->conf->csa_active)
 +              return;
  
        if (!ieee80211_sdata_running(sdata))
 -              goto unlock;
 -
 -      ieee80211_csa_finalize(sdata);
 +              return;
  
 -unlock:
 -      mutex_unlock(&local->chanctx_mtx);
 -      mutex_unlock(&local->mtx);
 -      sdata_unlock(sdata);
 +      ieee80211_csa_finalize(link);
  }
  
  static int ieee80211_set_csa_beacon(struct ieee80211_sub_if_data *sdata,
@@@ -3854,7 -3893,8 +3861,7 @@@ __ieee80211_channel_switch(struct wiph
        u64 changed = 0;
        int err;
  
 -      sdata_assert_lock(sdata);
 -      lockdep_assert_held(&local->mtx);
 +      lockdep_assert_wiphy(local->hw.wiphy);
  
        if (!list_empty(&local->roc_list) || local->scanning)
                return -EBUSY;
        if (sdata->vif.bss_conf.csa_active)
                return -EBUSY;
  
 -      mutex_lock(&local->chanctx_mtx);
        conf = rcu_dereference_protected(sdata->vif.bss_conf.chanctx_conf,
 -                                       lockdep_is_held(&local->chanctx_mtx));
 +                                       lockdep_is_held(&local->hw.wiphy->mtx));
        if (!conf) {
                err = -EBUSY;
                goto out;
                drv_channel_switch_beacon(sdata, &params->chandef);
        } else {
                /* if the beacon didn't change, we can finalize immediately */
 -              ieee80211_csa_finalize(sdata);
 +              ieee80211_csa_finalize(&sdata->deflink);
        }
  
  out:
 -      mutex_unlock(&local->chanctx_mtx);
        return err;
  }
  
@@@ -3953,15 -3995,18 +3960,15 @@@ int ieee80211_channel_switch(struct wip
  {
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
        struct ieee80211_local *local = sdata->local;
 -      int err;
  
 -      mutex_lock(&local->mtx);
 -      err = __ieee80211_channel_switch(wiphy, dev, params);
 -      mutex_unlock(&local->mtx);
 +      lockdep_assert_wiphy(local->hw.wiphy);
  
 -      return err;
 +      return __ieee80211_channel_switch(wiphy, dev, params);
  }
  
  u64 ieee80211_mgmt_tx_cookie(struct ieee80211_local *local)
  {
 -      lockdep_assert_held(&local->mtx);
 +      lockdep_assert_wiphy(local->hw.wiphy);
  
        local->roc_cookie_counter++;
  
@@@ -3993,8 -4038,7 +4000,8 @@@ int ieee80211_attach_ack_skb(struct iee
                return -ENOMEM;
        }
  
 -      IEEE80211_SKB_CB(skb)->ack_frame_id = id;
 +      IEEE80211_SKB_CB(skb)->status_data_idr = 1;
 +      IEEE80211_SKB_CB(skb)->status_data = id;
  
        *cookie = ieee80211_mgmt_tx_cookie(local);
        IEEE80211_SKB_CB(ack_skb)->ack.cookie = *cookie;
@@@ -4044,17 -4088,11 +4051,17 @@@ ieee80211_update_mgmt_frame_registratio
  static int ieee80211_set_antenna(struct wiphy *wiphy, u32 tx_ant, u32 rx_ant)
  {
        struct ieee80211_local *local = wiphy_priv(wiphy);
 +      int ret;
  
        if (local->started)
                return -EOPNOTSUPP;
  
 -      return drv_set_antenna(local, tx_ant, rx_ant);
 +      ret = drv_set_antenna(local, tx_ant, rx_ant);
 +      if (ret)
 +              return ret;
 +
 +      local->rx_chains = hweight8(rx_ant);
 +      return 0;
  }
  
  static int ieee80211_get_antenna(struct wiphy *wiphy, u32 *tx_ant, u32 *rx_ant)
@@@ -4096,7 -4134,7 +4103,7 @@@ static int ieee80211_probe_client(struc
        int ret;
  
        /* the lock is needed to assign the cookie later */
 -      mutex_lock(&local->mtx);
 +      lockdep_assert_wiphy(local->hw.wiphy);
  
        rcu_read_lock();
        sta = sta_info_get_bss(sdata, peer);
        ret = 0;
  unlock:
        rcu_read_unlock();
 -      mutex_unlock(&local->mtx);
  
        return ret;
  }
@@@ -4524,8 -4563,7 +4531,8 @@@ static int ieee80211_set_tid_config(str
  {
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
        struct sta_info *sta;
 -      int ret;
 +
 +      lockdep_assert_wiphy(sdata->local->hw.wiphy);
  
        if (!sdata->local->ops->set_tid_config)
                return -EOPNOTSUPP;
        if (!tid_conf->peer)
                return drv_set_tid_config(sdata->local, sdata, NULL, tid_conf);
  
 -      mutex_lock(&sdata->local->sta_mtx);
        sta = sta_info_get_bss(sdata, tid_conf->peer);
 -      if (!sta) {
 -              mutex_unlock(&sdata->local->sta_mtx);
 +      if (!sta)
                return -ENOENT;
 -      }
 -
 -      ret = drv_set_tid_config(sdata->local, sdata, &sta->sta, tid_conf);
 -      mutex_unlock(&sdata->local->sta_mtx);
  
 -      return ret;
 +      return drv_set_tid_config(sdata->local, sdata, &sta->sta, tid_conf);
  }
  
  static int ieee80211_reset_tid_config(struct wiphy *wiphy,
  {
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
        struct sta_info *sta;
 -      int ret;
 +
 +      lockdep_assert_wiphy(sdata->local->hw.wiphy);
  
        if (!sdata->local->ops->reset_tid_config)
                return -EOPNOTSUPP;
        if (!peer)
                return drv_reset_tid_config(sdata->local, sdata, NULL, tids);
  
 -      mutex_lock(&sdata->local->sta_mtx);
        sta = sta_info_get_bss(sdata, peer);
 -      if (!sta) {
 -              mutex_unlock(&sdata->local->sta_mtx);
 +      if (!sta)
                return -ENOENT;
 -      }
  
 -      ret = drv_reset_tid_config(sdata->local, sdata, &sta->sta, tids);
 -      mutex_unlock(&sdata->local->sta_mtx);
 -
 -      return ret;
 +      return drv_reset_tid_config(sdata->local, sdata, &sta->sta, tids);
  }
  
  static int ieee80211_set_sar_specs(struct wiphy *wiphy,
@@@ -4645,8 -4694,6 +4652,8 @@@ static voi
  ieee80211_color_change_bss_config_notify(struct ieee80211_sub_if_data *sdata,
                                         u8 color, int enable, u64 changed)
  {
 +      lockdep_assert_wiphy(sdata->local->hw.wiphy);
 +
        sdata->vif.bss_conf.he_bss_color.color = color;
        sdata->vif.bss_conf.he_bss_color.enabled = enable;
        changed |= BSS_CHANGED_HE_BSS_COLOR;
        if (!sdata->vif.bss_conf.nontransmitted && sdata->vif.mbssid_tx_vif) {
                struct ieee80211_sub_if_data *child;
  
 -              mutex_lock(&sdata->local->iflist_mtx);
                list_for_each_entry(child, &sdata->local->interfaces, list) {
                        if (child != sdata && child->vif.mbssid_tx_vif == &sdata->vif) {
                                child->vif.bss_conf.he_bss_color.color = color;
                                                                  BSS_CHANGED_HE_BSS_COLOR);
                        }
                }
 -              mutex_unlock(&sdata->local->iflist_mtx);
        }
  }
  
@@@ -4674,7 -4723,8 +4681,7 @@@ static int ieee80211_color_change_final
        u64 changed = 0;
        int err;
  
 -      sdata_assert_lock(sdata);
 -      lockdep_assert_held(&local->mtx);
 +      lockdep_assert_wiphy(local->hw.wiphy);
  
        sdata->vif.bss_conf.color_change_active = false;
  
        return 0;
  }
  
 -void ieee80211_color_change_finalize_work(struct work_struct *work)
 +void ieee80211_color_change_finalize_work(struct wiphy *wiphy,
 +                                        struct wiphy_work *work)
  {
        struct ieee80211_sub_if_data *sdata =
                container_of(work, struct ieee80211_sub_if_data,
                             deflink.color_change_finalize_work);
        struct ieee80211_local *local = sdata->local;
  
 -      sdata_lock(sdata);
 -      mutex_lock(&local->mtx);
 +      lockdep_assert_wiphy(local->hw.wiphy);
  
        /* AP might have been stopped while waiting for the lock. */
        if (!sdata->vif.bss_conf.color_change_active)
 -              goto unlock;
 +              return;
  
        if (!ieee80211_sdata_running(sdata))
 -              goto unlock;
 +              return;
  
        ieee80211_color_change_finalize(sdata);
 -
 -unlock:
 -      mutex_unlock(&local->mtx);
 -      sdata_unlock(sdata);
  }
  
  void ieee80211_color_collision_detection_work(struct work_struct *work)
                             color_collision_detect_work);
        struct ieee80211_sub_if_data *sdata = link->sdata;
  
 -      sdata_lock(sdata);
        cfg80211_obss_color_collision_notify(sdata->dev, link->color_bitmap);
 -      sdata_unlock(sdata);
  }
  
  void ieee80211_color_change_finish(struct ieee80211_vif *vif)
  {
        struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
  
 -      ieee80211_queue_work(&sdata->local->hw,
 -                           &sdata->deflink.color_change_finalize_work);
 +      wiphy_work_queue(sdata->local->hw.wiphy,
 +                       &sdata->deflink.color_change_finalize_work);
  }
  EXPORT_SYMBOL_GPL(ieee80211_color_change_finish);
  
@@@ -4764,11 -4820,13 +4771,11 @@@ ieee80211_color_change(struct wiphy *wi
        u64 changed = 0;
        int err;
  
 -      sdata_assert_lock(sdata);
 +      lockdep_assert_wiphy(local->hw.wiphy);
  
        if (sdata->vif.bss_conf.nontransmitted)
                return -EINVAL;
  
 -      mutex_lock(&local->mtx);
 -
        /* don't allow another color change if one is already active or if csa
         * is active
         */
                ieee80211_color_change_finalize(sdata);
  
  out:
 -      mutex_unlock(&local->mtx);
  
        return err;
  }
@@@ -4814,13 -4873,16 +4821,13 @@@ static int ieee80211_add_intf_link(stru
                                   unsigned int link_id)
  {
        struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
 -      int res;
 +
 +      lockdep_assert_wiphy(sdata->local->hw.wiphy);
  
        if (wdev->use_4addr)
                return -EOPNOTSUPP;
  
 -      mutex_lock(&sdata->local->mtx);
 -      res = ieee80211_vif_set_links(sdata, wdev->valid_links, 0);
 -      mutex_unlock(&sdata->local->mtx);
 -
 -      return res;
 +      return ieee80211_vif_set_links(sdata, wdev->valid_links, 0);
  }
  
  static void ieee80211_del_intf_link(struct wiphy *wiphy,
  {
        struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
  
 -      mutex_lock(&sdata->local->mtx);
 +      lockdep_assert_wiphy(sdata->local->hw.wiphy);
 +
        ieee80211_vif_set_links(sdata, wdev->valid_links, 0);
 -      mutex_unlock(&sdata->local->mtx);
  }
  
  static int sta_add_link_station(struct ieee80211_local *local,
@@@ -4871,10 -4933,13 +4878,10 @@@ ieee80211_add_link_station(struct wiph
  {
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
        struct ieee80211_local *local = wiphy_priv(wiphy);
 -      int ret;
  
 -      mutex_lock(&sdata->local->sta_mtx);
 -      ret = sta_add_link_station(local, sdata, params);
 -      mutex_unlock(&sdata->local->sta_mtx);
 +      lockdep_assert_wiphy(sdata->local->hw.wiphy);
  
 -      return ret;
 +      return sta_add_link_station(local, sdata, params);
  }
  
  static int sta_mod_link_station(struct ieee80211_local *local,
@@@ -4899,10 -4964,13 +4906,10 @@@ ieee80211_mod_link_station(struct wiph
  {
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
        struct ieee80211_local *local = wiphy_priv(wiphy);
 -      int ret;
  
 -      mutex_lock(&sdata->local->sta_mtx);
 -      ret = sta_mod_link_station(local, sdata, params);
 -      mutex_unlock(&sdata->local->sta_mtx);
 +      lockdep_assert_wiphy(sdata->local->hw.wiphy);
  
 -      return ret;
 +      return sta_mod_link_station(local, sdata, params);
  }
  
  static int sta_del_link_station(struct ieee80211_sub_if_data *sdata,
@@@ -4931,10 -4999,13 +4938,10 @@@ ieee80211_del_link_station(struct wiph
                           struct link_station_del_parameters *params)
  {
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 -      int ret;
  
 -      mutex_lock(&sdata->local->sta_mtx);
 -      ret = sta_del_link_station(sdata, params);
 -      mutex_unlock(&sdata->local->sta_mtx);
 +      lockdep_assert_wiphy(sdata->local->hw.wiphy);
  
 -      return ret;
 +      return sta_del_link_station(sdata, params);
  }
  
  static int ieee80211_set_hw_timestamp(struct wiphy *wiphy,
diff --combined net/mac80211/ibss.c
index a7736acadf3c55b267c061e13e2b8f04e2332cdd,5542c93edfba0b0bdb9d167fbab633c103697cde..8b1e02f2f9ae1cc73e3eb2d58e2ead71ad5fd061
@@@ -51,6 -51,7 +51,6 @@@ ieee80211_ibss_build_presp(struct ieee8
        u32 rate_flags, rates = 0, rates_added = 0;
        struct beacon_data *presp;
        int frame_len;
 -      int shift;
  
        /* Build IBSS probe response */
        frame_len = sizeof(struct ieee80211_hdr_3addr) +
@@@ -91,6 -92,7 +91,6 @@@
  
        sband = local->hw.wiphy->bands[chandef->chan->band];
        rate_flags = ieee80211_chandef_rate_flags(chandef);
 -      shift = ieee80211_chandef_get_shift(chandef);
        rates_n = 0;
        if (have_higher_than_11mbit)
                *have_higher_than_11mbit = false;
        *pos++ = WLAN_EID_SUPP_RATES;
        *pos++ = min_t(int, 8, rates_n);
        for (ri = 0; ri < sband->n_bitrates; ri++) {
 -              int rate = DIV_ROUND_UP(sband->bitrates[ri].bitrate,
 -                                      5 * (1 << shift));
 +              int rate = DIV_ROUND_UP(sband->bitrates[ri].bitrate, 5);
                u8 basic = 0;
                if (!(rates & BIT(ri)))
                        continue;
                *pos++ = WLAN_EID_EXT_SUPP_RATES;
                *pos++ = rates_n - 8;
                for (; ri < sband->n_bitrates; ri++) {
 -                      int rate = DIV_ROUND_UP(sband->bitrates[ri].bitrate,
 -                                              5 * (1 << shift));
 +                      int rate = DIV_ROUND_UP(sband->bitrates[ri].bitrate, 5);
                        u8 basic = 0;
                        if (!(rates & BIT(ri)))
                                continue;
@@@ -231,7 -235,7 +231,7 @@@ static void __ieee80211_sta_join_ibss(s
        bool radar_required;
        int err;
  
 -      sdata_assert_lock(sdata);
 +      lockdep_assert_wiphy(local->hw.wiphy);
  
        /* Reset own TSF to allow time synchronization work. */
        drv_reset_tsf(local, sdata);
  
        radar_required = err;
  
 -      mutex_lock(&local->mtx);
        if (ieee80211_link_use_channel(&sdata->deflink, &chandef,
                                       ifibss->fixed_channel ?
                                        IEEE80211_CHANCTX_SHARED :
                                        IEEE80211_CHANCTX_EXCLUSIVE)) {
                sdata_info(sdata, "Failed to join IBSS, no channel context\n");
 -              mutex_unlock(&local->mtx);
                return;
        }
        sdata->deflink.radar_required = radar_required;
 -      mutex_unlock(&local->mtx);
  
        memcpy(ifibss->bssid, bssid, ETH_ALEN);
  
                sdata->vif.cfg.ssid_len = 0;
                RCU_INIT_POINTER(ifibss->presp, NULL);
                kfree_rcu(presp, rcu_head);
 -              mutex_lock(&local->mtx);
                ieee80211_link_release_channel(&sdata->deflink);
 -              mutex_unlock(&local->mtx);
                sdata_info(sdata, "Failed to join IBSS, driver failure: %d\n",
                           err);
                return;
                  round_jiffies(jiffies + IEEE80211_IBSS_MERGE_INTERVAL));
  
        bss_meta.chan = chan;
 -      bss_meta.scan_width = cfg80211_chandef_to_scan_width(&chandef);
        bss = cfg80211_inform_bss_frame_data(local->hw.wiphy, &bss_meta, mgmt,
                                             presp->head_len, GFP_KERNEL);
  
@@@ -395,8 -405,9 +395,8 @@@ static void ieee80211_sta_join_ibss(str
        enum nl80211_channel_type chan_type;
        u64 tsf;
        u32 rate_flags;
 -      int shift;
  
 -      sdata_assert_lock(sdata);
 +      lockdep_assert_wiphy(sdata->local->hw.wiphy);
  
        if (beacon_int < 10)
                beacon_int = 10;
  
        sband = sdata->local->hw.wiphy->bands[cbss->channel->band];
        rate_flags = ieee80211_chandef_rate_flags(&sdata->u.ibss.chandef);
 -      shift = ieee80211_vif_get_shift(&sdata->vif);
  
        basic_rates = 0;
  
                            != rate_flags)
                                continue;
  
 -                      brate = DIV_ROUND_UP(sband->bitrates[j].bitrate,
 -                                           5 * (1 << shift));
 +                      brate = DIV_ROUND_UP(sband->bitrates[j].bitrate, 5);
                        if (brate == rate) {
                                if (is_basic)
                                        basic_rates |= BIT(j);
@@@ -475,7 -488,7 +475,7 @@@ int ieee80211_ibss_csa_beacon(struct ie
        u16 capability = WLAN_CAPABILITY_IBSS;
        u64 tsf;
  
 -      sdata_assert_lock(sdata);
 +      lockdep_assert_wiphy(sdata->local->hw.wiphy);
  
        if (ifibss->privacy)
                capability |= WLAN_CAPABILITY_PRIVACY;
@@@ -517,7 -530,7 +517,7 @@@ int ieee80211_ibss_finish_csa(struct ie
        struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
        struct cfg80211_bss *cbss;
  
 -      sdata_assert_lock(sdata);
 +      lockdep_assert_wiphy(sdata->local->hw.wiphy);
  
        /* When not connected/joined, sending CSA doesn't make sense. */
        if (ifibss->state != IEEE80211_IBSS_MLME_JOINED)
@@@ -587,6 -600,7 +587,6 @@@ ieee80211_ibss_add_sta(struct ieee80211
        struct sta_info *sta;
        struct ieee80211_chanctx_conf *chanctx_conf;
        struct ieee80211_supported_band *sband;
 -      enum nl80211_bss_scan_width scan_width;
        int band;
  
        /*
        if (WARN_ON_ONCE(!chanctx_conf))
                return NULL;
        band = chanctx_conf->def.chan->band;
 -      scan_width = cfg80211_chandef_to_scan_width(&chanctx_conf->def);
        rcu_read_unlock();
  
        sta = sta_info_alloc(sdata, addr, GFP_KERNEL);
        /* make sure mandatory rates are always added */
        sband = local->hw.wiphy->bands[band];
        sta->sta.deflink.supp_rates[band] = supp_rates |
 -                      ieee80211_mandatory_rates(sband, scan_width);
 +                      ieee80211_mandatory_rates(sband);
  
        return ieee80211_ibss_finish_sta(sta);
  }
@@@ -637,7 -652,7 +637,7 @@@ static int ieee80211_sta_active_ibss(st
        int active = 0;
        struct sta_info *sta;
  
 -      sdata_assert_lock(sdata);
 +      lockdep_assert_wiphy(sdata->local->hw.wiphy);
  
        rcu_read_lock();
  
@@@ -665,8 -680,6 +665,8 @@@ static void ieee80211_ibss_disconnect(s
        struct beacon_data *presp;
        struct sta_info *sta;
  
 +      lockdep_assert_wiphy(local->hw.wiphy);
 +
        if (!is_zero_ether_addr(ifibss->bssid)) {
                cbss = cfg80211_get_bss(local->hw.wiphy, ifibss->chandef.chan,
                                        ifibss->bssid, ifibss->ssid,
        ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED |
                                                BSS_CHANGED_IBSS);
        drv_leave_ibss(local, sdata);
 -      mutex_lock(&local->mtx);
        ieee80211_link_release_channel(&sdata->deflink);
 -      mutex_unlock(&local->mtx);
  }
  
  static void ieee80211_csa_connection_drop_work(struct wiphy *wiphy,
                container_of(work, struct ieee80211_sub_if_data,
                             u.ibss.csa_connection_drop_work);
  
 -      sdata_lock(sdata);
 -
        ieee80211_ibss_disconnect(sdata);
        synchronize_rcu();
        skb_queue_purge(&sdata->skb_queue);
  
        /* trigger a scan to find another IBSS network to join */
        wiphy_work_queue(sdata->local->hw.wiphy, &sdata->work);
 -
 -      sdata_unlock(sdata);
  }
  
  static void ieee80211_ibss_csa_mark_radar(struct ieee80211_sub_if_data *sdata)
@@@ -760,7 -779,7 +760,7 @@@ ieee80211_ibss_process_chanswitch(struc
        ieee80211_conn_flags_t conn_flags;
        u32 vht_cap_info = 0;
  
 -      sdata_assert_lock(sdata);
 +      lockdep_assert_wiphy(sdata->local->hw.wiphy);
  
        conn_flags = IEEE80211_CONN_DISABLE_VHT;
  
@@@ -932,7 -951,7 +932,7 @@@ static void ieee80211_rx_mgmt_auth_ibss
  {
        u16 auth_alg, auth_transaction;
  
 -      sdata_assert_lock(sdata);
 +      lockdep_assert_wiphy(sdata->local->hw.wiphy);
  
        if (len < 24 + 6)
                return;
@@@ -965,6 -984,7 +965,6 @@@ static void ieee80211_update_sta_info(s
  {
        struct sta_info *sta;
        enum nl80211_band band = rx_status->band;
 -      enum nl80211_bss_scan_width scan_width;
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_supported_band *sband;
        bool rates_updated = false;
                        u32 prev_rates;
  
                        prev_rates = sta->sta.deflink.supp_rates[band];
 -                      /* make sure mandatory rates are always added */
 -                      scan_width = NL80211_BSS_CHAN_WIDTH_20;
 -                      if (rx_status->bw == RATE_INFO_BW_5)
 -                              scan_width = NL80211_BSS_CHAN_WIDTH_5;
 -                      else if (rx_status->bw == RATE_INFO_BW_10)
 -                              scan_width = NL80211_BSS_CHAN_WIDTH_10;
  
                        sta->sta.deflink.supp_rates[band] = supp_rates |
 -                              ieee80211_mandatory_rates(sband, scan_width);
 +                              ieee80211_mandatory_rates(sband);
                        if (sta->sta.deflink.supp_rates[band] != prev_rates) {
                                ibss_dbg(sdata,
                                         "updated supp_rates set for %pM based on beacon/probe_resp (0x%x -> 0x%x)\n",
                                                   &chandef);
                        memcpy(&cap_ie, elems->vht_cap_elem, sizeof(cap_ie));
                        ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband,
-                                                           &cap_ie,
+                                                           &cap_ie, NULL,
                                                            &sta->deflink);
                        if (memcmp(&cap, &sta->sta.deflink.vht_cap, sizeof(cap)))
                                rates_updated |= true;
@@@ -1179,6 -1205,7 +1179,6 @@@ void ieee80211_ibss_rx_no_sta(struct ie
        struct sta_info *sta;
        struct ieee80211_chanctx_conf *chanctx_conf;
        struct ieee80211_supported_band *sband;
 -      enum nl80211_bss_scan_width scan_width;
        int band;
  
        /*
                return;
        }
        band = chanctx_conf->def.chan->band;
 -      scan_width = cfg80211_chandef_to_scan_width(&chanctx_conf->def);
        rcu_read_unlock();
  
        sta = sta_info_alloc(sdata, addr, GFP_ATOMIC);
        /* make sure mandatory rates are always added */
        sband = local->hw.wiphy->bands[band];
        sta->sta.deflink.supp_rates[band] = supp_rates |
 -                      ieee80211_mandatory_rates(sband, scan_width);
 +                      ieee80211_mandatory_rates(sband);
  
        spin_lock(&ifibss->incomplete_lock);
        list_add(&sta->list, &ifibss->incomplete_stations);
@@@ -1229,7 -1257,7 +1229,7 @@@ static void ieee80211_ibss_sta_expire(s
        unsigned long exp_time = IEEE80211_IBSS_INACTIVITY_LIMIT;
        unsigned long exp_rsn = IEEE80211_IBSS_RSN_INACTIVITY_LIMIT;
  
 -      mutex_lock(&local->sta_mtx);
 +      lockdep_assert_wiphy(local->hw.wiphy);
  
        list_for_each_entry_safe(sta, tmp, &local->sta_list, list) {
                unsigned long last_active = ieee80211_sta_last_active(sta);
                        WARN_ON(__sta_info_destroy(sta));
                }
        }
 -
 -      mutex_unlock(&local->sta_mtx);
  }
  
  /*
  static void ieee80211_sta_merge_ibss(struct ieee80211_sub_if_data *sdata)
  {
        struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
 -      enum nl80211_bss_scan_width scan_width;
  
 -      sdata_assert_lock(sdata);
 +      lockdep_assert_wiphy(sdata->local->hw.wiphy);
  
        mod_timer(&ifibss->timer,
                  round_jiffies(jiffies + IEEE80211_IBSS_MERGE_INTERVAL));
        sdata_info(sdata,
                   "No active IBSS STAs - trying to scan for other IBSS networks with same SSID (merge)\n");
  
 -      scan_width = cfg80211_chandef_to_scan_width(&ifibss->chandef);
        ieee80211_request_ibss_scan(sdata, ifibss->ssid, ifibss->ssid_len,
 -                                  NULL, 0, scan_width);
 +                                  NULL, 0);
  }
  
  static void ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata)
        u16 capability;
        int i;
  
 -      sdata_assert_lock(sdata);
 +      lockdep_assert_wiphy(sdata->local->hw.wiphy);
  
        if (ifibss->fixed_bssid) {
                memcpy(bssid, ifibss->bssid, ETH_ALEN);
@@@ -1403,9 -1435,10 +1403,9 @@@ static void ieee80211_sta_find_ibss(str
        struct cfg80211_bss *cbss;
        struct ieee80211_channel *chan = NULL;
        const u8 *bssid = NULL;
 -      enum nl80211_bss_scan_width scan_width;
        int active_ibss;
  
 -      sdata_assert_lock(sdata);
 +      lockdep_assert_wiphy(sdata->local->hw.wiphy);
  
        active_ibss = ieee80211_sta_active_ibss(sdata);
        ibss_dbg(sdata, "sta_find_ibss (active_ibss=%d)\n", active_ibss);
  
                sdata_info(sdata, "Trigger new scan to find an IBSS to join\n");
  
 -              scan_width = cfg80211_chandef_to_scan_width(&ifibss->chandef);
 -
                if (ifibss->fixed_channel) {
                        num = ieee80211_ibss_setup_scan_channels(local->hw.wiphy,
                                                                 &ifibss->chandef,
                                                                 ARRAY_SIZE(channels));
                        ieee80211_request_ibss_scan(sdata, ifibss->ssid,
                                                    ifibss->ssid_len, channels,
 -                                                  num, scan_width);
 +                                                  num);
                } else {
                        ieee80211_request_ibss_scan(sdata, ifibss->ssid,
 -                                                  ifibss->ssid_len, NULL,
 -                                                  0, scan_width);
 +                                                  ifibss->ssid_len, NULL, 0);
                }
        } else {
                int interval = IEEE80211_SCAN_INTERVAL;
@@@ -1496,7 -1532,7 +1496,7 @@@ static void ieee80211_rx_mgmt_probe_req
        struct beacon_data *presp;
        u8 *pos, *end;
  
 -      sdata_assert_lock(sdata);
 +      lockdep_assert_wiphy(sdata->local->hw.wiphy);
  
        presp = sdata_dereference(ifibss->presp, sdata);
  
@@@ -1592,8 -1628,10 +1592,8 @@@ void ieee80211_ibss_rx_queued_mgmt(stru
        mgmt = (struct ieee80211_mgmt *) skb->data;
        fc = le16_to_cpu(mgmt->frame_control);
  
 -      sdata_lock(sdata);
 -
        if (!sdata->u.ibss.ssid_len)
 -              goto mgmt_out; /* not ready to merge yet */
 +              return; /* not ready to merge yet */
  
        switch (fc & IEEE80211_FCTL_STYPE) {
        case IEEE80211_STYPE_PROBE_REQ:
                        break;
                }
        }
 -
 - mgmt_out:
 -      sdata_unlock(sdata);
  }
  
  void ieee80211_ibss_work(struct ieee80211_sub_if_data *sdata)
        struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
        struct sta_info *sta;
  
 -      sdata_lock(sdata);
 -
        /*
         * Work could be scheduled after scan or similar
         * when we aren't even joined (or trying) with a
         * network.
         */
        if (!ifibss->ssid_len)
 -              goto out;
 +              return;
  
        spin_lock_bh(&ifibss->incomplete_lock);
        while (!list_empty(&ifibss->incomplete_stations)) {
                WARN_ON(1);
                break;
        }
 -
 - out:
 -      sdata_unlock(sdata);
  }
  
  static void ieee80211_ibss_timer(struct timer_list *t)
@@@ -1698,8 -1744,7 +1698,8 @@@ void ieee80211_ibss_notify_scan_complet
  {
        struct ieee80211_sub_if_data *sdata;
  
 -      mutex_lock(&local->iflist_mtx);
 +      lockdep_assert_wiphy(local->hw.wiphy);
 +
        list_for_each_entry(sdata, &local->interfaces, list) {
                if (!ieee80211_sdata_running(sdata))
                        continue;
                        continue;
                sdata->u.ibss.last_scan_completed = jiffies;
        }
 -      mutex_unlock(&local->iflist_mtx);
  }
  
  int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
        int i;
        int ret;
  
 +      lockdep_assert_wiphy(local->hw.wiphy);
 +
        if (params->chandef.chan->freq_offset) {
                /* this may work, but is untested */
                return -EOPNOTSUPP;
        chanmode = (params->channel_fixed && !ret) ?
                IEEE80211_CHANCTX_SHARED : IEEE80211_CHANCTX_EXCLUSIVE;
  
 -      mutex_lock(&local->chanctx_mtx);
        ret = ieee80211_check_combinations(sdata, &params->chandef, chanmode,
                                           radar_detect_width);
 -      mutex_unlock(&local->chanctx_mtx);
        if (ret < 0)
                return ret;
  
index 19429f84afc359d77b3183fb04e08569c7151694,98ef1fe1226e72f8e19f785f8489537635c39799..e92eaf835ee056782a4e8998eb1d976a093ae06a
@@@ -85,12 -85,6 +85,12 @@@ extern const u8 ieee80211_ac_to_qos_mas
  
  #define IEEE80211_MAX_NAN_INSTANCE_ID 255
  
 +enum ieee80211_status_data {
 +      IEEE80211_STATUS_TYPE_MASK      = 0x00f,
 +      IEEE80211_STATUS_TYPE_INVALID   = 0,
 +      IEEE80211_STATUS_TYPE_SMPS      = 1,
 +      IEEE80211_STATUS_SUBDATA_MASK   = 0xff0,
 +};
  
  /*
   * Keep a station's queues on the active list for deficit accounting purposes
@@@ -467,24 -461,13 +467,24 @@@ struct ieee80211_sta_tx_tspec 
        bool downgraded;
  };
  
 +/* Advertised TID-to-link mapping info */
 +struct ieee80211_adv_ttlm_info {
 +      /* time in TUs at which the new mapping is established, or 0 if there is
 +       * no planned advertised TID-to-link mapping
 +       */
 +      u16 switch_time;
 +      u32 duration; /* duration of the planned T2L map in TUs */
 +      u16 map; /* map of usable links for all TIDs */
 +      bool active; /* whether the advertised mapping is active or not */
 +};
 +
  DECLARE_EWMA(beacon_signal, 4, 4)
  
  struct ieee80211_if_managed {
        struct timer_list timer;
        struct timer_list conn_mon_timer;
        struct timer_list bcn_mon_timer;
 -      struct work_struct monitor_work;
 +      struct wiphy_work monitor_work;
        struct wiphy_work beacon_connection_loss_work;
        struct wiphy_work csa_connection_drop_work;
  
  
        /* TDLS support */
        u8 tdls_peer[ETH_ALEN] __aligned(2);
 -      struct delayed_work tdls_peer_del_work;
 +      struct wiphy_delayed_work tdls_peer_del_work;
        struct sk_buff *orig_teardown_skb; /* The original teardown skb */
        struct sk_buff *teardown_skb; /* A copy to send through the AP */
        spinlock_t teardown_lock; /* To lock changing teardown_skb */
         * on the BE queue, but there's a lot of VO traffic, we might
         * get stuck in a downgraded situation and flush takes forever.
         */
 -      struct delayed_work tx_tspec_wk;
 +      struct wiphy_delayed_work tx_tspec_wk;
  
        /* Information elements from the last transmitted (Re)Association
         * Request frame.
  
        struct wiphy_delayed_work ml_reconf_work;
        u16 removed_links;
 +
 +      /* TID-to-link mapping support */
 +      struct wiphy_delayed_work ttlm_work;
 +      struct ieee80211_adv_ttlm_info ttlm_info;
  };
  
  struct ieee80211_if_ibss {
@@@ -639,9 -618,8 +639,9 @@@ struct ieee80211_if_ocb 
   * these declarations define the interface, which enables
   * vendor-specific mesh synchronization
   *
 + * @rx_bcn_presp: beacon/probe response was received
 + * @adjust_tsf: TSF adjustment method
   */
 -struct ieee802_11_elems;
  struct ieee80211_mesh_sync_ops {
        void (*rx_bcn_presp)(struct ieee80211_sub_if_data *sdata, u16 stype,
                             struct ieee80211_mgmt *mgmt, unsigned int len,
@@@ -698,7 -676,7 +698,7 @@@ struct ieee80211_if_mesh 
        struct timer_list mesh_path_root_timer;
  
        unsigned long wrkq_flags;
-       unsigned long mbss_changed;
+       unsigned long mbss_changed[64 / BITS_PER_LONG];
  
        bool userspace_handles_dfs;
  
@@@ -881,13 -859,12 +881,13 @@@ enum txq_info_flags 
   * struct txq_info - per tid queue
   *
   * @tin: contains packets split into multiple flows
 - * @def_flow: used as a fallback flow when a packet destined to @tin hashes to
 - *    a fq_flow which is already owned by a different tin
 - * @def_cvars: codel vars for @def_flow
 + * @def_cvars: codel vars for the @tin's default_flow
 + * @cstats: code statistics for this queue
   * @frags: used to keep fragments created after dequeue
   * @schedule_order: used with ieee80211_local->active_txqs
   * @schedule_round: counter to prevent infinite loops on TXQ scheduling
 + * @flags: TXQ flags from &enum txq_info_flags
 + * @txq: the driver visible part
   */
  struct txq_info {
        struct fq_tin tin;
@@@ -916,8 -893,7 +916,8 @@@ struct ieee80211_if_mntr 
   * struct ieee80211_if_nan - NAN state
   *
   * @conf: current NAN configuration
 - * @func_ids: a bitmap of available instance_id's
 + * @func_lock: lock for @func_inst_ids
 + * @function_inst_ids: a bitmap of available instance_id's
   */
  struct ieee80211_if_nan {
        struct cfg80211_nan_conf conf;
@@@ -950,9 -926,6 +950,9 @@@ struct ieee80211_link_data_managed 
        struct wiphy_delayed_work chswitch_work;
  
        struct wiphy_work request_smps_work;
 +      /* used to reconfigure hardware SM PS */
 +      struct wiphy_work recalc_smps;
 +
        bool beacon_crc_valid;
        u32 beacon_crc;
        struct ewma_beacon_signal ave_beacon_signal;
@@@ -997,8 -970,8 +997,8 @@@ struct ieee80211_link_data 
        struct ieee80211_sub_if_data *sdata;
        unsigned int link_id;
  
 -      struct list_head assigned_chanctx_list; /* protected by chanctx_mtx */
 -      struct list_head reserved_chanctx_list; /* protected by chanctx_mtx */
 +      struct list_head assigned_chanctx_list; /* protected by wiphy mutex */
 +      struct list_head reserved_chanctx_list; /* protected by wiphy mutex */
  
        /* multicast keys only */
        struct ieee80211_key __rcu *gtk[NUM_DEFAULT_KEYS +
        struct ieee80211_key __rcu *default_mgmt_key;
        struct ieee80211_key __rcu *default_beacon_key;
  
 -      struct work_struct csa_finalize_work;
 -      bool csa_block_tx; /* write-protected by sdata_lock and local->mtx */
 +      struct wiphy_work csa_finalize_work;
 +      bool csa_block_tx;
  
        bool operating_11g_mode;
  
        struct cfg80211_chan_def csa_chandef;
  
 -      struct work_struct color_change_finalize_work;
 +      struct wiphy_work color_change_finalize_work;
        struct delayed_work color_collision_detect_work;
        u64 color_bitmap;
  
 -      /* context reservation -- protected with chanctx_mtx */
 +      /* context reservation -- protected with wiphy mutex */
        struct ieee80211_chanctx *reserved_chanctx;
        struct cfg80211_chan_def reserved_chandef;
        bool reserved_radar_required;
        int ap_power_level; /* in dBm */
  
        bool radar_required;
 -      struct delayed_work dfs_cac_timer_work;
 +      struct wiphy_delayed_work dfs_cac_timer_work;
  
        union {
                struct ieee80211_link_data_managed mgd;
@@@ -1059,7 -1032,7 +1059,7 @@@ struct ieee80211_sub_if_data 
        /* count for keys needing tailroom space allocation */
        int crypto_tx_tailroom_needed_cnt;
        int crypto_tx_tailroom_pending_dec;
 -      struct delayed_work dec_tailroom_needed_wk;
 +      struct wiphy_delayed_work dec_tailroom_needed_wk;
  
        struct net_device *dev;
        struct ieee80211_local *local;
        atomic_t num_tx_queued;
        struct mac80211_qos_map __rcu *qos_map;
  
 -      /* used to reconfigure hardware SM PS */
 -      struct work_struct recalc_smps;
 -
        struct wiphy_work work;
        struct sk_buff_head skb_queue;
        struct sk_buff_head status_queue;
        struct ieee80211_link_data __rcu *link[IEEE80211_MLD_MAX_NUM_LINKS];
  
        /* for ieee80211_set_active_links_async() */
 -      struct work_struct activate_links_work;
 +      struct wiphy_work activate_links_work;
        u16 desired_active_links;
  
  #ifdef CONFIG_MAC80211_DEBUGFS
@@@ -1153,8 -1129,62 +1153,8 @@@ struct ieee80211_sub_if_data *vif_to_sd
        return container_of(p, struct ieee80211_sub_if_data, vif);
  }
  
 -static inline void sdata_lock(struct ieee80211_sub_if_data *sdata)
 -      __acquires(&sdata->wdev.mtx)
 -{
 -      mutex_lock(&sdata->wdev.mtx);
 -      __acquire(&sdata->wdev.mtx);
 -}
 -
 -static inline void sdata_unlock(struct ieee80211_sub_if_data *sdata)
 -      __releases(&sdata->wdev.mtx)
 -{
 -      mutex_unlock(&sdata->wdev.mtx);
 -      __release(&sdata->wdev.mtx);
 -}
 -
  #define sdata_dereference(p, sdata) \
 -      rcu_dereference_protected(p, lockdep_is_held(&sdata->wdev.mtx))
 -
 -static inline void
 -sdata_assert_lock(struct ieee80211_sub_if_data *sdata)
 -{
 -      lockdep_assert_held(&sdata->wdev.mtx);
 -}
 -
 -static inline int
 -ieee80211_chanwidth_get_shift(enum nl80211_chan_width width)
 -{
 -      switch (width) {
 -      case NL80211_CHAN_WIDTH_5:
 -              return 2;
 -      case NL80211_CHAN_WIDTH_10:
 -              return 1;
 -      default:
 -              return 0;
 -      }
 -}
 -
 -static inline int
 -ieee80211_chandef_get_shift(struct cfg80211_chan_def *chandef)
 -{
 -      return ieee80211_chanwidth_get_shift(chandef->width);
 -}
 -
 -static inline int
 -ieee80211_vif_get_shift(struct ieee80211_vif *vif)
 -{
 -      struct ieee80211_chanctx_conf *chanctx_conf;
 -      int shift = 0;
 -
 -      rcu_read_lock();
 -      chanctx_conf = rcu_dereference(vif->bss_conf.chanctx_conf);
 -      if (chanctx_conf)
 -              shift = ieee80211_chandef_get_shift(&chanctx_conf->def);
 -      rcu_read_unlock();
 -
 -      return shift;
 -}
 +      wiphy_dereference(sdata->local->hw.wiphy, p)
  
  static inline int
  ieee80211_get_mbssid_beacon_len(struct cfg80211_mbssid_elems *elems,
@@@ -1224,7 -1254,7 +1224,7 @@@ struct tpt_led_trigger 
  #endif
  
  /**
 - * mac80211 scan flags - currently active scan mode
 + * enum mac80211_scan_flags - currently active scan mode
   *
   * @SCAN_SW_SCANNING: We're currently in the process of scanning but may as
   *    well be on the operating channel
   *    and could send a probe request after receiving a beacon.
   * @SCAN_BEACON_DONE: Beacon received, we can now send a probe request
   */
 -enum {
 +enum mac80211_scan_flags {
        SCAN_SW_SCANNING,
        SCAN_HW_SCANNING,
        SCAN_ONCHANNEL_SCANNING,
@@@ -1332,7 -1362,7 +1332,7 @@@ struct ieee80211_local 
        spinlock_t filter_lock;
  
        /* used for uploading changed mc list */
 -      struct work_struct reconfig_filter;
 +      struct wiphy_work reconfig_filter;
  
        /* aggregated multicast list */
        struct netdev_hw_addr_list mc_list;
        /* wowlan is enabled -- don't reconfig on resume */
        bool wowlan;
  
 -      struct work_struct radar_detected_work;
 +      struct wiphy_work radar_detected_work;
  
        /* number of RX chains the hardware has */
        u8 rx_chains;
  
        /* Station data */
        /*
 -       * The mutex only protects the list, hash table and
 -       * counter, reads are done with RCU.
 +       * The list, hash table and counter are protected
 +       * by the wiphy mutex, reads are done with RCU.
         */
 -      struct mutex sta_mtx;
        spinlock_t tim_lock;
        unsigned long num_sta;
        struct list_head sta_list;
        struct list_head mon_list; /* only that are IFF_UP && !cooked */
        struct mutex iflist_mtx;
  
 -      /*
 -       * Key mutex, protects sdata's key_list and sta_info's
 -       * key pointers and ptk_idx (write access, they're RCU.)
 -       */
 -      struct mutex key_mtx;
 -
 -      /* mutex for scan and work locking */
 -      struct mutex mtx;
 -
        /* Scanning and BSS list */
        unsigned long scanning;
        struct cfg80211_ssid scan_ssid;
        int hw_scan_ies_bufsize;
        struct cfg80211_scan_info scan_info;
  
 -      struct work_struct sched_scan_stopped_work;
 +      struct wiphy_work sched_scan_stopped_work;
        struct ieee80211_sub_if_data __rcu *sched_scan_sdata;
        struct cfg80211_sched_scan_request __rcu *sched_scan_req;
        u8 scan_addr[ETH_ALEN];
  
        unsigned long leave_oper_channel_time;
        enum mac80211_scan_state next_scan_state;
 -      struct delayed_work scan_work;
 +      struct wiphy_delayed_work scan_work;
        struct ieee80211_sub_if_data __rcu *scan_sdata;
        /* For backward compatibility only -- do not use */
        struct cfg80211_chan_def _oper_chandef;
  
        /* channel contexts */
        struct list_head chanctx_list;
 -      struct mutex chanctx_mtx;
  
  #ifdef CONFIG_MAC80211_LEDS
        struct led_trigger tx_led, rx_led, assoc_led, radio_led;
         * interface (and monitors) in PS, this then points there.
         */
        struct ieee80211_sub_if_data *ps_sdata;
 -      struct work_struct dynamic_ps_enable_work;
 -      struct work_struct dynamic_ps_disable_work;
 +      struct wiphy_work dynamic_ps_enable_work;
 +      struct wiphy_work dynamic_ps_disable_work;
        struct timer_list dynamic_ps_timer;
        struct notifier_block ifa_notifier;
        struct notifier_block ifa6_notifier;
        /*
         * Remain-on-channel support
         */
 -      struct delayed_work roc_work;
 +      struct wiphy_delayed_work roc_work;
        struct list_head roc_list;
 -      struct work_struct hw_roc_start, hw_roc_done;
 +      struct wiphy_work hw_roc_start, hw_roc_done;
        unsigned long hw_roc_start_time;
        u64 roc_cookie_counter;
  
@@@ -1692,8 -1733,6 +1692,8 @@@ struct ieee802_11_elems 
        const struct ieee80211_eht_operation *eht_operation;
        const struct ieee80211_multi_link_elem *ml_basic;
        const struct ieee80211_multi_link_elem *ml_reconf;
 +      const struct ieee80211_bandwidth_indication *bandwidth_indication;
 +      const struct ieee80211_ttlm_elem *ttlm[IEEE80211_TTLM_MAX_CNT];
  
        /* length of them, respectively */
        u8 ext_capab_len;
        /* The reconfiguration Multi-Link element in the original IEs */
        const struct element *ml_reconf_elem;
  
 +      u8 ttlm_num;
 +
        /*
         * store the per station profile pointer and length in case that the
         * parsing also handled Multi-Link element parsing for a specific link
@@@ -1892,11 -1929,12 +1892,11 @@@ int ieee80211_mesh_finish_csa(struct ie
                              u64 *changed);
  
  /* scan/BSS handling */
 -void ieee80211_scan_work(struct work_struct *work);
 +void ieee80211_scan_work(struct wiphy *wiphy, struct wiphy_work *work);
  int ieee80211_request_ibss_scan(struct ieee80211_sub_if_data *sdata,
                                const u8 *ssid, u8 ssid_len,
                                struct ieee80211_channel **channels,
 -                              unsigned int n_channels,
 -                              enum nl80211_bss_scan_width scan_width);
 +                              unsigned int n_channels);
  int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata,
                           struct cfg80211_scan_request *req);
  void ieee80211_scan_cancel(struct ieee80211_local *local);
@@@ -1924,8 -1962,7 +1924,8 @@@ int ieee80211_request_sched_scan_start(
                                       struct cfg80211_sched_scan_request *req);
  int ieee80211_request_sched_scan_stop(struct ieee80211_local *local);
  void ieee80211_sched_scan_end(struct ieee80211_local *local);
 -void ieee80211_sched_scan_stopped_work(struct work_struct *work);
 +void ieee80211_sched_scan_stopped_work(struct wiphy *wiphy,
 +                                     struct wiphy_work *work);
  
  /* off-channel/mgmt-tx */
  void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local);
@@@ -1945,13 -1982,12 +1945,13 @@@ int ieee80211_mgmt_tx_cancel_wait(struc
                                  struct wireless_dev *wdev, u64 cookie);
  
  /* channel switch handling */
 -void ieee80211_csa_finalize_work(struct work_struct *work);
 +void ieee80211_csa_finalize_work(struct wiphy *wiphy, struct wiphy_work *work);
  int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
                             struct cfg80211_csa_settings *params);
  
  /* color change handling */
 -void ieee80211_color_change_finalize_work(struct work_struct *work);
 +void ieee80211_color_change_finalize_work(struct wiphy *wiphy,
 +                                        struct wiphy_work *work);
  void ieee80211_color_collision_detection_work(struct work_struct *work);
  
  /* interface handling */
@@@ -2001,10 -2037,8 +2001,10 @@@ void ieee80211_link_init(struct ieee802
  void ieee80211_link_stop(struct ieee80211_link_data *link);
  int ieee80211_vif_set_links(struct ieee80211_sub_if_data *sdata,
                            u16 new_links, u16 dormant_links);
 -void ieee80211_vif_clear_links(struct ieee80211_sub_if_data *sdata);
 -int __ieee80211_set_active_links(struct ieee80211_vif *vif, u16 active_links);
 +static inline void ieee80211_vif_clear_links(struct ieee80211_sub_if_data *sdata)
 +{
 +      ieee80211_vif_set_links(sdata, 0, 0);
 +}
  
  /* tx handling */
  void ieee80211_clear_tx_pending(struct ieee80211_local *local);
@@@ -2026,7 -2060,7 +2026,7 @@@ struct sk_buff 
  ieee80211_build_data_template(struct ieee80211_sub_if_data *sdata,
                              struct sk_buff *skb, u32 info_flags);
  void ieee80211_tx_monitor(struct ieee80211_local *local, struct sk_buff *skb,
 -                        int retry_count, int shift, bool send_to_cooked,
 +                        int retry_count, bool send_to_cooked,
                          struct ieee80211_tx_status *status);
  
  void ieee80211_check_fast_xmit(struct sta_info *sta);
@@@ -2059,17 -2093,19 +2059,17 @@@ void ieee80211_send_delba(struct ieee80
                          u16 initiator, u16 reason_code);
  int ieee80211_send_smps_action(struct ieee80211_sub_if_data *sdata,
                               enum ieee80211_smps_mode smps, const u8 *da,
 -                             const u8 *bssid);
 +                             const u8 *bssid, int link_id);
  bool ieee80211_smps_is_restrictive(enum ieee80211_smps_mode smps_mode_old,
                                   enum ieee80211_smps_mode smps_mode_new);
  
 -void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
 -                                   u16 initiator, u16 reason, bool stop);
  void __ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
                                    u16 initiator, u16 reason, bool stop);
 -void ___ieee80211_start_rx_ba_session(struct sta_info *sta,
 -                                    u8 dialog_token, u16 timeout,
 -                                    u16 start_seq_num, u16 ba_policy, u16 tid,
 -                                    u16 buf_size, bool tx, bool auto_seq,
 -                                    const struct ieee80211_addba_ext_ie *addbaext);
 +void __ieee80211_start_rx_ba_session(struct sta_info *sta,
 +                                   u8 dialog_token, u16 timeout,
 +                                   u16 start_seq_num, u16 ba_policy, u16 tid,
 +                                   u16 buf_size, bool tx, bool auto_seq,
 +                                   const struct ieee80211_addba_ext_ie *addbaext);
  void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta,
                                         enum ieee80211_agg_stop_reason reason);
  void ieee80211_process_delba(struct ieee80211_sub_if_data *sdata,
@@@ -2086,11 -2122,13 +2086,11 @@@ void ieee80211_process_addba_request(st
  
  int __ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
                                   enum ieee80211_agg_stop_reason reason);
 -int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
 -                                  enum ieee80211_agg_stop_reason reason);
  void ieee80211_start_tx_ba_cb(struct sta_info *sta, int tid,
                              struct tid_ampdu_tx *tid_tx);
  void ieee80211_stop_tx_ba_cb(struct sta_info *sta, int tid,
                             struct tid_ampdu_tx *tid_tx);
 -void ieee80211_ba_session_work(struct work_struct *work);
 +void ieee80211_ba_session_work(struct wiphy *wiphy, struct wiphy_work *work);
  void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid);
  void ieee80211_release_reorder_timeout(struct sta_info *sta, int tid);
  
@@@ -2103,6 -2141,7 +2103,7 @@@ voi
  ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata,
                                    struct ieee80211_supported_band *sband,
                                    const struct ieee80211_vht_cap *vht_cap_ie,
+                                   const struct ieee80211_vht_cap *vht_cap_ie2,
                                    struct link_sta_info *link_sta);
  enum ieee80211_sta_rx_bandwidth
  ieee80211_sta_cap_rx_bw(struct link_sta_info *link_sta);
@@@ -2167,7 -2206,7 +2168,7 @@@ void ieee80211_process_measurement_req(
   *    flags from &enum ieee80211_conn_flags.
   * @bssid: the currently connected bssid (for reporting)
   * @csa_ie: parsed 802.11 csa elements on count, mode, chandef and mesh ttl.
 -      All of them will be filled with if success only.
 + *    All of them will be filled with if success only.
   * Return: 0 on success, <0 on error and >0 if there is nothing to parse.
   */
  int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
@@@ -2199,7 -2238,8 +2200,7 @@@ static inline int __ieee80211_resume(st
  /* utility functions/constants */
  extern const void *const mac80211_wiphy_privid; /* for wiphy privid */
  int ieee80211_frame_duration(enum nl80211_band band, size_t len,
 -                           int rate, int erp, int short_preamble,
 -                           int shift);
 +                           int rate, int erp, int short_preamble);
  void ieee80211_regulatory_limit_wmm_params(struct ieee80211_sub_if_data *sdata,
                                           struct ieee80211_tx_queue_params *qparam,
                                           int ac);
@@@ -2294,6 -2334,8 +2295,6 @@@ ieee802_11_parse_elems(const u8 *start
        return ieee802_11_parse_elems_crc(start, len, action, 0, 0, bss);
  }
  
 -void ieee80211_fragment_element(struct sk_buff *skb, u8 *len_pos, u8 frag_id);
 -
  extern const int ieee802_1d_to_ac[8];
  
  static inline int ieee80211_ac_from_tid(int tid)
        return ieee802_1d_to_ac[tid & 7];
  }
  
 -void ieee80211_dynamic_ps_enable_work(struct work_struct *work);
 -void ieee80211_dynamic_ps_disable_work(struct work_struct *work);
 +void ieee80211_dynamic_ps_enable_work(struct wiphy *wiphy,
 +                                    struct wiphy_work *work);
 +void ieee80211_dynamic_ps_disable_work(struct wiphy *wiphy,
 +                                     struct wiphy_work *work);
  void ieee80211_dynamic_ps_timer(struct timer_list *t);
  void ieee80211_send_nullfunc(struct ieee80211_local *local,
                             struct ieee80211_sub_if_data *sdata,
@@@ -2482,7 -2522,7 +2483,7 @@@ bool ieee80211_chandef_vht_oper(struct 
                                const struct ieee80211_vht_operation *oper,
                                const struct ieee80211_ht_operation *htop,
                                struct cfg80211_chan_def *chandef);
 -void ieee80211_chandef_eht_oper(const struct ieee80211_eht_operation *eht_oper,
 +void ieee80211_chandef_eht_oper(const struct ieee80211_eht_operation_info *info,
                                bool support_160, bool support_320,
                                struct cfg80211_chan_def *chandef);
  bool ieee80211_chandef_he_6ghz_oper(struct ieee80211_sub_if_data *sdata,
@@@ -2524,10 -2564,9 +2525,10 @@@ void ieee80211_recalc_chanctx_min_def(s
                                      struct ieee80211_link_data *rsvd_for);
  bool ieee80211_is_radar_required(struct ieee80211_local *local);
  
 -void ieee80211_dfs_cac_timer_work(struct work_struct *work);
 +void ieee80211_dfs_cac_timer_work(struct wiphy *wiphy, struct wiphy_work *work);
  void ieee80211_dfs_cac_cancel(struct ieee80211_local *local);
 -void ieee80211_dfs_radar_detected_work(struct work_struct *work);
 +void ieee80211_dfs_radar_detected_work(struct wiphy *wiphy,
 +                                     struct wiphy_work *work);
  int ieee80211_send_action_csa(struct ieee80211_sub_if_data *sdata,
                              struct cfg80211_csa_settings *csa_settings);
  
@@@ -2549,7 -2588,7 +2550,7 @@@ int ieee80211_tdls_mgmt(struct wiphy *w
                        const u8 *extra_ies, size_t extra_ies_len);
  int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
                        const u8 *peer, enum nl80211_tdls_operation oper);
 -void ieee80211_tdls_peer_del_work(struct work_struct *wk);
 +void ieee80211_tdls_peer_del_work(struct wiphy *wiphy, struct wiphy_work *wk);
  int ieee80211_tdls_channel_switch(struct wiphy *wiphy, struct net_device *dev,
                                  const u8 *addr, u8 oper_class,
                                  struct cfg80211_chan_def *chandef);
diff --combined net/mac80211/key.c
index ac410f6632b541d71f9536801c5bcef6fe317cf6,0665ff5e456eb43605393ef193c9342760c71381..e0ff3a753e15d1dc3d39d69f29739a70c78f4036
  
  static const u8 bcast_addr[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
  
 -static void assert_key_lock(struct ieee80211_local *local)
 -{
 -      lockdep_assert_held(&local->key_mtx);
 -}
 -
  static void
  update_vlan_tailroom_need_count(struct ieee80211_sub_if_data *sdata, int delta)
  {
@@@ -62,7 -67,7 +62,7 @@@
                return;
  
        /* crypto_tx_tailroom_needed_cnt is protected by this */
 -      assert_key_lock(sdata->local);
 +      lockdep_assert_wiphy(sdata->local->hw.wiphy);
  
        rcu_read_lock();
  
@@@ -93,7 -98,7 +93,7 @@@ static void increment_tailroom_need_cou
         * http://mid.gmane.org/[email protected]
         */
  
 -      assert_key_lock(sdata->local);
 +      lockdep_assert_wiphy(sdata->local->hw.wiphy);
  
        update_vlan_tailroom_need_count(sdata, 1);
  
  static void decrease_tailroom_need_count(struct ieee80211_sub_if_data *sdata,
                                         int delta)
  {
 -      assert_key_lock(sdata->local);
 +      lockdep_assert_wiphy(sdata->local->hw.wiphy);
  
        WARN_ON_ONCE(sdata->crypto_tx_tailroom_needed_cnt < delta);
  
@@@ -124,7 -129,6 +124,7 @@@ static int ieee80211_key_enable_hw_acce
        int ret = -EOPNOTSUPP;
  
        might_sleep();
 +      lockdep_assert_wiphy(key->local->hw.wiphy);
  
        if (key->flags & KEY_FLAG_TAINTED) {
                /* If we get here, it's during resume and the key is
        if (!key->local->ops->set_key)
                goto out_unsupported;
  
 -      assert_key_lock(key->local);
 -
        sta = key->sta;
  
        /*
@@@ -236,14 -242,14 +236,14 @@@ static void ieee80211_key_disable_hw_ac
        if (!key || !key->local->ops->set_key)
                return;
  
 -      assert_key_lock(key->local);
 -
        if (!(key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE))
                return;
  
        sta = key->sta;
        sdata = key->sdata;
  
 +      lockdep_assert_wiphy(key->local->hw.wiphy);
 +
        if (key->conf.link_id >= 0 && sdata->vif.active_links &&
            !(sdata->vif.active_links & BIT(key->conf.link_id)))
                return;
@@@ -269,7 -275,7 +269,7 @@@ static int _ieee80211_set_tx_key(struc
        struct sta_info *sta = key->sta;
        struct ieee80211_local *local = key->local;
  
 -      assert_key_lock(local);
 +      lockdep_assert_wiphy(local->hw.wiphy);
  
        set_sta_flag(sta, WLAN_STA_USES_ENCRYPTION);
  
@@@ -294,7 -300,7 +294,7 @@@ static void ieee80211_pairwise_rekey(st
        struct sta_info *sta = new->sta;
        int i;
  
 -      assert_key_lock(local);
 +      lockdep_assert_wiphy(local->hw.wiphy);
  
        if (new->conf.flags & IEEE80211_KEY_FLAG_NO_AUTO_TX) {
                /* Extended Key ID key install, initial one or rekey */
                         * job done for the few ms we need it.)
                         */
                        set_sta_flag(sta, WLAN_STA_BLOCK_BA);
 -                      mutex_lock(&sta->ampdu_mlme.mtx);
                        for (i = 0; i <  IEEE80211_NUM_TIDS; i++)
 -                              ___ieee80211_stop_tx_ba_session(sta, i,
 -                                                              AGG_STOP_LOCAL_REQUEST);
 -                      mutex_unlock(&sta->ampdu_mlme.mtx);
 +                              __ieee80211_stop_tx_ba_session(sta, i,
 +                                                             AGG_STOP_LOCAL_REQUEST);
                }
        } else if (old) {
                /* Rekey without Extended Key ID.
@@@ -350,14 -358,12 +350,14 @@@ static void __ieee80211_set_default_key
        struct ieee80211_sub_if_data *sdata = link->sdata;
        struct ieee80211_key *key = NULL;
  
 -      assert_key_lock(sdata->local);
 +      lockdep_assert_wiphy(sdata->local->hw.wiphy);
  
        if (idx >= 0 && idx < NUM_DEFAULT_KEYS) {
 -              key = key_mtx_dereference(sdata->local, sdata->keys[idx]);
 +              key = wiphy_dereference(sdata->local->hw.wiphy,
 +                                      sdata->keys[idx]);
                if (!key)
 -                      key = key_mtx_dereference(sdata->local, link->gtk[idx]);
 +                      key = wiphy_dereference(sdata->local->hw.wiphy,
 +                                              link->gtk[idx]);
        }
  
        if (uni) {
  void ieee80211_set_default_key(struct ieee80211_link_data *link, int idx,
                               bool uni, bool multi)
  {
 -      mutex_lock(&link->sdata->local->key_mtx);
 +      lockdep_assert_wiphy(link->sdata->local->hw.wiphy);
 +
        __ieee80211_set_default_key(link, idx, uni, multi);
 -      mutex_unlock(&link->sdata->local->key_mtx);
  }
  
  static void
@@@ -387,12 -393,11 +387,12 @@@ __ieee80211_set_default_mgmt_key(struc
        struct ieee80211_sub_if_data *sdata = link->sdata;
        struct ieee80211_key *key = NULL;
  
 -      assert_key_lock(sdata->local);
 +      lockdep_assert_wiphy(sdata->local->hw.wiphy);
  
        if (idx >= NUM_DEFAULT_KEYS &&
            idx < NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS)
 -              key = key_mtx_dereference(sdata->local, link->gtk[idx]);
 +              key = wiphy_dereference(sdata->local->hw.wiphy,
 +                                      link->gtk[idx]);
  
        rcu_assign_pointer(link->default_mgmt_key, key);
  
  void ieee80211_set_default_mgmt_key(struct ieee80211_link_data *link,
                                    int idx)
  {
 -      mutex_lock(&link->sdata->local->key_mtx);
 +      lockdep_assert_wiphy(link->sdata->local->hw.wiphy);
 +
        __ieee80211_set_default_mgmt_key(link, idx);
 -      mutex_unlock(&link->sdata->local->key_mtx);
  }
  
  static void
@@@ -413,13 -418,12 +413,13 @@@ __ieee80211_set_default_beacon_key(stru
        struct ieee80211_sub_if_data *sdata = link->sdata;
        struct ieee80211_key *key = NULL;
  
 -      assert_key_lock(sdata->local);
 +      lockdep_assert_wiphy(sdata->local->hw.wiphy);
  
        if (idx >= NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS &&
            idx < NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS +
            NUM_DEFAULT_BEACON_KEYS)
 -              key = key_mtx_dereference(sdata->local, link->gtk[idx]);
 +              key = wiphy_dereference(sdata->local->hw.wiphy,
 +                                      link->gtk[idx]);
  
        rcu_assign_pointer(link->default_beacon_key, key);
  
  void ieee80211_set_default_beacon_key(struct ieee80211_link_data *link,
                                      int idx)
  {
 -      mutex_lock(&link->sdata->local->key_mtx);
 +      lockdep_assert_wiphy(link->sdata->local->hw.wiphy);
 +
        __ieee80211_set_default_beacon_key(link, idx);
 -      mutex_unlock(&link->sdata->local->key_mtx);
  }
  
  static int ieee80211_key_replace(struct ieee80211_sub_if_data *sdata,
        bool defunikey, defmultikey, defmgmtkey, defbeaconkey;
        bool is_wep;
  
 +      lockdep_assert_wiphy(sdata->local->hw.wiphy);
 +
        /* caller must provide at least one old/new */
        if (WARN_ON(!new && !old))
                return 0;
  
                if (sta) {
                        link_sta = rcu_dereference_protected(sta->link[link_id],
 -                                                           lockdep_is_held(&sta->local->sta_mtx));
 +                                                           lockdep_is_held(&sta->local->hw.wiphy->mtx));
                        if (!link_sta)
                                return -ENOLINK;
                }
                                ret = ieee80211_key_enable_hw_accel(new);
                }
        } else {
 -              if (!new->local->wowlan) {
 +              if (!new->local->wowlan)
                        ret = ieee80211_key_enable_hw_accel(new);
 -              } else {
 -                      assert_key_lock(new->local);
 +              else
                        new->flags |= KEY_FLAG_UPLOADED_TO_HARDWARE;
 -              }
        }
  
        if (ret)
                        ieee80211_check_fast_rx(sta);
        } else {
                defunikey = old &&
 -                      old == key_mtx_dereference(sdata->local,
 -                                              sdata->default_unicast_key);
 +                      old == wiphy_dereference(sdata->local->hw.wiphy,
 +                                               sdata->default_unicast_key);
                defmultikey = old &&
 -                      old == key_mtx_dereference(sdata->local,
 -                                                 link->default_multicast_key);
 +                      old == wiphy_dereference(sdata->local->hw.wiphy,
 +                                               link->default_multicast_key);
                defmgmtkey = old &&
 -                      old == key_mtx_dereference(sdata->local,
 -                                                 link->default_mgmt_key);
 +                      old == wiphy_dereference(sdata->local->hw.wiphy,
 +                                               link->default_mgmt_key);
                defbeaconkey = old &&
 -                      old == key_mtx_dereference(sdata->local,
 -                                                 link->default_beacon_key);
 +                      old == wiphy_dereference(sdata->local->hw.wiphy,
 +                                               link->default_beacon_key);
  
                if (defunikey && !new)
                        __ieee80211_set_default_key(link, -1, true, false);
@@@ -771,9 -775,8 +771,9 @@@ static void __ieee80211_key_destroy(str
                if (delay_tailroom) {
                        /* see ieee80211_delayed_tailroom_dec */
                        sdata->crypto_tx_tailroom_pending_dec++;
 -                      schedule_delayed_work(&sdata->dec_tailroom_needed_wk,
 -                                            HZ/2);
 +                      wiphy_delayed_work_queue(sdata->local->hw.wiphy,
 +                                               &sdata->dec_tailroom_needed_wk,
 +                                               HZ / 2);
                } else {
                        decrease_tailroom_need_count(sdata, 1);
                }
@@@ -799,6 -802,9 +799,9 @@@ static void ieee80211_key_destroy(struc
  
  void ieee80211_key_free_unused(struct ieee80211_key *key)
  {
+       if (!key)
+               return;
        WARN_ON(key->sdata || key->local);
        ieee80211_key_free_common(key);
  }
@@@ -853,48 -859,51 +856,52 @@@ int ieee80211_key_link(struct ieee80211
        bool delay_tailroom = sdata->vif.type == NL80211_IFTYPE_STATION;
        int ret;
  
 -      mutex_lock(&sdata->local->key_mtx);
 +      lockdep_assert_wiphy(sdata->local->hw.wiphy);
  
        if (sta && pairwise) {
                struct ieee80211_key *alt_key;
  
 -              old_key = key_mtx_dereference(sdata->local, sta->ptk[idx]);
 -              alt_key = key_mtx_dereference(sdata->local, sta->ptk[idx ^ 1]);
 +              old_key = wiphy_dereference(sdata->local->hw.wiphy,
 +                                          sta->ptk[idx]);
 +              alt_key = wiphy_dereference(sdata->local->hw.wiphy,
 +                                          sta->ptk[idx ^ 1]);
  
                /* The rekey code assumes that the old and new key are using
                 * the same cipher. Enforce the assumption for pairwise keys.
                 */
                if ((alt_key && alt_key->conf.cipher != key->conf.cipher) ||
-                   (old_key && old_key->conf.cipher != key->conf.cipher))
-                       return -EOPNOTSUPP;
+                   (old_key && old_key->conf.cipher != key->conf.cipher)) {
+                       ret = -EOPNOTSUPP;
+                       goto out;
+               }
        } else if (sta) {
                struct link_sta_info *link_sta = &sta->deflink;
                int link_id = key->conf.link_id;
  
                if (link_id >= 0) {
                        link_sta = rcu_dereference_protected(sta->link[link_id],
 -                                                           lockdep_is_held(&sta->local->sta_mtx));
 -                      if (!link_sta) {
 -                              ret = -ENOLINK;
 -                              goto out;
 -                      }
 +                                                           lockdep_is_held(&sta->local->hw.wiphy->mtx));
 +                      if (!link_sta)
 +                              return -ENOLINK;
                }
  
 -              old_key = key_mtx_dereference(sdata->local, link_sta->gtk[idx]);
 +              old_key = wiphy_dereference(sdata->local->hw.wiphy,
 +                                          link_sta->gtk[idx]);
        } else {
                if (idx < NUM_DEFAULT_KEYS)
 -                      old_key = key_mtx_dereference(sdata->local,
 -                                                    sdata->keys[idx]);
 +                      old_key = wiphy_dereference(sdata->local->hw.wiphy,
 +                                                  sdata->keys[idx]);
                if (!old_key)
 -                      old_key = key_mtx_dereference(sdata->local,
 -                                                    link->gtk[idx]);
 +                      old_key = wiphy_dereference(sdata->local->hw.wiphy,
 +                                                  link->gtk[idx]);
        }
  
        /* Non-pairwise keys must also not switch the cipher on rekey */
        if (!pairwise) {
-               if (old_key && old_key->conf.cipher != key->conf.cipher)
-                       return -EOPNOTSUPP;
+               if (old_key && old_key->conf.cipher != key->conf.cipher) {
+                       ret = -EOPNOTSUPP;
+                       goto out;
+               }
        }
  
        /*
         * new version of the key to avoid nonce reuse or replay issues.
         */
        if (ieee80211_key_identical(sdata, old_key, key)) {
-               ieee80211_key_free_unused(key);
-               return 0;
+               ret = -EALREADY;
 -              goto unlock;
++              goto out;
        }
  
        key->local = sdata->local;
                ieee80211_key_free(key, delay_tailroom);
        }
  
 - unlock:
 -      mutex_unlock(&sdata->local->key_mtx);
 -
+       key = NULL;
+  out:
+       ieee80211_key_free_unused(key);
        return ret;
  }
  
@@@ -952,6 -968,8 +963,6 @@@ void ieee80211_reenable_keys(struct iee
  
        lockdep_assert_wiphy(sdata->local->hw.wiphy);
  
 -      mutex_lock(&sdata->local->key_mtx);
 -
        sdata->crypto_tx_tailroom_needed_cnt = 0;
        sdata->crypto_tx_tailroom_pending_dec = 0;
  
                        ieee80211_key_enable_hw_accel(key);
                }
        }
 -
 -      mutex_unlock(&sdata->local->key_mtx);
  }
  
  void ieee80211_iter_keys(struct ieee80211_hw *hw,
  
        lockdep_assert_wiphy(hw->wiphy);
  
 -      mutex_lock(&local->key_mtx);
        if (vif) {
                sdata = vif_to_sdata(vif);
                list_for_each_entry_safe(key, tmp, &sdata->key_list, list)
                                     key->sta ? &key->sta->sta : NULL,
                                     &key->conf, iter_data);
        }
 -      mutex_unlock(&local->key_mtx);
  }
  EXPORT_SYMBOL(ieee80211_iter_keys);
  
@@@ -1078,8 -1100,7 +1089,8 @@@ void ieee80211_remove_link_keys(struct 
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_key *key, *tmp;
  
 -      mutex_lock(&local->key_mtx);
 +      lockdep_assert_wiphy(local->hw.wiphy);
 +
        list_for_each_entry_safe(key, tmp, &sdata->key_list, list) {
                if (key->conf.link_id != link->link_id)
                        continue;
                                      key, NULL);
                list_add_tail(&key->list, keys);
        }
 -      mutex_unlock(&local->key_mtx);
  }
  
  void ieee80211_free_key_list(struct ieee80211_local *local,
  {
        struct ieee80211_key *key, *tmp;
  
 -      mutex_lock(&local->key_mtx);
 +      lockdep_assert_wiphy(local->hw.wiphy);
 +
        list_for_each_entry_safe(key, tmp, keys, list)
                __ieee80211_key_destroy(key, false);
 -      mutex_unlock(&local->key_mtx);
  }
  
  void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata,
        struct ieee80211_key *key, *tmp;
        LIST_HEAD(keys);
  
 -      cancel_delayed_work_sync(&sdata->dec_tailroom_needed_wk);
 +      wiphy_delayed_work_cancel(local->hw.wiphy,
 +                                &sdata->dec_tailroom_needed_wk);
  
 -      mutex_lock(&local->key_mtx);
 +      lockdep_assert_wiphy(local->hw.wiphy);
  
        ieee80211_free_keys_iface(sdata, &keys);
  
                        WARN_ON_ONCE(vlan->crypto_tx_tailroom_needed_cnt ||
                                     vlan->crypto_tx_tailroom_pending_dec);
        }
 -
 -      mutex_unlock(&local->key_mtx);
  }
  
  void ieee80211_free_sta_keys(struct ieee80211_local *local,
        struct ieee80211_key *key;
        int i;
  
 -      mutex_lock(&local->key_mtx);
 +      lockdep_assert_wiphy(local->hw.wiphy);
 +
        for (i = 0; i < ARRAY_SIZE(sta->deflink.gtk); i++) {
 -              key = key_mtx_dereference(local, sta->deflink.gtk[i]);
 +              key = wiphy_dereference(local->hw.wiphy, sta->deflink.gtk[i]);
                if (!key)
                        continue;
                ieee80211_key_replace(key->sdata, NULL, key->sta,
        }
  
        for (i = 0; i < NUM_DEFAULT_KEYS; i++) {
 -              key = key_mtx_dereference(local, sta->ptk[i]);
 +              key = wiphy_dereference(local->hw.wiphy, sta->ptk[i]);
                if (!key)
                        continue;
                ieee80211_key_replace(key->sdata, NULL, key->sta,
                __ieee80211_key_destroy(key, key->sdata->vif.type ==
                                        NL80211_IFTYPE_STATION);
        }
 -
 -      mutex_unlock(&local->key_mtx);
  }
  
 -void ieee80211_delayed_tailroom_dec(struct work_struct *wk)
 +void ieee80211_delayed_tailroom_dec(struct wiphy *wiphy,
 +                                  struct wiphy_work *wk)
  {
        struct ieee80211_sub_if_data *sdata;
  
         * within an ESS this usually won't happen.
         */
  
 -      mutex_lock(&sdata->local->key_mtx);
        decrease_tailroom_need_count(sdata,
                                     sdata->crypto_tx_tailroom_pending_dec);
        sdata->crypto_tx_tailroom_pending_dec = 0;
 -      mutex_unlock(&sdata->local->key_mtx);
  }
  
  void ieee80211_gtk_rekey_notify(struct ieee80211_vif *vif, const u8 *bssid,
@@@ -1334,7 -1359,7 +1345,7 @@@ void ieee80211_remove_key(struct ieee80
  
        key = container_of(keyconf, struct ieee80211_key, conf);
  
 -      assert_key_lock(key->local);
 +      lockdep_assert_wiphy(key->local->hw.wiphy);
  
        /*
         * if key was uploaded, we assume the driver will/has remove(d)
diff --combined net/mac80211/mesh.c
index 0d0fbae51b61da26a319df8a3297197e85fcdca1,e31c312c124a1a9f81bc75779dcd8a5317f51b5a..092a1dc7314d6e337d2a4ef0b98ff1d8e8796ce9
@@@ -1175,7 -1175,7 +1175,7 @@@ void ieee80211_mbss_info_change_notify(
  
        /* if we race with running work, worst case this work becomes a noop */
        for_each_set_bit(bit, &bits, sizeof(changed) * BITS_PER_BYTE)
-               set_bit(bit, &ifmsh->mbss_changed);
+               set_bit(bit, ifmsh->mbss_changed);
        set_bit(MESH_WORK_MBSS_CHANGED, &ifmsh->wrkq_flags);
        wiphy_work_queue(sdata->local->hw.wiphy, &sdata->work);
  }
@@@ -1257,7 -1257,7 +1257,7 @@@ void ieee80211_stop_mesh(struct ieee802
  
        /* clear any mesh work (for next join) we may have accrued */
        ifmsh->wrkq_flags = 0;
-       ifmsh->mbss_changed = 0;
+       memset(ifmsh->mbss_changed, 0, sizeof(ifmsh->mbss_changed));
  
        local->fif_other_bss--;
        atomic_dec(&local->iff_allmultis);
@@@ -1291,7 -1291,7 +1291,7 @@@ ieee80211_mesh_process_chnswitch(struc
        ieee80211_conn_flags_t conn_flags = 0;
        u32 vht_cap_info = 0;
  
 -      sdata_assert_lock(sdata);
 +      lockdep_assert_wiphy(sdata->local->hw.wiphy);
  
        sband = ieee80211_get_sband(sdata);
        if (!sband)
@@@ -1559,7 -1559,7 +1559,7 @@@ int ieee80211_mesh_csa_beacon(struct ie
        struct mesh_csa_settings *tmp_csa_settings;
        int ret = 0;
  
 -      lockdep_assert_held(&sdata->wdev.mtx);
 +      lockdep_assert_wiphy(sdata->local->hw.wiphy);
  
        tmp_csa_settings = kmalloc(sizeof(*tmp_csa_settings),
                                   GFP_ATOMIC);
@@@ -1691,11 -1691,11 +1691,11 @@@ void ieee80211_mesh_rx_queued_mgmt(stru
        struct ieee80211_mgmt *mgmt;
        u16 stype;
  
 -      sdata_lock(sdata);
 +      lockdep_assert_wiphy(sdata->local->hw.wiphy);
  
        /* mesh already went down */
        if (!sdata->u.mesh.mesh_id_len)
 -              goto out;
 +              return;
  
        rx_status = IEEE80211_SKB_RXCB(skb);
        mgmt = (struct ieee80211_mgmt *) skb->data;
                ieee80211_mesh_rx_mgmt_action(sdata, mgmt, skb->len, rx_status);
                break;
        }
 -out:
 -      sdata_unlock(sdata);
  }
  
  static void mesh_bss_info_changed(struct ieee80211_sub_if_data *sdata)
        u32 bit;
        u64 changed = 0;
  
-       for_each_set_bit(bit, &ifmsh->mbss_changed,
+       for_each_set_bit(bit, ifmsh->mbss_changed,
                         sizeof(changed) * BITS_PER_BYTE) {
-               clear_bit(bit, &ifmsh->mbss_changed);
+               clear_bit(bit, ifmsh->mbss_changed);
                changed |= BIT(bit);
        }
  
@@@ -1743,11 -1745,11 +1743,11 @@@ void ieee80211_mesh_work(struct ieee802
  {
        struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
  
 -      sdata_lock(sdata);
 +      lockdep_assert_wiphy(sdata->local->hw.wiphy);
  
        /* mesh already went down */
        if (!sdata->u.mesh.mesh_id_len)
 -              goto out;
 +              return;
  
        if (ifmsh->preq_queue_len &&
            time_after(jiffies,
  
        if (test_and_clear_bit(MESH_WORK_MBSS_CHANGED, &ifmsh->wrkq_flags))
                mesh_bss_info_changed(sdata);
 -out:
 -      sdata_unlock(sdata);
  }
  
  
diff --combined net/mac80211/mlme.c
index 2ac36ad9fa91c706731b042fd4a3d9434fbc6037,0c9198997482bcfa41b9b4781d3419d7e4b5bb53..8d2514a9a6c4697f817dcbda6ad74070cc953631
@@@ -110,8 -110,7 +110,8 @@@ ieee80211_extract_dis_subch_bmap(const 
                return 0;
  
        /* set 160/320 supported to get the full AP definition */
 -      ieee80211_chandef_eht_oper(eht_oper, true, true, &ap_chandef);
 +      ieee80211_chandef_eht_oper((const void *)eht_oper->optional,
 +                                 true, true, &ap_chandef);
        ap_center_freq = ap_chandef.center_freq1;
        ap_bw = 20 * BIT(u8_get_bits(info->control,
                                     IEEE80211_EHT_OPER_CHAN_WIDTH));
@@@ -176,7 -175,7 +176,7 @@@ ieee80211_handle_puncturing_bitmap(stru
  static void run_again(struct ieee80211_sub_if_data *sdata,
                      unsigned long timeout)
  {
 -      sdata_assert_lock(sdata);
 +      lockdep_assert_wiphy(sdata->local->hw.wiphy);
  
        if (!timer_pending(&sdata->u.mgd.timer) ||
            time_before(timeout, sdata->u.mgd.timer.expires))
@@@ -389,7 -388,7 +389,7 @@@ ieee80211_determine_chantype(struct iee
        if (eht_oper && (eht_oper->params & IEEE80211_EHT_OPER_INFO_PRESENT)) {
                struct cfg80211_chan_def eht_chandef = *chandef;
  
 -              ieee80211_chandef_eht_oper(eht_oper,
 +              ieee80211_chandef_eht_oper((const void *)eht_oper->optional,
                                           eht_chandef.width ==
                                           NL80211_CHAN_WIDTH_160,
                                           false, &eht_chandef);
@@@ -831,6 -830,7 +831,6 @@@ static void ieee80211_assoc_add_rates(s
                                      struct ieee80211_supported_band *sband,
                                      struct ieee80211_mgd_assoc_data *assoc_data)
  {
 -      unsigned int shift = ieee80211_chanwidth_get_shift(width);
        unsigned int rates_len, supp_rates_len;
        u32 rates = 0;
        int i, count;
        count = 0;
        for (i = 0; i < sband->n_bitrates; i++) {
                if (BIT(i) & rates) {
 -                      int rate = DIV_ROUND_UP(sband->bitrates[i].bitrate,
 -                                              5 * (1 << shift));
 +                      int rate = DIV_ROUND_UP(sband->bitrates[i].bitrate, 5);
                        *pos++ = (u8)rate;
                        if (++count == 8)
                                break;
                        if (BIT(i) & rates) {
                                int rate;
  
 -                              rate = DIV_ROUND_UP(sband->bitrates[i].bitrate,
 -                                                  5 * (1 << shift));
 +                              rate = DIV_ROUND_UP(sband->bitrates[i].bitrate, 5);
                                *pos++ = (u8)rate;
                        }
                }
@@@ -1399,7 -1401,7 +1399,7 @@@ static int ieee80211_send_assoc(struct 
                                                      assoc_data->ie,
                                                      assoc_data->ie_len);
  
 -      sdata_assert_lock(sdata);
 +      lockdep_assert_wiphy(sdata->local->hw.wiphy);
  
        size = local->hw.extra_tx_headroom +
               sizeof(*mgmt) + /* bit too much but doesn't matter */
@@@ -1687,13 -1689,15 +1687,13 @@@ static void ieee80211_chswitch_work(str
        if (!ieee80211_sdata_running(sdata))
                return;
  
 -      sdata_lock(sdata);
 -      mutex_lock(&local->mtx);
 -      mutex_lock(&local->chanctx_mtx);
 +      lockdep_assert_wiphy(local->hw.wiphy);
  
        if (!ifmgd->associated)
 -              goto out;
 +              return;
  
        if (!link->conf->csa_active)
 -              goto out;
 +              return;
  
        /*
         * using reservation isn't immediate as it may be deferred until later
                 * reservations
                 */
                if (link->reserved_ready)
 -                      goto out;
 +                      return;
  
                ret = ieee80211_link_use_reserved_context(link);
                if (ret) {
                                   ret);
                        wiphy_work_queue(sdata->local->hw.wiphy,
                                         &ifmgd->csa_connection_drop_work);
 -                      goto out;
                }
 -
 -              goto out;
 +              return;
        }
  
        if (!cfg80211_chandef_identical(&link->conf->chandef,
                           "failed to finalize channel switch, disconnecting\n");
                wiphy_work_queue(sdata->local->hw.wiphy,
                                 &ifmgd->csa_connection_drop_work);
 -              goto out;
 +              return;
        }
  
        link->u.mgd.csa_waiting_bcn = true;
  
        ieee80211_sta_reset_beacon_monitor(sdata);
        ieee80211_sta_reset_conn_monitor(sdata);
 -
 -out:
 -      mutex_unlock(&local->chanctx_mtx);
 -      mutex_unlock(&local->mtx);
 -      sdata_unlock(sdata);
  }
  
  static void ieee80211_chswitch_post_beacon(struct ieee80211_link_data *link)
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        int ret;
  
 -      sdata_assert_lock(sdata);
 +      lockdep_assert_wiphy(sdata->local->hw.wiphy);
  
        WARN_ON(!link->conf->csa_active);
  
         */
        link->u.mgd.beacon_crc_valid = false;
  
 -      ret = drv_post_channel_switch(sdata);
 +      ret = drv_post_channel_switch(link);
        if (ret) {
                sdata_info(sdata,
                           "driver post channel switch failed, disconnecting\n");
                return;
        }
  
 -      cfg80211_ch_switch_notify(sdata->dev, &link->reserved_chandef, 0, 0);
 +      cfg80211_ch_switch_notify(sdata->dev, &link->reserved_chandef,
 +                                link->link_id, 0);
  }
  
 -void ieee80211_chswitch_done(struct ieee80211_vif *vif, bool success)
 +void ieee80211_chswitch_done(struct ieee80211_vif *vif, bool success,
 +                           unsigned int link_id)
  {
        struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
 -      struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
  
 -      if (WARN_ON(ieee80211_vif_is_mld(&sdata->vif)))
 -              success = false;
 +      trace_api_chswitch_done(sdata, success, link_id);
 +
 +      rcu_read_lock();
  
 -      trace_api_chswitch_done(sdata, success);
        if (!success) {
                sdata_info(sdata,
                           "driver channel switch failed, disconnecting\n");
                wiphy_work_queue(sdata->local->hw.wiphy,
 -                               &ifmgd->csa_connection_drop_work);
 +                               &sdata->u.mgd.csa_connection_drop_work);
        } else {
 +              struct ieee80211_link_data *link =
 +                      rcu_dereference(sdata->link[link_id]);
 +
 +              if (WARN_ON(!link)) {
 +                      rcu_read_unlock();
 +                      return;
 +              }
 +
                wiphy_delayed_work_queue(sdata->local->hw.wiphy,
 -                                       &sdata->deflink.u.mgd.chswitch_work,
 -                                       0);
 +                                       &link->u.mgd.chswitch_work, 0);
        }
 +
 +      rcu_read_unlock();
  }
  EXPORT_SYMBOL(ieee80211_chswitch_done);
  
@@@ -1812,12 -1813,14 +1812,12 @@@ ieee80211_sta_abort_chanswitch(struct i
        struct ieee80211_sub_if_data *sdata = link->sdata;
        struct ieee80211_local *local = sdata->local;
  
 +      lockdep_assert_wiphy(local->hw.wiphy);
 +
        if (!local->ops->abort_channel_switch)
                return;
  
 -      mutex_lock(&local->mtx);
 -
 -      mutex_lock(&local->chanctx_mtx);
        ieee80211_link_unreserve_chanctx(link);
 -      mutex_unlock(&local->chanctx_mtx);
  
        if (link->csa_block_tx)
                ieee80211_wake_vif_queues(local, sdata,
        link->csa_block_tx = false;
        link->conf->csa_active = false;
  
 -      mutex_unlock(&local->mtx);
 -
        drv_abort_channel_switch(sdata);
  }
  
@@@ -1848,7 -1853,7 +1848,7 @@@ ieee80211_sta_process_chanswitch(struc
        unsigned long timeout;
        int res;
  
 -      sdata_assert_lock(sdata);
 +      lockdep_assert_wiphy(local->hw.wiphy);
  
        if (!cbss)
                return;
        }
  
        if (res < 0)
 -              goto lock_and_drop_connection;
 +              goto drop_connection;
  
        if (beacon && link->conf->csa_active &&
            !link->u.mgd.csa_waiting_bcn) {
                           csa_ie.chandef.chan->center_freq,
                           csa_ie.chandef.width, csa_ie.chandef.center_freq1,
                           csa_ie.chandef.center_freq2);
 -              goto lock_and_drop_connection;
 +              goto drop_connection;
        }
  
        if (!cfg80211_chandef_usable(local->hw.wiphy, &csa_ie.chandef,
                           csa_ie.chandef.width, csa_ie.chandef.center_freq1,
                           csa_ie.chandef.freq1_offset,
                           csa_ie.chandef.center_freq2);
 -              goto lock_and_drop_connection;
 +              goto drop_connection;
        }
  
        if (cfg80211_chandef_identical(&csa_ie.chandef,
         */
        ieee80211_teardown_tdls_peers(sdata);
  
 -      mutex_lock(&local->mtx);
 -      mutex_lock(&local->chanctx_mtx);
        conf = rcu_dereference_protected(link->conf->chanctx_conf,
 -                                       lockdep_is_held(&local->chanctx_mtx));
 +                                       lockdep_is_held(&local->hw.wiphy->mtx));
        if (!conf) {
                sdata_info(sdata,
                           "no channel context assigned to vif?, disconnecting\n");
                           res);
                goto drop_connection;
        }
 -      mutex_unlock(&local->chanctx_mtx);
  
        link->conf->csa_active = true;
        link->csa_chandef = csa_ie.chandef;
        if (link->csa_block_tx)
                ieee80211_stop_vif_queues(local, sdata,
                                          IEEE80211_QUEUE_STOP_REASON_CSA);
 -      mutex_unlock(&local->mtx);
  
        cfg80211_ch_switch_started_notify(sdata->dev, &csa_ie.chandef,
                                          link->link_id, csa_ie.count,
                                 &link->u.mgd.chswitch_work,
                                 timeout);
        return;
 - lock_and_drop_connection:
 -      mutex_lock(&local->mtx);
 -      mutex_lock(&local->chanctx_mtx);
   drop_connection:
        /*
         * This is just so that the disconnect flow will know that
  
        wiphy_work_queue(sdata->local->hw.wiphy,
                         &ifmgd->csa_connection_drop_work);
 -      mutex_unlock(&local->chanctx_mtx);
 -      mutex_unlock(&local->mtx);
  }
  
  static bool
@@@ -2197,8 -2211,7 +2197,8 @@@ static void ieee80211_change_ps(struct 
                conf->flags &= ~IEEE80211_CONF_PS;
                ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
                del_timer_sync(&local->dynamic_ps_timer);
 -              cancel_work_sync(&local->dynamic_ps_enable_work);
 +              wiphy_work_cancel(local->hw.wiphy,
 +                                &local->dynamic_ps_enable_work);
        }
  }
  
@@@ -2295,8 -2308,7 +2295,8 @@@ void ieee80211_recalc_ps_vif(struct iee
        }
  }
  
 -void ieee80211_dynamic_ps_disable_work(struct work_struct *work)
 +void ieee80211_dynamic_ps_disable_work(struct wiphy *wiphy,
 +                                     struct wiphy_work *work)
  {
        struct ieee80211_local *local =
                container_of(work, struct ieee80211_local,
                                        false);
  }
  
 -void ieee80211_dynamic_ps_enable_work(struct work_struct *work)
 +void ieee80211_dynamic_ps_enable_work(struct wiphy *wiphy,
 +                                    struct wiphy_work *work)
  {
        struct ieee80211_local *local =
                container_of(work, struct ieee80211_local,
@@@ -2387,25 -2398,26 +2387,25 @@@ void ieee80211_dynamic_ps_timer(struct 
  {
        struct ieee80211_local *local = from_timer(local, t, dynamic_ps_timer);
  
 -      ieee80211_queue_work(&local->hw, &local->dynamic_ps_enable_work);
 +      wiphy_work_queue(local->hw.wiphy, &local->dynamic_ps_enable_work);
  }
  
 -void ieee80211_dfs_cac_timer_work(struct work_struct *work)
 +void ieee80211_dfs_cac_timer_work(struct wiphy *wiphy, struct wiphy_work *work)
  {
 -      struct delayed_work *delayed_work = to_delayed_work(work);
        struct ieee80211_link_data *link =
 -              container_of(delayed_work, struct ieee80211_link_data,
 -                           dfs_cac_timer_work);
 +              container_of(work, struct ieee80211_link_data,
 +                           dfs_cac_timer_work.work);
        struct cfg80211_chan_def chandef = link->conf->chandef;
        struct ieee80211_sub_if_data *sdata = link->sdata;
  
 -      mutex_lock(&sdata->local->mtx);
 +      lockdep_assert_wiphy(sdata->local->hw.wiphy);
 +
        if (sdata->wdev.cac_started) {
                ieee80211_link_release_channel(link);
                cfg80211_cac_event(sdata->dev, &chandef,
                                   NL80211_RADAR_CAC_FINISHED,
                                   GFP_KERNEL);
        }
 -      mutex_unlock(&sdata->local->mtx);
  }
  
  static bool
@@@ -2475,10 -2487,8 +2475,10 @@@ __ieee80211_sta_handle_tspec_ac_params(
                                         ac);
                        tx_tspec->action = TX_TSPEC_ACTION_NONE;
                        ret = true;
 -                      schedule_delayed_work(&ifmgd->tx_tspec_wk,
 -                              tx_tspec->time_slice_start + HZ - now + 1);
 +                      wiphy_delayed_work_queue(local->hw.wiphy,
 +                                               &ifmgd->tx_tspec_wk,
 +                                               tx_tspec->time_slice_start +
 +                                               HZ - now + 1);
                        break;
                case TX_TSPEC_ACTION_NONE:
                        /* nothing now */
@@@ -2496,8 -2506,7 +2496,8 @@@ void ieee80211_sta_handle_tspec_ac_para
                                                  BSS_CHANGED_QOS);
  }
  
 -static void ieee80211_sta_handle_tspec_ac_params_wk(struct work_struct *work)
 +static void ieee80211_sta_handle_tspec_ac_params_wk(struct wiphy *wiphy,
 +                                                  struct wiphy_work *work)
  {
        struct ieee80211_sub_if_data *sdata;
  
@@@ -2672,7 -2681,7 +2672,7 @@@ ieee80211_sta_wmm_params(struct ieee802
  
  static void __ieee80211_stop_poll(struct ieee80211_sub_if_data *sdata)
  {
 -      lockdep_assert_held(&sdata->local->mtx);
 +      lockdep_assert_wiphy(sdata->local->hw.wiphy);
  
        sdata->u.mgd.flags &= ~IEEE80211_STA_CONNECTION_POLL;
        ieee80211_run_deferred_scan(sdata->local);
  
  static void ieee80211_stop_poll(struct ieee80211_sub_if_data *sdata)
  {
 -      mutex_lock(&sdata->local->mtx);
 +      lockdep_assert_wiphy(sdata->local->hw.wiphy);
 +
        __ieee80211_stop_poll(sdata);
 -      mutex_unlock(&sdata->local->mtx);
  }
  
  static u64 ieee80211_handle_bss_capability(struct ieee80211_link_data *link,
@@@ -2800,8 -2809,6 +2800,8 @@@ static void ieee80211_set_associated(st
        u64 vif_changed = BSS_CHANGED_ASSOC;
        unsigned int link_id;
  
 +      lockdep_assert_wiphy(local->hw.wiphy);
 +
        sdata->u.mgd.associated = true;
  
        for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) {
                                                 vif_changed | changed[0]);
        }
  
 -      mutex_lock(&local->iflist_mtx);
        ieee80211_recalc_ps(local);
 -      mutex_unlock(&local->iflist_mtx);
  
        /* leave this here to not change ordering in non-MLO cases */
        if (!ieee80211_vif_is_mld(&sdata->vif))
@@@ -2885,7 -2894,7 +2885,7 @@@ static void ieee80211_set_disassoc(stru
                .subtype = stype,
        };
  
 -      sdata_assert_lock(sdata);
 +      lockdep_assert_wiphy(local->hw.wiphy);
  
        if (WARN_ON_ONCE(tx && !frame_buf))
                return;
        sdata->deflink.ap_power_level = IEEE80211_UNSET_POWER_LEVEL;
  
        del_timer_sync(&local->dynamic_ps_timer);
 -      cancel_work_sync(&local->dynamic_ps_enable_work);
 +      wiphy_work_cancel(local->hw.wiphy, &local->dynamic_ps_enable_work);
  
        /* Disable ARP filtering */
        if (sdata->vif.cfg.arp_addr_cnt)
  
        ifmgd->flags = 0;
        sdata->deflink.u.mgd.conn_flags = 0;
 -      mutex_lock(&local->mtx);
  
        for (link_id = 0; link_id < ARRAY_SIZE(sdata->link); link_id++) {
                struct ieee80211_link_data *link;
                                          IEEE80211_QUEUE_STOP_REASON_CSA);
                sdata->deflink.csa_block_tx = false;
        }
 -      mutex_unlock(&local->mtx);
  
        /* existing TX TSPEC sessions no longer exist */
        memset(ifmgd->tx_tspec, 0, sizeof(ifmgd->tx_tspec));
 -      cancel_delayed_work_sync(&ifmgd->tx_tspec_wk);
 +      wiphy_delayed_work_cancel(local->hw.wiphy, &ifmgd->tx_tspec_wk);
  
        sdata->vif.bss_conf.pwr_reduction = 0;
        sdata->vif.bss_conf.tx_pwr_env_num = 0;
        memset(sdata->vif.bss_conf.tx_pwr_env, 0,
               sizeof(sdata->vif.bss_conf.tx_pwr_env));
  
 +      memset(&sdata->u.mgd.ttlm_info, 0,
 +             sizeof(sdata->u.mgd.ttlm_info));
 +      wiphy_delayed_work_cancel(sdata->local->hw.wiphy, &ifmgd->ttlm_work);
        ieee80211_vif_set_links(sdata, 0, 0);
  }
  
@@@ -3065,17 -3073,18 +3065,17 @@@ static void ieee80211_reset_ap_probe(st
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        struct ieee80211_local *local = sdata->local;
  
 -      mutex_lock(&local->mtx);
 +      lockdep_assert_wiphy(local->hw.wiphy);
 +
        if (!(ifmgd->flags & IEEE80211_STA_CONNECTION_POLL))
 -              goto out;
 +              return;
  
        __ieee80211_stop_poll(sdata);
  
 -      mutex_lock(&local->iflist_mtx);
        ieee80211_recalc_ps(local);
 -      mutex_unlock(&local->iflist_mtx);
  
        if (ieee80211_hw_check(&sdata->local->hw, CONNECTION_MONITOR))
 -              goto out;
 +              return;
  
        /*
         * We've received a probe response, but are not sure whether
        mod_timer(&ifmgd->conn_mon_timer,
                  round_jiffies_up(jiffies +
                                   IEEE80211_CONNECTION_IDLE_TIME));
 -out:
 -      mutex_unlock(&local->mtx);
  }
  
  static void ieee80211_sta_tx_wmm_ac_notify(struct ieee80211_sub_if_data *sdata,
  
                if (tx_tspec->downgraded) {
                        tx_tspec->action = TX_TSPEC_ACTION_STOP_DOWNGRADE;
 -                      schedule_delayed_work(&ifmgd->tx_tspec_wk, 0);
 +                      wiphy_delayed_work_queue(sdata->local->hw.wiphy,
 +                                               &ifmgd->tx_tspec_wk, 0);
                }
        }
  
        if (tx_tspec->consumed_tx_time >= tx_tspec->admitted_time) {
                tx_tspec->downgraded = true;
                tx_tspec->action = TX_TSPEC_ACTION_DOWNGRADE;
 -              schedule_delayed_work(&ifmgd->tx_tspec_wk, 0);
 +              wiphy_delayed_work_queue(sdata->local->hw.wiphy,
 +                                       &ifmgd->tx_tspec_wk, 0);
        }
  }
  
@@@ -3170,8 -3179,6 +3170,8 @@@ static void ieee80211_mgd_probe_ap_send
        u8 unicast_limit = max(1, max_probe_tries - 3);
        struct sta_info *sta;
  
 +      lockdep_assert_wiphy(sdata->local->hw.wiphy);
 +
        if (WARN_ON(ieee80211_vif_is_mld(&sdata->vif)))
                return;
  
        ifmgd->probe_send_count++;
  
        if (dst) {
 -              mutex_lock(&sdata->local->sta_mtx);
                sta = sta_info_get(sdata, dst);
                if (!WARN_ON(!sta))
                        ieee80211_check_fast_rx(sta);
 -              mutex_unlock(&sdata->local->sta_mtx);
        }
  
        if (ieee80211_hw_check(&sdata->local->hw, REPORTS_TX_ACK_STATUS)) {
@@@ -3218,24 -3227,29 +3218,24 @@@ static void ieee80211_mgd_probe_ap(stru
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        bool already = false;
  
 +      lockdep_assert_wiphy(sdata->local->hw.wiphy);
 +
        if (WARN_ON_ONCE(ieee80211_vif_is_mld(&sdata->vif)))
                return;
  
        if (!ieee80211_sdata_running(sdata))
                return;
  
 -      sdata_lock(sdata);
 -
        if (!ifmgd->associated)
 -              goto out;
 -
 -      mutex_lock(&sdata->local->mtx);
 +              return;
  
 -      if (sdata->local->tmp_channel || sdata->local->scanning) {
 -              mutex_unlock(&sdata->local->mtx);
 -              goto out;
 -      }
 +      if (sdata->local->tmp_channel || sdata->local->scanning)
 +              return;
  
        if (sdata->local->suspending) {
                /* reschedule after resume */
 -              mutex_unlock(&sdata->local->mtx);
                ieee80211_reset_ap_probe(sdata);
 -              goto out;
 +              return;
        }
  
        if (beacon) {
  
        ifmgd->flags |= IEEE80211_STA_CONNECTION_POLL;
  
 -      mutex_unlock(&sdata->local->mtx);
 -
        if (already)
 -              goto out;
 +              return;
  
 -      mutex_lock(&sdata->local->iflist_mtx);
        ieee80211_recalc_ps(sdata->local);
 -      mutex_unlock(&sdata->local->iflist_mtx);
  
        ifmgd->probe_send_count = 0;
        ieee80211_mgd_probe_ap_send(sdata);
 - out:
 -      sdata_unlock(sdata);
  }
  
  struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw,
        const struct element *ssid;
        int ssid_len;
  
 +      lockdep_assert_wiphy(sdata->local->hw.wiphy);
 +
        if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION ||
                    ieee80211_vif_is_mld(&sdata->vif)))
                return NULL;
  
 -      sdata_assert_lock(sdata);
 -
        if (ifmgd->associated)
                cbss = sdata->deflink.u.mgd.bss;
        else if (ifmgd->auth_data)
@@@ -3333,15 -3353,13 +3333,15 @@@ static void ieee80211_report_disconnect
        drv_event_callback(sdata->local, sdata, &event);
  }
  
 -static void ___ieee80211_disconnect(struct ieee80211_sub_if_data *sdata)
 +static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata)
  {
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
        bool tx;
  
 +      lockdep_assert_wiphy(local->hw.wiphy);
 +
        if (!ifmgd->associated)
                return;
  
                                        WLAN_REASON_DEAUTH_LEAVING :
                                        WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY,
                               tx, frame_buf);
 -      mutex_lock(&local->mtx);
        /* the other links will be destroyed */
        sdata->vif.bss_conf.csa_active = false;
        sdata->deflink.u.mgd.csa_waiting_bcn = false;
                                          IEEE80211_QUEUE_STOP_REASON_CSA);
                sdata->deflink.csa_block_tx = false;
        }
 -      mutex_unlock(&local->mtx);
  
        ieee80211_report_disconnect(sdata, frame_buf, sizeof(frame_buf), tx,
                                    WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY,
        ifmgd->reconnect = false;
  }
  
 -static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata)
 -{
 -      sdata_lock(sdata);
 -      ___ieee80211_disconnect(sdata);
 -      sdata_unlock(sdata);
 -}
 -
  static void ieee80211_beacon_connection_loss_work(struct wiphy *wiphy,
                                                  struct wiphy_work *work)
  {
@@@ -3473,7 -3500,7 +3473,7 @@@ static void ieee80211_destroy_auth_data
  {
        struct ieee80211_mgd_auth_data *auth_data = sdata->u.mgd.auth_data;
  
 -      sdata_assert_lock(sdata);
 +      lockdep_assert_wiphy(sdata->local->hw.wiphy);
  
        if (!assoc) {
                /*
                                                  BSS_CHANGED_BSSID);
                sdata->u.mgd.flags = 0;
  
 -              mutex_lock(&sdata->local->mtx);
                ieee80211_link_release_channel(&sdata->deflink);
                ieee80211_vif_set_links(sdata, 0, 0);
 -              mutex_unlock(&sdata->local->mtx);
        }
  
        cfg80211_put_bss(sdata->local->hw.wiphy, auth_data->bss);
@@@ -3512,7 -3541,7 +3512,7 @@@ static void ieee80211_destroy_assoc_dat
  {
        struct ieee80211_mgd_assoc_data *assoc_data = sdata->u.mgd.assoc_data;
  
 -      sdata_assert_lock(sdata);
 +      lockdep_assert_wiphy(sdata->local->hw.wiphy);
  
        if (status != ASSOC_SUCCESS) {
                /*
                        cfg80211_assoc_failure(sdata->dev, &data);
                }
  
 -              mutex_lock(&sdata->local->mtx);
                ieee80211_link_release_channel(&sdata->deflink);
                ieee80211_vif_set_links(sdata, 0, 0);
 -              mutex_unlock(&sdata->local->mtx);
        }
  
        kfree(assoc_data);
@@@ -3591,8 -3622,7 +3591,8 @@@ static bool ieee80211_mark_sta_auth(str
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        const u8 *ap_addr = ifmgd->auth_data->ap_addr;
        struct sta_info *sta;
 -      bool result = true;
 +
 +      lockdep_assert_wiphy(sdata->local->hw.wiphy);
  
        sdata_info(sdata, "authenticated\n");
        ifmgd->auth_data->done = true;
        run_again(sdata, ifmgd->auth_data->timeout);
  
        /* move station state to auth */
 -      mutex_lock(&sdata->local->sta_mtx);
        sta = sta_info_get(sdata, ap_addr);
        if (!sta) {
                WARN_ONCE(1, "%s: STA %pM not found", sdata->name, ap_addr);
 -              result = false;
 -              goto out;
 +              return false;
        }
        if (sta_info_move_state(sta, IEEE80211_STA_AUTH)) {
                sdata_info(sdata, "failed moving %pM to auth\n", ap_addr);
 -              result = false;
 -              goto out;
 +              return false;
        }
  
 -out:
 -      mutex_unlock(&sdata->local->sta_mtx);
 -      return result;
 +      return true;
  }
  
  static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
                .subtype = IEEE80211_STYPE_AUTH,
        };
  
 -      sdata_assert_lock(sdata);
 +      lockdep_assert_wiphy(sdata->local->hw.wiphy);
  
        if (len < 24 + 6)
                return;
@@@ -3785,7 -3820,7 +3785,7 @@@ static void ieee80211_rx_mgmt_deauth(st
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        u16 reason_code = le16_to_cpu(mgmt->u.deauth.reason_code);
  
 -      sdata_assert_lock(sdata);
 +      lockdep_assert_wiphy(sdata->local->hw.wiphy);
  
        if (len < 24 + 2)
                return;
@@@ -3829,7 -3864,7 +3829,7 @@@ static void ieee80211_rx_mgmt_disassoc(
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        u16 reason_code;
  
 -      sdata_assert_lock(sdata);
 +      lockdep_assert_wiphy(sdata->local->hw.wiphy);
  
        if (len < 24 + 2)
                return;
@@@ -3859,7 -3894,8 +3859,7 @@@ static void ieee80211_get_rates(struct 
                                u8 *supp_rates, unsigned int supp_rates_len,
                                u32 *rates, u32 *basic_rates,
                                bool *have_higher_than_11mbit,
 -                              int *min_rate, int *min_rate_index,
 -                              int shift)
 +                              int *min_rate, int *min_rate_index)
  {
        int i, j;
  
                int rate = supp_rates[i] & 0x7f;
                bool is_basic = !!(supp_rates[i] & 0x80);
  
 -              if ((rate * 5 * (1 << shift)) > 110)
 +              if ((rate * 5) > 110)
                        *have_higher_than_11mbit = true;
  
                /*
  
                        br = &sband->bitrates[j];
  
 -                      brate = DIV_ROUND_UP(br->bitrate, (1 << shift) * 5);
 +                      brate = DIV_ROUND_UP(br->bitrate, 5);
                        if (brate == rate) {
                                *rates |= BIT(j);
                                if (is_basic)
@@@ -4166,10 -4202,33 +4166,33 @@@ static bool ieee80211_assoc_config_link
                                                  elems->ht_cap_elem,
                                                  link_sta);
  
-       if (elems->vht_cap_elem && !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_VHT))
+       if (elems->vht_cap_elem &&
+           !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_VHT)) {
+               const struct ieee80211_vht_cap *bss_vht_cap = NULL;
+               const struct cfg80211_bss_ies *ies;
+               /*
+                * Cisco AP module 9115 with FW 17.3 has a bug and sends a
+                * too large maximum MPDU length in the association response
+                * (indicating 12k) that it cannot actually process ...
+                * Work around that.
+                */
+               rcu_read_lock();
+               ies = rcu_dereference(cbss->ies);
+               if (ies) {
+                       const struct element *elem;
+                       elem = cfg80211_find_elem(WLAN_EID_VHT_CAPABILITY,
+                                                 ies->data, ies->len);
+                       if (elem && elem->datalen >= sizeof(*bss_vht_cap))
+                               bss_vht_cap = (const void *)elem->data;
+               }
                ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband,
                                                    elems->vht_cap_elem,
-                                                   link_sta);
+                                                   bss_vht_cap, link_sta);
+               rcu_read_unlock();
+       }
  
        if (elems->he_operation && !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HE) &&
            elems->he_cap) {
@@@ -4335,6 -4394,8 +4358,6 @@@ static int ieee80211_mgd_setup_link_sta
        u32 rates = 0, basic_rates = 0;
        bool have_higher_than_11mbit = false;
        int min_rate = INT_MAX, min_rate_index = -1;
 -      /* this is clearly wrong for MLO but we'll just remove it later */
 -      int shift = ieee80211_vif_get_shift(&sdata->vif);
        struct ieee80211_supported_band *sband;
  
        memcpy(link_sta->addr, cbss->bssid, ETH_ALEN);
  
        ieee80211_get_rates(sband, bss->supp_rates, bss->supp_rates_len,
                            &rates, &basic_rates, &have_higher_than_11mbit,
 -                          &min_rate, &min_rate_index, shift);
 +                          &min_rate, &min_rate_index);
  
        /*
         * This used to be a workaround for basic rates missing
@@@ -4756,7 -4817,6 +4779,7 @@@ ieee80211_verify_sta_eht_mcs_support(st
  static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
                                  struct ieee80211_link_data *link,
                                  struct cfg80211_bss *cbss,
 +                                bool mlo,
                                  ieee80211_conn_flags_t *conn_flags)
  {
        struct ieee80211_local *local = sdata->local;
        struct cfg80211_chan_def chandef;
        bool is_6ghz = cbss->channel->band == NL80211_BAND_6GHZ;
        bool is_5ghz = cbss->channel->band == NL80211_BAND_5GHZ;
 +      bool supports_mlo = false;
        struct ieee80211_bss *bss = (void *)cbss->priv;
        struct ieee80211_elems_parse_params parse_params = {
                .link_id = -1,
        u32 i;
        bool have_80mhz;
  
 +      lockdep_assert_wiphy(local->hw.wiphy);
 +
        rcu_read_lock();
  
        ies = rcu_dereference(cbss->ies);
                    ieee80211_mle_type_ok(eht_ml_elem->data + 1,
                                          IEEE80211_ML_CONTROL_TYPE_BASIC,
                                          eht_ml_elem->datalen - 1)) {
 +                      supports_mlo = true;
 +
                        sdata->vif.cfg.eml_cap =
                                ieee80211_mle_get_eml_cap(eht_ml_elem->data + 1);
                        sdata->vif.cfg.eml_med_sync_delay =
                return -EINVAL;
        }
  
 +      if (mlo && !supports_mlo) {
 +              sdata_info(sdata, "Rejecting MLO as it is not supported by AP\n");
 +              return -EINVAL;
 +      }
 +
        if (!link)
                return 0;
  
        /* will change later if needed */
        link->smps_mode = IEEE80211_SMPS_OFF;
  
 -      mutex_lock(&local->mtx);
        /*
         * If this fails (possibly due to channel context sharing
         * on incompatible channels, e.g. 80+80 and 160 sharing the
                                                 IEEE80211_CHANCTX_SHARED);
        }
   out:
 -      mutex_unlock(&local->mtx);
        return ret;
  }
  
@@@ -5063,7 -5115,7 +5086,7 @@@ static bool ieee80211_assoc_success(str
        u16 valid_links = 0, dormant_links = 0;
        int err;
  
 -      mutex_lock(&sdata->local->sta_mtx);
 +      lockdep_assert_wiphy(sdata->local->hw.wiphy);
        /*
         * station info was already allocated and inserted before
         * the association and should be available to us
                                continue;
  
                        valid_links |= BIT(link_id);
-                       if (assoc_data->link[link_id].disabled) {
+                       if (assoc_data->link[link_id].disabled)
                                dormant_links |= BIT(link_id);
-                       } else if (link_id != assoc_data->assoc_link_id) {
+                       if (link_id != assoc_data->assoc_link_id) {
                                err = ieee80211_sta_allocate_link(sta, link_id);
                                if (err)
                                        goto out_err;
                struct ieee80211_link_data *link;
                struct link_sta_info *link_sta;
  
-               if (!cbss || assoc_data->link[link_id].disabled)
+               if (!cbss)
                        continue;
  
                link = sdata_dereference(sdata->link[link_id], sdata);
                                        " (assoc)" : "");
  
                link_sta = rcu_dereference_protected(sta->link[link_id],
 -                                                   lockdep_is_held(&local->sta_mtx));
 +                                                   lockdep_is_held(&local->hw.wiphy->mtx));
                if (WARN_ON(!link_sta))
                        goto out_err;
  
                link->conf->dtim_period = link->u.mgd.dtim_period ?: 1;
  
                if (link_id != assoc_data->assoc_link_id) {
 -                      err = ieee80211_prep_channel(sdata, link, cbss,
 +                      err = ieee80211_prep_channel(sdata, link, cbss, true,
                                                     &link->u.mgd.conn_flags);
                        if (err) {
                                link_info(link, "prep_channel failed\n");
        if (sdata->wdev.use_4addr)
                drv_sta_set_4addr(local, sdata, &sta->sta, true);
  
 -      mutex_unlock(&sdata->local->sta_mtx);
 -
        ieee80211_set_associated(sdata, assoc_data, changed);
  
        /*
        return true;
  out_err:
        eth_zero_addr(sdata->vif.cfg.ap_addr);
 -      mutex_unlock(&sdata->local->sta_mtx);
        return false;
  }
  
@@@ -5248,7 -5304,7 +5272,7 @@@ static void ieee80211_rx_mgmt_assoc_res
        u8 ap_mld_addr[ETH_ALEN] __aligned(2);
        unsigned int link_id;
  
 -      sdata_assert_lock(sdata);
 +      lockdep_assert_wiphy(sdata->local->hw.wiphy);
  
        if (!assoc_data)
                return;
        for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) {
                struct ieee80211_link_data *link;
  
-               link = sdata_dereference(sdata->link[link_id], sdata);
-               if (!link)
-                       continue;
                if (!assoc_data->link[link_id].bss)
                        continue;
  
                resp.links[link_id].bss = assoc_data->link[link_id].bss;
-               resp.links[link_id].addr = link->conf->addr;
+               ether_addr_copy(resp.links[link_id].addr,
+                               assoc_data->link[link_id].addr);
                resp.links[link_id].status = assoc_data->link[link_id].status;
  
+               link = sdata_dereference(sdata->link[link_id], sdata);
+               if (!link)
+                       continue;
                /* get uapsd queues configuration - same for all links */
                resp.uapsd_queues = 0;
                for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
@@@ -5448,7 -5505,7 +5473,7 @@@ static void ieee80211_rx_bss_info(struc
        struct ieee80211_bss *bss;
        struct ieee80211_channel *channel;
  
 -      sdata_assert_lock(sdata);
 +      lockdep_assert_wiphy(sdata->local->hw.wiphy);
  
        channel = ieee80211_get_channel_khz(local->hw.wiphy,
                                        ieee80211_rx_status_to_khz(rx_status));
@@@ -5475,7 -5532,7 +5500,7 @@@ static void ieee80211_rx_mgmt_probe_res
  
        ifmgd = &sdata->u.mgd;
  
 -      sdata_assert_lock(sdata);
 +      lockdep_assert_wiphy(sdata->local->hw.wiphy);
  
        /*
         * According to Draft P802.11ax D6.0 clause 26.17.2.3.2:
@@@ -5686,16 -5743,21 +5711,16 @@@ static void ieee80211_ml_reconf_work(st
        u16 new_valid_links, new_active_links, new_dormant_links;
        int ret;
  
 -      sdata_lock(sdata);
 -      if (!sdata->u.mgd.removed_links) {
 -              sdata_unlock(sdata);
 +      if (!sdata->u.mgd.removed_links)
                return;
 -      }
  
        sdata_info(sdata,
                   "MLO Reconfiguration: work: valid=0x%x, removed=0x%x\n",
                   sdata->vif.valid_links, sdata->u.mgd.removed_links);
  
        new_valid_links = sdata->vif.valid_links & ~sdata->u.mgd.removed_links;
 -      if (new_valid_links == sdata->vif.valid_links) {
 -              sdata_unlock(sdata);
 +      if (new_valid_links == sdata->vif.valid_links)
                return;
 -      }
  
        if (!new_valid_links ||
            !(new_valid_links & ~sdata->vif.dormant_links)) {
                                BIT(ffs(new_valid_links &
                                        ~sdata->vif.dormant_links) - 1);
  
 -              ret = __ieee80211_set_active_links(&sdata->vif,
 -                                                 new_active_links);
 +              ret = ieee80211_set_active_links(&sdata->vif, new_active_links);
                if (ret) {
                        sdata_info(sdata,
                                   "Failed setting active links\n");
        if (ret)
                sdata_info(sdata, "Failed setting valid links\n");
  
 +      ieee80211_vif_cfg_change_notify(sdata, BSS_CHANGED_MLD_VALID_LINKS);
 +
  out:
        if (!ret)
                cfg80211_links_removed(sdata->dev, sdata->u.mgd.removed_links);
        else
 -              ___ieee80211_disconnect(sdata);
 +              __ieee80211_disconnect(sdata);
  
        sdata->u.mgd.removed_links = 0;
 -
 -      sdata_unlock(sdata);
  }
  
  static void ieee80211_ml_reconfiguration(struct ieee80211_sub_if_data *sdata,
                                 TU_TO_JIFFIES(delay));
  }
  
 +static void ieee80211_tid_to_link_map_work(struct wiphy *wiphy,
 +                                         struct wiphy_work *work)
 +{
 +      u16 new_active_links, new_dormant_links;
 +      struct ieee80211_sub_if_data *sdata =
 +              container_of(work, struct ieee80211_sub_if_data,
 +                           u.mgd.ttlm_work.work);
 +      int ret;
 +
 +      new_active_links = sdata->u.mgd.ttlm_info.map &
 +                         sdata->vif.valid_links;
 +      new_dormant_links = ~sdata->u.mgd.ttlm_info.map &
 +                          sdata->vif.valid_links;
 +      if (!new_active_links) {
 +              ieee80211_disconnect(&sdata->vif, false);
 +              return;
 +      }
 +
 +      ieee80211_vif_set_links(sdata, sdata->vif.valid_links, 0);
 +      new_active_links = BIT(ffs(new_active_links) - 1);
 +      ieee80211_set_active_links(&sdata->vif, new_active_links);
 +
 +      ret = ieee80211_vif_set_links(sdata, sdata->vif.valid_links,
 +                                    new_dormant_links);
 +
 +      sdata->u.mgd.ttlm_info.active = true;
 +      sdata->u.mgd.ttlm_info.switch_time = 0;
 +
 +      if (!ret)
 +              ieee80211_vif_cfg_change_notify(sdata,
 +                                              BSS_CHANGED_MLD_VALID_LINKS);
 +}
 +
 +static u16 ieee80211_get_ttlm(u8 bm_size, u8 *data)
 +{
 +      if (bm_size == 1)
 +              return *data;
 +      else
 +              return get_unaligned_le16(data);
 +}
 +
 +static int
 +ieee80211_parse_adv_t2l(struct ieee80211_sub_if_data *sdata,
 +                      const struct ieee80211_ttlm_elem *ttlm,
 +                      struct ieee80211_adv_ttlm_info *ttlm_info)
 +{
 +      /* The element size was already validated in
 +       * ieee80211_tid_to_link_map_size_ok()
 +       */
 +      u8 control, link_map_presence, map_size, tid;
 +      u8 *pos;
 +
 +      memset(ttlm_info, 0, sizeof(*ttlm_info));
 +      pos = (void *)ttlm->optional;
 +      control = ttlm->control;
 +
 +      if ((control & IEEE80211_TTLM_CONTROL_DEF_LINK_MAP) ||
 +          !(control & IEEE80211_TTLM_CONTROL_SWITCH_TIME_PRESENT))
 +              return 0;
 +
 +      if ((control & IEEE80211_TTLM_CONTROL_DIRECTION) !=
 +          IEEE80211_TTLM_DIRECTION_BOTH) {
 +              sdata_info(sdata, "Invalid advertised T2L map direction\n");
 +              return -EINVAL;
 +      }
 +
 +      link_map_presence = *pos;
 +      pos++;
 +
 +      ttlm_info->switch_time = get_unaligned_le16(pos);
 +      pos += 2;
 +
 +      if (control & IEEE80211_TTLM_CONTROL_EXPECTED_DUR_PRESENT) {
 +              ttlm_info->duration = pos[0] | pos[1] << 8 | pos[2] << 16;
 +              pos += 3;
 +      }
 +
 +      if (control & IEEE80211_TTLM_CONTROL_LINK_MAP_SIZE)
 +              map_size = 1;
 +      else
 +              map_size = 2;
 +
 +      /* According to Draft P802.11be_D3.0 clause 35.3.7.1.7, an AP MLD shall
 +       * not advertise a TID-to-link mapping that does not map all TIDs to the
 +       * same link set, reject frame if not all links have mapping
 +       */
 +      if (link_map_presence != 0xff) {
 +              sdata_info(sdata,
 +                         "Invalid advertised T2L mapping presence indicator\n");
 +              return -EINVAL;
 +      }
 +
 +      ttlm_info->map = ieee80211_get_ttlm(map_size, pos);
 +      if (!ttlm_info->map) {
 +              sdata_info(sdata,
 +                         "Invalid advertised T2L map for TID 0\n");
 +              return -EINVAL;
 +      }
 +
 +      pos += map_size;
 +
 +      for (tid = 1; tid < 8; tid++) {
 +              u16 map = ieee80211_get_ttlm(map_size, pos);
 +
 +              if (map != ttlm_info->map) {
 +                      sdata_info(sdata, "Invalid advertised T2L map for tid %d\n",
 +                                 tid);
 +                      return -EINVAL;
 +              }
 +
 +              pos += map_size;
 +      }
 +      return 0;
 +}
 +
 +static void ieee80211_process_adv_ttlm(struct ieee80211_sub_if_data *sdata,
 +                                        struct ieee802_11_elems *elems,
 +                                        u64 beacon_ts)
 +{
 +      u8 i;
 +      int ret;
 +
 +      if (!ieee80211_vif_is_mld(&sdata->vif))
 +              return;
 +
 +      if (!elems->ttlm_num) {
 +              if (sdata->u.mgd.ttlm_info.switch_time) {
 +                      /* if a planned TID-to-link mapping was cancelled -
 +                       * abort it
 +                       */
 +                      wiphy_delayed_work_cancel(sdata->local->hw.wiphy,
 +                                                &sdata->u.mgd.ttlm_work);
 +              } else if (sdata->u.mgd.ttlm_info.active) {
 +                      /* if no TID-to-link element, set to default mapping in
 +                       * which all TIDs are mapped to all setup links
 +                       */
 +                      ret = ieee80211_vif_set_links(sdata,
 +                                                    sdata->vif.valid_links,
 +                                                    0);
 +                      if (ret) {
 +                              sdata_info(sdata, "Failed setting valid/dormant links\n");
 +                              return;
 +                      }
 +                      ieee80211_vif_cfg_change_notify(sdata,
 +                                                      BSS_CHANGED_MLD_VALID_LINKS);
 +              }
 +              memset(&sdata->u.mgd.ttlm_info, 0,
 +                     sizeof(sdata->u.mgd.ttlm_info));
 +              return;
 +      }
 +
 +      for (i = 0; i < elems->ttlm_num; i++) {
 +              struct ieee80211_adv_ttlm_info ttlm_info;
 +              u32 res;
 +
 +              res = ieee80211_parse_adv_t2l(sdata, elems->ttlm[i],
 +                                            &ttlm_info);
 +
 +              if (res) {
 +                      __ieee80211_disconnect(sdata);
 +                      return;
 +              }
 +
 +              if (ttlm_info.switch_time) {
 +                      u32 st_us, delay = 0;
 +                      u32 ts_l26 = beacon_ts & GENMASK(25, 0);
 +
 +                      /* The t2l map switch time is indicated with a partial
 +                       * TSF value, convert it to TSF and calc the delay
 +                       * to the start time.
 +                       */
 +                      st_us = ieee80211_tu_to_usec(ttlm_info.switch_time);
 +                      if (st_us > ts_l26)
 +                              delay = st_us - ts_l26;
 +                      else
 +                              continue;
 +
 +                      sdata->u.mgd.ttlm_info = ttlm_info;
 +                      wiphy_delayed_work_cancel(sdata->local->hw.wiphy,
 +                                                &sdata->u.mgd.ttlm_work);
 +                      wiphy_delayed_work_queue(sdata->local->hw.wiphy,
 +                                               &sdata->u.mgd.ttlm_work,
 +                                               usecs_to_jiffies(delay));
 +                      return;
 +              }
 +      }
 +}
 +
  static void ieee80211_rx_mgmt_beacon(struct ieee80211_link_data *link,
                                     struct ieee80211_hdr *hdr, size_t len,
                                     struct ieee80211_rx_status *rx_status)
                .from_ap = true,
        };
  
 -      sdata_assert_lock(sdata);
 +      lockdep_assert_wiphy(local->hw.wiphy);
  
        /* Process beacon from the current BSS */
        bssid = ieee80211_get_bssid(hdr, len, sdata->vif.type);
                changed |= BSS_CHANGED_BEACON_INFO;
                link->u.mgd.have_beacon = true;
  
 -              mutex_lock(&local->iflist_mtx);
                ieee80211_recalc_ps(local);
 -              mutex_unlock(&local->iflist_mtx);
  
                ieee80211_recalc_ps_vif(sdata);
        }
                                le16_to_cpu(mgmt->u.beacon.capab_info),
                                erp_valid, erp_value);
  
 -      mutex_lock(&local->sta_mtx);
        sta = sta_info_get(sdata, sdata->vif.cfg.ap_addr);
        if (WARN_ON(!sta)) {
 -              mutex_unlock(&local->sta_mtx);
                goto free;
        }
        link_sta = rcu_dereference_protected(sta->link[link->link_id],
 -                                           lockdep_is_held(&local->sta_mtx));
 +                                           lockdep_is_held(&local->hw.wiphy->mtx));
        if (WARN_ON(!link_sta)) {
 -              mutex_unlock(&local->sta_mtx);
                goto free;
        }
  
                                elems->vht_operation, elems->he_operation,
                                elems->eht_operation,
                                elems->s1g_oper, bssid, &changed)) {
 -              mutex_unlock(&local->sta_mtx);
                sdata_info(sdata,
                           "failed to follow AP %pM bandwidth change, disconnect\n",
                           bssid);
                ieee80211_vht_handle_opmode(sdata, link_sta,
                                            *elems->opmode_notif,
                                            rx_status->band);
 -      mutex_unlock(&local->sta_mtx);
  
        changed |= ieee80211_handle_pwr_constr(link, chan, mgmt,
                                               elems->country_elem,
        }
  
        ieee80211_ml_reconfiguration(sdata, elems);
 +      ieee80211_process_adv_ttlm(sdata, elems,
 +                                    le64_to_cpu(mgmt->u.beacon.timestamp));
  
        ieee80211_link_info_change_notify(sdata, link, changed);
  free:
@@@ -6361,17 -6241,17 +6386,17 @@@ void ieee80211_sta_rx_queued_ext(struc
        struct ieee80211_hdr *hdr;
        u16 fc;
  
 +      lockdep_assert_wiphy(sdata->local->hw.wiphy);
 +
        rx_status = (struct ieee80211_rx_status *) skb->cb;
        hdr = (struct ieee80211_hdr *) skb->data;
        fc = le16_to_cpu(hdr->frame_control);
  
 -      sdata_lock(sdata);
        switch (fc & IEEE80211_FCTL_STYPE) {
        case IEEE80211_STYPE_S1G_BEACON:
                ieee80211_rx_mgmt_beacon(link, hdr, skb->len, rx_status);
                break;
        }
 -      sdata_unlock(sdata);
  }
  
  void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
        u16 fc;
        int ies_len;
  
 +      lockdep_assert_wiphy(sdata->local->hw.wiphy);
 +
        rx_status = (struct ieee80211_rx_status *) skb->cb;
        mgmt = (struct ieee80211_mgmt *) skb->data;
        fc = le16_to_cpu(mgmt->frame_control);
  
 -      sdata_lock(sdata);
 -
        if (rx_status->link_valid) {
                link = sdata_dereference(sdata->link[rx_status->link_id],
                                         sdata);
                if (!link)
 -                      goto out;
 +                      return;
        }
  
        switch (fc & IEEE80211_FCTL_STYPE) {
                }
                break;
        }
 -out:
 -      sdata_unlock(sdata);
  }
  
  static void ieee80211_sta_timer(struct timer_list *t)
@@@ -6510,7 -6392,7 +6535,7 @@@ static int ieee80211_auth(struct ieee80
                .subtype = IEEE80211_STYPE_AUTH,
        };
  
 -      sdata_assert_lock(sdata);
 +      lockdep_assert_wiphy(sdata->local->hw.wiphy);
  
        if (WARN_ON_ONCE(!auth_data))
                return -EINVAL;
@@@ -6579,7 -6461,7 +6604,7 @@@ static int ieee80211_do_assoc(struct ie
        struct ieee80211_local *local = sdata->local;
        int ret;
  
 -      sdata_assert_lock(sdata);
 +      lockdep_assert_wiphy(sdata->local->hw.wiphy);
  
        assoc_data->tries++;
        if (assoc_data->tries > IEEE80211_ASSOC_MAX_TRIES) {
@@@ -6635,7 -6517,7 +6660,7 @@@ void ieee80211_sta_work(struct ieee8021
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
  
 -      sdata_lock(sdata);
 +      lockdep_assert_wiphy(sdata->local->hw.wiphy);
  
        if (ifmgd->status_received) {
                __le16 fc = ifmgd->status_fc;
                                WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, false);
                }
        }
 -
 -      sdata_unlock(sdata);
  }
  
  static void ieee80211_sta_bcn_mon_timer(struct timer_list *t)
@@@ -6825,11 -6709,10 +6850,11 @@@ static void ieee80211_sta_conn_mon_time
                return;
        }
  
 -      ieee80211_queue_work(&local->hw, &ifmgd->monitor_work);
 +      wiphy_work_queue(local->hw.wiphy, &sdata->u.mgd.monitor_work);
  }
  
 -static void ieee80211_sta_monitor_work(struct work_struct *work)
 +static void ieee80211_sta_monitor_work(struct wiphy *wiphy,
 +                                     struct wiphy_work *work)
  {
        struct ieee80211_sub_if_data *sdata =
                container_of(work, struct ieee80211_sub_if_data,
@@@ -6845,8 -6728,8 +6870,8 @@@ static void ieee80211_restart_sta_timer
  
                /* let's probe the connection once */
                if (!ieee80211_hw_check(&sdata->local->hw, CONNECTION_MONITOR))
 -                      ieee80211_queue_work(&sdata->local->hw,
 -                                           &sdata->u.mgd.monitor_work);
 +                      wiphy_work_queue(sdata->local->hw.wiphy,
 +                                       &sdata->u.mgd.monitor_work);
        }
  }
  
@@@ -6856,7 -6739,7 +6881,7 @@@ void ieee80211_mgd_quiesce(struct ieee8
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
  
 -      sdata_lock(sdata);
 +      lockdep_assert_wiphy(sdata->local->hw.wiphy);
  
        if (ifmgd->auth_data || ifmgd->assoc_data) {
                const u8 *ap_addr = ifmgd->auth_data ?
                memcpy(bssid, sdata->vif.cfg.ap_addr, ETH_ALEN);
                ieee80211_mgd_deauth(sdata, &req);
        }
 -
 -      sdata_unlock(sdata);
  }
  #endif
  
@@@ -6915,10 -6800,11 +6940,10 @@@ void ieee80211_sta_restart(struct ieee8
  {
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
  
 -      sdata_lock(sdata);
 -      if (!ifmgd->associated) {
 -              sdata_unlock(sdata);
 +      lockdep_assert_wiphy(sdata->local->hw.wiphy);
 +
 +      if (!ifmgd->associated)
                return;
 -      }
  
        if (sdata->flags & IEEE80211_SDATA_DISCONNECT_RESUME) {
                sdata->flags &= ~IEEE80211_SDATA_DISCONNECT_RESUME;
                ieee80211_sta_connection_lost(sdata,
                                              WLAN_REASON_UNSPECIFIED,
                                              true);
 -              sdata_unlock(sdata);
                return;
        }
  
                ieee80211_sta_connection_lost(sdata,
                                              WLAN_REASON_UNSPECIFIED,
                                              true);
 -              sdata_unlock(sdata);
                return;
        }
 -
 -      sdata_unlock(sdata);
  }
  
  static void ieee80211_request_smps_mgd_work(struct wiphy *wiphy,
                container_of(work, struct ieee80211_link_data,
                             u.mgd.request_smps_work);
  
 -      sdata_lock(link->sdata);
        __ieee80211_request_smps_mgd(link->sdata, link,
                                     link->u.mgd.driver_smps_mode);
 -      sdata_unlock(link->sdata);
  }
  
  /* interface setup */
@@@ -6955,22 -6847,20 +6980,22 @@@ void ieee80211_sta_setup_sdata(struct i
  {
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
  
 -      INIT_WORK(&ifmgd->monitor_work, ieee80211_sta_monitor_work);
 +      wiphy_work_init(&ifmgd->monitor_work, ieee80211_sta_monitor_work);
        wiphy_work_init(&ifmgd->beacon_connection_loss_work,
                        ieee80211_beacon_connection_loss_work);
        wiphy_work_init(&ifmgd->csa_connection_drop_work,
                        ieee80211_csa_connection_drop_work);
 -      INIT_DELAYED_WORK(&ifmgd->tdls_peer_del_work,
 -                        ieee80211_tdls_peer_del_work);
 +      wiphy_delayed_work_init(&ifmgd->tdls_peer_del_work,
 +                              ieee80211_tdls_peer_del_work);
        wiphy_delayed_work_init(&ifmgd->ml_reconf_work,
                                ieee80211_ml_reconf_work);
        timer_setup(&ifmgd->timer, ieee80211_sta_timer, 0);
        timer_setup(&ifmgd->bcn_mon_timer, ieee80211_sta_bcn_mon_timer, 0);
        timer_setup(&ifmgd->conn_mon_timer, ieee80211_sta_conn_mon_timer, 0);
 -      INIT_DELAYED_WORK(&ifmgd->tx_tspec_wk,
 -                        ieee80211_sta_handle_tspec_ac_params_wk);
 +      wiphy_delayed_work_init(&ifmgd->tx_tspec_wk,
 +                              ieee80211_sta_handle_tspec_ac_params_wk);
 +      wiphy_delayed_work_init(&ifmgd->ttlm_work,
 +                              ieee80211_tid_to_link_map_work);
  
        ifmgd->flags = 0;
        ifmgd->powersave = sdata->wdev.ps;
        ifmgd->orig_teardown_skb = NULL;
  }
  
 +static void ieee80211_recalc_smps_work(struct wiphy *wiphy,
 +                                     struct wiphy_work *work)
 +{
 +      struct ieee80211_link_data *link =
 +              container_of(work, struct ieee80211_link_data,
 +                           u.mgd.recalc_smps);
 +
 +      ieee80211_recalc_smps(link->sdata, link);
 +}
 +
  void ieee80211_mgd_setup_link(struct ieee80211_link_data *link)
  {
        struct ieee80211_sub_if_data *sdata = link->sdata;
  
        wiphy_work_init(&link->u.mgd.request_smps_work,
                        ieee80211_request_smps_mgd_work);
 +      wiphy_work_init(&link->u.mgd.recalc_smps,
 +                      ieee80211_recalc_smps_work);
        if (local->hw.wiphy->features & NL80211_FEATURE_DYNAMIC_SMPS)
                link->u.mgd.req_smps = IEEE80211_SMPS_AUTOMATIC;
        else
@@@ -7169,7 -7047,7 +7194,7 @@@ static int ieee80211_prep_connection(st
        }
  
        if (new_sta || override) {
 -              err = ieee80211_prep_channel(sdata, link, cbss,
 +              err = ieee80211_prep_channel(sdata, link, cbss, mlo,
                                             &link->u.mgd.conn_flags);
                if (err) {
                        if (new_sta)
@@@ -7221,14 -7099,10 +7246,14 @@@ int ieee80211_mgd_auth(struct ieee80211
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        struct ieee80211_mgd_auth_data *auth_data;
 +      struct ieee80211_link_data *link;
 +      const struct element *csa_elem, *ecsa_elem;
        u16 auth_alg;
        int err;
        bool cont_auth;
  
 +      lockdep_assert_wiphy(sdata->local->hw.wiphy);
 +
        /* prepare auth data structure */
  
        switch (req->auth_type) {
        if (ifmgd->assoc_data)
                return -EBUSY;
  
 +      rcu_read_lock();
 +      csa_elem = ieee80211_bss_get_elem(req->bss, WLAN_EID_CHANNEL_SWITCH);
 +      ecsa_elem = ieee80211_bss_get_elem(req->bss,
 +                                         WLAN_EID_EXT_CHANSWITCH_ANN);
 +      if ((csa_elem &&
 +           csa_elem->datalen == sizeof(struct ieee80211_channel_sw_ie) &&
 +           ((struct ieee80211_channel_sw_ie *)csa_elem->data)->count != 0) ||
 +          (ecsa_elem &&
 +           ecsa_elem->datalen == sizeof(struct ieee80211_ext_chansw_ie) &&
 +           ((struct ieee80211_ext_chansw_ie *)ecsa_elem->data)->count != 0)) {
 +              rcu_read_unlock();
 +              sdata_info(sdata, "AP is in CSA process, reject auth\n");
 +              return -EINVAL;
 +      }
 +      rcu_read_unlock();
 +
        auth_data = kzalloc(sizeof(*auth_data) + req->auth_data_len +
                            req->ie_len, GFP_KERNEL);
        if (!auth_data)
                                            false);
        }
  
 -      sdata_info(sdata, "authenticate with %pM\n", auth_data->ap_addr);
 -
        /* needed for transmitting the auth frame(s) properly */
        memcpy(sdata->vif.cfg.ap_addr, auth_data->ap_addr, ETH_ALEN);
  
        if (err)
                goto err_clear;
  
 +      if (req->link_id > 0)
 +              link = sdata_dereference(sdata->link[req->link_id], sdata);
 +      else
 +              link = sdata_dereference(sdata->link[0], sdata);
 +
 +      if (WARN_ON(!link)) {
 +              err = -ENOLINK;
 +              goto err_clear;
 +      }
 +
 +      sdata_info(sdata, "authenticate with %pM (local address=%pM)\n",
 +                 auth_data->ap_addr, link->conf->addr);
 +
        err = ieee80211_auth(sdata);
        if (err) {
                sta_info_destroy_addr(sdata, auth_data->ap_addr);
                eth_zero_addr(sdata->deflink.u.mgd.bssid);
                ieee80211_link_info_change_notify(sdata, &sdata->deflink,
                                                  BSS_CHANGED_BSSID);
 -              mutex_lock(&sdata->local->mtx);
                ieee80211_link_release_channel(&sdata->deflink);
 -              mutex_unlock(&sdata->local->mtx);
        }
        ifmgd->auth_data = NULL;
        kfree(auth_data);
@@@ -7415,7 -7264,7 +7440,7 @@@ ieee80211_setup_assoc_link(struct ieee8
                           unsigned int link_id)
  {
        struct ieee80211_local *local = sdata->local;
 -      const struct cfg80211_bss_ies *beacon_ies;
 +      const struct cfg80211_bss_ies *bss_ies;
        struct ieee80211_supported_band *sband;
        const struct element *ht_elem, *vht_elem;
        struct ieee80211_link_data *link;
        link->conf->eht_puncturing = 0;
  
        rcu_read_lock();
 -      beacon_ies = rcu_dereference(cbss->beacon_ies);
 -      if (beacon_ies) {
 -              const struct ieee80211_eht_operation *eht_oper;
 -              const struct element *elem;
 +      bss_ies = rcu_dereference(cbss->beacon_ies);
 +      if (bss_ies) {
                u8 dtim_count = 0;
  
 -              ieee80211_get_dtim(beacon_ies, &dtim_count,
 +              ieee80211_get_dtim(bss_ies, &dtim_count,
                                   &link->u.mgd.dtim_period);
  
                sdata->deflink.u.mgd.have_beacon = true;
  
                if (ieee80211_hw_check(&local->hw, TIMING_BEACON_ONLY)) {
 -                      link->conf->sync_tsf = beacon_ies->tsf;
 +                      link->conf->sync_tsf = bss_ies->tsf;
                        link->conf->sync_device_ts = bss->device_ts_beacon;
                        link->conf->sync_dtim_count = dtim_count;
                }
 +      } else {
 +              bss_ies = rcu_dereference(cbss->ies);
 +      }
 +
 +      if (bss_ies) {
 +              const struct ieee80211_eht_operation *eht_oper;
 +              const struct element *elem;
  
                elem = cfg80211_find_ext_elem(WLAN_EID_EXT_MULTIPLE_BSSID_CONFIGURATION,
 -                                            beacon_ies->data, beacon_ies->len);
 +                                            bss_ies->data, bss_ies->len);
                if (elem && elem->datalen >= 3)
                        link->conf->profile_periodicity = elem->data[2];
                else
                        link->conf->profile_periodicity = 0;
  
                elem = cfg80211_find_elem(WLAN_EID_EXT_CAPABILITY,
 -                                        beacon_ies->data, beacon_ies->len);
 +                                        bss_ies->data, bss_ies->len);
                if (elem && elem->datalen >= 11 &&
                    (elem->data[10] & WLAN_EXT_CAPA11_EMA_SUPPORT))
                        link->conf->ema_ap = true;
                        link->conf->ema_ap = false;
  
                elem = cfg80211_find_ext_elem(WLAN_EID_EXT_EHT_OPERATION,
 -                                            beacon_ies->data, beacon_ies->len);
 +                                            bss_ies->data, bss_ies->len);
                eht_oper = (const void *)(elem->data + 1);
  
                if (elem &&
@@@ -7588,7 -7432,7 +7613,7 @@@ int ieee80211_mgd_assoc(struct ieee8021
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        struct ieee80211_mgd_assoc_data *assoc_data;
 -      const struct element *ssid_elem;
 +      const struct element *ssid_elem, *csa_elem, *ecsa_elem;
        struct ieee80211_vif_cfg *vif_cfg = &sdata->vif.cfg;
        ieee80211_conn_flags_t conn_flags = 0;
        struct ieee80211_link_data *link;
                kfree(assoc_data);
                return -EINVAL;
        }
 +
 +      csa_elem = ieee80211_bss_get_elem(cbss, WLAN_EID_CHANNEL_SWITCH);
 +      ecsa_elem = ieee80211_bss_get_elem(cbss, WLAN_EID_EXT_CHANSWITCH_ANN);
 +      if ((csa_elem &&
 +           csa_elem->datalen == sizeof(struct ieee80211_channel_sw_ie) &&
 +           ((struct ieee80211_channel_sw_ie *)csa_elem->data)->count != 0) ||
 +          (ecsa_elem &&
 +           ecsa_elem->datalen == sizeof(struct ieee80211_ext_chansw_ie) &&
 +           ((struct ieee80211_ext_chansw_ie *)ecsa_elem->data)->count != 0)) {
 +              sdata_info(sdata, "AP is in CSA process, reject assoc\n");
 +              rcu_read_unlock();
 +              kfree(assoc_data);
 +              return -EINVAL;
 +      }
 +
        memcpy(assoc_data->ssid, ssid_elem->data, ssid_elem->datalen);
        assoc_data->ssid_len = ssid_elem->datalen;
        memcpy(vif_cfg->ssid, assoc_data->ssid, assoc_data->ssid_len);
                if (i == assoc_data->assoc_link_id)
                        continue;
                /* only calculate the flags, hence link == NULL */
 -              err = ieee80211_prep_channel(sdata, NULL, assoc_data->link[i].bss,
 +              err = ieee80211_prep_channel(sdata, NULL,
 +                                           assoc_data->link[i].bss, true,
                                             &assoc_data->link[i].conn_flags);
 -              if (err)
 +              if (err) {
 +                      req->links[i].error = err;
                        goto err_clear;
 +              }
        }
  
        /* needed for transmitting the assoc frames properly */
@@@ -8023,8 -7849,6 +8048,8 @@@ void ieee80211_mgd_stop_link(struct iee
  {
        wiphy_work_cancel(link->sdata->local->hw.wiphy,
                          &link->u.mgd.request_smps_work);
 +      wiphy_work_cancel(link->sdata->local->hw.wiphy,
 +                        &link->u.mgd.recalc_smps);
        wiphy_delayed_work_cancel(link->sdata->local->hw.wiphy,
                                  &link->u.mgd.chswitch_work);
  }
@@@ -8038,18 -7862,16 +8063,18 @@@ void ieee80211_mgd_stop(struct ieee8021
         * they will not do anything but might not have been
         * cancelled when disconnecting.
         */
 -      cancel_work_sync(&ifmgd->monitor_work);
 +      wiphy_work_cancel(sdata->local->hw.wiphy,
 +                        &ifmgd->monitor_work);
        wiphy_work_cancel(sdata->local->hw.wiphy,
                          &ifmgd->beacon_connection_loss_work);
        wiphy_work_cancel(sdata->local->hw.wiphy,
                          &ifmgd->csa_connection_drop_work);
 -      cancel_delayed_work_sync(&ifmgd->tdls_peer_del_work);
 +      wiphy_delayed_work_cancel(sdata->local->hw.wiphy,
 +                                &ifmgd->tdls_peer_del_work);
        wiphy_delayed_work_cancel(sdata->local->hw.wiphy,
                                  &ifmgd->ml_reconf_work);
 +      wiphy_delayed_work_cancel(sdata->local->hw.wiphy, &ifmgd->ttlm_work);
  
 -      sdata_lock(sdata);
        if (ifmgd->assoc_data)
                ieee80211_destroy_assoc_data(sdata, ASSOC_TIMEOUT);
        if (ifmgd->auth_data)
        ifmgd->assoc_req_ies_len = 0;
        spin_unlock_bh(&ifmgd->teardown_lock);
        del_timer_sync(&ifmgd->timer);
 -      sdata_unlock(sdata);
  }
  
  void ieee80211_cqm_rssi_notify(struct ieee80211_vif *vif,
diff --combined net/mac80211/tx.c
index a984fc54644ef555d95a2384394979a577fb54e5,d45d4be63dd877e52ebf61cc5d1c624724b4077b..ed4fdf655343f2ef5c4e655e88df66898eb101ff
@@@ -43,7 -43,7 +43,7 @@@ static __le16 ieee80211_duration(struc
                                 struct sk_buff *skb, int group_addr,
                                 int next_frag_len)
  {
 -      int rate, mrate, erp, dur, i, shift = 0;
 +      int rate, mrate, erp, dur, i;
        struct ieee80211_rate *txrate;
        struct ieee80211_local *local = tx->local;
        struct ieee80211_supported_band *sband;
  
        rcu_read_lock();
        chanctx_conf = rcu_dereference(tx->sdata->vif.bss_conf.chanctx_conf);
 -      if (chanctx_conf) {
 -              shift = ieee80211_chandef_get_shift(&chanctx_conf->def);
 +      if (chanctx_conf)
                rate_flags = ieee80211_chandef_rate_flags(&chanctx_conf->def);
 -      }
        rcu_read_unlock();
  
        /* uh huh? */
                        continue;
  
                if (tx->sdata->vif.bss_conf.basic_rates & BIT(i))
 -                      rate = DIV_ROUND_UP(r->bitrate, 1 << shift);
 +                      rate = r->bitrate;
  
                switch (sband->band) {
                case NL80211_BAND_2GHZ:
        if (rate == -1) {
                /* No matching basic rate found; use highest suitable mandatory
                 * PHY rate */
 -              rate = DIV_ROUND_UP(mrate, 1 << shift);
 +              rate = mrate;
        }
  
        /* Don't calculate ACKs for QoS Frames with NoAck Policy set */
                 * (10 bytes + 4-byte FCS = 112 bits) plus SIFS; rounded up
                 * to closest integer */
                dur = ieee80211_frame_duration(sband->band, 10, rate, erp,
 -                              tx->sdata->vif.bss_conf.use_short_preamble,
 -                              shift);
 +                              tx->sdata->vif.bss_conf.use_short_preamble);
  
        if (next_frag_len) {
                /* Frame is fragmented: duration increases with time needed to
                /* next fragment */
                dur += ieee80211_frame_duration(sband->band, next_frag_len,
                                txrate->bitrate, erp,
 -                              tx->sdata->vif.bss_conf.use_short_preamble,
 -                              shift);
 +                              tx->sdata->vif.bss_conf.use_short_preamble);
        }
  
        return cpu_to_le16(dur);
@@@ -262,8 -266,8 +262,8 @@@ ieee80211_tx_h_dynamic_ps(struct ieee80
                                                IEEE80211_QUEUE_STOP_REASON_PS,
                                                false);
                ifmgd->flags &= ~IEEE80211_STA_NULLFUNC_ACKED;
 -              ieee80211_queue_work(&local->hw,
 -                                   &local->dynamic_ps_disable_work);
 +              wiphy_work_queue(local->hw.wiphy,
 +                               &local->dynamic_ps_disable_work);
        }
  
        /* Don't restart the timer if we're not disassociated */
@@@ -661,7 -665,8 +661,8 @@@ ieee80211_tx_h_select_key(struct ieee80
                }
  
                if (unlikely(tx->key && tx->key->flags & KEY_FLAG_TAINTED &&
-                            !ieee80211_is_deauth(hdr->frame_control)))
+                            !ieee80211_is_deauth(hdr->frame_control)) &&
+                            tx->skb->protocol != tx->sdata->control_port_protocol)
                        return TX_DROP;
  
                if (!skip_hw && tx->key &&
@@@ -2162,11 -2167,6 +2163,11 @@@ bool ieee80211_parse_tx_radiotap(struc
                        rate_found = true;
                        break;
  
 +              case IEEE80211_RADIOTAP_ANTENNA:
 +                      /* this can appear multiple times, keep a bitmap */
 +                      info->control.antennas |= BIT(*iterator.this_arg);
 +                      break;
 +
                case IEEE80211_RADIOTAP_DATA_RETRIES:
                        rate_retries = *iterator.this_arg;
                        break;
                }
  
                if (rate_flags & IEEE80211_TX_RC_MCS) {
 +                      /* reset antennas if not enough */
 +                      if (IEEE80211_HT_MCS_CHAINS(rate) >
 +                                      hweight8(info->control.antennas))
 +                              info->control.antennas = 0;
 +
                        info->control.rates[0].idx = rate;
                } else if (rate_flags & IEEE80211_TX_RC_VHT_MCS) {
 +                      /* reset antennas if not enough */
 +                      if (vht_nss > hweight8(info->control.antennas))
 +                              info->control.antennas = 0;
 +
                        ieee80211_rate_set_vht(info->control.rates, vht_mcs,
                                               vht_nss);
                } else if (sband) {
@@@ -2865,10 -2856,9 +2866,10 @@@ static struct sk_buff *ieee80211_build_
                goto free;
        }
  
 -      if (unlikely(!multicast && ((skb->sk &&
 -                   skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS) ||
 -                   ctrl_flags & IEEE80211_TX_CTL_REQ_TX_STATUS)))
 +      if (unlikely(!multicast &&
 +                   ((skb->sk &&
 +                     skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS) ||
 +                    ctrl_flags & IEEE80211_TX_CTL_REQ_TX_STATUS)))
                info_id = ieee80211_store_ack_skb(local, skb, &info_flags,
                                                  cookie);
  
        memset(info, 0, sizeof(*info));
  
        info->flags = info_flags;
 -      info->ack_frame_id = info_id;
 +      if (info_id) {
 +              info->status_data = info_id;
 +              info->status_data_idr = 1;
 +      }
        info->band = band;
  
        if (likely(!cookie)) {
@@@ -4488,8 -4475,6 +4489,8 @@@ static void ieee80211_mlo_multicast_tx(
   * @dev: incoming interface
   *
   * On failure skb will be freed.
 + *
 + * Returns: the netdev TX status (but really only %NETDEV_TX_OK)
   */
  netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
                                       struct net_device *dev)
@@@ -4654,12 -4639,9 +4655,12 @@@ static void ieee80211_8023_xmit(struct 
        }
  
        if (unlikely(skb->sk &&
 -                   skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS))
 -              info->ack_frame_id = ieee80211_store_ack_skb(local, skb,
 -                                                           &info->flags, NULL);
 +                   skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS)) {
 +              info->status_data = ieee80211_store_ack_skb(local, skb,
 +                                                          &info->flags, NULL);
 +              if (info->status_data)
 +                      info->status_data_idr = 1;
 +      }
  
        dev_sw_netstats_tx_add(dev, skbs, len);
        sta->deflink.tx_stats.packets[queue] += skbs;
@@@ -5568,6 -5550,7 +5569,6 @@@ struct sk_buff *ieee80211_beacon_get_ti
                                                     IEEE80211_INCLUDE_ALL_MBSSID_ELEMS,
                                                     NULL);
        struct sk_buff *copy;
 -      int shift;
  
        if (!bcn)
                return bcn;
        if (!copy)
                return bcn;
  
 -      shift = ieee80211_vif_get_shift(vif);
 -      ieee80211_tx_monitor(hw_to_local(hw), copy, 1, shift, false, NULL);
 +      ieee80211_tx_monitor(hw_to_local(hw), copy, 1, false, NULL);
  
        return bcn;
  }
@@@ -5937,7 -5921,7 +5938,7 @@@ int ieee80211_reserve_tid(struct ieee80
        int ret;
        u32 queues;
  
 -      lockdep_assert_held(&local->sta_mtx);
 +      lockdep_assert_wiphy(local->hw.wiphy);
  
        /* only some cases are supported right now */
        switch (sdata->vif.type) {
@@@ -5998,7 -5982,7 +5999,7 @@@ void ieee80211_unreserve_tid(struct iee
        struct sta_info *sta = container_of(pubsta, struct sta_info, sta);
        struct ieee80211_sub_if_data *sdata = sta->sdata;
  
 -      lockdep_assert_held(&sdata->local->sta_mtx);
 +      lockdep_assert_wiphy(sdata->local->hw.wiphy);
  
        /* only some cases are supported right now */
        switch (sdata->vif.type) {
@@@ -6119,9 -6103,6 +6120,9 @@@ int ieee80211_tx_control_port(struct wi
        u32 flags = 0;
        int err;
  
 +      /* mutex lock is only needed for incrementing the cookie counter */
 +      lockdep_assert_wiphy(local->hw.wiphy);
 +
        /* Only accept CONTROL_PORT_PROTOCOL configured in CONNECT/ASSOCIATE
         * or Pre-Authentication
         */
        rcu_read_unlock();
  
  start_xmit:
 -      /* mutex lock is only needed for incrementing the cookie counter */
 -      mutex_lock(&local->mtx);
 -
        local_bh_disable();
        __ieee80211_subif_start_xmit(skb, skb->dev, flags, ctrl_flags, cookie);
        local_bh_enable();
  
 -      mutex_unlock(&local->mtx);
 -
        return 0;
  }
  
diff --combined net/wireless/core.c
index b0f6ae3ce78fa6144bb0f9ebc54beb66c5e54dc5,64e8616171104f97571aca6bb6fe9a7e64763444..7df8ffcfa0c4410bd258c8a262167588b06c98d5
@@@ -5,7 -5,7 +5,7 @@@
   * Copyright 2006-2010                Johannes Berg <[email protected]>
   * Copyright 2013-2014  Intel Mobile Communications GmbH
   * Copyright 2015-2017        Intel Deutschland GmbH
 - * Copyright (C) 2018-2022 Intel Corporation
 + * Copyright (C) 2018-2023 Intel Corporation
   */
  
  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@@@ -60,7 -60,7 +60,7 @@@ struct cfg80211_registered_device *cfg8
  
        ASSERT_RTNL();
  
 -      list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
 +      for_each_rdev(rdev) {
                if (rdev->wiphy_idx == wiphy_idx) {
                        result = rdev;
                        break;
@@@ -116,7 -116,7 +116,7 @@@ static int cfg80211_dev_check_name(stru
        }
  
        /* Ensure another device does not already have this name. */
 -      list_for_each_entry(rdev2, &cfg80211_rdev_list, list)
 +      for_each_rdev(rdev2)
                if (strcmp(newname, wiphy_name(&rdev2->wiphy)) == 0)
                        return -EINVAL;
  
@@@ -821,7 -821,6 +821,7 @@@ int wiphy_register(struct wiphy *wiphy
  
        /* sanity check supported bands/channels */
        for (band = 0; band < NUM_NL80211_BANDS; band++) {
 +              const struct ieee80211_sband_iftype_data *iftd;
                u16 types = 0;
                bool have_he = false;
  
                                return -EINVAL;
                }
  
 -              for (i = 0; i < sband->n_iftype_data; i++) {
 -                      const struct ieee80211_sband_iftype_data *iftd;
 +              for_each_sband_iftype_data(sband, i, iftd) {
                        bool has_ap, has_non_ap;
                        u32 ap_bits = BIT(NL80211_IFTYPE_AP) |
                                      BIT(NL80211_IFTYPE_P2P_GO);
  
 -                      iftd = &sband->iftype_data[i];
 -
                        if (WARN_ON(!iftd->types_mask))
                                return -EINVAL;
                        if (WARN_ON(types & iftd->types_mask))
@@@ -1047,8 -1049,7 +1047,8 @@@ void wiphy_rfkill_start_polling(struct 
  }
  EXPORT_SYMBOL(wiphy_rfkill_start_polling);
  
 -void cfg80211_process_wiphy_works(struct cfg80211_registered_device *rdev)
 +void cfg80211_process_wiphy_works(struct cfg80211_registered_device *rdev,
 +                                struct wiphy_work *end)
  {
        unsigned int runaway_limit = 100;
        unsigned long flags;
                wk->func(&rdev->wiphy, wk);
  
                spin_lock_irqsave(&rdev->wiphy_work_lock, flags);
 +
 +              if (wk == end)
 +                      break;
 +
                if (WARN_ON(--runaway_limit == 0))
                        INIT_LIST_HEAD(&rdev->wiphy_work_list);
        }
@@@ -1121,7 -1118,7 +1121,7 @@@ void wiphy_unregister(struct wiphy *wip
  #endif
  
        /* surely nothing is reachable now, clean up work */
 -      cfg80211_process_wiphy_works(rdev);
 +      cfg80211_process_wiphy_works(rdev, NULL);
        wiphy_unlock(&rdev->wiphy);
        rtnl_unlock();
  
@@@ -1184,16 -1181,11 +1184,11 @@@ void wiphy_rfkill_set_hw_state_reason(s
  }
  EXPORT_SYMBOL(wiphy_rfkill_set_hw_state_reason);
  
- void cfg80211_cqm_config_free(struct wireless_dev *wdev)
- {
-       kfree(wdev->cqm_config);
-       wdev->cqm_config = NULL;
- }
  static void _cfg80211_unregister_wdev(struct wireless_dev *wdev,
                                      bool unregister_netdev)
  {
        struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+       struct cfg80211_cqm_config *cqm_config;
        unsigned int link_id;
  
        ASSERT_RTNL();
        kfree_sensitive(wdev->wext.keys);
        wdev->wext.keys = NULL;
  #endif
-       cfg80211_cqm_config_free(wdev);
+       wiphy_work_cancel(wdev->wiphy, &wdev->cqm_rssi_work);
+       /* deleted from the list, so can't be found from nl80211 any more */
+       cqm_config = rcu_access_pointer(wdev->cqm_config);
+       kfree_rcu(cqm_config, rcu_head);
  
        /*
         * Ensure that all events have been processed and
@@@ -1276,13 -1271,14 +1274,13 @@@ void cfg80211_update_iface_num(struct c
                rdev->num_running_monitor_ifaces += num;
  }
  
 -void __cfg80211_leave(struct cfg80211_registered_device *rdev,
 -                    struct wireless_dev *wdev)
 +void cfg80211_leave(struct cfg80211_registered_device *rdev,
 +                  struct wireless_dev *wdev)
  {
        struct net_device *dev = wdev->netdev;
        struct cfg80211_sched_scan_request *pos, *tmp;
  
        lockdep_assert_held(&rdev->wiphy.mtx);
 -      ASSERT_WDEV_LOCK(wdev);
  
        cfg80211_pmsr_wdev_down(wdev);
  
  
        switch (wdev->iftype) {
        case NL80211_IFTYPE_ADHOC:
 -              __cfg80211_leave_ibss(rdev, dev, true);
 +              cfg80211_leave_ibss(rdev, dev, true);
                break;
        case NL80211_IFTYPE_P2P_CLIENT:
        case NL80211_IFTYPE_STATION:
                                    WLAN_REASON_DEAUTH_LEAVING, true);
                break;
        case NL80211_IFTYPE_MESH_POINT:
 -              __cfg80211_leave_mesh(rdev, dev);
 +              cfg80211_leave_mesh(rdev, dev);
                break;
        case NL80211_IFTYPE_AP:
        case NL80211_IFTYPE_P2P_GO:
 -              __cfg80211_stop_ap(rdev, dev, -1, true);
 +              cfg80211_stop_ap(rdev, dev, -1, true);
                break;
        case NL80211_IFTYPE_OCB:
 -              __cfg80211_leave_ocb(rdev, dev);
 +              cfg80211_leave_ocb(rdev, dev);
                break;
        case NL80211_IFTYPE_P2P_DEVICE:
        case NL80211_IFTYPE_NAN:
        }
  }
  
 -void cfg80211_leave(struct cfg80211_registered_device *rdev,
 -                  struct wireless_dev *wdev)
 -{
 -      wdev_lock(wdev);
 -      __cfg80211_leave(rdev, wdev);
 -      wdev_unlock(wdev);
 -}
 -
  void cfg80211_stop_iface(struct wiphy *wiphy, struct wireless_dev *wdev,
                         gfp_t gfp)
  {
@@@ -1359,6 -1363,7 +1357,6 @@@ EXPORT_SYMBOL(cfg80211_stop_iface)
  
  void cfg80211_init_wdev(struct wireless_dev *wdev)
  {
 -      mutex_init(&wdev->mtx);
        INIT_LIST_HEAD(&wdev->event_list);
        spin_lock_init(&wdev->event_lock);
        INIT_LIST_HEAD(&wdev->mgmt_registrations);
        wdev->wext.connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC;
  #endif
  
+       wiphy_work_init(&wdev->cqm_rssi_work, cfg80211_cqm_rssi_notify_work);
        if (wdev->wiphy->flags & WIPHY_FLAG_PS_ON_BY_DEFAULT)
                wdev->ps = true;
        else
@@@ -1521,6 -1528,7 +1521,6 @@@ static int cfg80211_netdev_notifier_cal
        case NETDEV_UP:
                wiphy_lock(&rdev->wiphy);
                cfg80211_update_iface_num(rdev, wdev->iftype, 1);
 -              wdev_lock(wdev);
                switch (wdev->iftype) {
  #ifdef CONFIG_CFG80211_WEXT
                case NL80211_IFTYPE_ADHOC:
                default:
                        break;
                }
 -              wdev_unlock(wdev);
                rdev->opencount++;
  
                /*
@@@ -1592,7 -1601,7 +1592,7 @@@ static void __net_exit cfg80211_pernet_
        struct cfg80211_registered_device *rdev;
  
        rtnl_lock();
 -      list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
 +      for_each_rdev(rdev) {
                if (net_eq(wiphy_net(&rdev->wiphy), net))
                        WARN_ON(cfg80211_switch_netns(rdev, &init_net));
        }
@@@ -1631,21 -1640,6 +1631,21 @@@ void wiphy_work_cancel(struct wiphy *wi
  }
  EXPORT_SYMBOL_GPL(wiphy_work_cancel);
  
 +void wiphy_work_flush(struct wiphy *wiphy, struct wiphy_work *work)
 +{
 +      struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
 +      unsigned long flags;
 +      bool run;
 +
 +      spin_lock_irqsave(&rdev->wiphy_work_lock, flags);
 +      run = !work || !list_empty(&work->entry);
 +      spin_unlock_irqrestore(&rdev->wiphy_work_lock, flags);
 +
 +      if (run)
 +              cfg80211_process_wiphy_works(rdev, work);
 +}
 +EXPORT_SYMBOL_GPL(wiphy_work_flush);
 +
  void wiphy_delayed_work_timer(struct timer_list *t)
  {
        struct wiphy_delayed_work *dwork = from_timer(dwork, t, timer);
@@@ -1678,16 -1672,6 +1678,16 @@@ void wiphy_delayed_work_cancel(struct w
  }
  EXPORT_SYMBOL_GPL(wiphy_delayed_work_cancel);
  
 +void wiphy_delayed_work_flush(struct wiphy *wiphy,
 +                            struct wiphy_delayed_work *dwork)
 +{
 +      lockdep_assert_held(&wiphy->mtx);
 +
 +      del_timer_sync(&dwork->timer);
 +      wiphy_work_flush(wiphy, &dwork->work);
 +}
 +EXPORT_SYMBOL_GPL(wiphy_delayed_work_flush);
 +
  static int __init cfg80211_init(void)
  {
        int err;
diff --combined net/wireless/core.h
index 866f0a6934e65aa64d80e259f604163419869b6f,ba9c7170afa44e1a37c11d750cf6fd6c4998ba7c..79b1c6d17847701fd748fb58036f54d1606df4b7
@@@ -160,16 -160,6 +160,16 @@@ extern struct workqueue_struct *cfg8021
  extern struct list_head cfg80211_rdev_list;
  extern int cfg80211_rdev_list_generation;
  
 +/* This is constructed like this so it can be used in if/else */
 +static inline int for_each_rdev_check_rtnl(void)
 +{
 +      ASSERT_RTNL();
 +      return 0;
 +}
 +#define for_each_rdev(rdev)                                           \
 +      if (for_each_rdev_check_rtnl()) {} else                         \
 +              list_for_each_entry(rdev, &cfg80211_rdev_list, list)
 +
  struct cfg80211_internal_bss {
        struct list_head list;
        struct list_head hidden_list;
@@@ -235,6 -225,22 +235,6 @@@ void cfg80211_init_wdev(struct wireless
  void cfg80211_register_wdev(struct cfg80211_registered_device *rdev,
                            struct wireless_dev *wdev);
  
 -static inline void wdev_lock(struct wireless_dev *wdev)
 -      __acquires(wdev)
 -{
 -      mutex_lock(&wdev->mtx);
 -      __acquire(wdev->mtx);
 -}
 -
 -static inline void wdev_unlock(struct wireless_dev *wdev)
 -      __releases(wdev)
 -{
 -      __release(wdev->mtx);
 -      mutex_unlock(&wdev->mtx);
 -}
 -
 -#define ASSERT_WDEV_LOCK(wdev) lockdep_assert_held(&(wdev)->mtx)
 -
  static inline bool cfg80211_has_monitors_only(struct cfg80211_registered_device *rdev)
  {
        lockdep_assert_held(&rdev->wiphy.mtx);
@@@ -289,12 -295,17 +289,17 @@@ struct cfg80211_beacon_registration 
  };
  
  struct cfg80211_cqm_config {
+       struct rcu_head rcu_head;
        u32 rssi_hyst;
        s32 last_rssi_event_value;
+       enum nl80211_cqm_rssi_threshold_event last_rssi_event_type;
        int n_rssi_thresholds;
        s32 rssi_thresholds[] __counted_by(n_rssi_thresholds);
  };
  
+ void cfg80211_cqm_rssi_notify_work(struct wiphy *wiphy,
+                                  struct wiphy_work *work);
  void cfg80211_destroy_ifaces(struct cfg80211_registered_device *rdev);
  
  /* free object */
@@@ -318,6 -329,8 +323,6 @@@ int __cfg80211_join_ibss(struct cfg8021
                         struct cfg80211_ibss_params *params,
                         struct cfg80211_cached_keys *connkeys);
  void cfg80211_clear_ibss(struct net_device *dev, bool nowext);
 -int __cfg80211_leave_ibss(struct cfg80211_registered_device *rdev,
 -                        struct net_device *dev, bool nowext);
  int cfg80211_leave_ibss(struct cfg80211_registered_device *rdev,
                        struct net_device *dev, bool nowext);
  void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid,
@@@ -332,6 -345,8 +337,6 @@@ int __cfg80211_join_mesh(struct cfg8021
                         struct net_device *dev,
                         struct mesh_setup *setup,
                         const struct mesh_config *conf);
 -int __cfg80211_leave_mesh(struct cfg80211_registered_device *rdev,
 -                        struct net_device *dev);
  int cfg80211_leave_mesh(struct cfg80211_registered_device *rdev,
                        struct net_device *dev);
  int cfg80211_set_mesh_channel(struct cfg80211_registered_device *rdev,
                              struct cfg80211_chan_def *chandef);
  
  /* OCB */
 -int __cfg80211_join_ocb(struct cfg80211_registered_device *rdev,
 -                      struct net_device *dev,
 -                      struct ocb_setup *setup);
  int cfg80211_join_ocb(struct cfg80211_registered_device *rdev,
                      struct net_device *dev,
                      struct ocb_setup *setup);
 -int __cfg80211_leave_ocb(struct cfg80211_registered_device *rdev,
 -                       struct net_device *dev);
  int cfg80211_leave_ocb(struct cfg80211_registered_device *rdev,
                       struct net_device *dev);
  
  /* AP */
 -int __cfg80211_stop_ap(struct cfg80211_registered_device *rdev,
 -                     struct net_device *dev, int link,
 -                     bool notify);
  int cfg80211_stop_ap(struct cfg80211_registered_device *rdev,
                     struct net_device *dev, int link,
                     bool notify);
@@@ -441,8 -464,7 +446,8 @@@ int cfg80211_change_iface(struct cfg802
                          struct net_device *dev, enum nl80211_iftype ntype,
                          struct vif_params *params);
  void cfg80211_process_rdev_events(struct cfg80211_registered_device *rdev);
 -void cfg80211_process_wiphy_works(struct cfg80211_registered_device *rdev);
 +void cfg80211_process_wiphy_works(struct cfg80211_registered_device *rdev,
 +                                struct wiphy_work *end);
  void cfg80211_process_wdev_events(struct wireless_dev *wdev);
  
  bool cfg80211_does_bw_fit_range(const struct ieee80211_freq_range *freq_range,
@@@ -452,12 -474,29 +457,12 @@@ int cfg80211_scan(struct cfg80211_regis
  
  extern struct work_struct cfg80211_disconnect_work;
  
 -/**
 - * cfg80211_chandef_dfs_usable - checks if chandef is DFS usable
 - * @wiphy: the wiphy to validate against
 - * @chandef: the channel definition to check
 - *
 - * Checks if chandef is usable and we can/need start CAC on such channel.
 - *
 - * Return: true if all channels available and at least
 - *       one channel requires CAC (NL80211_DFS_USABLE)
 - */
 -bool cfg80211_chandef_dfs_usable(struct wiphy *wiphy,
 -                               const struct cfg80211_chan_def *chandef);
 -
  void cfg80211_set_dfs_state(struct wiphy *wiphy,
                            const struct cfg80211_chan_def *chandef,
                            enum nl80211_dfs_state dfs_state);
  
  void cfg80211_dfs_channels_update_work(struct work_struct *work);
  
 -unsigned int
 -cfg80211_chandef_dfs_cac_time(struct wiphy *wiphy,
 -                            const struct cfg80211_chan_def *chandef);
 -
  void cfg80211_sched_dfs_chan_update(struct cfg80211_registered_device *rdev);
  
  int
@@@ -506,6 -545,8 +511,6 @@@ int cfg80211_validate_beacon_int(struc
  void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev,
                               enum nl80211_iftype iftype, int num);
  
 -void __cfg80211_leave(struct cfg80211_registered_device *rdev,
 -                    struct wireless_dev *wdev);
  void cfg80211_leave(struct cfg80211_registered_device *rdev,
                    struct wireless_dev *wdev);
  
@@@ -530,8 -571,6 +535,6 @@@ cfg80211_bss_update(struct cfg80211_reg
  #define CFG80211_DEV_WARN_ON(cond)    ({bool __r = (cond); __r; })
  #endif
  
- void cfg80211_cqm_config_free(struct wireless_dev *wdev);
  void cfg80211_release_pmsr(struct wireless_dev *wdev, u32 portid);
  void cfg80211_pmsr_wdev_down(struct wireless_dev *wdev);
  void cfg80211_pmsr_free_wk(struct work_struct *work);
diff --combined net/wireless/mlme.c
index cc7ae9ea84eac79613bc57be8b4f899e75afa07c,3e2c398abddccb3ae0ac9926a374d8dacfe6d1f0..579fea2f354889084ffca6cf25d87c8259e7812c
@@@ -4,7 -4,7 +4,7 @@@
   *
   * Copyright (c) 2009, Jouni Malinen <[email protected]>
   * Copyright (c) 2015         Intel Deutschland GmbH
 - * Copyright (C) 2019-2020, 2022 Intel Corporation
 + * Copyright (C) 2019-2020, 2022-2023 Intel Corporation
   */
  
  #include <linux/kernel.h>
@@@ -52,7 -52,8 +52,8 @@@ void cfg80211_rx_assoc_resp(struct net_
                cr.links[link_id].bssid = data->links[link_id].bss->bssid;
                cr.links[link_id].addr = data->links[link_id].addr;
                /* need to have local link addresses for MLO connections */
-               WARN_ON(cr.ap_mld_addr && !cr.links[link_id].addr);
+               WARN_ON(cr.ap_mld_addr &&
+                       !is_valid_ether_addr(cr.links[link_id].addr));
  
                BUG_ON(!cr.links[link_id].bss->channel);
  
@@@ -149,7 -150,7 +150,7 @@@ void cfg80211_rx_mlme_mgmt(struct net_d
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        struct ieee80211_mgmt *mgmt = (void *)buf;
  
 -      ASSERT_WDEV_LOCK(wdev);
 +      lockdep_assert_wiphy(wdev->wiphy);
  
        trace_cfg80211_rx_mlme_mgmt(dev, buf, len);
  
@@@ -214,7 -215,7 +215,7 @@@ void cfg80211_tx_mlme_mgmt(struct net_d
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        struct ieee80211_mgmt *mgmt = (void *)buf;
  
 -      ASSERT_WDEV_LOCK(wdev);
 +      lockdep_assert_wiphy(wdev->wiphy);
  
        trace_cfg80211_tx_mlme_mgmt(dev, buf, len, reconnect);
  
@@@ -262,7 -263,7 +263,7 @@@ int cfg80211_mlme_auth(struct cfg80211_
  {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
  
 -      ASSERT_WDEV_LOCK(wdev);
 +      lockdep_assert_wiphy(wdev->wiphy);
  
        if (!req->bss)
                return -ENOENT;
@@@ -331,7 -332,7 +332,7 @@@ int cfg80211_mlme_assoc(struct cfg80211
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        int err, i, j;
  
 -      ASSERT_WDEV_LOCK(wdev);
 +      lockdep_assert_wiphy(wdev->wiphy);
  
        for (i = 1; i < ARRAY_SIZE(req->links); i++) {
                if (!req->links[i].bss)
@@@ -393,7 -394,7 +394,7 @@@ int cfg80211_mlme_deauth(struct cfg8021
                .local_state_change = local_state_change,
        };
  
 -      ASSERT_WDEV_LOCK(wdev);
 +      lockdep_assert_wiphy(wdev->wiphy);
  
        if (local_state_change &&
            (!wdev->connected ||
@@@ -423,7 -424,7 +424,7 @@@ int cfg80211_mlme_disassoc(struct cfg80
        };
        int err;
  
 -      ASSERT_WDEV_LOCK(wdev);
 +      lockdep_assert_wiphy(wdev->wiphy);
  
        if (!wdev->connected)
                return -ENOTCONN;
@@@ -446,7 -447,7 +447,7 @@@ void cfg80211_mlme_down(struct cfg80211
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        u8 bssid[ETH_ALEN];
  
 -      ASSERT_WDEV_LOCK(wdev);
 +      lockdep_assert_wiphy(wdev->wiphy);
  
        if (!rdev->ops->deauth)
                return;
@@@ -726,8 -727,6 +727,8 @@@ int cfg80211_mlme_mgmt_tx(struct cfg802
        const struct ieee80211_mgmt *mgmt;
        u16 stype;
  
 +      lockdep_assert_wiphy(&rdev->wiphy);
 +
        if (!wdev->wiphy->mgmt_stypes)
                return -EOPNOTSUPP;
  
            mgmt->u.action.category != WLAN_CATEGORY_PUBLIC) {
                int err = 0;
  
 -              wdev_lock(wdev);
 -
                switch (wdev->iftype) {
                case NL80211_IFTYPE_ADHOC:
                        /*
                        err = -EOPNOTSUPP;
                        break;
                }
 -              wdev_unlock(wdev);
  
                if (err)
                        return err;
diff --combined net/wireless/nl80211.c
index 87b21c0c0f2551832bfd4a893be3329a5221bb50,931a03f4549c9fc1e85e20870742c935baca83a2..2650543dcebeea3b6182975a70f2cbbeb581100b
@@@ -106,7 -106,7 +106,7 @@@ __cfg80211_wdev_from_attrs(struct cfg80
  
        ASSERT_RTNL();
  
 -      list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
 +      for_each_rdev(rdev) {
                struct wireless_dev *wdev;
  
                if (wiphy_net(&rdev->wiphy) != netns)
@@@ -1115,10 -1115,6 +1115,10 @@@ static int nl80211_msg_put_channel(stru
        if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_OFFSET, chan->freq_offset))
                goto nla_put_failure;
  
 +      if ((chan->flags & IEEE80211_CHAN_PSD) &&
 +          nla_put_s8(msg, NL80211_FREQUENCY_ATTR_PSD, chan->psd))
 +              goto nla_put_failure;
 +
        if ((chan->flags & IEEE80211_CHAN_DISABLED) &&
            nla_put_flag(msg, NL80211_FREQUENCY_ATTR_DISABLED))
                goto nla_put_failure;
@@@ -1548,7 -1544,7 +1548,7 @@@ nl80211_parse_connkeys(struct cfg80211_
  
  static int nl80211_key_allowed(struct wireless_dev *wdev)
  {
 -      ASSERT_WDEV_LOCK(wdev);
 +      lockdep_assert_wiphy(wdev->wiphy);
  
        switch (wdev->iftype) {
        case NL80211_IFTYPE_AP:
@@@ -1917,20 -1913,20 +1917,20 @@@ static int nl80211_send_band_rateinfo(s
                struct nlattr *nl_iftype_data =
                        nla_nest_start_noflag(msg,
                                              NL80211_BAND_ATTR_IFTYPE_DATA);
 +              const struct ieee80211_sband_iftype_data *iftd;
                int err;
  
                if (!nl_iftype_data)
                        return -ENOBUFS;
  
 -              for (i = 0; i < sband->n_iftype_data; i++) {
 +              for_each_sband_iftype_data(sband, i, iftd) {
                        struct nlattr *iftdata;
  
                        iftdata = nla_nest_start_noflag(msg, i + 1);
                        if (!iftdata)
                                return -ENOBUFS;
  
 -                      err = nl80211_send_iftype_data(msg, sband,
 -                                                     &sband->iftype_data[i]);
 +                      err = nl80211_send_iftype_data(msg, sband, iftd);
                        if (err)
                                return err;
  
@@@ -3079,7 -3075,7 +3079,7 @@@ static int nl80211_dump_wiphy(struct sk
                cb->args[0] = (long)state;
        }
  
 -      list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
 +      for_each_rdev(rdev) {
                if (!net_eq(wiphy_net(&rdev->wiphy), sock_net(skb->sk)))
                        continue;
                if (++idx <= state->start)
@@@ -3427,8 -3423,13 +3427,8 @@@ static int nl80211_set_channel(struct s
        struct cfg80211_registered_device *rdev = info->user_ptr[0];
        int link_id = nl80211_link_id_or_invalid(info->attrs);
        struct net_device *netdev = info->user_ptr[1];
 -      int ret;
 -
 -      wdev_lock(netdev->ieee80211_ptr);
 -      ret = __nl80211_set_channel(rdev, netdev, info, link_id);
 -      wdev_unlock(netdev->ieee80211_ptr);
  
 -      return ret;
 +      return __nl80211_set_channel(rdev, netdev, info, link_id);
  }
  
  static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
                        txq_params.link_id =
                                nl80211_link_id_or_invalid(info->attrs);
  
 -                      wdev_lock(netdev->ieee80211_ptr);
                        if (txq_params.link_id >= 0 &&
                            !(netdev->ieee80211_ptr->valid_links &
                              BIT(txq_params.link_id)))
                        else
                                result = rdev_set_txq_params(rdev, netdev,
                                                             &txq_params);
 -                      wdev_unlock(netdev->ieee80211_ptr);
                        if (result)
                                goto out;
                }
                int link_id = nl80211_link_id_or_invalid(info->attrs);
  
                if (wdev) {
 -                      wdev_lock(wdev);
                        result = __nl80211_set_channel(
                                rdev,
                                nl80211_can_set_dev_channel(wdev) ? netdev : NULL,
                                info, link_id);
 -                      wdev_unlock(wdev);
                } else {
                        result = __nl80211_set_channel(rdev, netdev, info, link_id);
                }
@@@ -3865,31 -3870,33 +3865,31 @@@ static int nl80211_send_iface(struct sk
                        goto nla_put_failure;
        }
  
 -      wdev_lock(wdev);
        switch (wdev->iftype) {
        case NL80211_IFTYPE_AP:
        case NL80211_IFTYPE_P2P_GO:
                if (wdev->u.ap.ssid_len &&
                    nla_put(msg, NL80211_ATTR_SSID, wdev->u.ap.ssid_len,
                            wdev->u.ap.ssid))
 -                      goto nla_put_failure_locked;
 +                      goto nla_put_failure;
                break;
        case NL80211_IFTYPE_STATION:
        case NL80211_IFTYPE_P2P_CLIENT:
                if (wdev->u.client.ssid_len &&
                    nla_put(msg, NL80211_ATTR_SSID, wdev->u.client.ssid_len,
                            wdev->u.client.ssid))
 -                      goto nla_put_failure_locked;
 +                      goto nla_put_failure;
                break;
        case NL80211_IFTYPE_ADHOC:
                if (wdev->u.ibss.ssid_len &&
                    nla_put(msg, NL80211_ATTR_SSID, wdev->u.ibss.ssid_len,
                            wdev->u.ibss.ssid))
 -                      goto nla_put_failure_locked;
 +                      goto nla_put_failure;
                break;
        default:
                /* nothing */
                break;
        }
 -      wdev_unlock(wdev);
  
        if (rdev->ops->get_txq_stats) {
                struct cfg80211_txq_stats txqstats = {};
        genlmsg_end(msg, hdr);
        return 0;
  
 - nla_put_failure_locked:
 -      wdev_unlock(wdev);
   nla_put_failure:
        genlmsg_cancel(msg, hdr);
        return -EMSGSIZE;
@@@ -3976,7 -3985,7 +3976,7 @@@ static int nl80211_dump_interface(struc
                filter_wiphy = cb->args[2] - 1;
        }
  
 -      list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
 +      for_each_rdev(rdev) {
                if (!net_eq(wiphy_net(&rdev->wiphy), sock_net(skb->sk)))
                        continue;
                if (wp_idx < wp_start) {
@@@ -4182,6 -4191,7 +4182,6 @@@ static int nl80211_set_interface(struc
                if (netif_running(dev))
                        return -EBUSY;
  
 -              wdev_lock(wdev);
                BUILD_BUG_ON(IEEE80211_MAX_SSID_LEN !=
                             IEEE80211_MAX_MESH_ID_LEN);
                wdev->u.mesh.id_up_len =
                memcpy(wdev->u.mesh.id,
                       nla_data(info->attrs[NL80211_ATTR_MESH_ID]),
                       wdev->u.mesh.id_up_len);
 -              wdev_unlock(wdev);
        }
  
        if (info->attrs[NL80211_ATTR_4ADDR]) {
@@@ -4289,6 -4300,7 +4289,6 @@@ static int _nl80211_new_interface(struc
        case NL80211_IFTYPE_MESH_POINT:
                if (!info->attrs[NL80211_ATTR_MESH_ID])
                        break;
 -              wdev_lock(wdev);
                BUILD_BUG_ON(IEEE80211_MAX_SSID_LEN !=
                             IEEE80211_MAX_MESH_ID_LEN);
                wdev->u.mesh.id_up_len =
                memcpy(wdev->u.mesh.id,
                       nla_data(info->attrs[NL80211_ATTR_MESH_ID]),
                       wdev->u.mesh.id_up_len);
 -              wdev_unlock(wdev);
                break;
        case NL80211_IFTYPE_NAN:
        case NL80211_IFTYPE_P2P_DEVICE:
@@@ -4586,67 -4599,79 +4586,67 @@@ static int nl80211_set_key(struct sk_bu
            !(key.p.mode == NL80211_KEY_SET_TX))
                return -EINVAL;
  
 -      wdev_lock(wdev);
 -
        if (key.def) {
 -              if (!rdev->ops->set_default_key) {
 -                      err = -EOPNOTSUPP;
 -                      goto out;
 -              }
 +              if (!rdev->ops->set_default_key)
 +                      return -EOPNOTSUPP;
  
                err = nl80211_key_allowed(wdev);
                if (err)
 -                      goto out;
 +                      return err;
  
                err = nl80211_validate_key_link_id(info, wdev, link_id, false);
                if (err)
 -                      goto out;
 +                      return err;
  
                err = rdev_set_default_key(rdev, dev, link_id, key.idx,
                                           key.def_uni, key.def_multi);
  
                if (err)
 -                      goto out;
 +                      return err;
  
  #ifdef CONFIG_CFG80211_WEXT
                wdev->wext.default_key = key.idx;
  #endif
 +              return 0;
        } else if (key.defmgmt) {
 -              if (key.def_uni || !key.def_multi) {
 -                      err = -EINVAL;
 -                      goto out;
 -              }
 +              if (key.def_uni || !key.def_multi)
 +                      return -EINVAL;
  
 -              if (!rdev->ops->set_default_mgmt_key) {
 -                      err = -EOPNOTSUPP;
 -                      goto out;
 -              }
 +              if (!rdev->ops->set_default_mgmt_key)
 +                      return -EOPNOTSUPP;
  
                err = nl80211_key_allowed(wdev);
                if (err)
 -                      goto out;
 +                      return err;
  
                err = nl80211_validate_key_link_id(info, wdev, link_id, false);
                if (err)
 -                      goto out;
 +                      return err;
  
                err = rdev_set_default_mgmt_key(rdev, dev, link_id, key.idx);
                if (err)
 -                      goto out;
 +                      return err;
  
  #ifdef CONFIG_CFG80211_WEXT
                wdev->wext.default_mgmt_key = key.idx;
  #endif
 +              return 0;
        } else if (key.defbeacon) {
 -              if (key.def_uni || !key.def_multi) {
 -                      err = -EINVAL;
 -                      goto out;
 -              }
 +              if (key.def_uni || !key.def_multi)
 +                      return -EINVAL;
  
 -              if (!rdev->ops->set_default_beacon_key) {
 -                      err = -EOPNOTSUPP;
 -                      goto out;
 -              }
 +              if (!rdev->ops->set_default_beacon_key)
 +                      return -EOPNOTSUPP;
  
                err = nl80211_key_allowed(wdev);
                if (err)
 -                      goto out;
 +                      return err;
  
                err = nl80211_validate_key_link_id(info, wdev, link_id, false);
                if (err)
 -                      goto out;
 +                      return err;
  
 -              err = rdev_set_default_beacon_key(rdev, dev, link_id, key.idx);
 -              if (err)
 -                      goto out;
 +              return rdev_set_default_beacon_key(rdev, dev, link_id, key.idx);
        } else if (key.p.mode == NL80211_KEY_SET_TX &&
                   wiphy_ext_feature_isset(&rdev->wiphy,
                                           NL80211_EXT_FEATURE_EXT_KEY_ID)) {
                if (info->attrs[NL80211_ATTR_MAC])
                        mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
  
 -              if (!mac_addr || key.idx < 0 || key.idx > 1) {
 -                      err = -EINVAL;
 -                      goto out;
 -              }
 +              if (!mac_addr || key.idx < 0 || key.idx > 1)
 +                      return -EINVAL;
  
                err = nl80211_validate_key_link_id(info, wdev, link_id, true);
                if (err)
 -                      goto out;
 +                      return err;
  
 -              err = rdev_add_key(rdev, dev, link_id, key.idx,
 -                                 NL80211_KEYTYPE_PAIRWISE,
 -                                 mac_addr, &key.p);
 -      } else {
 -              err = -EINVAL;
 +              return rdev_add_key(rdev, dev, link_id, key.idx,
 +                                  NL80211_KEYTYPE_PAIRWISE,
 +                                  mac_addr, &key.p);
        }
 - out:
 -      wdev_unlock(wdev);
  
 -      return err;
 +      return -EINVAL;
  }
  
  static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info)
                return -EINVAL;
        }
  
 -      wdev_lock(wdev);
        err = nl80211_key_allowed(wdev);
        if (err)
                GENL_SET_ERR_MSG(info, "key not allowed");
                if (err)
                        GENL_SET_ERR_MSG(info, "key addition failed");
        }
 -      wdev_unlock(wdev);
  
        return err;
  }
@@@ -4775,6 -4808,7 +4775,6 @@@ static int nl80211_del_key(struct sk_bu
        if (!rdev->ops->del_key)
                return -EOPNOTSUPP;
  
 -      wdev_lock(wdev);
        err = nl80211_key_allowed(wdev);
  
        if (key.type == NL80211_KEYTYPE_GROUP && mac_addr &&
                        wdev->wext.default_mgmt_key = -1;
        }
  #endif
 -      wdev_unlock(wdev);
  
        return err;
  }
@@@ -5636,10 -5671,11 +5636,10 @@@ static int nl80211_parse_he_obss_pd(str
  
  static int nl80211_parse_fils_discovery(struct cfg80211_registered_device *rdev,
                                        struct nlattr *attrs,
 -                                      struct cfg80211_ap_settings *params)
 +                                      struct cfg80211_fils_discovery *fd)
  {
        struct nlattr *tb[NL80211_FILS_DISCOVERY_ATTR_MAX + 1];
        int ret;
 -      struct cfg80211_fils_discovery *fd = &params->fils_discovery;
  
        if (!wiphy_ext_feature_isset(&rdev->wiphy,
                                     NL80211_EXT_FEATURE_FILS_DISCOVERY))
        if (ret)
                return ret;
  
 +      if (!tb[NL80211_FILS_DISCOVERY_ATTR_INT_MIN] &&
 +          !tb[NL80211_FILS_DISCOVERY_ATTR_INT_MAX] &&
 +          !tb[NL80211_FILS_DISCOVERY_ATTR_TMPL]) {
 +              fd->update = true;
 +              return 0;
 +      }
 +
        if (!tb[NL80211_FILS_DISCOVERY_ATTR_INT_MIN] ||
            !tb[NL80211_FILS_DISCOVERY_ATTR_INT_MAX] ||
            !tb[NL80211_FILS_DISCOVERY_ATTR_TMPL])
        fd->tmpl = nla_data(tb[NL80211_FILS_DISCOVERY_ATTR_TMPL]);
        fd->min_interval = nla_get_u32(tb[NL80211_FILS_DISCOVERY_ATTR_INT_MIN]);
        fd->max_interval = nla_get_u32(tb[NL80211_FILS_DISCOVERY_ATTR_INT_MAX]);
 -
 +      fd->update = true;
        return 0;
  }
  
  static int
  nl80211_parse_unsol_bcast_probe_resp(struct cfg80211_registered_device *rdev,
                                     struct nlattr *attrs,
 -                                   struct cfg80211_ap_settings *params)
 +                                   struct cfg80211_unsol_bcast_probe_resp *presp)
  {
        struct nlattr *tb[NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_MAX + 1];
        int ret;
 -      struct cfg80211_unsol_bcast_probe_resp *presp =
 -                                      &params->unsol_bcast_probe_resp;
  
        if (!wiphy_ext_feature_isset(&rdev->wiphy,
                                     NL80211_EXT_FEATURE_UNSOL_BCAST_PROBE_RESP))
        if (ret)
                return ret;
  
 +      if (!tb[NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_INT] &&
 +          !tb[NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_TMPL]) {
 +              presp->update = true;
 +              return 0;
 +      }
 +
        if (!tb[NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_INT] ||
            !tb[NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_TMPL])
                return -EINVAL;
        presp->tmpl = nla_data(tb[NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_TMPL]);
        presp->tmpl_len = nla_len(tb[NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_TMPL]);
        presp->interval = nla_get_u32(tb[NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_INT]);
 +      presp->update = true;
        return 0;
  }
  
        nlmsg_free(msg);
  }
  
+ static int nl80211_validate_ap_phy_operation(struct cfg80211_ap_settings *params)
+ {
+       struct ieee80211_channel *channel = params->chandef.chan;
+       if ((params->he_cap ||  params->he_oper) &&
+           (channel->flags & IEEE80211_CHAN_NO_HE))
+               return -EOPNOTSUPP;
+       if ((params->eht_cap || params->eht_oper) &&
+           (channel->flags & IEEE80211_CHAN_NO_EHT))
+               return -EOPNOTSUPP;
+       return 0;
+ }
  static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
  {
        struct cfg80211_registered_device *rdev = info->user_ptr[0];
                goto out;
        }
  
 -      wdev_lock(wdev);
 -
        if (info->attrs[NL80211_ATTR_TX_RATES]) {
                err = nl80211_parse_tx_bitrate_mask(info, info->attrs,
                                                    NL80211_ATTR_TX_RATES,
                                                    &params->beacon_rate,
                                                    dev, false, link_id);
                if (err)
 -                      goto out_unlock;
 +                      goto out;
  
                err = validate_beacon_tx_rate(rdev, params->chandef.chan->band,
                                              &params->beacon_rate);
                if (err)
 -                      goto out_unlock;
 +                      goto out;
        }
  
        if (info->attrs[NL80211_ATTR_SMPS_MODE]) {
                        if (!(rdev->wiphy.features &
                              NL80211_FEATURE_STATIC_SMPS)) {
                                err = -EINVAL;
 -                              goto out_unlock;
 +                              goto out;
                        }
                        break;
                case NL80211_SMPS_DYNAMIC:
                        if (!(rdev->wiphy.features &
                              NL80211_FEATURE_DYNAMIC_SMPS)) {
                                err = -EINVAL;
 -                              goto out_unlock;
 +                              goto out;
                        }
                        break;
                default:
                        err = -EINVAL;
 -                      goto out_unlock;
 +                      goto out;
                }
        } else {
                params->smps_mode = NL80211_SMPS_OFF;
        params->pbss = nla_get_flag(info->attrs[NL80211_ATTR_PBSS]);
        if (params->pbss && !rdev->wiphy.bands[NL80211_BAND_60GHZ]) {
                err = -EOPNOTSUPP;
 -              goto out_unlock;
 +              goto out;
        }
  
        if (info->attrs[NL80211_ATTR_ACL_POLICY]) {
                if (IS_ERR(params->acl)) {
                        err = PTR_ERR(params->acl);
                        params->acl = NULL;
 -                      goto out_unlock;
 +                      goto out;
                }
        }
  
                                        info->attrs[NL80211_ATTR_HE_OBSS_PD],
                                        &params->he_obss_pd);
                if (err)
 -                      goto out_unlock;
 +                      goto out;
        }
  
        if (info->attrs[NL80211_ATTR_FILS_DISCOVERY]) {
                err = nl80211_parse_fils_discovery(rdev,
                                                   info->attrs[NL80211_ATTR_FILS_DISCOVERY],
 -                                                 params);
 +                                                 &params->fils_discovery);
                if (err)
 -                      goto out_unlock;
 +                      goto out;
        }
  
        if (info->attrs[NL80211_ATTR_UNSOL_BCAST_PROBE_RESP]) {
                err = nl80211_parse_unsol_bcast_probe_resp(
                        rdev, info->attrs[NL80211_ATTR_UNSOL_BCAST_PROBE_RESP],
 -                      params);
 +                      &params->unsol_bcast_probe_resp);
                if (err)
 -                      goto out_unlock;
 +                      goto out;
        }
  
        if (info->attrs[NL80211_ATTR_MBSSID_CONFIG]) {
                                                        params->beacon.mbssid_ies->cnt :
                                                        0);
                if (err)
 -                      goto out_unlock;
 +                      goto out;
        }
  
        if (!params->mbssid_config.ema && params->beacon.rnr_ies) {
                err = -EINVAL;
 -              goto out_unlock;
 +              goto out;
        }
  
        err = nl80211_calculate_ap_params(params);
        if (err)
 -              goto out_unlock;
 +              goto out;
  
 -              goto out_unlock;
+       err = nl80211_validate_ap_phy_operation(params);
+       if (err)
++              goto out;
        if (info->attrs[NL80211_ATTR_AP_SETTINGS_FLAGS])
                params->flags = nla_get_u32(
                        info->attrs[NL80211_ATTR_AP_SETTINGS_FLAGS]);
            info->attrs[NL80211_ATTR_SOCKET_OWNER] &&
            wdev->conn_owner_nlportid != info->snd_portid) {
                err = -EINVAL;
 -              goto out_unlock;
 +              goto out;
        }
  
        /* FIXME: validate MLO/link-id against driver capabilities */
  
                nl80211_send_ap_started(wdev, link_id);
        }
 -out_unlock:
 -      wdev_unlock(wdev);
  out:
        kfree(params->acl);
        kfree(params->beacon.mbssid_ies);
@@@ -6199,8 -6246,7 +6218,8 @@@ static int nl80211_set_beacon(struct sk
        unsigned int link_id = nl80211_link_id(info->attrs);
        struct net_device *dev = info->user_ptr[1];
        struct wireless_dev *wdev = dev->ieee80211_ptr;
 -      struct cfg80211_beacon_data params;
 +      struct cfg80211_ap_update *params;
 +      struct nlattr *attr;
        int err;
  
        if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
        if (!wdev->links[link_id].ap.beacon_interval)
                return -EINVAL;
  
 -      err = nl80211_parse_beacon(rdev, info->attrs, &params, info->extack);
 +      params = kzalloc(sizeof(*params), GFP_KERNEL);
 +      if (!params)
 +              return -ENOMEM;
 +
 +      err = nl80211_parse_beacon(rdev, info->attrs, &params->beacon,
 +                                 info->extack);
        if (err)
                goto out;
  
 -      wdev_lock(wdev);
 -      err = rdev_change_beacon(rdev, dev, &params);
 -      wdev_unlock(wdev);
 +      attr = info->attrs[NL80211_ATTR_FILS_DISCOVERY];
 +      if (attr) {
 +              err = nl80211_parse_fils_discovery(rdev, attr,
 +                                                 &params->fils_discovery);
 +              if (err)
 +                      goto out;
 +      }
 +
 +      attr = info->attrs[NL80211_ATTR_UNSOL_BCAST_PROBE_RESP];
 +      if (attr) {
 +              err = nl80211_parse_unsol_bcast_probe_resp(rdev, attr,
 +                                                         &params->unsol_bcast_probe_resp);
 +              if (err)
 +                      goto out;
 +      }
 +
 +      err = rdev_change_beacon(rdev, dev, params);
  
  out:
 -      kfree(params.mbssid_ies);
 -      kfree(params.rnr_ies);
 +      kfree(params->beacon.mbssid_ies);
 +      kfree(params->beacon.rnr_ies);
 +      kfree(params);
        return err;
  }
  
@@@ -7298,7 -7324,9 +7317,7 @@@ static int nl80211_set_station(struct s
        }
  
        /* driver will call cfg80211_check_station_change() */
 -      wdev_lock(dev->ieee80211_ptr);
        err = rdev_change_station(rdev, dev, mac_addr, &params);
 -      wdev_unlock(dev->ieee80211_ptr);
  
   out_put_vlan:
        dev_put(params.vlan);
@@@ -7566,6 -7594,7 +7585,6 @@@ static int nl80211_new_station(struct s
  
        /* be aware of params.vlan when changing code here */
  
 -      wdev_lock(dev->ieee80211_ptr);
        if (wdev->valid_links) {
                if (params.link_sta_params.link_id < 0) {
                        err = -EINVAL;
        }
        err = rdev_add_station(rdev, dev, mac_addr, &params);
  out:
 -      wdev_unlock(dev->ieee80211_ptr);
        dev_put(params.vlan);
        return err;
  }
@@@ -7592,6 -7622,7 +7611,6 @@@ static int nl80211_del_station(struct s
        struct cfg80211_registered_device *rdev = info->user_ptr[0];
        struct net_device *dev = info->user_ptr[1];
        struct station_del_parameters params;
 -      int ret;
  
        memset(&params, 0, sizeof(params));
  
                params.reason_code = WLAN_REASON_PREV_AUTH_NOT_VALID;
        }
  
 -      wdev_lock(dev->ieee80211_ptr);
 -      ret = rdev_del_station(rdev, dev, &params);
 -      wdev_unlock(dev->ieee80211_ptr);
 -
 -      return ret;
 +      return rdev_del_station(rdev, dev, &params);
  }
  
  static int nl80211_send_mpath(struct sk_buff *msg, u32 portid, u32 seq,
@@@ -7958,7 -7993,9 +7977,7 @@@ static int nl80211_set_bss(struct sk_bu
  {
        struct cfg80211_registered_device *rdev = info->user_ptr[0];
        struct net_device *dev = info->user_ptr[1];
 -      struct wireless_dev *wdev = dev->ieee80211_ptr;
        struct bss_parameters params;
 -      int err;
  
        memset(&params, 0, sizeof(params));
        params.link_id = nl80211_link_id_or_invalid(info->attrs);
            dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
                return -EOPNOTSUPP;
  
 -      wdev_lock(wdev);
 -      err = rdev_change_bss(rdev, dev, &params);
 -      wdev_unlock(wdev);
 -
 -      return err;
 +      return rdev_change_bss(rdev, dev, &params);
  }
  
  static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info)
@@@ -8092,11 -8133,13 +8111,11 @@@ static int nl80211_get_mesh_config(stru
        if (!rdev->ops->get_mesh_config)
                return -EOPNOTSUPP;
  
 -      wdev_lock(wdev);
        /* If not connected, get default parameters */
        if (!wdev->u.mesh.id_len)
                memcpy(&cur_params, &default_mesh_config, sizeof(cur_params));
        else
                err = rdev_get_mesh_config(rdev, dev, &cur_params);
 -      wdev_unlock(wdev);
  
        if (err)
                return err;
@@@ -8458,7 -8501,7 +8477,7 @@@ static int nl80211_update_mesh_config(s
        struct cfg80211_registered_device *rdev = info->user_ptr[0];
        struct net_device *dev = info->user_ptr[1];
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct mesh_config cfg;
+       struct mesh_config cfg = {};
        u32 mask;
        int err;
  
        if (err)
                return err;
  
 -      wdev_lock(wdev);
        if (!wdev->u.mesh.id_len)
                err = -ENOLINK;
  
        if (!err)
                err = rdev_update_mesh_config(rdev, dev, mask, &cfg);
  
 -      wdev_unlock(wdev);
 -
        return err;
  }
  
@@@ -8532,11 -8578,6 +8551,11 @@@ static int nl80211_put_regdom(const str
                                reg_rule->dfs_cac_ms))
                        goto nla_put_failure;
  
 +              if ((reg_rule->flags & NL80211_RRF_PSD) &&
 +                  nla_put_s8(msg, NL80211_ATTR_POWER_RULE_PSD,
 +                             reg_rule->psd))
 +                      goto nla_put_failure;
 +
                nla_nest_end(msg, nl_reg_rule);
        }
  
@@@ -8973,7 -9014,7 +8992,7 @@@ static bool cfg80211_off_channel_oper_a
        unsigned int link_id;
        bool all_ok = true;
  
 -      ASSERT_WDEV_LOCK(wdev);
 +      lockdep_assert_wiphy(wdev->wiphy);
  
        if (!cfg80211_beaconing_iface_active(wdev))
                return true;
@@@ -9223,6 -9264,7 +9242,6 @@@ static int nl80211_trigger_scan(struct 
  
        request->n_channels = i;
  
 -      wdev_lock(wdev);
        for (i = 0; i < request->n_channels; i++) {
                struct ieee80211_channel *chan = request->channels[i];
  
                        continue;
  
                if (!cfg80211_wdev_on_sub_chan(wdev, chan, true)) {
 -                      wdev_unlock(wdev);
                        err = -EBUSY;
                        goto out_free;
                }
        }
 -      wdev_unlock(wdev);
  
        i = 0;
        if (n_ssids) {
@@@ -10240,7 -10284,9 +10259,7 @@@ skip_beacons
                        goto free;
        }
  
 -      wdev_lock(wdev);
        err = rdev_channel_switch(rdev, dev, &params);
 -      wdev_unlock(wdev);
  
  free:
        kfree(params.beacon_after.mbssid_ies);
@@@ -10263,7 -10309,7 +10282,7 @@@ static int nl80211_send_bss(struct sk_b
        void *hdr;
        struct nlattr *bss;
  
 -      ASSERT_WDEV_LOCK(wdev);
 +      lockdep_assert_wiphy(wdev->wiphy);
  
        hdr = nl80211hdr_put(msg, NETLINK_CB(cb->skb).portid, seq, flags,
                             NL80211_CMD_NEW_SCAN_RESULTS);
            nla_put_u32(msg, NL80211_BSS_FREQUENCY, res->channel->center_freq) ||
            nla_put_u32(msg, NL80211_BSS_FREQUENCY_OFFSET,
                        res->channel->freq_offset) ||
 -          nla_put_u32(msg, NL80211_BSS_CHAN_WIDTH, res->scan_width) ||
            nla_put_u32(msg, NL80211_BSS_SEEN_MS_AGO,
                        jiffies_to_msecs(jiffies - intbss->ts)))
                goto nla_put_failure;
@@@ -10411,6 -10458,7 +10430,6 @@@ static int nl80211_dump_scan(struct sk_
        /* nl80211_prepare_wdev_dump acquired it in the successful case */
        __acquire(&rdev->wiphy.mtx);
  
 -      wdev_lock(wdev);
        spin_lock_bh(&rdev->bss_lock);
  
        /*
        }
  
        spin_unlock_bh(&rdev->bss_lock);
 -      wdev_unlock(wdev);
  
        cb->args[2] = idx;
        wiphy_unlock(&rdev->wiphy);
@@@ -10558,7 -10607,9 +10577,7 @@@ static int nl80211_dump_survey(struct s
        }
  
        while (1) {
 -              wdev_lock(wdev);
                res = rdev_dump_survey(rdev, wdev->netdev, survey_idx, &survey);
 -              wdev_unlock(wdev);
                if (res == -ENOENT)
                        break;
                if (res)
@@@ -10731,7 -10782,9 +10750,7 @@@ static int nl80211_authenticate(struct 
        if (!req.bss)
                return -ENOENT;
  
 -      wdev_lock(dev->ieee80211_ptr);
        err = cfg80211_mlme_auth(rdev, dev, &req);
 -      wdev_unlock(dev->ieee80211_ptr);
  
        cfg80211_put_bss(&rdev->wiphy, req.bss);
  
@@@ -10941,9 -10994,8 +10960,9 @@@ static int nl80211_associate(struct sk_
  
                if (cfg80211_find_ext_elem(WLAN_EID_EXT_NON_INHERITANCE,
                                           req.ie, req.ie_len)) {
 -                      GENL_SET_ERR_MSG(info,
 -                                       "non-inheritance makes no sense");
 +                      NL_SET_ERR_MSG_ATTR(info->extack,
 +                                          info->attrs[NL80211_ATTR_IE],
 +                                          "non-inheritance makes no sense");
                        return -EINVAL;
                }
        }
  
                        if (!attrs[NL80211_ATTR_MLO_LINK_ID]) {
                                err = -EINVAL;
 +                              NL_SET_BAD_ATTR(info->extack, link);
                                goto free;
                        }
  
                        /* cannot use the same link ID again */
                        if (req.links[link_id].bss) {
                                err = -EINVAL;
 +                              NL_SET_BAD_ATTR(info->extack, link);
                                goto free;
                        }
                        req.links[link_id].bss =
                        if (IS_ERR(req.links[link_id].bss)) {
                                err = PTR_ERR(req.links[link_id].bss);
                                req.links[link_id].bss = NULL;
 +                              NL_SET_ERR_MSG_ATTR(info->extack,
 +                                                  link, "Error fetching BSS for link");
                                goto free;
                        }
  
                                if (cfg80211_find_elem(WLAN_EID_FRAGMENT,
                                                       req.links[link_id].elems,
                                                       req.links[link_id].elems_len)) {
 -                                      GENL_SET_ERR_MSG(info,
 -                                                       "cannot deal with fragmentation");
 +                                      NL_SET_ERR_MSG_ATTR(info->extack,
 +                                                          attrs[NL80211_ATTR_IE],
 +                                                          "cannot deal with fragmentation");
                                        err = -EINVAL;
                                        goto free;
                                }
                                if (cfg80211_find_ext_elem(WLAN_EID_EXT_NON_INHERITANCE,
                                                           req.links[link_id].elems,
                                                           req.links[link_id].elems_len)) {
 -                                      GENL_SET_ERR_MSG(info,
 -                                                       "cannot deal with non-inheritance");
 +                                      NL_SET_ERR_MSG_ATTR(info->extack,
 +                                                          attrs[NL80211_ATTR_IE],
 +                                                          "cannot deal with non-inheritance");
                                        err = -EINVAL;
                                        goto free;
                                }
  
        err = nl80211_crypto_settings(rdev, info, &req.crypto, 1);
        if (!err) {
 -              wdev_lock(dev->ieee80211_ptr);
 +              struct nlattr *link;
 +              int rem = 0;
  
                err = cfg80211_mlme_assoc(rdev, dev, &req);
  
                               ap_addr, ETH_ALEN);
                }
  
 -              wdev_unlock(dev->ieee80211_ptr);
 +              /* Report error from first problematic link */
 +              if (info->attrs[NL80211_ATTR_MLO_LINKS]) {
 +                      nla_for_each_nested(link,
 +                                          info->attrs[NL80211_ATTR_MLO_LINKS],
 +                                          rem) {
 +                              struct nlattr *link_id_attr =
 +                                      nla_find_nested(link, NL80211_ATTR_MLO_LINK_ID);
 +
 +                              if (!link_id_attr)
 +                                      continue;
 +
 +                              link_id = nla_get_u8(link_id_attr);
 +
 +                              if (link_id == req.link_id)
 +                                      continue;
 +
 +                              if (!req.links[link_id].error ||
 +                                  WARN_ON(req.links[link_id].error > 0))
 +                                      continue;
 +
 +                              WARN_ON(err >= 0);
 +
 +                              NL_SET_BAD_ATTR(info->extack, link);
 +                              err = req.links[link_id].error;
 +                              break;
 +                      }
 +              }
        }
  
  free:
@@@ -11208,7 -11227,7 +11227,7 @@@ static int nl80211_deauthenticate(struc
        struct cfg80211_registered_device *rdev = info->user_ptr[0];
        struct net_device *dev = info->user_ptr[1];
        const u8 *ie = NULL, *bssid;
 -      int ie_len = 0, err;
 +      int ie_len = 0;
        u16 reason_code;
        bool local_state_change;
  
  
        local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE];
  
 -      wdev_lock(dev->ieee80211_ptr);
 -      err = cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason_code,
 -                                 local_state_change);
 -      wdev_unlock(dev->ieee80211_ptr);
 -      return err;
 +      return cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason_code,
 +                                  local_state_change);
  }
  
  static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info)
        struct cfg80211_registered_device *rdev = info->user_ptr[0];
        struct net_device *dev = info->user_ptr[1];
        const u8 *ie = NULL, *bssid;
 -      int ie_len = 0, err;
 +      int ie_len = 0;
        u16 reason_code;
        bool local_state_change;
  
  
        local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE];
  
 -      wdev_lock(dev->ieee80211_ptr);
 -      err = cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason_code,
 -                                   local_state_change);
 -      wdev_unlock(dev->ieee80211_ptr);
 -      return err;
 +      return cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason_code,
 +                                    local_state_change);
  }
  
  static bool
@@@ -11468,11 -11493,13 +11487,11 @@@ static int nl80211_join_ibss(struct sk_
        ibss.userspace_handles_dfs =
                nla_get_flag(info->attrs[NL80211_ATTR_HANDLE_DFS]);
  
 -      wdev_lock(dev->ieee80211_ptr);
        err = __cfg80211_join_ibss(rdev, dev, &ibss, connkeys);
        if (err)
                kfree_sensitive(connkeys);
        else if (info->attrs[NL80211_ATTR_SOCKET_OWNER])
                dev->ieee80211_ptr->conn_owner_nlportid = info->snd_portid;
 -      wdev_unlock(dev->ieee80211_ptr);
  
        return err;
  }
@@@ -12005,6 -12032,8 +12024,6 @@@ static int nl80211_connect(struct sk_bu
        if (nla_get_flag(info->attrs[NL80211_ATTR_MLO_SUPPORT]))
                connect.flags |= CONNECT_REQ_MLO_SUPPORT;
  
 -      wdev_lock(dev->ieee80211_ptr);
 -
        err = cfg80211_connect(rdev, dev, &connect, connkeys,
                               connect.prev_bssid);
        if (err)
                        eth_zero_addr(dev->ieee80211_ptr->disconnect_bssid);
        }
  
 -      wdev_unlock(dev->ieee80211_ptr);
 -
        return err;
  }
  
@@@ -12032,6 -12063,7 +12051,6 @@@ static int nl80211_update_connect_param
        bool fils_sk_offload;
        u32 auth_type;
        u32 changed = 0;
 -      int ret;
  
        if (!rdev->ops->update_connect_params)
                return -EOPNOTSUPP;
                changed |= UPDATE_AUTH_TYPE;
        }
  
 -      wdev_lock(dev->ieee80211_ptr);
        if (!wdev->connected)
 -              ret = -ENOLINK;
 -      else
 -              ret = rdev_update_connect_params(rdev, dev, &connect, changed);
 -      wdev_unlock(dev->ieee80211_ptr);
 +              return -ENOLINK;
  
 -      return ret;
 +      return rdev_update_connect_params(rdev, dev, &connect, changed);
  }
  
  static int nl80211_disconnect(struct sk_buff *skb, struct genl_info *info)
        struct cfg80211_registered_device *rdev = info->user_ptr[0];
        struct net_device *dev = info->user_ptr[1];
        u16 reason;
 -      int ret;
  
        if (dev->ieee80211_ptr->conn_owner_nlportid &&
            dev->ieee80211_ptr->conn_owner_nlportid != info->snd_portid)
            dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
                return -EOPNOTSUPP;
  
 -      wdev_lock(dev->ieee80211_ptr);
 -      ret = cfg80211_disconnect(rdev, dev, reason, true);
 -      wdev_unlock(dev->ieee80211_ptr);
 -      return ret;
 +      return cfg80211_disconnect(rdev, dev, reason, true);
  }
  
  static int nl80211_wiphy_netns(struct sk_buff *skb, struct genl_info *info)
@@@ -12331,6 -12371,7 +12350,6 @@@ static int nl80211_remain_on_channel(st
        if (err)
                return err;
  
 -      wdev_lock(wdev);
        if (!cfg80211_off_channel_oper_allowed(wdev, chandef.chan)) {
                const struct cfg80211_chan_def *oper_chandef, *compat_chandef;
  
                if (WARN_ON(!oper_chandef)) {
                        /* cannot happen since we must beacon to get here */
                        WARN_ON(1);
 -                      wdev_unlock(wdev);
                        return -EBUSY;
                }
  
                compat_chandef = cfg80211_chandef_compatible(&chandef,
                                                             oper_chandef);
  
 -              if (compat_chandef != &chandef) {
 -                      wdev_unlock(wdev);
 +              if (compat_chandef != &chandef)
                        return -EBUSY;
 -              }
        }
 -      wdev_unlock(wdev);
  
        msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
        if (!msg)
@@@ -12407,18 -12452,23 +12426,18 @@@ static int nl80211_set_tx_bitrate_mask(
        unsigned int link_id = nl80211_link_id(info->attrs);
        struct cfg80211_registered_device *rdev = info->user_ptr[0];
        struct net_device *dev = info->user_ptr[1];
 -      struct wireless_dev *wdev = dev->ieee80211_ptr;
        int err;
  
        if (!rdev->ops->set_bitrate_mask)
                return -EOPNOTSUPP;
  
 -      wdev_lock(wdev);
        err = nl80211_parse_tx_bitrate_mask(info, info->attrs,
                                            NL80211_ATTR_TX_RATES, &mask,
                                            dev, true, link_id);
        if (err)
 -              goto out;
 +              return err;
  
 -      err = rdev_set_bitrate_mask(rdev, dev, link_id, NULL, &mask);
 -out:
 -      wdev_unlock(wdev);
 -      return err;
 +      return rdev_set_bitrate_mask(rdev, dev, link_id, NULL, &mask);
  }
  
  static int nl80211_register_mgmt(struct sk_buff *skb, struct genl_info *info)
@@@ -12547,9 -12597,12 +12566,9 @@@ static int nl80211_tx_mgmt(struct sk_bu
        if (!chandef.chan && params.offchan)
                return -EINVAL;
  
 -      wdev_lock(wdev);
        if (params.offchan &&
 -          !cfg80211_off_channel_oper_allowed(wdev, chandef.chan)) {
 -              wdev_unlock(wdev);
 +          !cfg80211_off_channel_oper_allowed(wdev, chandef.chan))
                return -EBUSY;
 -      }
  
        params.link_id = nl80211_link_id_or_invalid(info->attrs);
        /*
         * to the driver.
         */
        if (params.link_id >= 0 &&
 -          !(wdev->valid_links & BIT(params.link_id))) {
 -              wdev_unlock(wdev);
 +          !(wdev->valid_links & BIT(params.link_id)))
                return -EINVAL;
 -      }
 -      wdev_unlock(wdev);
  
        params.buf = nla_data(info->attrs[NL80211_ATTR_FRAME]);
        params.len = nla_len(info->attrs[NL80211_ATTR_FRAME]);
@@@ -12759,7 -12815,8 +12778,8 @@@ static int nl80211_set_cqm_txe(struct g
  }
  
  static int cfg80211_cqm_rssi_update(struct cfg80211_registered_device *rdev,
-                                   struct net_device *dev)
+                                   struct net_device *dev,
+                                   struct cfg80211_cqm_config *cqm_config)
  {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        s32 last, low, high;
        int err;
  
        /* RSSI reporting disabled? */
-       if (!wdev->cqm_config)
+       if (!cqm_config)
                return rdev_set_cqm_rssi_range_config(rdev, dev, 0, 0);
  
        /*
         * connection is established and enough beacons received to calculate
         * the average.
         */
-       if (!wdev->cqm_config->last_rssi_event_value &&
+       if (!cqm_config->last_rssi_event_value &&
            wdev->links[0].client.current_bss &&
            rdev->ops->get_station) {
                struct station_info sinfo = {};
  
                cfg80211_sinfo_release_content(&sinfo);
                if (sinfo.filled & BIT_ULL(NL80211_STA_INFO_BEACON_SIGNAL_AVG))
-                       wdev->cqm_config->last_rssi_event_value =
+                       cqm_config->last_rssi_event_value =
                                (s8) sinfo.rx_beacon_signal_avg;
        }
  
-       last = wdev->cqm_config->last_rssi_event_value;
-       hyst = wdev->cqm_config->rssi_hyst;
-       n = wdev->cqm_config->n_rssi_thresholds;
+       last = cqm_config->last_rssi_event_value;
+       hyst = cqm_config->rssi_hyst;
+       n = cqm_config->n_rssi_thresholds;
  
        for (i = 0; i < n; i++) {
                i = array_index_nospec(i, n);
-               if (last < wdev->cqm_config->rssi_thresholds[i])
+               if (last < cqm_config->rssi_thresholds[i])
                        break;
        }
  
        low_index = i - 1;
        if (low_index >= 0) {
                low_index = array_index_nospec(low_index, n);
-               low = wdev->cqm_config->rssi_thresholds[low_index] - hyst;
+               low = cqm_config->rssi_thresholds[low_index] - hyst;
        } else {
                low = S32_MIN;
        }
        if (i < n) {
                i = array_index_nospec(i, n);
-               high = wdev->cqm_config->rssi_thresholds[i] + hyst - 1;
+               high = cqm_config->rssi_thresholds[i] + hyst - 1;
        } else {
                high = S32_MAX;
        }
@@@ -12827,10 -12884,11 +12847,11 @@@ static int nl80211_set_cqm_rssi(struct 
                                u32 hysteresis)
  {
        struct cfg80211_registered_device *rdev = info->user_ptr[0];
+       struct cfg80211_cqm_config *cqm_config = NULL, *old;
        struct net_device *dev = info->user_ptr[1];
        struct wireless_dev *wdev = dev->ieee80211_ptr;
 -      int i, err;
        s32 prev = S32_MIN;
-       int i;
++      int i, err;
  
        /* Check all values negative and sorted */
        for (i = 0; i < n_thresholds; i++) {
            wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)
                return -EOPNOTSUPP;
  
-       cfg80211_cqm_config_free(wdev);
        if (n_thresholds <= 1 && rdev->ops->set_cqm_rssi_config) {
                if (n_thresholds == 0 || thresholds[0] == 0) /* Disabling */
                        return rdev_set_cqm_rssi_config(rdev, dev, 0, 0);
        if (n_thresholds == 1 && thresholds[0] == 0) /* Disabling */
                n_thresholds = 0;
  
-       if (n_thresholds) {
-               struct cfg80211_cqm_config *cqm_config;
 -      wdev_lock(wdev);
 -      old = rcu_dereference_protected(wdev->cqm_config,
 -                                      lockdep_is_held(&wdev->mtx));
++      old = wiphy_dereference(wdev->wiphy, wdev->cqm_config);
  
+       if (n_thresholds) {
                cqm_config = kzalloc(struct_size(cqm_config, rssi_thresholds,
                                                 n_thresholds),
                                     GFP_KERNEL);
 -              if (!cqm_config) {
 -                      err = -ENOMEM;
 -                      goto unlock;
 -              }
 +              if (!cqm_config)
 +                      return -ENOMEM;
  
                cqm_config->rssi_hyst = hysteresis;
                cqm_config->n_rssi_thresholds = n_thresholds;
                       flex_array_size(cqm_config, rssi_thresholds,
                                       n_thresholds));
  
-               wdev->cqm_config = cqm_config;
+               rcu_assign_pointer(wdev->cqm_config, cqm_config);
+       } else {
+               RCU_INIT_POINTER(wdev->cqm_config, NULL);
+       }
+       err = cfg80211_cqm_rssi_update(rdev, dev, cqm_config);
+       if (err) {
+               rcu_assign_pointer(wdev->cqm_config, old);
+               kfree_rcu(cqm_config, rcu_head);
+       } else {
+               kfree_rcu(old, rcu_head);
        }
 -unlock:
 -      wdev_unlock(wdev);
  
-       return cfg80211_cqm_rssi_update(rdev, dev);
+       return err;
  }
  
  static int nl80211_set_cqm(struct sk_buff *skb, struct genl_info *info)
@@@ -13061,9 -13133,11 +13090,9 @@@ static int nl80211_join_mesh(struct sk_
                setup.control_port_over_nl80211 = true;
        }
  
 -      wdev_lock(dev->ieee80211_ptr);
        err = __cfg80211_join_mesh(rdev, dev, &setup, &cfg);
        if (!err && info->attrs[NL80211_ATTR_SOCKET_OWNER])
                dev->ieee80211_ptr->conn_owner_nlportid = info->snd_portid;
 -      wdev_unlock(dev->ieee80211_ptr);
  
        return err;
  }
@@@ -14007,13 -14081,21 +14036,13 @@@ static int nl80211_set_rekey_data(struc
        if (tb[NL80211_REKEY_DATA_AKM])
                rekey_data.akm = nla_get_u32(tb[NL80211_REKEY_DATA_AKM]);
  
 -      wdev_lock(wdev);
 -      if (!wdev->connected) {
 -              err = -ENOTCONN;
 -              goto out;
 -      }
 +      if (!wdev->connected)
 +              return -ENOTCONN;
  
 -      if (!rdev->ops->set_rekey_data) {
 -              err = -EOPNOTSUPP;
 -              goto out;
 -      }
 +      if (!rdev->ops->set_rekey_data)
 +              return -EOPNOTSUPP;
  
 -      err = rdev_set_rekey_data(rdev, dev, &rekey_data);
 - out:
 -      wdev_unlock(wdev);
 -      return err;
 +      return rdev_set_rekey_data(rdev, dev, &rekey_data);
  }
  
  static int nl80211_register_unexpected_frame(struct sk_buff *skb,
@@@ -15217,9 -15299,11 +15246,9 @@@ static int nl80211_set_qos_map(struct s
                memcpy(qos_map->up, pos, IEEE80211_QOS_MAP_LEN_MIN);
        }
  
 -      wdev_lock(dev->ieee80211_ptr);
        ret = nl80211_key_allowed(dev->ieee80211_ptr);
        if (!ret)
                ret = rdev_set_qos_map(rdev, dev, qos_map);
 -      wdev_unlock(dev->ieee80211_ptr);
  
        kfree(qos_map);
        return ret;
@@@ -15233,6 -15317,7 +15262,6 @@@ static int nl80211_add_tx_ts(struct sk_
        const u8 *peer;
        u8 tsid, up;
        u16 admitted_time = 0;
 -      int err;
  
        if (!(rdev->wiphy.features & NL80211_FEATURE_SUPPORTS_WMM_ADMISSION))
                return -EOPNOTSUPP;
                        return -EINVAL;
        }
  
 -      wdev_lock(wdev);
        switch (wdev->iftype) {
        case NL80211_IFTYPE_STATION:
        case NL80211_IFTYPE_P2P_CLIENT:
                if (wdev->connected)
                        break;
 -              err = -ENOTCONN;
 -              goto out;
 +              return -ENOTCONN;
        default:
 -              err = -EOPNOTSUPP;
 -              goto out;
 +              return -EOPNOTSUPP;
        }
  
 -      err = rdev_add_tx_ts(rdev, dev, tsid, peer, up, admitted_time);
 -
 - out:
 -      wdev_unlock(wdev);
 -      return err;
 +      return rdev_add_tx_ts(rdev, dev, tsid, peer, up, admitted_time);
  }
  
  static int nl80211_del_tx_ts(struct sk_buff *skb, struct genl_info *info)
  {
        struct cfg80211_registered_device *rdev = info->user_ptr[0];
        struct net_device *dev = info->user_ptr[1];
 -      struct wireless_dev *wdev = dev->ieee80211_ptr;
        const u8 *peer;
        u8 tsid;
 -      int err;
  
        if (!info->attrs[NL80211_ATTR_TSID] || !info->attrs[NL80211_ATTR_MAC])
                return -EINVAL;
        tsid = nla_get_u8(info->attrs[NL80211_ATTR_TSID]);
        peer = nla_data(info->attrs[NL80211_ATTR_MAC]);
  
 -      wdev_lock(wdev);
 -      err = rdev_del_tx_ts(rdev, dev, tsid, peer);
 -      wdev_unlock(wdev);
 -
 -      return err;
 +      return rdev_del_tx_ts(rdev, dev, tsid, peer);
  }
  
  static int nl80211_tdls_channel_switch(struct sk_buff *skb,
        addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
        oper_class = nla_get_u8(info->attrs[NL80211_ATTR_OPER_CLASS]);
  
 -      wdev_lock(wdev);
 -      err = rdev_tdls_channel_switch(rdev, dev, addr, oper_class, &chandef);
 -      wdev_unlock(wdev);
 -
 -      return err;
 +      return rdev_tdls_channel_switch(rdev, dev, addr, oper_class, &chandef);
  }
  
  static int nl80211_tdls_cancel_channel_switch(struct sk_buff *skb,
  {
        struct cfg80211_registered_device *rdev = info->user_ptr[0];
        struct net_device *dev = info->user_ptr[1];
 -      struct wireless_dev *wdev = dev->ieee80211_ptr;
        const u8 *addr;
  
        if (!rdev->ops->tdls_channel_switch ||
  
        addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
  
 -      wdev_lock(wdev);
        rdev_tdls_cancel_channel_switch(rdev, dev, addr);
 -      wdev_unlock(wdev);
  
        return 0;
  }
@@@ -15405,6 -15510,7 +15434,6 @@@ static int nl80211_set_pmk(struct sk_bu
        struct net_device *dev = info->user_ptr[1];
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        struct cfg80211_pmk_conf pmk_conf = {};
 -      int ret;
  
        if (wdev->iftype != NL80211_IFTYPE_STATION &&
            wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)
        if (!info->attrs[NL80211_ATTR_MAC] || !info->attrs[NL80211_ATTR_PMK])
                return -EINVAL;
  
 -      wdev_lock(wdev);
 -      if (!wdev->connected) {
 -              ret = -ENOTCONN;
 -              goto out;
 -      }
 +      if (!wdev->connected)
 +              return -ENOTCONN;
  
        pmk_conf.aa = nla_data(info->attrs[NL80211_ATTR_MAC]);
 -      if (memcmp(pmk_conf.aa, wdev->u.client.connected_addr, ETH_ALEN)) {
 -              ret = -EINVAL;
 -              goto out;
 -      }
 +      if (memcmp(pmk_conf.aa, wdev->u.client.connected_addr, ETH_ALEN))
 +              return -EINVAL;
  
        pmk_conf.pmk = nla_data(info->attrs[NL80211_ATTR_PMK]);
        pmk_conf.pmk_len = nla_len(info->attrs[NL80211_ATTR_PMK]);
        if (pmk_conf.pmk_len != WLAN_PMK_LEN &&
 -          pmk_conf.pmk_len != WLAN_PMK_LEN_SUITE_B_192) {
 -              ret = -EINVAL;
 -              goto out;
 -      }
 +          pmk_conf.pmk_len != WLAN_PMK_LEN_SUITE_B_192)
 +              return -EINVAL;
  
        if (info->attrs[NL80211_ATTR_PMKR0_NAME])
                pmk_conf.pmk_r0_name =
                        nla_data(info->attrs[NL80211_ATTR_PMKR0_NAME]);
  
 -      ret = rdev_set_pmk(rdev, dev, &pmk_conf);
 -out:
 -      wdev_unlock(wdev);
 -      return ret;
 +      return rdev_set_pmk(rdev, dev, &pmk_conf);
  }
  
  static int nl80211_del_pmk(struct sk_buff *skb, struct genl_info *info)
        struct net_device *dev = info->user_ptr[1];
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        const u8 *aa;
 -      int ret;
  
        if (wdev->iftype != NL80211_IFTYPE_STATION &&
            wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)
        if (!info->attrs[NL80211_ATTR_MAC])
                return -EINVAL;
  
 -      wdev_lock(wdev);
        aa = nla_data(info->attrs[NL80211_ATTR_MAC]);
 -      ret = rdev_del_pmk(rdev, dev, aa);
 -      wdev_unlock(wdev);
 -
 -      return ret;
 +      return rdev_del_pmk(rdev, dev, aa);
  }
  
  static int nl80211_external_auth(struct sk_buff *skb, struct genl_info *info)
@@@ -15530,6 -15651,8 +15559,6 @@@ static int nl80211_tx_control_port(stru
                return -EINVAL;
        }
  
 -      wdev_lock(wdev);
 -
        switch (wdev->iftype) {
        case NL80211_IFTYPE_AP:
        case NL80211_IFTYPE_P2P_GO:
        case NL80211_IFTYPE_ADHOC:
                if (wdev->u.ibss.current_bss)
                        break;
 -              err = -ENOTCONN;
 -              goto out;
 +              return -ENOTCONN;
        case NL80211_IFTYPE_STATION:
        case NL80211_IFTYPE_P2P_CLIENT:
                if (wdev->connected)
                        break;
 -              err = -ENOTCONN;
 -              goto out;
 +              return -ENOTCONN;
        default:
 -              err = -EOPNOTSUPP;
 -              goto out;
 +              return -EOPNOTSUPP;
        }
  
 -      wdev_unlock(wdev);
 -
        buf = nla_data(info->attrs[NL80211_ATTR_FRAME]);
        len = nla_len(info->attrs[NL80211_ATTR_FRAME]);
        dest = nla_data(info->attrs[NL80211_ATTR_MAC]);
        if (!err && !dont_wait_for_ack)
                nl_set_extack_cookie_u64(info->extack, cookie);
        return err;
 - out:
 -      wdev_unlock(wdev);
 -      return err;
  }
  
  static int nl80211_get_ftm_responder_stats(struct sk_buff *skb,
@@@ -15840,6 -15971,8 +15869,6 @@@ static int nl80211_set_tid_config(struc
        if (info->attrs[NL80211_ATTR_MAC])
                tid_config->peer = nla_data(info->attrs[NL80211_ATTR_MAC]);
  
 -      wdev_lock(dev->ieee80211_ptr);
 -
        nla_for_each_nested(tid, info->attrs[NL80211_ATTR_TID_CONFIG],
                            rem_conf) {
                ret = nla_parse_nested(attrs, NL80211_TID_CONFIG_ATTR_MAX,
  
  bad_tid_conf:
        kfree(tid_config);
 -      wdev_unlock(dev->ieee80211_ptr);
        return ret;
  }
  
@@@ -15957,7 -16091,9 +15986,7 @@@ static int nl80211_color_change(struct 
                params.counter_offset_presp = offset;
        }
  
 -      wdev_lock(wdev);
        err = rdev_color_change(rdev, dev, &params);
 -      wdev_unlock(wdev);
  
  out:
        kfree(params.beacon_next.mbssid_ies);
@@@ -16013,6 -16149,7 +16042,6 @@@ static int nl80211_add_link(struct sk_b
            !is_valid_ether_addr(nla_data(info->attrs[NL80211_ATTR_MAC])))
                return -EINVAL;
  
 -      wdev_lock(wdev);
        wdev->valid_links |= BIT(link_id);
        ether_addr_copy(wdev->links[link_id].addr,
                        nla_data(info->attrs[NL80211_ATTR_MAC]));
                wdev->valid_links &= ~BIT(link_id);
                eth_zero_addr(wdev->links[link_id].addr);
        }
 -      wdev_unlock(wdev);
  
        return ret;
  }
@@@ -16043,7 -16181,9 +16072,7 @@@ static int nl80211_remove_link(struct s
                return -EINVAL;
        }
  
 -      wdev_lock(wdev);
        cfg80211_remove_link(wdev, link_id);
 -      wdev_unlock(wdev);
  
        return 0;
  }
@@@ -16133,10 -16273,14 +16162,10 @@@ nl80211_add_mod_link_station(struct sk_
        if (err)
                return err;
  
 -      wdev_lock(dev->ieee80211_ptr);
        if (add)
 -              err = rdev_add_link_station(rdev, dev, &params);
 -      else
 -              err = rdev_mod_link_station(rdev, dev, &params);
 -      wdev_unlock(dev->ieee80211_ptr);
 +              return rdev_add_link_station(rdev, dev, &params);
  
 -      return err;
 +      return rdev_mod_link_station(rdev, dev, &params);
  }
  
  static int
@@@ -16157,6 -16301,7 +16186,6 @@@ nl80211_remove_link_station(struct sk_b
        struct link_station_del_parameters params = {};
        struct cfg80211_registered_device *rdev = info->user_ptr[0];
        struct net_device *dev = info->user_ptr[1];
 -      int ret;
  
        if (!rdev->ops->del_link_station)
                return -EOPNOTSUPP;
        params.mld_mac = nla_data(info->attrs[NL80211_ATTR_MLD_ADDR]);
        params.link_id = nla_get_u8(info->attrs[NL80211_ATTR_MLO_LINK_ID]);
  
 -      wdev_lock(dev->ieee80211_ptr);
 -      ret = rdev_del_link_station(rdev, dev, &params);
 -      wdev_unlock(dev->ieee80211_ptr);
 -
 -      return ret;
 +      return rdev_del_link_station(rdev, dev, &params);
  }
  
  static int nl80211_set_hw_timestamp(struct sk_buff *skb,
@@@ -18176,7 -18325,7 +18205,7 @@@ void cfg80211_links_removed(struct net_
        struct nlattr *links;
        void *hdr;
  
 -      ASSERT_WDEV_LOCK(wdev);
 +      lockdep_assert_wiphy(wdev->wiphy);
        trace_cfg80211_links_removed(dev, link_mask);
  
        if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION &&
@@@ -18949,9 -19098,8 +18978,8 @@@ void cfg80211_cqm_rssi_notify(struct ne
                              enum nl80211_cqm_rssi_threshold_event rssi_event,
                              s32 rssi_level, gfp_t gfp)
  {
-       struct sk_buff *msg;
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+       struct cfg80211_cqm_config *cqm_config;
  
        trace_cfg80211_cqm_rssi_notify(dev, rssi_event, rssi_level);
  
                    rssi_event != NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH))
                return;
  
-       if (wdev->cqm_config) {
-               wdev->cqm_config->last_rssi_event_value = rssi_level;
+       rcu_read_lock();
+       cqm_config = rcu_dereference(wdev->cqm_config);
+       if (cqm_config) {
+               cqm_config->last_rssi_event_value = rssi_level;
+               cqm_config->last_rssi_event_type = rssi_event;
+               wiphy_work_queue(wdev->wiphy, &wdev->cqm_rssi_work);
+       }
+       rcu_read_unlock();
+ }
+ EXPORT_SYMBOL(cfg80211_cqm_rssi_notify);
  
-               cfg80211_cqm_rssi_update(rdev, dev);
+ void cfg80211_cqm_rssi_notify_work(struct wiphy *wiphy, struct wiphy_work *work)
+ {
+       struct wireless_dev *wdev = container_of(work, struct wireless_dev,
+                                                cqm_rssi_work);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+       enum nl80211_cqm_rssi_threshold_event rssi_event;
+       struct cfg80211_cqm_config *cqm_config;
+       struct sk_buff *msg;
+       s32 rssi_level;
  
-               if (rssi_level == 0)
-                       rssi_level = wdev->cqm_config->last_rssi_event_value;
-       }
 -      wdev_lock(wdev);
 -      cqm_config = rcu_dereference_protected(wdev->cqm_config,
 -                                             lockdep_is_held(&wdev->mtx));
++      cqm_config = wiphy_dereference(wdev->wiphy, wdev->cqm_config);
+       if (!wdev->cqm_config)
 -              goto unlock;
++              return;
+       cfg80211_cqm_rssi_update(rdev, wdev->netdev, cqm_config);
+       rssi_level = cqm_config->last_rssi_event_value;
+       rssi_event = cqm_config->last_rssi_event_type;
  
-       msg = cfg80211_prepare_cqm(dev, NULL, gfp);
+       msg = cfg80211_prepare_cqm(wdev->netdev, NULL, GFP_KERNEL);
        if (!msg)
 -              goto unlock;
 +              return;
  
        if (nla_put_u32(msg, NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT,
                        rssi_event))
                                      rssi_level))
                goto nla_put_failure;
  
-       cfg80211_send_cqm(msg, gfp);
+       cfg80211_send_cqm(msg, GFP_KERNEL);
  
 -      goto unlock;
 +      return;
  
   nla_put_failure:
        nlmsg_free(msg);
 - unlock:
 -      wdev_unlock(wdev);
  }
- EXPORT_SYMBOL(cfg80211_cqm_rssi_notify);
  
  void cfg80211_cqm_txe_notify(struct net_device *dev,
                             const u8 *peer, u32 num_packets,
@@@ -19230,7 -19402,7 +19278,7 @@@ void cfg80211_ch_switch_notify(struct n
        struct wiphy *wiphy = wdev->wiphy;
        struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
  
 -      ASSERT_WDEV_LOCK(wdev);
 +      lockdep_assert_wiphy(wdev->wiphy);
        WARN_INVALID_LINK_ID(wdev, link_id);
  
        trace_cfg80211_ch_switch_notify(dev, chandef, link_id, punct_bitmap);
@@@ -19275,7 -19447,7 +19323,7 @@@ void cfg80211_ch_switch_started_notify(
        struct wiphy *wiphy = wdev->wiphy;
        struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
  
 -      ASSERT_WDEV_LOCK(wdev);
 +      lockdep_assert_wiphy(wdev->wiphy);
        WARN_INVALID_LINK_ID(wdev, link_id);
  
        trace_cfg80211_ch_switch_started_notify(dev, chandef, link_id,
@@@ -19298,7 -19470,7 +19346,7 @@@ int cfg80211_bss_color_notify(struct ne
        struct sk_buff *msg;
        void *hdr;
  
 -      ASSERT_WDEV_LOCK(wdev);
 +      lockdep_assert_wiphy(wdev->wiphy);
  
        trace_cfg80211_bss_color_notify(dev, cmd, count, color_bitmap);
  
diff --combined net/wireless/scan.c
index a5758edf53b855654a2094ab99febb05f14d9e5a,939deecf0bbeffd550b2f571f7da4897881d534c..8d114faf48425b51d967cd7dbe7f02d7992b712a
@@@ -908,6 -908,10 +908,10 @@@ static int cfg80211_scan_6ghz(struct cf
                    !cfg80211_find_ssid_match(ap, request))
                        continue;
  
+               if (!is_broadcast_ether_addr(request->bssid) &&
+                   !ether_addr_equal(request->bssid, ap->bssid))
+                       continue;
                if (!request->n_ssids && ap->multi_bss && !ap->transmitted_bssid)
                        continue;
  
@@@ -1638,6 -1642,8 +1642,6 @@@ static bool cfg80211_combine_bsses(stru
                        continue;
                if (bss->pub.channel != new->pub.channel)
                        continue;
 -              if (bss->pub.scan_width != new->pub.scan_width)
 -                      continue;
                if (rcu_access_pointer(bss->pub.beacon_ies))
                        continue;
                ies = rcu_access_pointer(bss->pub.ies);
@@@ -1934,7 -1940,8 +1938,7 @@@ EXPORT_SYMBOL(cfg80211_get_ies_channel_
   */
  static struct ieee80211_channel *
  cfg80211_get_bss_channel(struct wiphy *wiphy, const u8 *ie, size_t ielen,
 -                       struct ieee80211_channel *channel,
 -                       enum nl80211_bss_scan_width scan_width)
 +                       struct ieee80211_channel *channel)
  {
        u32 freq;
        int channel_number;
                return channel;
        }
  
 -      if (scan_width == NL80211_BSS_CHAN_WIDTH_10 ||
 -          scan_width == NL80211_BSS_CHAN_WIDTH_5) {
 -              /*
 -               * Ignore channel number in 5 and 10 MHz channels where there
 -               * may not be an n:1 or 1:n mapping between frequencies and
 -               * channel numbers.
 -               */
 -              return channel;
 -      }
 -
        /*
         * Use the channel determined through the payload channel number
         * instead of the RX channel reported by the driver.
@@@ -2033,12 -2050,14 +2037,12 @@@ cfg80211_inform_single_bss_data(struct 
        channel = data->channel;
        if (!channel)
                channel = cfg80211_get_bss_channel(wiphy, data->ie, data->ielen,
 -                                                 drv_data->chan,
 -                                                 drv_data->scan_width);
 +                                                 drv_data->chan);
        if (!channel)
                return NULL;
  
        memcpy(tmp.pub.bssid, data->bssid, ETH_ALEN);
        tmp.pub.channel = channel;
 -      tmp.pub.scan_width = drv_data->scan_width;
        if (data->bss_source != BSS_SOURCE_STA_PROFILE)
                tmp.pub.signal = drv_data->signal;
        else
@@@ -2339,8 -2358,8 +2343,8 @@@ ssize_t cfg80211_defragment_element(con
  
        /* elem might be invalid after the memmove */
        next = (void *)(elem->data + elem->datalen);
 -
        elem_datalen = elem->datalen;
 +
        if (elem->id == WLAN_EID_EXTENSION) {
                copied = elem->datalen - 1;
                if (copied > data_len)
  
        for (elem = next;
             elem->data < ies + ieslen &&
 -              elem->data + elem->datalen < ies + ieslen;
 +              elem->data + elem->datalen <= ies + ieslen;
             elem = next) {
                /* elem might be invalid after the memmove */
                next = (void *)(elem->data + elem->datalen);
@@@ -2799,7 -2818,8 +2803,7 @@@ cfg80211_inform_single_bss_frame_data(s
                        variable = ext->u.s1g_beacon.variable;
        }
  
 -      channel = cfg80211_get_bss_channel(wiphy, variable,
 -                                         ielen, data->chan, data->scan_width);
 +      channel = cfg80211_get_bss_channel(wiphy, variable, ielen, data->chan);
        if (!channel)
                return NULL;
  
        tmp.pub.beacon_interval = beacon_int;
        tmp.pub.capability = capability;
        tmp.pub.channel = channel;
 -      tmp.pub.scan_width = data->scan_width;
        tmp.pub.signal = data->signal;
        tmp.ts_boottime = data->boottime_ns;
        tmp.parent_tsf = data->parent_tsf;
@@@ -3405,63 -3426,59 +3409,63 @@@ ieee80211_bss(struct wiphy *wiphy, stru
                        cfg = (u8 *)ie + 2;
                        memset(&iwe, 0, sizeof(iwe));
                        iwe.cmd = IWEVCUSTOM;
 -                      sprintf(buf, "Mesh Network Path Selection Protocol ID: "
 -                              "0x%02X", cfg[0]);
 -                      iwe.u.data.length = strlen(buf);
 +                      iwe.u.data.length = sprintf(buf,
 +                                                  "Mesh Network Path Selection Protocol ID: 0x%02X",
 +                                                  cfg[0]);
                        current_ev = iwe_stream_add_point_check(info,
                                                                current_ev,
                                                                end_buf,
                                                                &iwe, buf);
                        if (IS_ERR(current_ev))
                                goto unlock;
 -                      sprintf(buf, "Path Selection Metric ID: 0x%02X",
 -                              cfg[1]);
 -                      iwe.u.data.length = strlen(buf);
 +                      iwe.u.data.length = sprintf(buf,
 +                                                  "Path Selection Metric ID: 0x%02X",
 +                                                  cfg[1]);
                        current_ev = iwe_stream_add_point_check(info,
                                                                current_ev,
                                                                end_buf,
                                                                &iwe, buf);
                        if (IS_ERR(current_ev))
                                goto unlock;
 -                      sprintf(buf, "Congestion Control Mode ID: 0x%02X",
 -                              cfg[2]);
 -                      iwe.u.data.length = strlen(buf);
 +                      iwe.u.data.length = sprintf(buf,
 +                                                  "Congestion Control Mode ID: 0x%02X",
 +                                                  cfg[2]);
                        current_ev = iwe_stream_add_point_check(info,
                                                                current_ev,
                                                                end_buf,
                                                                &iwe, buf);
                        if (IS_ERR(current_ev))
                                goto unlock;
 -                      sprintf(buf, "Synchronization ID: 0x%02X", cfg[3]);
 -                      iwe.u.data.length = strlen(buf);
 +                      iwe.u.data.length = sprintf(buf,
 +                                                  "Synchronization ID: 0x%02X",
 +                                                  cfg[3]);
                        current_ev = iwe_stream_add_point_check(info,
                                                                current_ev,
                                                                end_buf,
                                                                &iwe, buf);
                        if (IS_ERR(current_ev))
                                goto unlock;
 -                      sprintf(buf, "Authentication ID: 0x%02X", cfg[4]);
 -                      iwe.u.data.length = strlen(buf);
 +                      iwe.u.data.length = sprintf(buf,
 +                                                  "Authentication ID: 0x%02X",
 +                                                  cfg[4]);
                        current_ev = iwe_stream_add_point_check(info,
                                                                current_ev,
                                                                end_buf,
                                                                &iwe, buf);
                        if (IS_ERR(current_ev))
                                goto unlock;
 -                      sprintf(buf, "Formation Info: 0x%02X", cfg[5]);
 -                      iwe.u.data.length = strlen(buf);
 +                      iwe.u.data.length = sprintf(buf,
 +                                                  "Formation Info: 0x%02X",
 +                                                  cfg[5]);
                        current_ev = iwe_stream_add_point_check(info,
                                                                current_ev,
                                                                end_buf,
                                                                &iwe, buf);
                        if (IS_ERR(current_ev))
                                goto unlock;
 -                      sprintf(buf, "Capabilities: 0x%02X", cfg[6]);
 -                      iwe.u.data.length = strlen(buf);
 +                      iwe.u.data.length = sprintf(buf,
 +                                                  "Capabilities: 0x%02X",
 +                                                  cfg[6]);
                        current_ev = iwe_stream_add_point_check(info,
                                                                current_ev,
                                                                end_buf,
  
        memset(&iwe, 0, sizeof(iwe));
        iwe.cmd = IWEVCUSTOM;
 -      sprintf(buf, "tsf=%016llx", (unsigned long long)(ies->tsf));
 -      iwe.u.data.length = strlen(buf);
 +      iwe.u.data.length = sprintf(buf, "tsf=%016llx",
 +                                  (unsigned long long)(ies->tsf));
        current_ev = iwe_stream_add_point_check(info, current_ev, end_buf,
                                                &iwe, buf);
        if (IS_ERR(current_ev))
                goto unlock;
        memset(&iwe, 0, sizeof(iwe));
        iwe.cmd = IWEVCUSTOM;
 -      sprintf(buf, " Last beacon: %ums ago",
 -              elapsed_jiffies_msecs(bss->ts));
 -      iwe.u.data.length = strlen(buf);
 +      iwe.u.data.length = sprintf(buf, " Last beacon: %ums ago",
 +                                  elapsed_jiffies_msecs(bss->ts));
        current_ev = iwe_stream_add_point_check(info, current_ev,
                                                end_buf, &iwe, buf);
        if (IS_ERR(current_ev))
This page took 0.428373 seconds and 4 git commands to generate.