wifi: mac80211: Support disabled links during association
authorIlan Peer <ilan.peer@intel.com>
Thu, 8 Jun 2023 13:36:11 +0000 (16:36 +0300)
committerJohannes Berg <johannes.berg@intel.com>
Mon, 19 Jun 2023 10:04:49 +0000 (12:04 +0200)
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>
include/net/mac80211.h
net/mac80211/cfg.c
net/mac80211/ieee80211_i.h
net/mac80211/link.c
net/mac80211/mlme.c

index 2ecc8d0c6ef48054ac39fc5e6aca4160d4b24c1c..914448cb0ecf20aea38b3b953455ed16b41137c5 100644 (file)
@@ -1846,6 +1846,8 @@ struct ieee80211_vif_cfg {
  * @active_links: The bitmap of active links, or 0 for non-MLO.
  *     The driver shouldn't change this directly, but use the
  *     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
  * @p2p: indicates whether this AP or STA interface is a p2p
  *     interface, i.e. a GO or p2p-sta respectively
@@ -1883,7 +1885,7 @@ struct ieee80211_vif {
        struct ieee80211_vif_cfg cfg;
        struct ieee80211_bss_conf bss_conf;
        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);
        bool p2p;
 
@@ -1916,7 +1918,7 @@ struct ieee80211_vif {
  */
 static inline u16 ieee80211_vif_usable_links(const struct ieee80211_vif *vif)
 {
-       return vif->valid_links;
+       return vif->valid_links & ~vif->dormant_links;
 }
 
 /**
index ca6d53d5751d2666685d57b2385108218b141bd2..359589d525d5a7dada174bb9c63f6a32ac2f7872 100644 (file)
@@ -4868,7 +4868,7 @@ static int ieee80211_add_intf_link(struct wiphy *wiphy,
                return -EOPNOTSUPP;
 
        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);
 
        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);
 
        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);
 }
 
index 7a98fa26ec921cf50966fae67506563f6071feda..4afd5095366c6518ded671268c1447377f2d925e 100644 (file)
@@ -410,6 +410,8 @@ struct ieee80211_mgd_assoc_data {
                ieee80211_conn_flags_t conn_flags;
 
                u16 status;
+
+               bool disabled;
        } link[IEEE80211_MLD_MAX_NUM_LINKS];
 
        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);
 void ieee80211_link_stop(struct ieee80211_link_data *link);
 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);
 
 /* tx handling */
index 55cba3760ef5c903d571b07a4cd35c1110c18e34..d090ecc41ea2f4cad3f3712762ca4cc9679636b6 100644 (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,
-                                           u16 links)
+                                           u16 valid_links, u16 dormant_links)
 {
-       sdata->vif.valid_links = links;
-
-       if (!links) {
+       sdata->vif.valid_links = valid_links;
+       sdata->vif.dormant_links = dormant_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.dormant_links = 0;
                return;
        }
 
        switch (sdata->vif.type) {
        case NL80211_IFTYPE_AP:
                /* 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;
        case NL80211_IFTYPE_STATION:
                if (sdata->vif.active_links)
                        break;
-               WARN_ON(hweight16(links) > 1);
-               sdata->vif.active_links = links;
+               sdata->vif.active_links = valid_links & ~dormant_links;
+               WARN_ON(hweight16(sdata->vif.active_links) > 1);
                break;
        default:
                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,
                                      struct link_container **to_free,
-                                     u16 new_links)
+                                     u16 new_links, u16 dormant_links)
 {
        u16 old_links = sdata->vif.valid_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 */
                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 */
                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 */
                memcpy(sdata->link, old_data, sizeof(old_data));
                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 */
                memset(to_free, 0, sizeof(links));
                goto free;
@@ -282,12 +291,13 @@ deinit:
 }
 
 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];
        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);
 
        return ret;
@@ -304,7 +314,7 @@ void ieee80211_vif_clear_links(struct ieee80211_sub_if_data *sdata)
         */
 
        sdata_lock(sdata);
