diff mbox series

[v2,2/2] wifi: mac80211: restructure vif and link conf for mlo mbssid support

Message ID 20241025013857.2793346-3-quic_msinada@quicinc.com (mailing list archive)
State Changes Requested
Delegated to: Johannes Berg
Headers show
Series MLO MBSSID Support | expand

Commit Message

Muna Sinada Oct. 25, 2024, 1:38 a.m. UTC
From: Rameshkumar Sundaram <quic_ramess@quicinc.com>

For MLD, if transmitting interface is an MLD then BSS configuration
inside that MLD has to be saved inside non-Tx MLD's link. With MLO,
there are multiple links where each link can have its own
transmitting vif.

Currently mbssid_tx_vif is inside vif structure which won't suffice for
finding Tx link for any non-Tx link since it is not link specific.

Remove mbssid_tx_vif and add mbssid_tx_bss to ieee80211_bss_conf.This
new member is used to find Tx BSS for any non-Tx link.

Modify CSA completion and BCCA event notifier routines to fetch
non-Tx BSS links based on mbssid_tx_bss.

Update mbssid_tx_bss to be accessed from struct ieee80211_bss_conf
instead of mbssid_tx_vif from struct ieee80211_vif in ATH drivers.

Signed-off-by: Rameshkumar Sundaram <quic_ramess@quicinc.com>
Signed-off-by: Muna Sinada <quic_msinada@quicinc.com>
---
v2: addressed review comments
 - removed usage of mbssid_tx_vif and mbssid_tx_vif_linkid and added
   link to Tx link BSS instead
 - correct accessing rcu protected members

---
 drivers/net/wireless/ath/ath11k/mac.c         | 26 ++---
 drivers/net/wireless/ath/ath12k/mac.c         | 24 ++---
 drivers/net/wireless/virtual/mac80211_hwsim.c |  2 +-
 include/net/mac80211.h                        |  6 +-
 net/mac80211/cfg.c                            | 83 +++++++++++++---
 net/mac80211/ieee80211_i.h                    |  1 +
 net/mac80211/iface.c                          | 98 +++++++++++++++----
 net/wireless/sme.c                            |  4 +-
 8 files changed, 180 insertions(+), 64 deletions(-)

Comments

