int i;
 
        for (i = 0; i < ARRAY_SIZE(vif->link_conf); i++) {
-               struct ieee80211_bss_conf *conf = vif->link_conf[i];
+               struct ieee80211_bss_conf *conf;
                struct ieee80211_chanctx_conf *chanctx;
 
+               conf = rcu_dereference(vif->link_conf[i]);
                if (!conf)
                        continue;
 
 {
        struct mac80211_hwsim_link_data *link_data = arg;
        u32 link_id = link_data->link_id;
-       struct ieee80211_bss_conf *link_conf = vif->link_conf[link_id];
+       struct ieee80211_bss_conf *link_conf;
        struct mac80211_hwsim_data *data =
                container_of(link_data, struct mac80211_hwsim_data,
                             link_data[link_id]);
 
        hwsim_check_magic(vif);
 
+       link_conf = rcu_dereference(vif->link_conf[link_id]);
+       if (!link_conf)
+               return;
+
        if (vif->type != NL80211_IFTYPE_AP &&
            vif->type != NL80211_IFTYPE_MESH_POINT &&
            vif->type != NL80211_IFTYPE_ADHOC &&
 
 static void mac80211_hwsim_link_info_changed(struct ieee80211_hw *hw,
                                             struct ieee80211_vif *vif,
-                                            u32 link_id,
-                                            u64 changed)
+                                            struct ieee80211_bss_conf *info,
+                                            u32 link_id, u64 changed)
 {
        struct hwsim_vif_priv *vp = (void *)vif->drv_priv;
        struct mac80211_hwsim_data *data = hw->priv;
-       struct ieee80211_bss_conf *info = vif->link_conf[link_id];
        struct mac80211_hwsim_link_data *link_data = &data->link_data[link_id];
 
        hwsim_check_magic(vif);
 
        enum nl80211_iftype type;
        struct ieee80211_vif_cfg cfg;
        struct ieee80211_bss_conf bss_conf;
-       struct ieee80211_bss_conf *link_conf[IEEE80211_MLD_MAX_NUM_LINKS];
+       struct ieee80211_bss_conf __rcu *link_conf[IEEE80211_MLD_MAX_NUM_LINKS];
        u16 valid_links;
        u8 addr[ETH_ALEN] __aligned(2);
        bool p2p;
                                u64 changed);
        void (*link_info_changed)(struct ieee80211_hw *hw,
                                  struct ieee80211_vif *vif,
-                                 unsigned int link_id, u64 changed);
+                                 struct ieee80211_bss_conf *info,
+                                 unsigned int link_id,
+                                 u64 changed);
 
        int (*start_ap)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                        unsigned int link_id);
 
                memcpy(sdata->vif.bss_conf.mu_group.position,
                       params->vht_mumimo_groups + WLAN_MEMBERSHIP_LEN,
                       WLAN_USER_POSITION_LEN);
-               ieee80211_link_info_change_notify(sdata, 0,
+               ieee80211_link_info_change_notify(sdata, &sdata->deflink,
                                                  BSS_CHANGED_MU_GROUPS);
                /* don't care about endianness - just check for 0 */
                memcpy(&membership, params->vht_mumimo_groups,
                sdata = wiphy_dereference(local->hw.wiphy,
                                          local->monitor_sdata);
                if (sdata) {
-                       ieee80211_link_release_channel(sdata->link[0]);
-                       ret = ieee80211_link_use_channel(sdata->link[0],
+                       ieee80211_link_release_channel(&sdata->deflink);
+                       ret = ieee80211_link_use_channel(&sdata->deflink,
                                                         chandef,
                                                         IEEE80211_CHANCTX_EXCLUSIVE);
                }
 }
 
 static int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
+                                  struct ieee80211_link_data *link,
                                   struct cfg80211_beacon_data *params,
                                   const struct ieee80211_csa_settings *csa,
                                   const struct ieee80211_color_change_settings *cca)
        int new_head_len, new_tail_len;
        int size, err;
        u32 changed = BSS_CHANGED_BEACON;
-       struct ieee80211_link_data *link = sdata->link[params->link_id];
-       struct ieee80211_bss_conf *link_conf =
-               sdata->vif.link_conf[params->link_id];
+       struct ieee80211_bss_conf *link_conf = link->conf;
 
        old = sdata_dereference(link->u.ap.beacon, sdata);
 
        int i, err;
        int prev_beacon_int;
        unsigned int link_id = params->beacon.link_id;
-       struct ieee80211_link_data *link = sdata->link[link_id];
-       struct ieee80211_bss_conf *link_conf = sdata->vif.link_conf[link_id];
+       struct ieee80211_link_data *link;
+       struct ieee80211_bss_conf *link_conf;
+
+       link = sdata_dereference(sdata->link[link_id], sdata);
+       if (!link)
+               return -ENOLINK;
+
+       link_conf = link->conf;
 
        old = sdata_dereference(link->u.ap.beacon, sdata);
        if (old)
        if (ieee80211_hw_check(&local->hw, HAS_RATE_CONTROL))
                link_conf->beacon_tx_rate = params->beacon_rate;
 
-       err = ieee80211_assign_beacon(sdata, ¶ms->beacon, NULL, NULL);
+       err = ieee80211_assign_beacon(sdata, link, ¶ms->beacon, NULL, NULL);
        if (err < 0)
                goto error;
        changed |= err;
 
        ieee80211_recalc_dtim(local, sdata);
        ieee80211_vif_cfg_change_notify(sdata, BSS_CHANGED_SSID);
-       ieee80211_link_info_change_notify(sdata, link_id, changed);
+       ieee80211_link_info_change_notify(sdata, link, changed);
 
        netif_carrier_on(dev);
        list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
                                   struct cfg80211_beacon_data *params)
 {
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       struct ieee80211_link_data *link;
        struct beacon_data *old;
        int err;
-       struct ieee80211_bss_conf *link_conf =
-               sdata->vif.link_conf[params->link_id];
+       struct ieee80211_bss_conf *link_conf;
 
        sdata_assert_lock(sdata);
 
+       link = sdata_dereference(sdata->link[params->link_id], sdata);
+       if (!link)
+               return -ENOLINK;
+
+       link_conf = link->conf;
+
        /* don't allow changing the beacon while a countdown is in place - offset
         * of channel switch counter may change
         */
        if (link_conf->csa_active || link_conf->color_change_active)
                return -EBUSY;
 
-       old = sdata_dereference(sdata->link[params->link_id]->u.ap.beacon,
-                               sdata);
+       old = sdata_dereference(link->u.ap.beacon, sdata);
        if (!old)
                return -ENOENT;
 
-       err = ieee80211_assign_beacon(sdata, params, NULL, NULL);
+       err = ieee80211_assign_beacon(sdata, link, params, NULL, NULL);
        if (err < 0)
                return err;
 
                err |= BSS_CHANGED_HE_BSS_COLOR;
        }
 
-       ieee80211_link_info_change_notify(sdata, params->link_id, err);
+       ieee80211_link_info_change_notify(sdata, link, err);
        return 0;
 }
 
        struct fils_discovery_data *old_fils_discovery;
        struct unsol_bcast_probe_resp_data *old_unsol_bcast_probe_resp;
        struct cfg80211_chan_def chandef;
-       struct ieee80211_link_data *link = sdata->link[link_id];
-       struct ieee80211_bss_conf *link_conf = sdata->vif.link_conf[link_id];
+       struct ieee80211_link_data *link =
+               sdata_dereference(sdata->link[link_id], sdata);
+       struct ieee80211_bss_conf *link_conf = link->conf;
 
        sdata_assert_lock(sdata);
 
        sdata->beacon_rate_set = false;
        sdata->vif.cfg.ssid_len = 0;
        clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, &sdata->state);
-       ieee80211_link_info_change_notify(sdata, link_id,
+       ieee80211_link_info_change_notify(sdata, link,
                                          BSS_CHANGED_BEACON_ENABLED);
 
        if (sdata->wdev.cac_started) {
        struct ieee80211_supported_band *sband;
        struct ieee80211_sub_if_data *sdata = sta->sdata;
        u32 link_id = params->link_id < 0 ? 0 : params->link_id;
+       struct ieee80211_link_data *link =
+               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));
 
-       if (!link_sta)
+       if (!link || !link_sta)
                return -EINVAL;
 
-       sband = ieee80211_get_link_sband(sdata, link_id);
+       sband = ieee80211_get_link_sband(link);
        if (!sband)
                return -EINVAL;
 
 
        if (params->supported_rates &&
            params->supported_rates_len) {
-               ieee80211_parse_bitrates(&sdata->vif.link_conf[link_id]->chandef,
+               ieee80211_parse_bitrates(&link->conf->chandef,
                                         sband, params->supported_rates,
                                         params->supported_rates_len,
                                         &link_sta->pub->supp_rates[sband->band]);
        struct ieee80211_sub_if_data *sdata = sta->sdata;
        u32 link_id = params->link_sta_params.link_id < 0 ?
                      0 : params->link_sta_params.link_id;
+       struct ieee80211_link_data *link;
        u32 mask, set;
 
-       sband = ieee80211_get_link_sband(sdata, link_id);
+       link = sdata_dereference(sdata->link[link_id], sdata);
+       if (!link)
+               return -ENOLINK;
+
+       sband = ieee80211_get_link_sband(link);
        if (!sband)
                return -EINVAL;
 
                }
        }
 
