wifi: mac80211: Support disabled links during association

When the association is complete, do not configure disabled
links, and track them as part of the interface data.

Signed-off-by: Ilan Peer <ilan.peer@intel.com>
Signed-off-by: Gregory Greenman <gregory.greenman@intel.com>
Link: https://lore.kernel.org/r/20230608163202.c194fabeb81a.Iaefdef5ba0492afe9a5ede14c68060a4af36e444@changeid
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
Ilan Peer 2023-06-08 16:36:11 +03:00 committed by Johannes Berg
parent d5a17cfb98
commit 6d543b34db
5 changed files with 53 additions and 32 deletions

View File

@ -1846,6 +1846,8 @@ struct ieee80211_vif_cfg {
* @active_links: The bitmap of active links, or 0 for non-MLO. * @active_links: The bitmap of active links, or 0 for non-MLO.
* The driver shouldn't change this directly, but use the * The driver shouldn't change this directly, but use the
* API calls meant for that purpose. * API calls meant for that purpose.
* @dormant_links: bitmap of valid but disabled links, or 0 for non-MLO.
* Must be a subset of valid_links.
* @addr: address of this interface * @addr: address of this interface
* @p2p: indicates whether this AP or STA interface is a p2p * @p2p: indicates whether this AP or STA interface is a p2p
* interface, i.e. a GO or p2p-sta respectively * interface, i.e. a GO or p2p-sta respectively
@ -1883,7 +1885,7 @@ struct ieee80211_vif {
struct ieee80211_vif_cfg cfg; struct ieee80211_vif_cfg cfg;
struct ieee80211_bss_conf bss_conf; struct ieee80211_bss_conf bss_conf;
struct ieee80211_bss_conf __rcu *link_conf[IEEE80211_MLD_MAX_NUM_LINKS]; struct ieee80211_bss_conf __rcu *link_conf[IEEE80211_MLD_MAX_NUM_LINKS];
u16 valid_links, active_links; u16 valid_links, active_links, dormant_links;
u8 addr[ETH_ALEN] __aligned(2); u8 addr[ETH_ALEN] __aligned(2);
bool p2p; bool p2p;
@ -1916,7 +1918,7 @@ struct ieee80211_vif {
*/ */
static inline u16 ieee80211_vif_usable_links(const struct ieee80211_vif *vif) static inline u16 ieee80211_vif_usable_links(const struct ieee80211_vif *vif)
{ {
return vif->valid_links; return vif->valid_links & ~vif->dormant_links;
} }
/** /**

View File

@ -4868,7 +4868,7 @@ static int ieee80211_add_intf_link(struct wiphy *wiphy,
return -EOPNOTSUPP; return -EOPNOTSUPP;
mutex_lock(&sdata->local->mtx); mutex_lock(&sdata->local->mtx);
res = ieee80211_vif_set_links(sdata, wdev->valid_links); res = ieee80211_vif_set_links(sdata, wdev->valid_links, 0);
mutex_unlock(&sdata->local->mtx); mutex_unlock(&sdata->local->mtx);
return res; return res;
@ -4881,7 +4881,7 @@ static void ieee80211_del_intf_link(struct wiphy *wiphy,
struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
mutex_lock(&sdata->local->mtx); mutex_lock(&sdata->local->mtx);
ieee80211_vif_set_links(sdata, wdev->valid_links); ieee80211_vif_set_links(sdata, wdev->valid_links, 0);
mutex_unlock(&sdata->local->mtx); mutex_unlock(&sdata->local->mtx);
} }

View File

@ -410,6 +410,8 @@ struct ieee80211_mgd_assoc_data {
ieee80211_conn_flags_t conn_flags; ieee80211_conn_flags_t conn_flags;
u16 status; u16 status;
bool disabled;
} link[IEEE80211_MLD_MAX_NUM_LINKS]; } link[IEEE80211_MLD_MAX_NUM_LINKS];
u8 ap_addr[ETH_ALEN] __aligned(2); u8 ap_addr[ETH_ALEN] __aligned(2);
@ -2019,7 +2021,7 @@ void ieee80211_link_init(struct ieee80211_sub_if_data *sdata,
struct ieee80211_bss_conf *link_conf); struct ieee80211_bss_conf *link_conf);
void ieee80211_link_stop(struct ieee80211_link_data *link); void ieee80211_link_stop(struct ieee80211_link_data *link);
int ieee80211_vif_set_links(struct ieee80211_sub_if_data *sdata, int ieee80211_vif_set_links(struct ieee80211_sub_if_data *sdata,
u16 new_links); u16 new_links, u16 dormant_links);
void ieee80211_vif_clear_links(struct ieee80211_sub_if_data *sdata); void ieee80211_vif_clear_links(struct ieee80211_sub_if_data *sdata);
/* tx handling */ /* tx handling */

View File

@ -142,25 +142,34 @@ static int ieee80211_check_dup_link_addrs(struct ieee80211_sub_if_data *sdata)
} }
static void ieee80211_set_vif_links_bitmaps(struct ieee80211_sub_if_data *sdata, static void ieee80211_set_vif_links_bitmaps(struct ieee80211_sub_if_data *sdata,
u16 links) u16 valid_links, u16 dormant_links)
{ {
sdata->vif.valid_links = links; sdata->vif.valid_links = valid_links;
sdata->vif.dormant_links = dormant_links;
if (!links) { if (!valid_links ||
WARN((~valid_links & dormant_links) ||
!(valid_links & ~dormant_links),
"Invalid links: valid=0x%x, dormant=0x%x",
valid_links, dormant_links)) {
sdata->vif.active_links = 0; sdata->vif.active_links = 0;
sdata->vif.dormant_links = 0;
return; return;
} }
switch (sdata->vif.type) { switch (sdata->vif.type) {
case NL80211_IFTYPE_AP: case NL80211_IFTYPE_AP:
/* in an AP all links are always active */ /* in an AP all links are always active */
sdata->vif.active_links = links; sdata->vif.active_links = valid_links;
/* AP links are not expected to be disabled */
WARN_ON(dormant_links);
break; break;
case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_STATION:
if (sdata->vif.active_links) if (sdata->vif.active_links)
break; break;
WARN_ON(hweight16(links) > 1); sdata->vif.active_links = valid_links & ~dormant_links;
sdata->vif.active_links = links; WARN_ON(hweight16(sdata->vif.active_links) > 1);
break; break;
default: default:
WARN_ON(1); WARN_ON(1);
@ -169,7 +178,7 @@ static void ieee80211_set_vif_links_bitmaps(struct ieee80211_sub_if_data *sdata,
static int ieee80211_vif_update_links(struct ieee80211_sub_if_data *sdata, static int ieee80211_vif_update_links(struct ieee80211_sub_if_data *sdata,
struct link_container **to_free, struct link_container **to_free,
u16 new_links) u16 new_links, u16 dormant_links)
{ {
u16 old_links = sdata->vif.valid_links; u16 old_links = sdata->vif.valid_links;
u16 old_active = sdata->vif.active_links; u16 old_active = sdata->vif.active_links;
@ -245,7 +254,7 @@ static int ieee80211_vif_update_links(struct ieee80211_sub_if_data *sdata,
/* for keys we will not be able to undo this */ /* for keys we will not be able to undo this */
ieee80211_tear_down_links(sdata, to_free, rem); ieee80211_tear_down_links(sdata, to_free, rem);
ieee80211_set_vif_links_bitmaps(sdata, new_links); ieee80211_set_vif_links_bitmaps(sdata, new_links, dormant_links);
/* tell the driver */ /* tell the driver */
ret = drv_change_vif_links(sdata->local, sdata, ret = drv_change_vif_links(sdata->local, sdata,
@ -258,7 +267,7 @@ static int ieee80211_vif_update_links(struct ieee80211_sub_if_data *sdata,
/* restore config */ /* restore config */
memcpy(sdata->link, old_data, sizeof(old_data)); memcpy(sdata->link, old_data, sizeof(old_data));
memcpy(sdata->vif.link_conf, old, sizeof(old)); memcpy(sdata->vif.link_conf, old, sizeof(old));
ieee80211_set_vif_links_bitmaps(sdata, old_links); ieee80211_set_vif_links_bitmaps(sdata, old_links, dormant_links);
/* and free (only) the newly allocated links */ /* and free (only) the newly allocated links */
memset(to_free, 0, sizeof(links)); memset(to_free, 0, sizeof(links));
goto free; goto free;
@ -282,12 +291,13 @@ static int ieee80211_vif_update_links(struct ieee80211_sub_if_data *sdata,
} }
int ieee80211_vif_set_links(struct ieee80211_sub_if_data *sdata, int ieee80211_vif_set_links(struct ieee80211_sub_if_data *sdata,
u16 new_links) u16 new_links, u16 dormant_links)
{ {
struct link_container *links[IEEE80211_MLD_MAX_NUM_LINKS]; struct link_container *links[IEEE80211_MLD_MAX_NUM_LINKS];
int ret; int ret;
ret = ieee80211_vif_update_links(sdata, links, new_links); ret = ieee80211_vif_update_links(sdata, links, new_links,
dormant_links);
ieee80211_free_links(sdata, links); ieee80211_free_links(sdata, links);
return ret; return ret;
@ -304,7 +314,7 @@ void ieee80211_vif_clear_links(struct ieee80211_sub_if_data *sdata)
*/ */
sdata_lock(sdata); sdata_lock(sdata);
ieee80211_vif_update_links(sdata, links, 0); ieee80211_vif_update_links(sdata, links, 0, 0);
sdata_unlock(sdata); sdata_unlock(sdata);
ieee80211_free_links(sdata, links); ieee80211_free_links(sdata, links);
@ -328,7 +338,6 @@ static int _ieee80211_set_active_links(struct ieee80211_sub_if_data *sdata,
if (sdata->vif.type != NL80211_IFTYPE_STATION) if (sdata->vif.type != NL80211_IFTYPE_STATION)
return -EINVAL; return -EINVAL;
/* cannot activate links that don't exist */
if (active_links & ~ieee80211_vif_usable_links(&sdata->vif)) if (active_links & ~ieee80211_vif_usable_links(&sdata->vif))
return -EINVAL; return -EINVAL;
@ -484,7 +493,6 @@ void ieee80211_set_active_links_async(struct ieee80211_vif *vif,
if (sdata->vif.type != NL80211_IFTYPE_STATION) if (sdata->vif.type != NL80211_IFTYPE_STATION)
return; return;
/* cannot activate links that don't exist */
if (active_links & ~ieee80211_vif_usable_links(&sdata->vif)) if (active_links & ~ieee80211_vif_usable_links(&sdata->vif))
return; return;

View File

@ -2818,6 +2818,10 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
assoc_data->link[link_id].status != WLAN_STATUS_SUCCESS) assoc_data->link[link_id].status != WLAN_STATUS_SUCCESS)
continue; continue;
if (ieee80211_vif_is_mld(&sdata->vif) &&
!(ieee80211_vif_usable_links(&sdata->vif) & BIT(link_id)))
continue;
link = sdata_dereference(sdata->link[link_id], sdata); link = sdata_dereference(sdata->link[link_id], sdata);
if (WARN_ON(!link)) if (WARN_ON(!link))
return; return;
@ -2844,6 +2848,8 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
struct cfg80211_bss *cbss = assoc_data->link[link_id].bss; struct cfg80211_bss *cbss = assoc_data->link[link_id].bss;
if (!cbss || if (!cbss ||
!(BIT(link_id) &
ieee80211_vif_usable_links(&sdata->vif)) ||
assoc_data->link[link_id].status != WLAN_STATUS_SUCCESS) assoc_data->link[link_id].status != WLAN_STATUS_SUCCESS)
continue; continue;
@ -3058,7 +3064,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
memset(sdata->vif.bss_conf.tx_pwr_env, 0, memset(sdata->vif.bss_conf.tx_pwr_env, 0,
sizeof(sdata->vif.bss_conf.tx_pwr_env)); sizeof(sdata->vif.bss_conf.tx_pwr_env));
ieee80211_vif_set_links(sdata, 0); ieee80211_vif_set_links(sdata, 0, 0);
} }
static void ieee80211_reset_ap_probe(struct ieee80211_sub_if_data *sdata) static void ieee80211_reset_ap_probe(struct ieee80211_sub_if_data *sdata)
@ -3511,7 +3517,7 @@ static void ieee80211_destroy_auth_data(struct ieee80211_sub_if_data *sdata,
mutex_lock(&sdata->local->mtx); mutex_lock(&sdata->local->mtx);
ieee80211_link_release_channel(&sdata->deflink); ieee80211_link_release_channel(&sdata->deflink);
ieee80211_vif_set_links(sdata, 0); ieee80211_vif_set_links(sdata, 0, 0);
mutex_unlock(&sdata->local->mtx); mutex_unlock(&sdata->local->mtx);
} }
@ -3570,7 +3576,7 @@ static void ieee80211_destroy_assoc_data(struct ieee80211_sub_if_data *sdata,
mutex_lock(&sdata->local->mtx); mutex_lock(&sdata->local->mtx);
ieee80211_link_release_channel(&sdata->deflink); ieee80211_link_release_channel(&sdata->deflink);
ieee80211_vif_set_links(sdata, 0); ieee80211_vif_set_links(sdata, 0, 0);
mutex_unlock(&sdata->local->mtx); mutex_unlock(&sdata->local->mtx);
} }
@ -4979,7 +4985,7 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
unsigned int link_id; unsigned int link_id;
struct sta_info *sta; struct sta_info *sta;
u64 changed[IEEE80211_MLD_MAX_NUM_LINKS] = {}; u64 changed[IEEE80211_MLD_MAX_NUM_LINKS] = {};
u16 valid_links = 0; u16 valid_links = 0, dormant_links = 0;
int err; int err;
mutex_lock(&sdata->local->sta_mtx); mutex_lock(&sdata->local->sta_mtx);
@ -4995,16 +5001,18 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) { for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) {
if (!assoc_data->link[link_id].bss) if (!assoc_data->link[link_id].bss)
continue; continue;
valid_links |= BIT(link_id);
if (link_id != assoc_data->assoc_link_id) { valid_links |= BIT(link_id);
if (assoc_data->link[link_id].disabled) {
dormant_links |= BIT(link_id);
} else if (link_id != assoc_data->assoc_link_id) {
err = ieee80211_sta_allocate_link(sta, link_id); err = ieee80211_sta_allocate_link(sta, link_id);
if (err) if (err)
goto out_err; goto out_err;
} }
} }
ieee80211_vif_set_links(sdata, valid_links); ieee80211_vif_set_links(sdata, valid_links, dormant_links);
} }
for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) { for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) {
@ -5012,7 +5020,7 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
struct ieee80211_link_data *link; struct ieee80211_link_data *link;
struct link_sta_info *link_sta; struct link_sta_info *link_sta;
if (!cbss) if (!cbss || assoc_data->link[link_id].disabled)
continue; continue;
link = sdata_dereference(sdata->link[link_id], sdata); link = sdata_dereference(sdata->link[link_id], sdata);
@ -5084,7 +5092,7 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
} }
/* links might have changed due to rejected ones, set them again */ /* links might have changed due to rejected ones, set them again */
ieee80211_vif_set_links(sdata, valid_links); ieee80211_vif_set_links(sdata, valid_links, dormant_links);
rate_control_rate_init(sta); rate_control_rate_init(sta);
@ -6627,12 +6635,12 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,
mlo = true; mlo = true;
if (WARN_ON(!ap_mld_addr)) if (WARN_ON(!ap_mld_addr))
return -EINVAL; return -EINVAL;
err = ieee80211_vif_set_links(sdata, BIT(link_id)); err = ieee80211_vif_set_links(sdata, BIT(link_id), 0);
} else { } else {
if (WARN_ON(ap_mld_addr)) if (WARN_ON(ap_mld_addr))
return -EINVAL; return -EINVAL;
ap_mld_addr = cbss->bssid; ap_mld_addr = cbss->bssid;
err = ieee80211_vif_set_links(sdata, 0); err = ieee80211_vif_set_links(sdata, 0, 0);
link_id = 0; link_id = 0;
mlo = false; mlo = false;
} }
@ -6784,7 +6792,7 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,
out_err: out_err:
ieee80211_link_release_channel(&sdata->deflink); ieee80211_link_release_channel(&sdata->deflink);
ieee80211_vif_set_links(sdata, 0); ieee80211_vif_set_links(sdata, 0, 0);
return err; return err;
} }
@ -7324,10 +7332,11 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
for (i = 0; i < ARRAY_SIZE(assoc_data->link); i++) { for (i = 0; i < ARRAY_SIZE(assoc_data->link); i++) {
assoc_data->link[i].conn_flags = conn_flags; assoc_data->link[i].conn_flags = conn_flags;
assoc_data->link[i].bss = req->links[i].bss; assoc_data->link[i].bss = req->links[i].bss;
assoc_data->link[i].disabled = req->links[i].disabled;
} }
/* if there was no authentication, set up the link */ /* if there was no authentication, set up the link */
err = ieee80211_vif_set_links(sdata, BIT(assoc_link_id)); err = ieee80211_vif_set_links(sdata, BIT(assoc_link_id), 0);
if (err) if (err)
goto err_clear; goto err_clear;
} else { } else {