wifi: mac80211: synchronize valid links for WDS AP_VLAN interfaces

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>
This commit is contained in:
Tamizh Chelvam Raja 2026-03-26 22:17:21 +05:30 committed by Johannes Berg
parent b5b8e29597
commit 469d5d5a3b
3 changed files with 101 additions and 19 deletions

View 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 &&

View 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))

View File

@ -14,27 +14,39 @@
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;
new_links = ap_bss_links;
add = new_links & ~vlan_links;
if (!add)
if (add == vlan->vif.valid_links)
continue;
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);
}
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);
for_each_set_bit(link_id, &add, IEEE80211_MLD_MAX_NUM_LINKS) {
@ -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));
}