]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
wifi: mac80211: synchronize valid links for WDS AP_VLAN interfaces
authorTamizh Chelvam Raja <tamizh.raja@oss.qualcomm.com>
Thu, 26 Mar 2026 16:47:21 +0000 (22:17 +0530)
committerJohannes Berg <johannes.berg@intel.com>
Tue, 7 Apr 2026 13:43:02 +0000 (15:43 +0200)
The current code does not provide any link-configuration support
for 4-address mode WDS AP_VLAN interfaces in MLO setups, preventing
MLD stations from being added correctly. Add the required handling
to enable proper integration of 4-address WDS stations into
an MLO environment.

When a 4-address station associates with an MLO AP, compute the
intersection of valid links between the master AP interface and
the station's advertised capabilities. Configure the AP_VLAN interface
with only these common links to ensure correct data-path operation.

This update ensures AP_VLAN interfaces correctly track link-state
transitions and maintain consistent addressing across all active MLO links.

Co-developed-by: Muna Sinada <muna.sinada@oss.qualcomm.com>
Signed-off-by: Muna Sinada <muna.sinada@oss.qualcomm.com>
Signed-off-by: Tamizh Chelvam Raja <tamizh.raja@oss.qualcomm.com>
Link: https://patch.msgid.link/20260326164723.553927-2-tamizh.raja@oss.qualcomm.com
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
net/mac80211/cfg.c
net/mac80211/chan.c
net/mac80211/link.c

index 5a908eaddbf25e6e16b76a3d9e929d8f60fe0e1d..6a5bf403fd85d7e93a3f8b5670c4ac2803a0fac6 100644 (file)
@@ -2527,6 +2527,65 @@ static int ieee80211_del_station(struct wiphy *wiphy, struct wireless_dev *wdev,
        return 0;
 }
 
+static int ieee80211_set_sta_4addr(struct ieee80211_local *local,
+                                  struct ieee80211_sub_if_data *sdata,
+                                  struct sta_info *sta)
+{
+       struct ieee80211_vif *vif = &sdata->vif;
+       struct wiphy *wiphy = local->hw.wiphy;
+       struct ieee80211_sub_if_data *master;
+       struct ieee80211_bss_conf *link_conf;
+       struct wireless_dev *wdev;
+       unsigned long master_iter;
+       int link_id;
+       int err;
+
+       lockdep_assert_wiphy(local->hw.wiphy);
+
+       if (sdata->u.vlan.sta)
+               return -EBUSY;
+
+       wdev = &sdata->wdev;
+       master = container_of(sdata->bss,
+                             struct ieee80211_sub_if_data,
+                             u.ap);
+
+       if (sta->sta.valid_links) {
+               u16 sta_links = sta->sta.valid_links;
+               u16 new_links = master->vif.valid_links & sta_links;
+               u16 orig_links = wdev->valid_links;
+
+               wdev->valid_links = new_links;
+
+               err = ieee80211_vif_set_links(sdata, new_links, 0);
+               if (err) {
+                       wdev->valid_links = orig_links;
+                       return err;
+               }
+
+               master_iter = master->vif.valid_links;
+
+               for_each_set_bit(link_id, &master_iter,
+                                IEEE80211_MLD_MAX_NUM_LINKS) {
+                       if (!(sta_links & BIT(link_id))) {
+                               eth_zero_addr(wdev->links[link_id].addr);
+                       } else {
+                               link_conf = wiphy_dereference(wiphy,
+                                                             vif->link_conf[link_id]);
+
+                               ether_addr_copy(wdev->links[link_id].addr,
+                                               link_conf->bssid);
+                       }
+               }
+       }
+
+       rcu_assign_pointer(sdata->u.vlan.sta, sta);
+       __ieee80211_check_fast_rx_iface(sdata);
+       drv_sta_set_4addr(local, sta->sdata, &sta->sta, true);
+
+       return 0;
+}
+
 static int ieee80211_change_station(struct wiphy *wiphy,
                                    struct wireless_dev *wdev, const u8 *mac,
                                    struct station_parameters *params)