-       ieee80211_vif_update_links(sdata, links, 0);
+       ieee80211_vif_update_links(sdata, links, 0, 0);
        sdata_unlock(sdata);
 
        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)
                return -EINVAL;
 
-       /* cannot activate links that don't exist */
        if (active_links & ~ieee80211_vif_usable_links(&sdata->vif))
                return -EINVAL;
 
@@ -484,7 +493,6 @@ void ieee80211_set_active_links_async(struct ieee80211_vif *vif,
        if (sdata->vif.type != NL80211_IFTYPE_STATION)
                return;
 
-       /* cannot activate links that don't exist */
        if (active_links & ~ieee80211_vif_usable_links(&sdata->vif))
                return;
 
index 959695ed7649143bebebf3769203053b86700589..738822b82d3e40b38fea307fca8654c29fd1ccef 100644 (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)
                        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);
                if (WARN_ON(!link))
                        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;
 
                        if (!cbss ||
+                           !(BIT(link_id) &
+                             ieee80211_vif_usable_links(&sdata->vif)) ||
                            assoc_data->link[link_id].status != WLAN_STATUS_SUCCESS)
                                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,
               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)
@@ -3511,7 +3517,7 @@ static void ieee80211_destroy_auth_data(struct ieee80211_sub_if_data *sdata,
 
                mutex_lock(&sdata->local->mtx);
                ieee80211_link_release_channel(&sdata->deflink);
-               ieee80211_vif_set_links(sdata, 0);
+               ieee80211_vif_set_links(sdata, 0, 0);
                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);
                ieee80211_link_release_channel(&sdata->deflink);
-               ieee80211_vif_set_links(sdata, 0);
+               ieee80211_vif_set_links(sdata, 0, 0);
                mutex_unlock(&sdata->local->mtx);
        }
 
@@ -4979,7 +4985,7 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
        unsigned int link_id;
        struct sta_info *sta;
        u64 changed[IEEE80211_MLD_MAX_NUM_LINKS] = {};
-       u16 valid_links = 0;
+       u16 valid_links = 0, dormant_links = 0;
        int err;
 
        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++) {
                        if (!assoc_data->link[link_id].bss)
                                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);
                                if (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++) {
@@ -5012,7 +5020,7 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
                struct ieee80211_link_data *link;
                struct link_sta_info *link_sta;
 
-               if (!cbss)
+               if (!cbss || assoc_data->link[link_id].disabled)
                        continue;
 
                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 */
-       ieee80211_vif_set_links(sdata, valid_links);
+       ieee80211_vif_set_links(sdata, valid_links, dormant_links);
 
        rate_control_rate_init(sta);
 
@@ -6627,12 +6635,12 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,
                mlo = true;
                if (WARN_ON(!ap_mld_addr))
                        return -EINVAL;
-               err = ieee80211_vif_set_links(sdata, BIT(link_id));
+               err = ieee80211_vif_set_links(sdata, BIT(link_id), 0);
        } else {
                if (WARN_ON(ap_mld_addr))
                        return -EINVAL;
                ap_mld_addr = cbss->bssid;
-               err = ieee80211_vif_set_links(sdata, 0);
+               err = ieee80211_vif_set_links(sdata, 0, 0);
                link_id = 0;
                mlo = false;
        }
@@ -6784,7 +6792,7 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,
 
 out_err:
        ieee80211_link_release_channel(&sdata->deflink);
-       ieee80211_vif_set_links(sdata, 0);
+       ieee80211_vif_set_links(sdata, 0, 0);
        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++) {
                        assoc_data->link[i].conn_flags = conn_flags;
                        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 */
-               err = ieee80211_vif_set_links(sdata, BIT(assoc_link_id));
+               err = ieee80211_vif_set_links(sdata, BIT(assoc_link_id), 0);
                if (err)
                        goto err_clear;
        } else {