-       err = sta_apply_parameters(local, sta, params);
+       /* 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);
+       }
        if (err)
                goto out_err;
 
        if (_chg_mesh_attr(NL80211_MESHCONF_HT_OPMODE, mask)) {
                conf->ht_opmode = nconf->ht_opmode;
                sdata->vif.bss_conf.ht_operation_mode = nconf->ht_opmode;
-               ieee80211_link_info_change_notify(sdata, 0, BSS_CHANGED_HT);
+               ieee80211_link_info_change_notify(sdata, &sdata->deflink,
+                                                 BSS_CHANGED_HT);
        }
        if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT, mask))
                conf->dot11MeshHWMPactivePathToRootTimeout =
        sdata->deflink.needed_rx_chains = sdata->local->rx_chains;
 
        mutex_lock(&sdata->local->mtx);
-       err = ieee80211_link_use_channel(sdata->link[0], &setup->chandef,
+       err = ieee80211_link_use_channel(&sdata->deflink, &setup->chandef,
                                         IEEE80211_CHANCTX_SHARED);
        mutex_unlock(&sdata->local->mtx);
        if (err)
 
        ieee80211_stop_mesh(sdata);
        mutex_lock(&sdata->local->mtx);
-       ieee80211_link_release_channel(sdata->link[0]);
+       ieee80211_link_release_channel(&sdata->deflink);
        kfree(sdata->u.mesh.ie);
        mutex_unlock(&sdata->local->mtx);
 
                changed |= BSS_CHANGED_P2P_PS;
        }
 
-       ieee80211_link_info_change_notify(sdata, 0, changed);
+       ieee80211_link_info_change_notify(sdata, &sdata->deflink, changed);
 
        return 0;
 }
                return -EINVAL;
        }
 
-       ieee80211_link_info_change_notify(sdata, 0, BSS_CHANGED_QOS);
+       ieee80211_link_info_change_notify(sdata, &sdata->deflink,
+                                         BSS_CHANGED_QOS);
 
        return 0;
 }
        memcpy(sdata->vif.bss_conf.mcast_rate, rate,
               sizeof(int) * NUM_NL80211_BANDS);
 
-       ieee80211_link_info_change_notify(sdata, 0, BSS_CHANGED_MCAST_RATE);
+       ieee80211_link_info_change_notify(sdata, &sdata->deflink,
+                                         BSS_CHANGED_MCAST_RATE);
 
        return 0;
 }
 #endif
 
 int __ieee80211_request_smps_mgd(struct ieee80211_sub_if_data *sdata,
-                                unsigned int link_id,
+                                struct ieee80211_link_data *link,
                                 enum ieee80211_smps_mode smps_mode)
 {
        const u8 *ap;
        if (WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_STATION))
                return -EINVAL;
 
-       old_req = sdata->link[link_id]->u.mgd.req_smps;
-       sdata->link[link_id]->u.mgd.req_smps = smps_mode;
+       old_req = link->u.mgd.req_smps;
+       link->u.mgd.req_smps = smps_mode;
 
        if (old_req == smps_mode &&
            smps_mode != IEEE80211_SMPS_AUTOMATIC)
         * the new value until we associate.
         */
        if (!sdata->u.mgd.associated ||
-           sdata->vif.link_conf[link_id]->chandef.width == NL80211_CHAN_WIDTH_20_NOHT)
+           link->conf->chandef.width == NL80211_CHAN_WIDTH_20_NOHT)
                return 0;
 
-       ap = sdata->link[link_id]->u.mgd.bssid;
+       ap = link->u.mgd.bssid;
 
        rcu_read_lock();
        list_for_each_entry_rcu(sta, &sdata->local->sta_list, list) {
        err = ieee80211_send_smps_action(sdata, smps_mode,
                                         ap, ap);
        if (err)
-               sdata->link[link_id]->u.mgd.req_smps = old_req;
+               link->u.mgd.req_smps = old_req;
        else if (smps_mode != IEEE80211_SMPS_OFF && tdls_peer_found)
                ieee80211_teardown_tdls_peers(sdata);
 
        /* no change, but if automatic follow powersave */
        sdata_lock(sdata);
        for (link_id = 0; link_id < ARRAY_SIZE(sdata->link); link_id++) {
-               if (!sdata->link[link_id])
+               struct ieee80211_link_data *link;
+
+               link = sdata_dereference(sdata->link[link_id], sdata);
+
+               if (!link)
                        continue;
-               __ieee80211_request_smps_mgd(sdata, link_id,
-                                            sdata->link[link_id]->u.mgd.req_smps);
+               __ieee80211_request_smps_mgd(sdata, link,
+                                            link->u.mgd.req_smps);
        }
        sdata_unlock(sdata);
 
        /* tell the driver upon association, unless already associated */
        if (sdata->u.mgd.associated &&
            sdata->vif.driver_flags & IEEE80211_VIF_SUPPORTS_CQM_RSSI)
-               ieee80211_link_info_change_notify(sdata, 0, BSS_CHANGED_CQM);
+               ieee80211_link_info_change_notify(sdata, &sdata->deflink,
+                                                 BSS_CHANGED_CQM);
 
        return 0;
 }
        /* tell the driver upon association, unless already associated */
        if (sdata->u.mgd.associated &&
            sdata->vif.driver_flags & IEEE80211_VIF_SUPPORTS_CQM_RSSI)
-               ieee80211_link_info_change_notify(sdata, 0, BSS_CHANGED_CQM);
+               ieee80211_link_info_change_notify(sdata, &sdata->deflink,
+                                                 BSS_CHANGED_CQM);
 
        return 0;
 }
        sdata->deflink.smps_mode = IEEE80211_SMPS_OFF;
        sdata->deflink.needed_rx_chains = local->rx_chains;
 
-       err = ieee80211_link_use_channel(sdata->link[0], chandef,
+       err = ieee80211_link_use_channel(&sdata->deflink, chandef,
                                         IEEE80211_CHANCTX_SHARED);
        if (err)
                goto out_unlock;
                cancel_delayed_work(&sdata->deflink.dfs_cac_timer_work);
 
                if (sdata->wdev.cac_started) {
-                       ieee80211_link_release_channel(sdata->link[0]);
+                       ieee80211_link_release_channel(&sdata->deflink);
                        sdata->wdev.cac_started = false;
                }
        }
                if (!sdata->deflink.u.ap.next_beacon)
                        return -EINVAL;
 