Johannes Berg Oct. 25, 2024, 8:51 a.m. UTC | #1
> +++ b/include/net/mac80211.h
> @@ -682,6 +682,8 @@ struct ieee80211_parsed_tpe {
>   *	responder functionality.
>   * @ftmr_params: configurable lci/civic parameter when enabling FTM responder.
>   * @nontransmitted: this BSS is a nontransmitted BSS profile
> + * @mbssid_tx_bss: Pointer to the BSS configuration of transmitting interface
> + *	if MBSSID is enabled.
>   * @transmitter_bssid: the address of transmitter AP
>   * @bssid_index: index inside the multiple BSSID set
>   * @bssid_indicator: 2^bssid_indicator is the maximum number of APs in set
> @@ -790,6 +792,7 @@ struct ieee80211_bss_conf {
>  	struct ieee80211_ftm_responder_params *ftmr_params;
>  	/* Multiple BSSID data */
>  	bool nontransmitted;
> +	struct ieee80211_bss_conf *mbssid_tx_bss;

Please say a few words about the safety of accessing this pointer.

It doesn't _feel_ safe to me, if the vif/bss_conf itself may have been
reached via RCU for example. But I won't say that my gut feeling here is
necessarily correct. Please check and comment.

> @@ -2032,8 +2034,6 @@ struct ieee80211_vif {
>  	bool probe_req_reg;
>  	bool rx_mcast_action_reg;
>  
> -	struct ieee80211_vif *mbssid_tx_vif;

The same would've been true before, I guess, though perhaps less likely
to have been an issue since the other netdev must obviously be unlinked
first. Still pointer races could happen if they both go away at the same
time and we have a stale one via RCU?

> @@ -3636,23 +3651,43 @@ void ieee80211_csa_finish(struct ieee80211_vif *vif, unsigned int link_id)
>  		return;
>  	}
>  
> -	/* TODO: MBSSID with MLO changes */
> -	if (vif->mbssid_tx_vif == vif) {
> +	if (link_data->conf->mbssid_tx_bss == vif->link_conf[link_id]) {

And this shouldn't even build without (sparse) warnings (for similar
reasons)?

> +			valid_links = iter->vif.valid_links | BIT(0);
> +			for_each_set_bit(link_id_iter, &valid_links,
> +					 IEEE80211_MLD_MAX_NUM_LINKS) {

for_each_vif_active_link() is probably enough, on AP side you can't have
active != valid links I think. Or for_each_valid_link()?

> +				link_iter =
> +				      rcu_dereference(iter->link[link_id_iter]);
> +				if (!link_iter)
> +					continue;
> +				/* Check if any of link of iterator sdata
> +				 * belongs to same mbssid group as the tx link
> +				 */
> +				if (link_iter->conf->mbssid_tx_bss !=
> +				    vif->link_conf[link_id])
> +					continue;
> +
> +				wiphy_work_queue(iter->local->hw.wiphy,
> +						 &link_iter->csa.finalize_work);

This really got indented a bit too far anyway though - hardly readable
at all. Please refactor.

Or you could use for_each_sdata_link() I guess, at the small expense of
iterating all links of the interfaces you're going to skip anyway, but
that seems hardly important here?

> +			}
>  		}
>  	}
>  	wiphy_work_queue(local->hw.wiphy, &link_data->csa.finalize_work);
> @@ -4757,15 +4792,35 @@ ieee80211_color_change_bss_config_notify(struct ieee80211_link_data *link,
>  
>  	ieee80211_link_info_change_notify(sdata, link, changed);
>  
> -	if (!sdata->vif.bss_conf.nontransmitted && sdata->vif.mbssid_tx_vif) {
> +	if (!link->conf->nontransmitted && link->conf->mbssid_tx_bss) {
>  		struct ieee80211_sub_if_data *child;
> +		unsigned int link_id_iter;
> +		unsigned long valid_links;
> +		struct ieee80211_link_data *link_iter;
>  
>  		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;
> -				child->vif.bss_conf.he_bss_color.enabled = enable;
> +			if (child == sdata)
> +				continue;

All the same comments here ...

> +	lockdep_assert_wiphy(sdata->local->hw.wiphy);
> +	/* Check link 0 by default for non MLO. */
> +	iter_valid_links = sdata->vif.valid_links | BIT(0);
> +	/* Check if any of the links of current sdata is an MBSSID. */
> +	for_each_set_bit(iter_link_id, &iter_valid_links,
> +			 IEEE80211_MLD_MAX_NUM_LINKS) {

And also here.

> +++ b/net/wireless/sme.c
> @@ -1581,7 +1581,7 @@ void cfg80211_autodisconnect_wk(struct work_struct *work)
>  		container_of(work, struct wireless_dev, disconnect_wk);
>  	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
>  
> -	wiphy_lock(wdev->wiphy);
> +	wiphy_lock(&rdev->wiphy);
>  
>  	if (wdev->conn_owner_nlportid) {
>  		switch (wdev->iftype) {
> @@ -1618,5 +1618,5 @@ void cfg80211_autodisconnect_wk(struct work_struct *work)
>  		}
>  	}
>  
> -	wiphy_unlock(wdev->wiphy);
> +	wiphy_unlock(&rdev->wiphy);
>  }

?????

johannes
Johannes Berg Oct. 25, 2024, 9:01 a.m. UTC | #2
On Fri, 2024-10-25 at 10:51 +0200, Johannes Berg wrote:
> 
> 
> > +				link_iter =
> > +				      rcu_dereference(iter->link[link_id_iter]);
> > +				if (!link_iter)
> > +					continue;
> > +				/* Check if any of link of iterator sdata
> > +				 * belongs to same mbssid group as the tx link
> > +				 */
> > +				if (link_iter->conf->mbssid_tx_bss !=
> > +				    vif->link_conf[link_id])
> > +					continue;
> > +
> > +				wiphy_work_queue(iter->local->hw.wiphy,
> > +						 &link_iter->csa.finalize_work);
> 
> This really got indented a bit too far anyway though - hardly readable
> at all. Please refactor.

I feel like maybe I should say also: Remember that the _primary_ target
audience for your code should be the _human_ reading it (including
yourself a year from now).

johannes
diff mbox series

Patch

diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c
index 7c0ef6916dd2..5089449a6e9a 100644
--- a/drivers/net/wireless/ath/ath11k/mac.c
+++ b/drivers/net/wireless/ath/ath11k/mac.c
@@ -1538,7 +1538,7 @@  static int ath11k_mac_setup_bcn_tmpl_ema(struct ath11k_vif *arvif)
 	u32 params = 0;
 	u8 i = 0;
 
-	tx_arvif = ath11k_vif_to_arvif(arvif->vif->mbssid_tx_vif);
+	tx_arvif = ath11k_vif_to_arvif(arvif->vif->bss_conf.mbssid_tx_bss->vif);
 
 	beacons = ieee80211_beacon_get_template_ema_list(tx_arvif->ar->hw,
 							 tx_arvif->vif, 0);
@@ -1596,8 +1596,8 @@  static int ath11k_mac_setup_bcn_tmpl_mbssid(struct ath11k_vif *arvif)
 	struct sk_buff *bcn;
 	int ret;
 
-	if (vif->mbssid_tx_vif) {
-		tx_arvif = ath11k_vif_to_arvif(vif->mbssid_tx_vif);
+	if (vif->bss_conf.mbssid_tx_bss) {
+		tx_arvif = ath11k_vif_to_arvif(vif->bss_conf.mbssid_tx_bss->vif);
 		if (tx_arvif != arvif) {
 			ar = tx_arvif->ar;
 			ab = ar->ab;
@@ -1639,11 +1639,11 @@  static int ath11k_mac_setup_bcn_tmpl(struct ath11k_vif *arvif)
 	/* Target does not expect beacon templates for the already up
 	 * non-transmitting interfaces, and results in a crash if sent.
 	 */
-	if (vif->mbssid_tx_vif &&
-	    arvif != ath11k_vif_to_arvif(vif->mbssid_tx_vif) && arvif->is_up)
+	if (vif->bss_conf.mbssid_tx_bss->vif &&
+	    arvif != ath11k_vif_to_arvif(vif->bss_conf.mbssid_tx_bss->vif) && arvif->is_up)
 		return 0;
 
-	if (vif->bss_conf.ema_ap && vif->mbssid_tx_vif)
+	if (vif->bss_conf.ema_ap && vif->bss_conf.mbssid_tx_bss)
 		return ath11k_mac_setup_bcn_tmpl_ema(arvif);
 
 	return ath11k_mac_setup_bcn_tmpl_mbssid(arvif);
@@ -1703,8 +1703,8 @@  static void ath11k_control_beaconing(struct ath11k_vif *arvif,
 
 	ether_addr_copy(arvif->bssid, info->bssid);
 
-	if (arvif->vif->mbssid_tx_vif)
-		tx_arvif = ath11k_vif_to_arvif(arvif->vif->mbssid_tx_vif);
+	if (arvif->vif->bss_conf.mbssid_tx_bss)
+		tx_arvif = ath11k_vif_to_arvif(arvif->vif->bss_conf.mbssid_tx_bss->vif);
 
 	ret = ath11k_wmi_vdev_up(arvif->ar, arvif->vdev_id, arvif->aid,
 				 arvif->bssid,
@@ -6334,7 +6334,7 @@  static int ath11k_mac_setup_vdev_params_mbssid(struct ath11k_vif *arvif,
 	struct ieee80211_vif *tx_vif;
 
 	*tx_vdev_id = 0;
-	tx_vif = arvif->vif->mbssid_tx_vif;
+	tx_vif = arvif->vif->bss_conf.mbssid_tx_bss->vif;
 	if (!tx_vif) {
 		*flags = WMI_HOST_VDEV_FLAGS_NON_MBSSID_AP;
 		return 0;
@@ -7296,7 +7296,7 @@  ath11k_mac_update_vif_chan(struct ath11k *ar,
 {
 	struct ath11k_base *ab = ar->ab;
 	struct ath11k_vif *arvif, *tx_arvif = NULL;
-	struct ieee80211_vif *mbssid_tx_vif;
+	struct ieee80211_bss_conf *mbssid_tx_bss;
 	int ret;
 	int i;
 	bool monitor_vif = false;
@@ -7350,9 +7350,9 @@  ath11k_mac_update_vif_chan(struct ath11k *ar,
 			ath11k_warn(ab, "failed to update bcn tmpl during csa: %d\n",
 				    ret);
 
-		mbssid_tx_vif = arvif->vif->mbssid_tx_vif;
-		if (mbssid_tx_vif)
-			tx_arvif = ath11k_vif_to_arvif(mbssid_tx_vif);
+		mbssid_tx_bss = arvif->vif->bss_conf.mbssid_tx_bss;
+		if (mbssid_tx_bss)
+			tx_arvif = ath11k_vif_to_arvif(mbssid_tx_bss->vif);
 
 		ret = ath11k_wmi_vdev_up(arvif->ar, arvif->vdev_id, arvif->aid,
 					 arvif->bssid,
diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c
index ce41c8153080..3f8a99e6d2b5 100644
--- a/drivers/net/wireless/ath/ath12k/mac.c
+++ b/drivers/net/wireless/ath/ath12k/mac.c
@@ -1480,7 +1480,7 @@  static int ath12k_mac_setup_bcn_tmpl_ema(struct ath12k_vif *arvif)
 	int ret = 0;
 	u8 i;
 
-	tx_arvif = ath12k_vif_to_arvif(arvif->vif->mbssid_tx_vif);
+	tx_arvif = ath12k_vif_to_arvif(bss_conf->mbssid_tx_bss->vif);
 	beacons = ieee80211_beacon_get_template_ema_list(ath12k_ar_to_hw(tx_arvif->ar),
 							 tx_arvif->vif, 0);
 	if (!beacons || !beacons->cnt) {
@@ -1534,8 +1534,8 @@  static int ath12k_mac_setup_bcn_tmpl(struct ath12k_vif *arvif)
 	if (arvif->vdev_type != WMI_VDEV_TYPE_AP)
 		return 0;
 
-	if (vif->mbssid_tx_vif) {
-		tx_arvif = ath12k_vif_to_arvif(vif->mbssid_tx_vif);
+	if (vif->bss_conf.mbssid_tx_bss) {
+		tx_arvif = ath12k_vif_to_arvif(vif->bss_conf.mbssid_tx_bss->vif);
 		if (tx_arvif != arvif && arvif->is_up)
 			return 0;
 
@@ -1629,8 +1629,9 @@  static void ath12k_control_beaconing(struct ath12k_vif *arvif,
 	params.vdev_id = arvif->vdev_id;
 	params.aid = arvif->aid;
 	params.bssid = arvif->bssid;
-	if (arvif->vif->mbssid_tx_vif) {
-		params.tx_bssid = ath12k_vif_to_arvif(arvif->vif->mbssid_tx_vif)->bssid;
+	if (arvif->vif->bss_conf.mbssid_tx_bss) {
+		params.tx_bssid =
+			   ath12k_vif_to_arvif(arvif->vif->bss_conf.mbssid_tx_bss->vif)->bssid;
 		params.nontx_profile_idx = info->bssid_index;
 		params.nontx_profile_cnt = 1 << info->bssid_indicator;
 	}
@@ -6225,17 +6226,17 @@  ath12k_mac_get_vdev_stats_id(struct ath12k_vif *arvif)
 static int ath12k_mac_setup_vdev_params_mbssid(struct ath12k_vif *arvif,
 					       u32 *flags, u32 *tx_vdev_id)
 {
-	struct ieee80211_vif *tx_vif = arvif->vif->mbssid_tx_vif;
+	struct ieee80211_bss_conf *tx_bss = arvif->vif->bss_conf.mbssid_tx_bss;
 	struct ath12k *ar = arvif->ar;
 	struct ath12k_vif *tx_arvif;
 
-	if (!tx_vif)
+	if (!tx_bss)
 		return 0;
 
-	tx_arvif = ath12k_vif_to_arvif(tx_vif);
+	tx_arvif = ath12k_vif_to_arvif(tx_bss->vif);
 
 	if (arvif->vif->bss_conf.nontransmitted) {
-		if (ar->ah->hw->wiphy != ieee80211_vif_to_wdev(tx_vif)->wiphy)
+		if (ar->ah->hw->wiphy != ieee80211_vif_to_wdev(tx_bss->vif)->wiphy)
 			return -EINVAL;
 
 		*flags = WMI_VDEV_MBSSID_FLAGS_NON_TRANSMIT_AP;
@@ -7486,8 +7487,9 @@  ath12k_mac_update_vif_chan(struct ath12k *ar,
 		params.vdev_id = arvif->vdev_id;
 		params.aid = arvif->aid;
 		params.bssid = arvif->bssid;
-		if (vif->mbssid_tx_vif) {
-			params.tx_bssid = ath12k_vif_to_arvif(vif->mbssid_tx_vif)->bssid;
+		if (vif->bss_conf.mbssid_tx_bss) {
+			params.tx_bssid =
+				  ath12k_vif_to_arvif(vif->bss_conf.mbssid_tx_bss->vif)->bssid;
 			params.nontx_profile_idx = vif->bss_conf.bssid_index;
 			params.nontx_profile_cnt = 1 << vif->bss_conf.bssid_indicator;
 		}
diff --git a/drivers/net/wireless/virtual/mac80211_hwsim.c b/drivers/net/wireless/virtual/mac80211_hwsim.c
index d86e6ff4523d..4e938f1eb05d 100644
--- a/drivers/net/wireless/virtual/mac80211_hwsim.c
+++ b/drivers/net/wireless/virtual/mac80211_hwsim.c
@@ -2290,7 +2290,7 @@  static void mac80211_hwsim_beacon_tx(void *arg, u8 *mac,
 	    vif->type != NL80211_IFTYPE_OCB)
 		return;
 
-	if (vif->mbssid_tx_vif && vif->mbssid_tx_vif != vif)
+	if (link_conf->mbssid_tx_bss && link_conf->mbssid_tx_bss != link_conf)
 		return;
 
 	if (vif->bss_conf.ema_ap) {
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 0a04eaf5343c..c747df4dc465 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -682,6 +682,8 @@  struct ieee80211_parsed_tpe {
  *	responder functionality.
  * @ftmr_params: configurable lci/civic parameter when enabling FTM responder.
  * @nontransmitted: this BSS is a nontransmitted BSS profile
+ * @mbssid_tx_bss: Pointer to the BSS configuration of transmitting interface
+ *	if MBSSID is enabled.
  * @transmitter_bssid: the address of transmitter AP
  * @bssid_index: index inside the multiple BSSID set
  * @bssid_indicator: 2^bssid_indicator is the maximum number of APs in set
@@ -790,6 +792,7 @@  struct ieee80211_bss_conf {
 	struct ieee80211_ftm_responder_params *ftmr_params;
 	/* Multiple BSSID data */
 	bool nontransmitted;
+	struct ieee80211_bss_conf *mbssid_tx_bss;
 	u8 transmitter_bssid[ETH_ALEN];
 	u8 bssid_index;
 	u8 bssid_indicator;
@@ -2004,7 +2007,6 @@  enum ieee80211_neg_ttlm_res {
  * @txq: the multicast data TX queue
  * @offload_flags: 802.3 -> 802.11 enapsulation offload flags, see
  *	&enum ieee80211_offload_flags.
- * @mbssid_tx_vif: Pointer to the transmitting interface if MBSSID is enabled.
  */
 struct ieee80211_vif {
 	enum nl80211_iftype type;
@@ -2032,8 +2034,6 @@  struct ieee80211_vif {
 	bool probe_req_reg;
 	bool rx_mcast_action_reg;
 
-	struct ieee80211_vif *mbssid_tx_vif;
-
 	/* must be last */
 	u8 drv_priv[] __aligned(sizeof(void *));
 };
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index b02b84ce2130..60a02b76994a 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -142,8 +142,9 @@  static int ieee80211_set_ap_mbssid_options(struct ieee80211_sub_if_data *sdata,
 					   struct ieee80211_bss_conf *link_conf)
 {
 	struct ieee80211_sub_if_data *tx_sdata;
+	struct ieee80211_link_data *tx_link;
 
-	sdata->vif.mbssid_tx_vif = NULL;
+	link_conf->mbssid_tx_bss = NULL;
 	link_conf->bssid_index = 0;
 	link_conf->nontransmitted = false;
 	link_conf->ema_ap = false;
@@ -157,9 +158,22 @@  static int ieee80211_set_ap_mbssid_options(struct ieee80211_sub_if_data *sdata,
 		return -EINVAL;
 
 	if (tx_sdata == sdata) {
-		sdata->vif.mbssid_tx_vif = &sdata->vif;
+		link_conf->mbssid_tx_bss = link_conf;
 	} else {
-		sdata->vif.mbssid_tx_vif = &tx_sdata->vif;
+		tx_link = sdata_dereference(tx_sdata->link[params.tx_link_id],
+					    tx_sdata);
+		if (!tx_link || !tx_link->conf)
+			return -ENOLINK;
+
+		/* Make sure input tx vif from user is really configured as a
+		 * transmitting vif as per our internal data before referring it.
+		 */
+		if (tx_link->conf->mbssid_tx_bss !=
+		    tx_sdata->vif.link_conf[params.tx_link_id])
+			return -EINVAL;
+
+		link_conf->mbssid_tx_bss =
+				     tx_sdata->vif.link_conf[params.tx_link_id];
 		link_conf->nontransmitted = true;
 		link_conf->bssid_index = params.index;
 	}
@@ -1641,7 +1655,6 @@  static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev,
 	kfree(link_conf->ftmr_params);
 	link_conf->ftmr_params = NULL;
 
-	sdata->vif.mbssid_tx_vif = NULL;
 	link_conf->bssid_index = 0;
 	link_conf->nontransmitted = false;
 	link_conf->ema_ap = false;
@@ -1655,6 +1668,8 @@  static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev,
 		ieee80211_free_key_list(local, &keys);
 	}
 
+	ieee80211_stop_mbssid(sdata);
+	link_conf->mbssid_tx_bss = NULL;
 	link_conf->enable_beacon = false;
 	sdata->beacon_rate_set = false;
 	sdata->vif.cfg.ssid_len = 0;
@@ -3636,23 +3651,43 @@  void ieee80211_csa_finish(struct ieee80211_vif *vif, unsigned int link_id)
 		return;
 	}
 
-	/* TODO: MBSSID with MLO changes */
-	if (vif->mbssid_tx_vif == vif) {
+	if (link_data->conf->mbssid_tx_bss == vif->link_conf[link_id]) {
 		/* Trigger ieee80211_csa_finish() on the non-transmitting
 		 * interfaces when channel switch is received on
 		 * transmitting interface
 		 */
 		struct ieee80211_sub_if_data *iter;
+		struct ieee80211_link_data *link_iter;
+		unsigned int link_id_iter;
+		unsigned long valid_links;
 
 		list_for_each_entry_rcu(iter, &local->interfaces, list) {
 			if (!ieee80211_sdata_running(iter))
 				continue;
 
-			if (iter == sdata || iter->vif.mbssid_tx_vif != vif)
+			if (iter == sdata)
 				continue;
 
-			wiphy_work_queue(iter->local->hw.wiphy,
-					 &iter->deflink.csa.finalize_work);
+			/* check link 0 by default for Non-ML non-tx vif's
+			 * deflinks
+			 */
+			valid_links = iter->vif.valid_links | BIT(0);
+			for_each_set_bit(link_id_iter, &valid_links,
+					 IEEE80211_MLD_MAX_NUM_LINKS) {
+				link_iter =
+				      rcu_dereference(iter->link[link_id_iter]);
+				if (!link_iter)
+					continue;
+				/* Check if any of link of iterator sdata
+				 * belongs to same mbssid group as the tx link
+				 */
+				if (link_iter->conf->mbssid_tx_bss !=
+				    vif->link_conf[link_id])
+					continue;
+
+				wiphy_work_queue(iter->local->hw.wiphy,
+						 &link_iter->csa.finalize_work);
+			}
 		}
 	}
 	wiphy_work_queue(local->hw.wiphy, &link_data->csa.finalize_work);
@@ -4757,15 +4792,35 @@  ieee80211_color_change_bss_config_notify(struct ieee80211_link_data *link,
 
 	ieee80211_link_info_change_notify(sdata, link, changed);
 
-	if (!sdata->vif.bss_conf.nontransmitted && sdata->vif.mbssid_tx_vif) {
+	if (!link->conf->nontransmitted && link->conf->mbssid_tx_bss) {
 		struct ieee80211_sub_if_data *child;
+		unsigned int link_id_iter;
+		unsigned long valid_links;
+		struct ieee80211_link_data *link_iter;
 
 		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;
-				child->vif.bss_conf.he_bss_color.enabled = enable;
+			if (child == sdata)
+				continue;
+
+			/* check deflink by default */
+			valid_links = child->vif.valid_links | BIT(0);
+			for_each_set_bit(link_id_iter, &valid_links,
+					 IEEE80211_MLD_MAX_NUM_LINKS) {
+				link_iter = sdata_dereference(child->link[link_id_iter],
+							      child);
+				if (!link_iter)
+					continue;
+				/* Check if any of link of iterator sdata
+				 * belongs to same mbssid group as the tx link
+				 */
+				if (link_iter->conf->mbssid_tx_bss !=
+				    link->conf)
+					continue;
+
+				link_iter->conf->he_bss_color.color = color;
+				link_iter->conf->he_bss_color.enabled = enable;
 				ieee80211_link_info_change_notify(child,
-								  &child->deflink,
+								  link_iter,
 								  BSS_CHANGED_HE_BSS_COLOR);
 			}
 		}
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index a3485e4c6132..751f055a70af 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -2708,6 +2708,7 @@  int ieee80211_req_neg_ttlm(struct ieee80211_sub_if_data *sdata,
 void ieee80211_check_wbrf_support(struct ieee80211_local *local);
 void ieee80211_add_wbrf(struct ieee80211_local *local, struct cfg80211_chan_def *chandef);
 void ieee80211_remove_wbrf(struct ieee80211_local *local, struct cfg80211_chan_def *chandef);
+void ieee80211_stop_mbssid(struct ieee80211_sub_if_data *sdata);
 
 #if IS_ENABLED(CONFIG_MAC80211_KUNIT_TEST)
 #define EXPORT_SYMBOL_IF_MAC80211_KUNIT(sym) EXPORT_SYMBOL_IF_KUNIT(sym)
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index b4ad66af3af3..e48bfaff635e 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -717,30 +717,84 @@  static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_do
 		ieee80211_add_virtual_monitor(local);
 }
 
-static void ieee80211_stop_mbssid(struct ieee80211_sub_if_data *sdata)
+void ieee80211_stop_mbssid(struct ieee80211_sub_if_data *sdata)
 {
 	struct ieee80211_sub_if_data *tx_sdata, *non_tx_sdata, *tmp_sdata;
-	struct ieee80211_vif *tx_vif = sdata->vif.mbssid_tx_vif;
+	struct ieee80211_bss_conf *tx_bss;
+	unsigned int link_id, iter_link_id;
+	unsigned long iter_valid_links, valid_links;
+	struct ieee80211_link_data *link_iter, *link;
 
-	if (!tx_vif)
-		return;
+	lockdep_assert_wiphy(sdata->local->hw.wiphy);
+	/* Check link 0 by default for non MLO. */
+	iter_valid_links = sdata->vif.valid_links | BIT(0);
+	/* Check if any of the links of current sdata is an MBSSID. */
+	for_each_set_bit(iter_link_id, &iter_valid_links,
+			 IEEE80211_MLD_MAX_NUM_LINKS) {
+		link_iter = sdata_dereference(sdata->link[iter_link_id], sdata);
+		if (!link_iter)
+			continue;
+
+		tx_bss = link_iter->conf->mbssid_tx_bss;
+		if (!tx_bss)
+			continue;
 
-	tx_sdata = vif_to_sdata(tx_vif);
-	sdata->vif.mbssid_tx_vif = NULL;
+		tx_sdata = vif_to_sdata(tx_bss->vif);
+		link_iter->conf->mbssid_tx_bss = NULL;
 
-	list_for_each_entry_safe(non_tx_sdata, tmp_sdata,
-				 &tx_sdata->local->interfaces, list) {
-		if (non_tx_sdata != sdata && non_tx_sdata != tx_sdata &&
-		    non_tx_sdata->vif.mbssid_tx_vif == tx_vif &&
-		    ieee80211_sdata_running(non_tx_sdata)) {
-			non_tx_sdata->vif.mbssid_tx_vif = NULL;
-			dev_close(non_tx_sdata->wdev.netdev);
+		/* If we are not tx sdata reset tx sdata's tx_bss to avoid
+		 * recusrion while closing tx sdata at the end of outer loop
+		 * below.
+		 */
+		if (sdata != tx_sdata) {
+			link = sdata_dereference(tx_sdata->link[tx_bss->link_id],
+						 tx_sdata);
+			if (!link)
+				continue;
+			link->conf->mbssid_tx_bss = NULL;
+		}
+
+		/* loop through sdatas to find if any of their links
+		 * belong to same mbssid group as the one getting deleted.
+		 */
+		list_for_each_entry_safe(non_tx_sdata, tmp_sdata,
+					 &tx_sdata->local->interfaces, list) {
+			if (non_tx_sdata != sdata && non_tx_sdata != tx_sdata &&
+			    ieee80211_sdata_running(non_tx_sdata)) {
+				valid_links = non_tx_sdata->vif.valid_links |
+					      BIT(0);
+				for_each_set_bit(link_id, &valid_links,
+						 IEEE80211_MLD_MAX_NUM_LINKS) {
+					link = sdata_dereference(non_tx_sdata->link[link_id],
+								 non_tx_sdata);
+					if (!link)
+						continue;
+
+					/* If both tx vif and tx linkid is
+					 * matching then it belongs to same
+					 * MBSSID group.
+					 */
+					if (link->conf->mbssid_tx_bss != tx_bss)
+						continue;
+
+					link->conf->mbssid_tx_bss = NULL;
+
+					/* Remove all links of matching MLD
+					 * until dynamic link removal can be
+					 * supported.
+					 */
+					cfg80211_stop_iface(non_tx_sdata->wdev.wiphy,
+							    &non_tx_sdata->wdev,
+							    GFP_KERNEL);
+				}
+			}
 		}
-	}
 
-	if (sdata != tx_sdata && ieee80211_sdata_running(tx_sdata)) {
-		tx_sdata->vif.mbssid_tx_vif = NULL;
-		dev_close(tx_sdata->wdev.netdev);
+		/* If we are not tx sdata, remove links of tx sdata and proceed.
+		 */
+		if (sdata != tx_sdata && ieee80211_sdata_running(tx_sdata))
+			cfg80211_stop_iface(tx_sdata->wdev.wiphy,
+					    &tx_sdata->wdev, GFP_KERNEL);
 	}
 }
 
@@ -748,20 +802,24 @@  static int ieee80211_stop(struct net_device *dev)
 {
 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
-	/* close dependent VLAN and MBSSID interfaces before locking wiphy */
+	/* close dependent VLAN interfaces before locking wiphy */
 	if (sdata->vif.type == NL80211_IFTYPE_AP) {
 		struct ieee80211_sub_if_data *vlan, *tmpsdata;
 
 		list_for_each_entry_safe(vlan, tmpsdata, &sdata->u.ap.vlans,
 					 u.vlan.list)
 			dev_close(vlan->dev);
-
-		ieee80211_stop_mbssid(sdata);
 	}
 
 	wiphy_lock(sdata->local->hw.wiphy);
 	wiphy_work_cancel(sdata->local->hw.wiphy, &sdata->activate_links_work);
 
+	/* Close the dependent MBSSID interfaces with wiphy lock as we may be
+	 * terminating its partner links too in case of MLD.
+	 */
+	if (sdata->vif.type == NL80211_IFTYPE_AP)
+		ieee80211_stop_mbssid(sdata);
+
 	ieee80211_do_stop(sdata, true);
 	wiphy_unlock(sdata->local->hw.wiphy);
 
diff --git a/net/wireless/sme.c b/net/wireless/sme.c
index d9d7bf8bb5c1..5d5abf2c3e91 100644
--- a/net/wireless/sme.c
+++ b/net/wireless/sme.c
@@ -1581,7 +1581,7 @@  void cfg80211_autodisconnect_wk(struct work_struct *work)
 		container_of(work, struct wireless_dev, disconnect_wk);
 	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
 
-	wiphy_lock(wdev->wiphy);
+	wiphy_lock(&rdev->wiphy);
 
 	if (wdev->conn_owner_nlportid) {
 		switch (wdev->iftype) {
@@ -1618,5 +1618,5 @@  void cfg80211_autodisconnect_wk(struct work_struct *work)
 		}
 	}
 
-	wiphy_unlock(wdev->wiphy);
+	wiphy_unlock(&rdev->wiphy);
 }