@@ -2589,12 +2648,10 @@ static int ieee80211_change_station(struct wiphy *wiphy,
                vlansdata = IEEE80211_DEV_TO_SUB_IF(params->vlan);
 
                if (params->vlan->ieee80211_ptr->use_4addr) {
-                       if (vlansdata->u.vlan.sta)
-                               return -EBUSY;
+                       err = ieee80211_set_sta_4addr(local, vlansdata, sta);
+                       if (err)
+                               return err;
 
-                       rcu_assign_pointer(vlansdata->u.vlan.sta, sta);
-                       __ieee80211_check_fast_rx_iface(vlansdata);
-                       drv_sta_set_4addr(local, sta->sdata, &sta->sta, true);
                }
 
                if (sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN &&
index 50a06de928bac033ab60527b6135b16f75a9c4be..fda692316f081d5356fe965013378b7a16db537c 100644 (file)
@@ -1321,6 +1321,10 @@ __ieee80211_link_copy_chanctx_to_vlans(struct ieee80211_link_data *link,
        list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) {
                struct ieee80211_bss_conf *vlan_conf;
 
+               if (vlan->vif.valid_links &&
+                   !(vlan->vif.valid_links & BIT(link_id)))
+                       continue;
+
                vlan_conf = wiphy_dereference(local->hw.wiphy,
                                              vlan->vif.link_conf[link_id]);
                if (WARN_ON(!vlan_conf))
@@ -1564,6 +1568,10 @@ ieee80211_link_update_chanreq(struct ieee80211_link_data *link,
        list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) {
                struct ieee80211_bss_conf *vlan_conf;
 
+               if (vlan->vif.valid_links &&
+                   !(vlan->vif.valid_links & BIT(link_id)))
+                       continue;
+
                vlan_conf = wiphy_dereference(sdata->local->hw.wiphy,
                                              vlan->vif.link_conf[link_id]);
                if (WARN_ON(!vlan_conf))
index 03bfca27d2054aca771be3bb5775b7e4dd85a0b1..93e290dd783f2b33b489f02016e193742cbe07fb 100644 (file)
 
 static void ieee80211_update_apvlan_links(struct ieee80211_sub_if_data *sdata)
 {
+       unsigned long rem = ~sdata->vif.valid_links &
+                                   GENMASK(IEEE80211_MLD_MAX_NUM_LINKS - 1, 0);
+       struct ieee80211_local *local = sdata->local;
+       unsigned long add = sdata->vif.valid_links;
+       struct wiphy *wiphy = local->hw.wiphy;
        struct ieee80211_sub_if_data *vlan;
        struct ieee80211_link_data *link;
-       u16 ap_bss_links = sdata->vif.valid_links;
-       u16 new_links, vlan_links;
-       unsigned long add;
+       struct sta_info *sta;
 
        list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) {
                int link_id;
 
-               /* No support for 4addr with MLO yet */
-               if (vlan->wdev.use_4addr)
-                       return;
+               if (vlan->wdev.use_4addr) {
+                       sta = wiphy_dereference(wiphy,
+                                               vlan->u.vlan.sta);
+                       if (sta)
+                               add = add & sta->sta.valid_links;
+               }
 
-               vlan_links = vlan->vif.valid_links;
+               if (add == vlan->vif.valid_links)
+                       continue;
 
-               new_links = ap_bss_links;
+               for_each_set_bit(link_id, &add, IEEE80211_MLD_MAX_NUM_LINKS) {
+                       vlan->wdev.valid_links |= BIT(link_id);
+                       ether_addr_copy(vlan->wdev.links[link_id].addr,
+                                       sdata->wdev.links[link_id].addr);
+               }
 
-               add = new_links & ~vlan_links;
-               if (!add)
-                       continue;
+               for_each_set_bit(link_id, &rem, IEEE80211_MLD_MAX_NUM_LINKS) {
+                       vlan->wdev.valid_links &= ~BIT(link_id);
+                       eth_zero_addr(vlan->wdev.links[link_id].addr);
+               }
 
                ieee80211_vif_set_links(vlan, add, 0);
 
@@ -96,8 +108,13 @@ void ieee80211_link_init(struct ieee80211_sub_if_data *sdata,
 
                ap_bss = container_of(sdata->bss,
                                      struct ieee80211_sub_if_data, u.ap);
-               ap_bss_conf = sdata_dereference(ap_bss->vif.link_conf[link_id],
-                                               ap_bss);
+
+               if (deflink)
+                       ap_bss_conf = &ap_bss->vif.bss_conf;
+               else
+                       ap_bss_conf = sdata_dereference(ap_bss->vif.link_conf[link_id],
+                                                       ap_bss);
+
                memcpy(link_conf, ap_bss_conf, sizeof(*link_conf));
        }