-               err = ieee80211_assign_beacon(sdata,
+               err = ieee80211_assign_beacon(sdata, &sdata->deflink,
                                              sdata->deflink.u.ap.next_beacon,
                                              NULL, NULL);
                ieee80211_free_next_beacon(&sdata->deflink);
                if (sdata->deflink.reserved_ready)
                        return 0;
 
-               return ieee80211_link_use_reserved_context(sdata->link[0]);
+               return ieee80211_link_use_reserved_context(&sdata->deflink);
        }
 
        if (!cfg80211_chandef_identical(&sdata->vif.bss_conf.chandef,
        if (err)
                return err;
 
-       ieee80211_link_info_change_notify(sdata, 0, changed);
+       ieee80211_link_info_change_notify(sdata, &sdata->deflink, changed);
 
        if (sdata->deflink.csa_block_tx) {
                ieee80211_wake_vif_queues(local, sdata,
                csa.n_counter_offsets_presp = params->n_counter_offsets_presp;
                csa.count = params->count;
 
-               err = ieee80211_assign_beacon(sdata, ¶ms->beacon_csa, &csa, NULL);
+               err = ieee80211_assign_beacon(sdata, &sdata->deflink,
+                                             ¶ms->beacon_csa, &csa,
+                                             NULL);
                if (err < 0) {
                        ieee80211_free_next_beacon(&sdata->deflink);
                        return err;
        if (err)
                goto out;
 
-       err = ieee80211_link_reserve_chanctx(sdata->link[0], ¶ms->chandef,
+       err = ieee80211_link_reserve_chanctx(&sdata->deflink, ¶ms->chandef,
                                             chanctx->mode,
                                             params->radar_required);
        if (err)
        /* if reservation is invalid then this will fail */
        err = ieee80211_check_combinations(sdata, NULL, chanctx->mode, 0);
        if (err) {
-               ieee80211_link_unreserve_chanctx(sdata->link[0]);
+               ieee80211_link_unreserve_chanctx(&sdata->deflink);
                goto out;
        }
 
 
        err = ieee80211_set_csa_beacon(sdata, params, &changed);
        if (err) {
-               ieee80211_link_unreserve_chanctx(sdata->link[0]);
+               ieee80211_link_unreserve_chanctx(&sdata->deflink);
                goto out;
        }
 
                                          params->count, params->block_tx);
 
        if (changed) {
-               ieee80211_link_info_change_notify(sdata, 0, changed);
+               ieee80211_link_info_change_notify(sdata, &sdata->deflink,
+                                                 changed);
                drv_channel_switch_beacon(sdata, ¶ms->chandef);
        } else {
                /* if the beacon didn't change, we can finalize immediately */
        struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
        struct ieee80211_local *local = wiphy_priv(wiphy);
        struct ieee80211_chanctx_conf *chanctx_conf;
+       struct ieee80211_link_data *link;
        int ret = -ENODATA;
 
        rcu_read_lock();
-       chanctx_conf = rcu_dereference(sdata->vif.link_conf[link_id]->chanctx_conf);
+       link = rcu_dereference(sdata->link[link_id]);
+       if (!link) {
+               ret = -ENOLINK;
+               goto out;
+       }
+
+       chanctx_conf = rcu_dereference(link->conf->chanctx_conf);
        if (chanctx_conf) {
-               *chandef = sdata->vif.link_conf[link_id]->chandef;
+               *chandef = link->conf->chandef;
                ret = 0;
        } else if (local->open_count > 0 &&
                   local->open_count == local->monitors &&
                        *chandef = local->_oper_chandef;
                ret = 0;
        }
+out:
        rcu_read_unlock();
 
        return ret;
                                      struct cfg80211_chan_def *chandef)
 {
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       struct ieee80211_link_data *link;
        int ret;
        u32 changed = 0;
 
-       ret = ieee80211_link_change_bandwidth(sdata->link[link_id], chandef,
-                                             &changed);
+       link = sdata_dereference(sdata->link[link_id], sdata);
+
+       ret = ieee80211_link_change_bandwidth(link, chandef, &changed);
        if (ret == 0)
-               ieee80211_link_info_change_notify(sdata, link_id, changed);
+               ieee80211_link_info_change_notify(sdata, link, changed);
 
        return ret;
 }
                if (!sdata->deflink.u.ap.next_beacon)
                        return -EINVAL;
 
-               ret = ieee80211_assign_beacon(sdata,
+               ret = ieee80211_assign_beacon(sdata, &sdata->deflink,
                                              sdata->deflink.u.ap.next_beacon,
                                              NULL, NULL);
                ieee80211_free_next_beacon(&sdata->deflink);
                        params->counter_offset_presp;
                color_change.count = params->count;
 
-               err = ieee80211_assign_beacon(sdata, ¶ms->beacon_color_change,
+               err = ieee80211_assign_beacon(sdata, &sdata->deflink,
+                                             ¶ms->beacon_color_change,
                                              NULL, &color_change);
                if (err < 0) {
                        ieee80211_free_next_beacon(&sdata->deflink);
        sdata->vif.bss_conf.he_bss_color.enabled = enable;
        changed |= BSS_CHANGED_HE_BSS_COLOR;
 
-       ieee80211_link_info_change_notify(sdata, 0, changed);
+       ieee80211_link_info_change_notify(sdata, &sdata->deflink, changed);
 
        if (!sdata->vif.bss_conf.nontransmitted && sdata->vif.mbssid_tx_vif) {
                struct ieee80211_sub_if_data *child;
                        if (child != sdata && child->vif.mbssid_tx_vif == &sdata->vif) {
                                child->vif.bss_conf.he_bss_color.color = color;
                                child->vif.bss_conf.he_bss_color.enabled = enable;
-                               ieee80211_link_info_change_notify(child, 0,
+                               ieee80211_link_info_change_notify(child,
+                                                                 &child->deflink,
                                                                  BSS_CHANGED_HE_BSS_COLOR);
                        }
                }
 
 }
 
 static struct ieee80211_chanctx *
-ieee80211_vif_get_chanctx(struct ieee80211_sub_if_data *sdata,
-                         unsigned int link_id)
+ieee80211_link_get_chanctx(struct ieee80211_link_data *link)
 {
-       struct ieee80211_bss_conf *link_conf = sdata->vif.link_conf[link_id];
-       struct ieee80211_local *local __maybe_unused = sdata->local;
+       struct ieee80211_local *local __maybe_unused = link->sdata->local;
        struct ieee80211_chanctx_conf *conf;
 
-       conf = rcu_dereference_protected(link_conf->chanctx_conf,
+       conf = rcu_dereference_protected(link->conf->chanctx_conf,
                                         lockdep_is_held(&local->chanctx_mtx));
        if (!conf)
                return NULL;
        return container_of(conf, struct ieee80211_chanctx, conf);
 }
 
-static struct ieee80211_chanctx *
-ieee80211_link_get_chanctx(struct ieee80211_link_data *link)
-{
-       return ieee80211_vif_get_chanctx(link->sdata, link->link_id);
-}
-
 static const struct cfg80211_chan_def *
 ieee80211_chanctx_reserved_chandef(struct ieee80211_local *local,
                                   struct ieee80211_chanctx *ctx,
 
        list_for_each_entry(link, &ctx->assigned_links,
                            assigned_chanctx_list) {
-               struct ieee80211_bss_conf *link_conf =
-                       link->sdata->vif.link_conf[link->link_id];
+               struct ieee80211_bss_conf *link_conf = link->conf;
 
                if (link->reserved_chanctx)
                        continue;
        enum nl80211_chan_width max_bw = NL80211_CHAN_WIDTH_20_NOHT;
        struct sta_info *sta;
 
-       rcu_read_lock();
        list_for_each_entry_rcu(sta, &sdata->local->sta_list, list) {
                if (sdata != sta->sdata &&
                    !(sta->sdata->bss && sta->sdata->bss == sdata->bss))
 
                max_bw = max(max_bw, ieee80211_get_sta_bw(sta, link_id));
        }
-       rcu_read_unlock();
 
        return max_bw;
 }
        struct ieee80211_vif *vif = &sdata->vif;
        int link_id;
 
+       rcu_read_lock();
        for (link_id = 0; link_id < ARRAY_SIZE(sdata->link); link_id++) {
                enum nl80211_chan_width width = NL80211_CHAN_WIDTH_20_NOHT;
                struct ieee80211_bss_conf *link_conf =
-                       sdata->vif.link_conf[link_id];
+                       rcu_dereference(sdata->vif.link_conf[link_id]);
 
                if (!link_conf)
                        continue;
 
                max_bw = max(max_bw, width);
        }
+       rcu_read_unlock();
 
        return max_bw;
 }
        /* use the configured bandwidth in case of monitor interface */
        sdata = rcu_dereference(local->monitor_sdata);
        if (sdata &&
-           rcu_access_pointer(sdata->vif.link_conf[0]->chanctx_conf) == conf)
+           rcu_access_pointer(sdata->vif.bss_conf.chanctx_conf) == conf)
                max_bw = max(max_bw, conf->def.width);
 
        rcu_read_unlock();
 
                for (link_id = 0; link_id < ARRAY_SIZE(sta->sdata->link); link_id++) {
                        struct ieee80211_bss_conf *link_conf =
-                               sdata->vif.link_conf[link_id];
+                               rcu_dereference(sdata->vif.link_conf[link_id]);
                        struct link_sta_info *link_sta;
 
                        if (!link_conf)
                unsigned int link_id;
 
                for (link_id = 0; link_id < ARRAY_SIZE(sdata->link); link_id++) {
-                       if (sdata->link[link_id] &&
-                           sdata->link[link_id]->radar_required) {
+                       struct ieee80211_link_data *link;
+
+                       link = rcu_dereference(sdata->link[link_id]);
+
+                       if (link && link->radar_required) {
                                rcu_read_unlock();
                                return true;
                        }
                if (!ieee80211_sdata_running(sdata))
                        continue;
                for (link_id = 0; link_id < ARRAY_SIZE(sdata->link); link_id++) {
-                       struct ieee80211_bss_conf *link_conf =
-                               sdata->vif.link_conf[link_id];
+                       struct ieee80211_link_data *link;
 
-                       if (!link_conf)
+                       link = rcu_dereference(sdata->link[link_id]);
+                       if (!link)
                                continue;
 
-                       if (rcu_access_pointer(link_conf->chanctx_conf) != conf)
+                       if (rcu_access_pointer(link->conf->chanctx_conf) != conf)
                                continue;
-                       if (!sdata->link[link_id]->radar_required)
+                       if (!link->radar_required)
                                continue;
                        required = true;
                        break;
 
                for (link_id = 0; link_id < ARRAY_SIZE(sdata->link); link_id++) {
                        struct ieee80211_bss_conf *link_conf =
-                               sdata->vif.link_conf[link_id];
+                               rcu_dereference(sdata->vif.link_conf[link_id]);
 
                        if (!link_conf)
                                continue;
        if (WARN_ON(sdata->vif.type == NL80211_IFTYPE_NAN))
                return -ENOTSUPP;
 
-       conf = rcu_dereference_protected(sdata->vif.link_conf[link_id]->chanctx_conf,
+       conf = rcu_dereference_protected(link->conf->chanctx_conf,
                                         lockdep_is_held(&local->chanctx_mtx));
 
        if (conf) {
        }
 
 out:
-       rcu_assign_pointer(sdata->vif.link_conf[link_id]->chanctx_conf, conf);
+       rcu_assign_pointer(link->conf->chanctx_conf, conf);
 
        sdata->vif.cfg.idle = !conf;
 
                }
 
                for (link_id = 0; link_id < ARRAY_SIZE(sdata->link); link_id++) {
-                       struct ieee80211_link_data *link = sdata->link[link_id];
-                       struct ieee80211_bss_conf *link_conf =
-                               sdata->vif.link_conf[link_id];
+                       struct ieee80211_link_data *link;
 
-                       if (!link_conf)
+                       link = rcu_dereference(sdata->link[link_id]);
+
+                       if (!link)
                                continue;
 
-                       if (rcu_access_pointer(link_conf->chanctx_conf) != &chanctx->conf)
+                       if (rcu_access_pointer(link->conf->chanctx_conf) != &chanctx->conf)
                                continue;
 
                        switch (link->smps_mode) {
        /* Disable SMPS for the monitor interface */
        sdata = rcu_dereference(local->monitor_sdata);
        if (sdata &&
-           rcu_access_pointer(sdata->vif.link_conf[0]->chanctx_conf) == &chanctx->conf)
+           rcu_access_pointer(sdata->vif.bss_conf.chanctx_conf) == &chanctx->conf)
                rx_chains_dynamic = rx_chains_static = local->rx_chains;
 
        rcu_read_unlock();
 {
        struct ieee80211_sub_if_data *sdata = link->sdata;
        unsigned int link_id = link->link_id;
-       struct ieee80211_bss_conf *link_conf = sdata->vif.link_conf[link_id];
+       struct ieee80211_bss_conf *link_conf = link->conf;
        struct ieee80211_local *local __maybe_unused = sdata->local;
        struct ieee80211_sub_if_data *vlan;
        struct ieee80211_chanctx_conf *conf;
        if (clear)
                conf = NULL;
 
-       list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
-               rcu_assign_pointer(vlan->vif.link_conf[link_id]->chanctx_conf,
-                                  conf);
+       rcu_read_lock();
+       list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) {
+               struct ieee80211_bss_conf *vlan_conf;
+
+               vlan_conf = rcu_dereference(vlan->vif.link_conf[link_id]);
+               if (WARN_ON(!vlan_conf))
+                       continue;
+
+               rcu_assign_pointer(vlan_conf->chanctx_conf, conf);
+       }
+       rcu_read_unlock();
 }
 
 void ieee80211_link_copy_chanctx_to_vlans(struct ieee80211_link_data *link,
        unsigned int link_id = link->link_id;
        struct ieee80211_sub_if_data *vlan;
 
-       sdata->vif.link_conf[link_id]->chandef = *chandef;
+       link->conf->chandef = *chandef;
 
        if (sdata->vif.type != NL80211_IFTYPE_AP)
                return;
 
-       list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
-               vlan->vif.link_conf[link_id]->chandef = *chandef;
+       rcu_read_lock();
+       list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) {
+               struct ieee80211_bss_conf *vlan_conf;
+
+               vlan_conf = rcu_dereference(vlan->vif.link_conf[link_id]);
+               if (WARN_ON(!vlan_conf))
+                       continue;
+
+               vlan_conf->chandef = *chandef;
+       }
+       rcu_read_unlock();
 }
 
 static int
 ieee80211_link_use_reserved_reassign(struct ieee80211_link_data *link)
 {
        struct ieee80211_sub_if_data *sdata = link->sdata;
-       unsigned int link_id = link->link_id;
-       struct ieee80211_bss_conf *link_conf = sdata->vif.link_conf[link_id];
+       struct ieee80211_bss_conf *link_conf = link->conf;
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_vif_chanctx_switch vif_chsw[1] = {};
        struct ieee80211_chanctx *old_ctx, *new_ctx;
        ieee80211_recalc_radar_chanctx(local, new_ctx);
 
        if (changed)
-               ieee80211_link_info_change_notify(sdata, link_id, changed);
+               ieee80211_link_info_change_notify(sdata, link, changed);
 
 out:
        ieee80211_link_chanctx_reservation_complete(link);
 ieee80211_link_use_reserved_assign(struct ieee80211_link_data *link)
 {
        struct ieee80211_sub_if_data *sdata = link->sdata;
-       unsigned int link_id = link->link_id;
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_chanctx *old_ctx, *new_ctx;
        const struct cfg80211_chan_def *chandef;
        int err;
 
-       old_ctx = ieee80211_vif_get_chanctx(sdata, link_id);
+       old_ctx = ieee80211_link_get_chanctx(link);
        new_ctx = link->reserved_chanctx;
 
        if (WARN_ON(!link->reserved_ready))
                list_for_each_entry(link, &ctx->reserved_links,
                                    reserved_chanctx_list) {
                        struct ieee80211_sub_if_data *sdata = link->sdata;
-                       struct ieee80211_bss_conf *link_conf =
-                               sdata->vif.link_conf[link->link_id];
+                       struct ieee80211_bss_conf *link_conf = link->conf;
                        u32 changed = 0;
 
                        if (!ieee80211_link_has_in_place_reservation(link))
                        ieee80211_link_update_chandef(link, &link->reserved_chandef);
                        if (changed)
                                ieee80211_link_info_change_notify(sdata,
-                                                                 link->link_id,
+                                                                 link,
                                                                  changed);
 
                        ieee80211_recalc_txpower(sdata, false);
 static void __ieee80211_link_release_channel(struct ieee80211_link_data *link)
 {
        struct ieee80211_sub_if_data *sdata = link->sdata;
-       unsigned int link_id = link->link_id;
-       struct ieee80211_bss_conf *link_conf = sdata->vif.link_conf[link_id];
+       struct ieee80211_bss_conf *link_conf = link->conf;
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_chanctx_conf *conf;
        struct ieee80211_chanctx *ctx;
                               enum ieee80211_chanctx_mode mode)
 {
        struct ieee80211_sub_if_data *sdata = link->sdata;
-       unsigned int link_id = link->link_id;
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_chanctx *ctx;
        u8 radar_detect_width = 0;
        if (ret > 0)
                radar_detect_width = BIT(chandef->width);
 
-       sdata->link[link_id]->radar_required = ret;
+       link->radar_required = ret;
 
        ret = ieee80211_check_combinations(sdata, chandef, mode,
                                           radar_detect_width);
                                    u32 *changed)
 {
        struct ieee80211_sub_if_data *sdata = link->sdata;
-       unsigned int link_id = link->link_id;
-       struct ieee80211_bss_conf *link_conf = sdata->vif.link_conf[link_id];
+       struct ieee80211_bss_conf *link_conf = link->conf;
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_chanctx_conf *conf;
        struct ieee80211_chanctx *ctx;
 {
        struct ieee80211_sub_if_data *sdata = link->sdata;
        unsigned int link_id = link->link_id;
-       struct ieee80211_bss_conf *link_conf = sdata->vif.link_conf[link_id];
+       struct ieee80211_bss_conf *link_conf = link->conf;
+       struct ieee80211_bss_conf *ap_conf;
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_sub_if_data *ap;
        struct ieee80211_chanctx_conf *conf;
 
        mutex_lock(&local->chanctx_mtx);
 
-       conf = rcu_dereference_protected(ap->vif.link_conf[link_id]->chanctx_conf,
+       rcu_read_lock();
+       ap_conf = rcu_dereference(ap->vif.link_conf[link_id]);
+       conf = rcu_dereference_protected(ap_conf->chanctx_conf,
                                         lockdep_is_held(&local->chanctx_mtx));
        rcu_assign_pointer(link_conf->chanctx_conf, conf);
+       rcu_read_unlock();
        mutex_unlock(&local->chanctx_mtx);
 }
 
 
                return -EOPNOTSUPP;
 
        sdata_lock(sdata);
-       err = __ieee80211_request_smps_mgd(sdata, 0, smps_mode);
+       err = __ieee80211_request_smps_mgd(sdata, &sdata->deflink, smps_mode);
        sdata_unlock(sdata);
 
        return err;
 
 
 static inline void drv_link_info_changed(struct ieee80211_local *local,
                                         struct ieee80211_sub_if_data *sdata,
+                                        struct ieee80211_bss_conf *info,
                                         int link_id, u64 changed)
 {
        might_sleep();
        if (!check_sdata_in_driver(sdata))
                return;
 
-       trace_drv_link_info_changed(local, sdata, link_id, changed);
+       trace_drv_link_info_changed(local, sdata, info, link_id, changed);
        if (local->ops->link_info_changed)
                local->ops->link_info_changed(&local->hw, &sdata->vif,
-                                             link_id, changed);
+                                             info, link_id, changed);
        else if (local->ops->bss_info_changed)
                local->ops->bss_info_changed(&local->hw, &sdata->vif,
-                                            &sdata->vif.bss_conf, changed);
+                                            info, changed);
        trace_drv_return_void(local);
 }
 
        if (!check_sdata_in_driver(sdata))
                return -EIO;
 
-       trace_drv_start_ap(local, sdata, sdata->vif.link_conf[link_id],
-                          link_id);
+       trace_drv_start_ap(local, sdata, link_id);
        if (local->ops->start_ap)
                ret = local->ops->start_ap(&local->hw, &sdata->vif, link_id);
        trace_drv_return_int(local, ret);
 
                                       const struct ieee80211_ht_cap *ht_cap_ie,
                                       struct link_sta_info *link_sta)
 {
+       struct ieee80211_bss_conf *link_conf;
        struct sta_info *sta = link_sta->sta;
        struct ieee80211_sta_ht_cap ht_cap, own_cap;
        u8 ampdu_info, tx_mcs_set_cap;
        int i, max_tx_streams;
        bool changed;
        enum ieee80211_sta_rx_bandwidth bw;
+       enum nl80211_chan_width width;
 
        memset(&ht_cap, 0, sizeof(ht_cap));
 
 
        memcpy(&link_sta->pub->ht_cap, &ht_cap, sizeof(ht_cap));
 
-       switch (sdata->vif.link_conf[link_sta->link_id]->chandef.width) {
+       rcu_read_lock();
+       link_conf = rcu_dereference(sdata->vif.link_conf[link_sta->link_id]);
+       if (WARN_ON(!link_conf))
+               width = NL80211_CHAN_WIDTH_20_NOHT;
+       else
+               width = link_conf->chandef.width;
+
+       switch (width) {
        default:
                WARN_ON_ONCE(1);
                fallthrough;
                                IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20;
                break;
        }
+       rcu_read_unlock();
 
        link_sta->pub->bandwidth = bw;
 
                             u.mgd.request_smps_work);
 
        sdata_lock(link->sdata);
-       __ieee80211_request_smps_mgd(link->sdata, link->link_id,
+       __ieee80211_request_smps_mgd(link->sdata, link,
                                     link->u.mgd.driver_smps_mode);
        sdata_unlock(link->sdata);
 }
                            enum ieee80211_smps_mode smps_mode)
 {
        struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
-       struct ieee80211_link_data *link = sdata->link[link_id];
+       struct ieee80211_link_data *link;
 
        if (WARN_ON_ONCE(vif->type != NL80211_IFTYPE_STATION))
                return;
 
+       rcu_read_lock();
+       link = rcu_dereference(sdata->link[link_id]);
        if (WARN_ON(!link))
-               return;
+               goto out;
 
        if (link->u.mgd.driver_smps_mode == smps_mode)
-               return;
+               goto out;
 
        link->u.mgd.driver_smps_mode = smps_mode;
        ieee80211_queue_work(&sdata->local->hw, &link->u.mgd.request_smps_work);
+out:
+       rcu_read_unlock();
 }
 /* this might change ... don't want non-open drivers using it */
 EXPORT_SYMBOL_GPL(ieee80211_request_smps);
 
        radar_required = err;
 
        mutex_lock(&local->mtx);
-       if (ieee80211_link_use_channel(sdata->link[0], &chandef,
+       if (ieee80211_link_use_channel(&sdata->deflink, &chandef,
                                       ifibss->fixed_channel ?
                                        IEEE80211_CHANCTX_SHARED :
                                        IEEE80211_CHANCTX_EXCLUSIVE)) {
                RCU_INIT_POINTER(ifibss->presp, NULL);
                kfree_rcu(presp, rcu_head);
                mutex_lock(&local->mtx);
-               ieee80211_link_release_channel(sdata->link[0]);
+               ieee80211_link_release_channel(&sdata->deflink);
                mutex_unlock(&local->mtx);
                sdata_info(sdata, "Failed to join IBSS, driver failure: %d\n",
                           err);
                                                BSS_CHANGED_IBSS);
        drv_leave_ibss(local, sdata);
        mutex_lock(&local->mtx);
-       ieee80211_link_release_channel(sdata->link[0]);
+       ieee80211_link_release_channel(&sdata->deflink);
        mutex_unlock(&local->mtx);
 }
 
                | IEEE80211_HT_PARAM_RIFS_MODE;
 
        changed |= BSS_CHANGED_HT | BSS_CHANGED_MCAST_RATE;
-       ieee80211_link_info_change_notify(sdata, 0, changed);
+       ieee80211_link_info_change_notify(sdata, &sdata->deflink, changed);
 
        sdata->deflink.smps_mode = IEEE80211_SMPS_OFF;
        sdata->deflink.needed_rx_chains = local->rx_chains;
 
                struct ieee80211_link_data_managed mgd;
                struct ieee80211_link_data_ap ap;
        } u;
+
+       struct ieee80211_bss_conf *conf;
 };
 
 struct ieee80211_sub_if_data {
        } u;
 
        struct ieee80211_link_data deflink;
-       struct ieee80211_link_data *link[IEEE80211_MLD_MAX_NUM_LINKS];
+       struct ieee80211_link_data __rcu *link[IEEE80211_MLD_MAX_NUM_LINKS];
 
 #ifdef CONFIG_MAC80211_DEBUGFS
        struct {
 }
 
 static inline struct ieee80211_supported_band *
-ieee80211_get_link_sband(struct ieee80211_sub_if_data *sdata, u32 link_id)
+ieee80211_get_link_sband(struct ieee80211_link_data *link)
 {
-       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_local *local = link->sdata->local;
        struct ieee80211_chanctx_conf *chanctx_conf;
        enum nl80211_band band;
 
        rcu_read_lock();
-       chanctx_conf =
-               rcu_dereference(sdata->vif.link_conf[link_id]->chanctx_conf);
-
+       chanctx_conf = rcu_dereference(link->conf->chanctx_conf);
        if (!chanctx_conf) {
                rcu_read_unlock();
                return NULL;
 void ieee80211_vif_cfg_change_notify(struct ieee80211_sub_if_data *sdata,
                                     u64 changed);
 void ieee80211_link_info_change_notify(struct ieee80211_sub_if_data *sdata,
-                                      int link_id, u64 changed);
+                                      struct ieee80211_link_data *link,
+                                      u64 changed);
 void ieee80211_configure_filter(struct ieee80211_local *local);
 u32 ieee80211_reset_erp_info(struct ieee80211_sub_if_data *sdata);
 
 enum nl80211_chan_width
 ieee80211_sta_cap_chan_bw(struct link_sta_info *link_sta);
 void ieee80211_process_mu_groups(struct ieee80211_sub_if_data *sdata,
-                                unsigned int link_id,
+                                struct ieee80211_link_data *link,
                                 struct ieee80211_mgmt *mgmt);
 u32 __ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata,
                                  struct link_sta_info *sta,
                            struct ieee802_11_elems *elems,
                            enum nl80211_band band, u32 *basic_rates);
 int __ieee80211_request_smps_mgd(struct ieee80211_sub_if_data *sdata,
-                                unsigned int link_id,
+                                struct ieee80211_link_data *link,
                                 enum ieee80211_smps_mode smps_mode);
 void ieee80211_recalc_smps(struct ieee80211_sub_if_data *sdata,
-                          unsigned int link_id);
+                          struct ieee80211_link_data *link);
 void ieee80211_recalc_min_chandef(struct ieee80211_sub_if_data *sdata);
 
 size_t ieee80211_ie_split_vendor(const u8 *ies, size_t ielen, size_t offset);
 
 {
        if (__ieee80211_recalc_txpower(sdata) ||
            (update_bss && ieee80211_sdata_running(sdata)))
-               ieee80211_link_info_change_notify(sdata, 0,
+               ieee80211_link_info_change_notify(sdata, &sdata->deflink,
                                                  BSS_CHANGED_TXPOWER);
 }
 
                chandef = sdata->vif.bss_conf.chandef;
                WARN_ON(local->suspended);
                mutex_lock(&local->mtx);
-               ieee80211_link_release_channel(sdata->link[0]);
+               ieee80211_link_release_channel(&sdata->deflink);
                mutex_unlock(&local->mtx);
                cfg80211_cac_event(sdata->dev, &chandef,
                                   NL80211_RADAR_CAC_ABORTED,
        if (link_id < 0)
                link_id = 0;
 
-       sdata->vif.link_conf[link_id] = link_conf;
-       sdata->link[link_id] = link;
+       rcu_assign_pointer(sdata->vif.link_conf[link_id], link_conf);
+       rcu_assign_pointer(sdata->link[link_id], link);
 
        link->sdata = sdata;
        link->link_id = link_id;
+       link->conf = link_conf;
 
        INIT_WORK(&link->csa_finalize_work,
                  ieee80211_csa_finalize_work);
        mutex_unlock(&local->iflist_mtx);
 
        mutex_lock(&local->mtx);
-       ret = ieee80211_link_use_channel(sdata->link[0], &local->monitor_chandef,
+       ret = ieee80211_link_use_channel(&sdata->deflink, &local->monitor_chandef,
                                         IEEE80211_CHANCTX_EXCLUSIVE);
        mutex_unlock(&local->mtx);
        if (ret) {
        synchronize_net();
 
        mutex_lock(&local->mtx);
-       ieee80211_link_release_channel(sdata->link[0]);
+       ieee80211_link_release_channel(&sdata->deflink);
        mutex_unlock(&local->mtx);
 
        drv_remove_interface(local, sdata);
        case NL80211_IFTYPE_AP_VLAN:
                /* no need to tell driver, but set carrier and chanctx */
                if (sdata->bss->active) {
-                       ieee80211_link_vlan_copy_chanctx(sdata->link[0]);
+                       ieee80211_link_vlan_copy_chanctx(&sdata->deflink);
                        netif_carrier_on(dev);
                        ieee80211_set_vif_encap_ops(sdata);
                } else {
                if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE &&
                    sdata->vif.type != NL80211_IFTYPE_NAN)
                        changed |= ieee80211_reset_erp_info(sdata);
-               ieee80211_link_info_change_notify(sdata, 0, changed);
+               ieee80211_link_info_change_notify(sdata, &sdata->deflink,
+                                                 changed);
 
                switch (sdata->vif.type) {
                case NL80211_IFTYPE_STATION:
                        break;
                }
                case WLAN_VHT_ACTION_GROUPID_MGMT:
-                       ieee80211_process_mu_groups(sdata, 0, mgmt);
+                       ieee80211_process_mu_groups(sdata, &sdata->deflink,
+                                                   mgmt);
                        break;
                default:
                        WARN_ON(1);
        struct ieee80211_sub_if_data *sdata =
                container_of(work, struct ieee80211_sub_if_data, recalc_smps);
 
-       ieee80211_recalc_smps(sdata, 0);
+       ieee80211_recalc_smps(sdata, &sdata->deflink);
 }
 
 /*
        struct {
                struct ieee80211_link_data data;
                struct ieee80211_bss_conf conf;
+               struct rcu_head rcu_head;
        } *links[IEEE80211_MLD_MAX_NUM_LINKS] = {}, *link;
        struct ieee80211_bss_conf *old[IEEE80211_MLD_MAX_NUM_LINKS];
        struct ieee80211_link_data *old_data[IEEE80211_MLD_MAX_NUM_LINKS];
        /* link them into data structures */
        for_each_set_bit(link_id, &add, IEEE80211_MLD_MAX_NUM_LINKS) {
                WARN_ON(!use_deflink &&
-                       sdata->link[link_id] == &sdata->deflink);
+                       rcu_access_pointer(sdata->link[link_id]) == &sdata->deflink);
 
                link = links[link_id];
                ieee80211_link_init(sdata, link_id, &link->data, &link->conf);
        }
 
        for_each_set_bit(link_id, &rem, IEEE80211_MLD_MAX_NUM_LINKS) {
-               sdata->link[link_id] = NULL;
-               sdata->vif.link_conf[link_id] = NULL;
+               RCU_INIT_POINTER(sdata->link[link_id], NULL);
+               RCU_INIT_POINTER(sdata->vif.link_conf[link_id], NULL);
        }
 
        sdata->vif.valid_links = new_links;
        /* now use this to free the old links */
        memset(links, 0, sizeof(links));
        for_each_set_bit(link_id, &rem, IEEE80211_MLD_MAX_NUM_LINKS) {
-               if (sdata->link[link_id] == &sdata->deflink)
+               if (rcu_access_pointer(sdata->link[link_id]) == &sdata->deflink)
                        continue;
                /*
                 * we must have allocated the data through this path so
                 * we know we can free both at the same time
                 */
-               links[link_id] = container_of(sdata->link[link_id],
+               links[link_id] = container_of(rcu_access_pointer(sdata->link[link_id]),
                                              typeof(*links[link_id]),
                                              data);
        }
 
 free:
        for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++)
-               kfree(links[link_id]);
+               kfree_rcu(links[link_id], rcu_head);
        if (use_deflink)
                ieee80211_link_init(sdata, -1, &sdata->deflink,
                                    &sdata->vif.bss_conf);
 
                u64 ch = changed & ~BSS_CHANGED_VIF_CFG_FLAGS;
 
                /* FIXME: should be for each link */
-               trace_drv_link_info_changed(local, sdata, 0, changed);
+               trace_drv_link_info_changed(local, sdata, &sdata->vif.bss_conf,
+                                           0, changed);
                if (local->ops->link_info_changed)
                        local->ops->link_info_changed(&local->hw, &sdata->vif,
+                                                     &sdata->vif.bss_conf,
                                                      0, ch);
        }
 
 }
 
 void ieee80211_link_info_change_notify(struct ieee80211_sub_if_data *sdata,
-                                      int link_id, u64 changed)
+                                      struct ieee80211_link_data *link,
+                                      u64 changed)
 {
        struct ieee80211_local *local = sdata->local;
 
        if (!check_sdata_in_driver(sdata))
                return;
 
-       drv_link_info_changed(local, sdata, link_id, changed);
+       drv_link_info_changed(local, sdata, link->conf, link->link_id, changed);
 }
 
 u32 ieee80211_reset_erp_info(struct ieee80211_sub_if_data *sdata)
 
        }
 
        ieee80211_recalc_dtim(local, sdata);
-       ieee80211_link_info_change_notify(sdata, 0, changed);
+       ieee80211_link_info_change_notify(sdata, &sdata->deflink, changed);
 
        netif_carrier_on(sdata->dev);
        return 0;
        sdata->vif.bss_conf.enable_beacon = false;
        sdata->beacon_rate_set = false;
        clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, &sdata->state);
-       ieee80211_link_info_change_notify(sdata, 0, BSS_CHANGED_BEACON_ENABLED);
+       ieee80211_link_info_change_notify(sdata, &sdata->deflink,
+                                         BSS_CHANGED_BEACON_ENABLED);
 
        /* remove beacon */
        bcn = sdata_dereference(ifmsh->beacon, sdata);
                if (ieee80211_mesh_rebuild_beacon(sdata))
                        return;
 
-       ieee80211_link_info_change_notify(sdata, 0, changed);
+       ieee80211_link_info_change_notify(sdata, &sdata->deflink, changed);
 }
 
 void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata)
 
 
        if (sdata->vif.bss_conf.ps != ps_allowed) {
                sdata->vif.bss_conf.ps = ps_allowed;
-               ieee80211_link_info_change_notify(sdata, 0, BSS_CHANGED_PS);
+               ieee80211_link_info_change_notify(sdata, &sdata->deflink,
+                                                 BSS_CHANGED_PS);
        }
 }
 
 void ieee80211_sta_handle_tspec_ac_params(struct ieee80211_sub_if_data *sdata)
 {
        if (__ieee80211_sta_handle_tspec_ac_params(sdata))
-               ieee80211_link_info_change_notify(sdata, 0, BSS_CHANGED_QOS);
+               ieee80211_link_info_change_notify(sdata, &sdata->deflink,
+                                                 BSS_CHANGED_QOS);
 }
 
 static void ieee80211_sta_handle_tspec_ac_params_wk(struct work_struct *work)
        ieee80211_recalc_ps(local);
        mutex_unlock(&local->iflist_mtx);
 
-       ieee80211_recalc_smps(sdata, 0);
+       ieee80211_recalc_smps(sdata, &sdata->deflink);
        ieee80211_recalc_ps_vif(sdata);
 
        netif_carrier_on(sdata->dev);
                sta_info_destroy_addr(sdata, auth_data->bss->bssid);
 
                eth_zero_addr(sdata->deflink.u.mgd.bssid);
-               ieee80211_link_info_change_notify(sdata, 0, BSS_CHANGED_BSSID);
+               ieee80211_link_info_change_notify(sdata, &sdata->deflink,
+                                                 BSS_CHANGED_BSSID);
                sdata->u.mgd.flags = 0;
                mutex_lock(&sdata->local->mtx);
                ieee80211_link_release_channel(&sdata->deflink);
                sta_info_destroy_addr(sdata, assoc_data->bss->bssid);
 
                eth_zero_addr(sdata->deflink.u.mgd.bssid);
-               ieee80211_link_info_change_notify(sdata, 0, BSS_CHANGED_BSSID);
+               ieee80211_link_info_change_notify(sdata, &sdata->deflink,
+                                                 BSS_CHANGED_BSSID);
                sdata->u.mgd.flags = 0;
                sdata->vif.bss_conf.mu_mimo_owner = false;
 
                                               elems->pwr_constr_elem,
                                               elems->cisco_dtpc_elem);
 
-       ieee80211_link_info_change_notify(sdata, 0, changed);
+       ieee80211_link_info_change_notify(sdata, &sdata->deflink,
+                                         changed);
 free:
        kfree(elems);
 }
                 * tell driver about BSSID, basic rates and timing
                 * this was set up above, before setting the channel
                 */
-               ieee80211_link_info_change_notify(sdata, 0,
-                       BSS_CHANGED_BSSID | BSS_CHANGED_BASIC_RATES |
-                       BSS_CHANGED_BEACON_INT);
+               ieee80211_link_info_change_notify(sdata, &sdata->deflink,
+                                                 BSS_CHANGED_BSSID |
+                                                 BSS_CHANGED_BASIC_RATES |
+                                                 BSS_CHANGED_BEACON_INT);
 
                if (assoc)
                        sta_info_pre_move_state(new_sta, IEEE80211_STA_AUTH);
 
  err_clear:
        eth_zero_addr(sdata->deflink.u.mgd.bssid);
-       ieee80211_link_info_change_notify(sdata, 0, BSS_CHANGED_BSSID);
+       ieee80211_link_info_change_notify(sdata, &sdata->deflink,
+                                         BSS_CHANGED_BSSID);
        ifmgd->auth_data = NULL;
        mutex_lock(&sdata->local->mtx);
        ieee80211_link_release_channel(&sdata->deflink);
        return 0;
  err_clear:
        eth_zero_addr(sdata->deflink.u.mgd.bssid);
-       ieee80211_link_info_change_notify(sdata, 0, BSS_CHANGED_BSSID);
+       ieee80211_link_info_change_notify(sdata, &sdata->deflink,
+                                         BSS_CHANGED_BSSID);
        ifmgd->assoc_data = NULL;
  err_free:
        kfree(assoc_data);
 
        sdata->deflink.needed_rx_chains = sdata->local->rx_chains;
 
        mutex_lock(&sdata->local->mtx);
-       err = ieee80211_link_use_channel(sdata->link[0], &setup->chandef,
+       err = ieee80211_link_use_channel(&sdata->deflink, &setup->chandef,
                                         IEEE80211_CHANCTX_SHARED);
        mutex_unlock(&sdata->local->mtx);
        if (err)
        ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_OCB);
 
        mutex_lock(&sdata->local->mtx);
-       ieee80211_link_release_channel(sdata->link[0]);
+       ieee80211_link_release_channel(&sdata->deflink);
        mutex_unlock(&sdata->local->mtx);
 
        skb_queue_purge(&sdata->skb_queue);
 
                                &sdata->state);
                        sdata->vif.bss_conf.enable_beacon = false;
                        ieee80211_link_info_change_notify(
-                               sdata, 0, BSS_CHANGED_BEACON_ENABLED);
+                               sdata, &sdata->deflink,
+                               BSS_CHANGED_BEACON_ENABLED);
                }
 
                if (sdata->vif.type == NL80211_IFTYPE_STATION &&
                                       &sdata->state)) {
                        sdata->vif.bss_conf.enable_beacon = true;
                        ieee80211_link_info_change_notify(
-                               sdata, 0, BSS_CHANGED_BEACON_ENABLED);
+                               sdata, &sdata->deflink,
+                               BSS_CHANGED_BEACON_ENABLED);
                }
        }
        mutex_unlock(&local->iflist_mtx);
                rcu_read_lock();
                /* Check all the links first */
                for (i = 0; i < ARRAY_SIZE(sdata->vif.link_conf); i++) {
-                       if (!sdata->vif.link_conf[i])
+                       struct ieee80211_bss_conf *conf;
+
+                       conf = rcu_dereference(sdata->vif.link_conf[i]);
+                       if (!conf)
                                continue;
 
-                       chanctx_conf = rcu_dereference(sdata->vif.link_conf[i]->chanctx_conf);
+                       chanctx_conf = rcu_dereference(conf->chanctx_conf);
                        if (!chanctx_conf)
                                continue;
 
-                       if (ether_addr_equal(sdata->vif.link_conf[i]->addr, mgmt->sa))
+                       if (ether_addr_equal(conf->addr, mgmt->sa))
                                break;
 
                        chanctx_conf = NULL;
 
                return false;
 
        for (link_id = 0; link_id < ARRAY_SIZE(sdata->vif.link_conf); link_id++) {
-               if (!sdata->vif.link_conf[link_id])
+               struct ieee80211_bss_conf *conf;
+
+               conf = rcu_dereference(sdata->vif.link_conf[link_id]);
+
+               if (!conf)
                        continue;
-               if (ether_addr_equal(sdata->vif.link_conf[link_id]->addr, addr)) {
+               if (ether_addr_equal(conf->addr, addr)) {
                        if (out_link_id)
                                *out_link_id = link_id;
                        return true;
 
 
        if (allow_p2p_go_ps != sdata->vif.bss_conf.allow_p2p_go_ps) {
                sdata->vif.bss_conf.allow_p2p_go_ps = allow_p2p_go_ps;
-               ieee80211_link_info_change_notify(sdata, 0, BSS_CHANGED_P2P_PS);
+               ieee80211_link_info_change_notify(sdata, &sdata->deflink,
+                                                 BSS_CHANGED_P2P_PS);
        }
 }
 
 
                return;
 
        sdata->vif.bss_conf.ht_operation_mode = opmode;
-       ieee80211_link_info_change_notify(sdata, 0, BSS_CHANGED_HT);
+       ieee80211_link_info_change_notify(sdata, &sdata->deflink,
+                                         BSS_CHANGED_HT);
 }
 
 int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
 
 TRACE_EVENT(drv_link_info_changed,
        TP_PROTO(struct ieee80211_local *local,
                 struct ieee80211_sub_if_data *sdata,
+                struct ieee80211_bss_conf *link_conf,
                 int link_id, u64 changed),
 
-       TP_ARGS(local, sdata, link_id, changed),
+       TP_ARGS(local, sdata, link_conf, link_id, changed),
 
        TP_STRUCT__entry(
                LOCAL_ENTRY
        ),
 
        TP_fast_assign(
-               struct ieee80211_bss_conf *link_conf = sdata->vif.link_conf[link_id];
-
                LOCAL_ASSIGN;
                VIF_ASSIGN;
                __entry->changed = changed;
 TRACE_EVENT(drv_start_ap,
        TP_PROTO(struct ieee80211_local *local,
                 struct ieee80211_sub_if_data *sdata,
-                struct ieee80211_bss_conf *info,
                 unsigned int link_id),
 
-       TP_ARGS(local, sdata, info, link_id),
+       TP_ARGS(local, sdata, link_id),
 
        TP_STRUCT__entry(
                LOCAL_ENTRY
        ),
 
        TP_fast_assign(
+               struct ieee80211_bss_conf *info =
+                       sdata_dereference(sdata->vif.link_conf[link_id], sdata);
+
                LOCAL_ASSIGN;
                VIF_ASSIGN;
                __entry->link_id = link_id;
-               __entry->dtimper = info->dtim_period;
-               __entry->bcnint = info->beacon_int;
+               if (info) {
+                       __entry->dtimper = info->dtim_period;
+                       __entry->bcnint = info->beacon_int;
+                       __entry->hidden_ssid = info->hidden_ssid;
+               }
                memcpy(__get_dynamic_array(ssid),
                       sdata->vif.cfg.ssid,
                       sdata->vif.cfg.ssid_len);
-               __entry->hidden_ssid = info->hidden_ssid;
        ),
 
        TP_printk(
 
 /* functions for drivers to get certain frames */
 
 static void __ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata,
+                                      struct ieee80211_link_data *link,
                                       struct ps_data *ps, struct sk_buff *skb,
-                                      bool is_template, unsigned int link_id)
+                                      bool is_template)
 {
        u8 *pos, *tim;
        int aid0 = 0;
        int i, have_bits = 0, n1, n2;
-       struct ieee80211_bss_conf *link_conf = sdata->vif.link_conf[link_id];
+       struct ieee80211_bss_conf *link_conf = link->conf;
 
        /* Generate bitmap for TIM only if there are any STAs in power save
         * mode. */
 }
 
 static int ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata,
+                                   struct ieee80211_link_data *link,
                                    struct ps_data *ps, struct sk_buff *skb,
-                                   bool is_template, unsigned int link_id)
+                                   bool is_template)
 {
        struct ieee80211_local *local = sdata->local;
 
         * of the tim bitmap in mac80211 and the driver.
         */
        if (local->tim_in_locked_section) {
-               __ieee80211_beacon_add_tim(sdata, ps, skb, is_template,
-                                          link_id);
+               __ieee80211_beacon_add_tim(sdata, link, ps, skb, is_template);
        } else {
                spin_lock_bh(&local->tim_lock);
-               __ieee80211_beacon_add_tim(sdata, ps, skb, is_template,
-                                          link_id);
+               __ieee80211_beacon_add_tim(sdata, link, ps, skb, is_template);
                spin_unlock_bh(&local->tim_lock);
        }
 
 
 static void ieee80211_set_beacon_cntdwn(struct ieee80211_sub_if_data *sdata,
                                        struct beacon_data *beacon,
-                                       unsigned int link_id)
+                                       struct ieee80211_link_data *link)
 {
        u8 *beacon_data, count, max_count = 1;
        struct probe_resp *resp;
                return;
        }
 
-       rcu_read_lock();
-       resp = rcu_dereference(sdata->link[link_id]->u.ap.probe_resp);
+       resp = rcu_dereference(link->u.ap.probe_resp);
 
        bcn_offsets = beacon->cntdwn_counter_offsets;
        count = beacon->cntdwn_current_counter;
-       if (sdata->vif.link_conf[link_id]->csa_active)
+       if (link->conf->csa_active)
                max_count = IEEE80211_MAX_CNTDWN_COUNTERS_NUM;
 
        for (i = 0; i < max_count; ++i) {
                if (bcn_offsets[i]) {
-                       if (WARN_ON_ONCE(bcn_offsets[i] >= beacon_data_len)) {
-                               rcu_read_unlock();
+                       if (WARN_ON_ONCE(bcn_offsets[i] >= beacon_data_len))
                                return;
-                       }
                        beacon_data[bcn_offsets[i]] = count;
                }
 
                        resp->data[resp_offsets[i]] = count;
                }
        }
-       rcu_read_unlock();
 }
 
 static u8 __ieee80211_beacon_update_cntdwn(struct beacon_data *beacon)
 static int ieee80211_beacon_protect(struct sk_buff *skb,
                                    struct ieee80211_local *local,
                                    struct ieee80211_sub_if_data *sdata,
-                                   unsigned int link_id)
+                                   struct ieee80211_link_data *link)
 {
        ieee80211_tx_result res;
        struct ieee80211_tx_data tx;
        struct sk_buff *check_skb;
 
        memset(&tx, 0, sizeof(tx));
-       tx.key = rcu_dereference(sdata->link[link_id]->default_beacon_key);
+       tx.key = rcu_dereference(link->default_beacon_key);
        if (!tx.key)
                return 0;
        tx.local = local;
 static void
 ieee80211_beacon_get_finish(struct ieee80211_hw *hw,
                            struct ieee80211_vif *vif,
+                           struct ieee80211_link_data *link,
                            struct ieee80211_mutable_offsets *offs,
                            struct beacon_data *beacon,
                            struct sk_buff *skb,
                            struct ieee80211_chanctx_conf *chanctx_conf,
-                           u16 csa_off_base,
-                           unsigned int link_id)
+                           u16 csa_off_base)
 {
        struct ieee80211_local *local = hw_to_local(hw);
        struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
        memset(&txrc, 0, sizeof(txrc));
        txrc.hw = hw;
        txrc.sband = local->hw.wiphy->bands[band];
-       txrc.bss_conf = sdata->vif.link_conf[link_id];
+       txrc.bss_conf = link->conf;
        txrc.skb = skb;
        txrc.reported_rate.idx = -1;
        if (sdata->beacon_rate_set && sdata->beacon_rateidx_mask[band])
 static struct sk_buff *
 ieee80211_beacon_get_ap(struct ieee80211_hw *hw,
                        struct ieee80211_vif *vif,
+                       struct ieee80211_link_data *link,
                        struct ieee80211_mutable_offsets *offs,
                        bool is_template,
                        struct beacon_data *beacon,
-                       struct ieee80211_chanctx_conf *chanctx_conf,
-                       unsigned int link_id)
+                       struct ieee80211_chanctx_conf *chanctx_conf)
 {
        struct ieee80211_local *local = hw_to_local(hw);
        struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
                if (!is_template)
                        ieee80211_beacon_update_cntdwn(vif);
 
-               ieee80211_set_beacon_cntdwn(sdata, beacon, link_id);
+               ieee80211_set_beacon_cntdwn(sdata, beacon, link);
        }
 
        /* headroom, head length,
        skb_reserve(skb, local->tx_headroom);
        skb_put_data(skb, beacon->head, beacon->head_len);
 
-       ieee80211_beacon_add_tim(sdata, &ap->ps, skb, is_template, link_id);
+       ieee80211_beacon_add_tim(sdata, link, &ap->ps, skb, is_template);
 
        if (offs) {
                offs->tim_offset = beacon->head_len;
        if (beacon->tail)
                skb_put_data(skb, beacon->tail, beacon->tail_len);
 
-       if (ieee80211_beacon_protect(skb, local, sdata, link_id) < 0)
+       if (ieee80211_beacon_protect(skb, local, sdata, link) < 0)
                return NULL;
 
-       ieee80211_beacon_get_finish(hw, vif, offs, beacon, skb, chanctx_conf,
-                                   csa_off_base, link_id);
+       ieee80211_beacon_get_finish(hw, vif, link, offs, beacon, skb,
+                                   chanctx_conf, csa_off_base);
        return skb;
 }
 
        struct sk_buff *skb = NULL;
        struct ieee80211_sub_if_data *sdata = NULL;
        struct ieee80211_chanctx_conf *chanctx_conf;
+       struct ieee80211_link_data *link;
 
        rcu_read_lock();
 
        sdata = vif_to_sdata(vif);
+       link = rcu_dereference(sdata->link[link_id]);
+       if (!link)
+               goto out;
        chanctx_conf =
-               rcu_dereference(sdata->vif.link_conf[link_id]->chanctx_conf);
+               rcu_dereference(link->conf->chanctx_conf);
 
        if (!ieee80211_sdata_running(sdata) || !chanctx_conf)
                goto out;
                memset(offs, 0, sizeof(*offs));
 
        if (sdata->vif.type == NL80211_IFTYPE_AP) {
-               beacon = rcu_dereference(sdata->link[link_id]->u.ap.beacon);
+               beacon = rcu_dereference(link->u.ap.beacon);
                if (!beacon)
                        goto out;
 
-               skb = ieee80211_beacon_get_ap(hw, vif, offs, is_template,
-                                             beacon, chanctx_conf, link_id);
+               skb = ieee80211_beacon_get_ap(hw, vif, link, offs, is_template,
+                                             beacon, chanctx_conf);
        } else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) {
                struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
                struct ieee80211_hdr *hdr;
                        if (!is_template)
                                __ieee80211_beacon_update_cntdwn(beacon);
 
-                       ieee80211_set_beacon_cntdwn(sdata, beacon, link_id);
+                       ieee80211_set_beacon_cntdwn(sdata, beacon, link);
                }
 
                skb = dev_alloc_skb(local->tx_headroom + beacon->head_len +
                hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
                                                 IEEE80211_STYPE_BEACON);
 
-               ieee80211_beacon_get_finish(hw, vif, offs, beacon, skb,
-                                           chanctx_conf, 0, link_id);
+               ieee80211_beacon_get_finish(hw, vif, link, offs, beacon, skb,
+                                           chanctx_conf, 0);
        } else if (ieee80211_vif_is_mesh(&sdata->vif)) {
                struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
 
                                 */
                                __ieee80211_beacon_update_cntdwn(beacon);
 
-                       ieee80211_set_beacon_cntdwn(sdata, beacon, link_id);
+                       ieee80211_set_beacon_cntdwn(sdata, beacon, link);
                }
 
                if (ifmsh->sync_ops)
                        goto out;
                skb_reserve(skb, local->tx_headroom);
                skb_put_data(skb, beacon->head, beacon->head_len);
-               ieee80211_beacon_add_tim(sdata, &ifmsh->ps, skb, is_template,
-                                        link_id);
+               ieee80211_beacon_add_tim(sdata, link, &ifmsh->ps, skb,
+                                        is_template);
 
                if (offs) {
                        offs->tim_offset = beacon->head_len;
                }
 
                skb_put_data(skb, beacon->tail, beacon->tail_len);
-               ieee80211_beacon_get_finish(hw, vif, offs, beacon, skb,
-                                           chanctx_conf, 0, link_id);
+               ieee80211_beacon_get_finish(hw, vif, link, offs, beacon, skb,
+                                           chanctx_conf, 0);
        } else {
                WARN_ON(1);
                goto out;
                link = IEEE80211_LINK_UNSPECIFIED;
        } else {
                /* otherwise must be addressed from a link */
+               rcu_read_lock();
                for (link = 0; link < ARRAY_SIZE(sdata->vif.link_conf); link++) {
-                       if (memcmp(sdata->vif.link_conf[link]->addr,
-                                  hdr->addr2, ETH_ALEN) == 0)
+                       struct ieee80211_bss_conf *link_conf;
+
+                       link_conf = rcu_dereference(sdata->vif.link_conf[link]);
+                       if (!link_conf)
+                               continue;
+                       if (memcmp(link_conf->addr, hdr->addr2, ETH_ALEN) == 0)
                                break;
                }
+               rcu_read_unlock();
 
                if (WARN_ON_ONCE(link == ARRAY_SIZE(sdata->vif.link_conf)))
                        link = ffs(sdata->vif.valid_links) - 1;
 
            sdata->vif.type != NL80211_IFTYPE_NAN) {
                sdata->vif.bss_conf.qos = enable_qos;
                if (bss_notify)
-                       ieee80211_link_info_change_notify(sdata, 0,
+                       ieee80211_link_info_change_notify(sdata,
+                                                         &sdata->deflink,
                                                          BSS_CHANGED_QOS);
        }
 }
 
 static void ieee80211_assign_chanctx(struct ieee80211_local *local,
                                     struct ieee80211_sub_if_data *sdata,
-                                    unsigned int link_id)
+                                    struct ieee80211_link_data *link)
 {
        struct ieee80211_chanctx_conf *conf;
        struct ieee80211_chanctx *ctx;
                return;
 
        mutex_lock(&local->chanctx_mtx);
-       conf = rcu_dereference_protected(sdata->vif.link_conf[link_id]->chanctx_conf,
+       conf = rcu_dereference_protected(link->conf->chanctx_conf,
                                         lockdep_is_held(&local->chanctx_mtx));
        if (conf) {
                ctx = container_of(conf, struct ieee80211_chanctx, conf);
-               drv_assign_vif_chanctx(local, sdata, link_id, ctx);
+               drv_assign_vif_chanctx(local, sdata, link->link_id, ctx);
        }
        mutex_unlock(&local->chanctx_mtx);
 }
                sdata = wiphy_dereference(local->hw.wiphy,
                                          local->monitor_sdata);
                if (sdata && ieee80211_sdata_running(sdata))
-                       ieee80211_assign_chanctx(local, sdata, 0);
+                       ieee80211_assign_chanctx(local, sdata, &sdata->deflink);
        }
 
        /* reconfigure hardware */
 
        /* Finally also reconfigure all the BSS information */
        list_for_each_entry(sdata, &local->interfaces, list) {
-               unsigned int link;
+               unsigned int link_id;
                u32 changed;
 
                if (!ieee80211_sdata_running(sdata))
                        continue;
 
-               for (link = 0; link < ARRAY_SIZE(sdata->vif.link_conf); link++) {
-                       if (sdata->vif.link_conf[link])
+               sdata_lock(sdata);
+               for (link_id = 0;
+                    link_id < ARRAY_SIZE(sdata->vif.link_conf);
+                    link_id++) {
+                       struct ieee80211_link_data *link;
+
+                       link = sdata_dereference(sdata->link[link_id], sdata);
+                       if (link)
                                ieee80211_assign_chanctx(local, sdata, link);
                }
+               sdata_unlock(sdata);
 
                switch (sdata->vif.type) {
                case NL80211_IFTYPE_AP_VLAN:
 EXPORT_SYMBOL_GPL(ieee80211_resume_disconnect);
 
 void ieee80211_recalc_smps(struct ieee80211_sub_if_data *sdata,
-                          unsigned int link_id)
+                          struct ieee80211_link_data *link)
 {
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_chanctx_conf *chanctx_conf;
 
        mutex_lock(&local->chanctx_mtx);
 
-       chanctx_conf = rcu_dereference_protected(sdata->vif.link_conf[link_id]->chanctx_conf,
+       chanctx_conf = rcu_dereference_protected(link->conf->chanctx_conf,
                                                 lockdep_is_held(&local->chanctx_mtx));
 
        /*
 
                if (sdata->wdev.cac_started) {
                        chandef = sdata->vif.bss_conf.chandef;
-                       ieee80211_link_release_channel(sdata->link[0]);
+                       ieee80211_link_release_channel(&sdata->deflink);
                        cfg80211_cac_event(sdata->dev,
                                           &chandef,
                                           NL80211_RADAR_CAC_ABORTED,
                !list_empty(&ctx->assigned_links));
 
        list_for_each_entry(link, &ctx->assigned_links, assigned_chanctx_list) {
-               struct ieee80211_sub_if_data *sdata = link->sdata;
-
                if (!link->radar_required)
                        continue;
 
                radar_detect |=
-                       BIT(sdata->vif.link_conf[link->link_id]->chandef.width);
+                       BIT(link->conf->chandef.width);
        }
 
        return radar_detect;
 
 {
        unsigned int link_id = link_sta->link_id;
        struct ieee80211_sub_if_data *sdata = link_sta->sta->sdata;
-       struct ieee80211_bss_conf *link_conf = sdata->vif.link_conf[link_id];
        struct ieee80211_sta_vht_cap *vht_cap = &link_sta->pub->vht_cap;
        struct ieee80211_sta_he_cap *he_cap = &link_sta->pub->he_cap;
        struct ieee80211_sta_eht_cap *eht_cap = &link_sta->pub->eht_cap;
        u32 cap_width;
 
        if (he_cap->has_he) {
+               struct ieee80211_bss_conf *link_conf;
+               enum ieee80211_sta_rx_bandwidth ret;
                u8 info;
 
+               rcu_read_lock();
+               link_conf = rcu_dereference(sdata->vif.link_conf[link_id]);
+
                if (eht_cap->has_eht &&
                    link_conf->chandef.chan->band == NL80211_BAND_6GHZ) {
                        info = eht_cap->eht_cap_elem.phy_cap_info[0];
 
-                       if (info & IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ)
-                               return IEEE80211_STA_RX_BW_320;
+                       if (info & IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ) {
+                               ret = IEEE80211_STA_RX_BW_320;
+                               goto out;
+                       }
                }
 
                info = he_cap->he_cap_elem.phy_cap_info[0];
 
                if (link_conf->chandef.chan->band == NL80211_BAND_2GHZ) {
                        if (info & IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G)
-                               return IEEE80211_STA_RX_BW_40;
+                               ret = IEEE80211_STA_RX_BW_40;
                        else
-                               return IEEE80211_STA_RX_BW_20;
+                               ret = IEEE80211_STA_RX_BW_20;
+                       goto out;
                }
 
                if (info & IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G ||
                    info & IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G)
-                       return IEEE80211_STA_RX_BW_160;
+                       ret = IEEE80211_STA_RX_BW_160;
                else if (info & IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G)
-                       return IEEE80211_STA_RX_BW_80;
+                       ret = IEEE80211_STA_RX_BW_80;
+               else
+                       ret = IEEE80211_STA_RX_BW_20;
+out:
+               rcu_read_unlock();
 
-               return IEEE80211_STA_RX_BW_20;
+               return ret;
        }
 
        if (!vht_cap->vht_supported)
 ieee80211_sta_cur_vht_bw(struct link_sta_info *link_sta)
 {
        struct sta_info *sta = link_sta->sta;
-       struct ieee80211_bss_conf *link_conf =
-               sta->sdata->vif.link_conf[link_sta->link_id];
-       enum nl80211_chan_width bss_width = link_conf->chandef.width;
+       struct ieee80211_bss_conf *link_conf;
+       enum nl80211_chan_width bss_width;
        enum ieee80211_sta_rx_bandwidth bw;
 
+       rcu_read_lock();
+       link_conf = rcu_dereference(sta->sdata->vif.link_conf[link_sta->link_id]);
+       if (WARN_ON(!link_conf))
+               bss_width = NL80211_CHAN_WIDTH_20_NOHT;
+       else
+               bss_width = link_conf->chandef.width;
+       rcu_read_unlock();
+
        bw = ieee80211_sta_cap_rx_bw(link_sta);
        bw = min(bw, link_sta->cur_max_bandwidth);
 
 }
 
 void ieee80211_process_mu_groups(struct ieee80211_sub_if_data *sdata,
-                                unsigned int link_id,
+                                struct ieee80211_link_data *link,
                                 struct ieee80211_mgmt *mgmt)
 {
-       struct ieee80211_bss_conf *link_conf = sdata->vif.link_conf[link_id];
+       struct ieee80211_bss_conf *link_conf = link->conf;
 
        if (!link_conf->mu_mimo_owner)
                return;
               mgmt->u.action.u.vht_group_notif.position,
               WLAN_USER_POSITION_LEN);
 
-       ieee80211_link_info_change_notify(sdata, link_id, BSS_CHANGED_MU_GROUPS);
+       ieee80211_link_info_change_notify(sdata, link,
+                                         BSS_CHANGED_MU_GROUPS);
 }
 
 void ieee80211_update_mu_groups(struct ieee80211_vif *vif, unsigned int link_id,
                                const u8 *membership, const u8 *position)
 {
-       struct ieee80211_bss_conf *link_conf = vif->link_conf[link_id];
+       struct ieee80211_bss_conf *link_conf;
 
-       if (WARN_ON_ONCE(!link_conf->mu_mimo_owner))
-               return;
+       rcu_read_lock();
+       link_conf = rcu_dereference(vif->link_conf[link_id]);
 
-       memcpy(link_conf->mu_group.membership, membership, WLAN_MEMBERSHIP_LEN);
-       memcpy(link_conf->mu_group.position, position, WLAN_USER_POSITION_LEN);
+       if (!WARN_ON_ONCE(!link_conf || !link_conf->mu_mimo_owner)) {
+               memcpy(link_conf->mu_group.membership, membership,
+                      WLAN_MEMBERSHIP_LEN);
+               memcpy(link_conf->mu_group.position, position,
+                      WLAN_USER_POSITION_LEN);
+       }
+       rcu_read_unlock();
 }
 EXPORT_SYMBOL_GPL(ieee80211_update_mu_groups);