wifi: mac80211: support handling of advertised TID-to-link mapping
authorAyala Beker <ayala.beker@intel.com>
Wed, 20 Sep 2023 18:25:25 +0000 (21:25 +0300)
committerJohannes Berg <johannes.berg@intel.com>
Mon, 25 Sep 2023 07:12:34 +0000 (09:12 +0200)
Support handling of advertised TID-to-link mapping elements received
in a beacon.
These elements are used by AP MLD to disable specific links and force
all clients to stop using these links.
By default if no TID-to-link mapping is advertised, all TIDs shall be
mapped to all links.

Signed-off-by: Ayala Beker <ayala.beker@intel.com>
Signed-off-by: Gregory Greenman <gregory.greenman@intel.com>
Link: https://lore.kernel.org/r/20230920211508.623c4b692ff9.Iab0a6f561d85b8ab6efe541590985a2b6e9e74aa@changeid
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
net/mac80211/ieee80211_i.h
net/mac80211/mlme.c

index d1a73095c914af9c03c79f27fd2b086e4a052138..19429f84afc359d77b3183fb04e08569c7151694 100644 (file)
@@ -467,6 +467,17 @@ struct ieee80211_sta_tx_tspec {
        bool downgraded;
 };
 
+/* Advertised TID-to-link mapping info */
+struct ieee80211_adv_ttlm_info {
+       /* time in TUs at which the new mapping is established, or 0 if there is
+        * no planned advertised TID-to-link mapping
+        */
+       u16 switch_time;
+       u32 duration; /* duration of the planned T2L map in TUs */
+       u16 map; /* map of usable links for all TIDs */
+       bool active; /* whether the advertised mapping is active or not */
+};
+
 DECLARE_EWMA(beacon_signal, 4, 4)
 
 struct ieee80211_if_managed {
@@ -560,6 +571,10 @@ struct ieee80211_if_managed {
 
        struct wiphy_delayed_work ml_reconf_work;
        u16 removed_links;
+
+       /* TID-to-link mapping support */
+       struct wiphy_delayed_work ttlm_work;
+       struct ieee80211_adv_ttlm_info ttlm_info;
 };
 
 struct ieee80211_if_ibss {
index 085efae8e23cc5851162872d6c86c81ad8e8bd74..663ea7430b73967cf469f7972bed6903fa687275 100644 (file)
@@ -3053,6 +3053,9 @@ 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));
 
+       memset(&sdata->u.mgd.ttlm_info, 0,
+              sizeof(sdata->u.mgd.ttlm_info));
+       wiphy_delayed_work_cancel(sdata->local->hw.wiphy, &ifmgd->ttlm_work);
        ieee80211_vif_set_links(sdata, 0, 0);
 }
 
@@ -5821,6 +5824,194 @@ static void ieee80211_ml_reconfiguration(struct ieee80211_sub_if_data *sdata,
                                 TU_TO_JIFFIES(delay));
 }
 
+static void ieee80211_tid_to_link_map_work(struct wiphy *wiphy,
+                                          struct wiphy_work *work)
+{
+       u16 new_active_links, new_dormant_links;
+       struct ieee80211_sub_if_data *sdata =
+               container_of(work, struct ieee80211_sub_if_data,
+                            u.mgd.ttlm_work.work);
+       int ret;
+
+       new_active_links = sdata->u.mgd.ttlm_info.map &
+                          sdata->vif.valid_links;
+       new_dormant_links = ~sdata->u.mgd.ttlm_info.map &
+                           sdata->vif.valid_links;
+       if (!new_active_links) {
+               ieee80211_disconnect(&sdata->vif, false);
+               return;
+       }
+
+       ieee80211_vif_set_links(sdata, sdata->vif.valid_links, 0);
+       new_active_links = BIT(ffs(new_active_links) - 1);
+       ieee80211_set_active_links(&sdata->vif, new_active_links);
+
+       ret = ieee80211_vif_set_links(sdata, sdata->vif.valid_links,
+                                     new_dormant_links);
+
+       sdata->u.mgd.ttlm_info.active = true;
+       sdata->u.mgd.ttlm_info.switch_time = 0;
+
+       if (!ret)
+               ieee80211_vif_cfg_change_notify(sdata,
+                                               BSS_CHANGED_MLD_VALID_LINKS);
+}
+
+static u16 ieee80211_get_ttlm(u8 bm_size, u8 *data)
+{
+       if (bm_size == 1)
+               return *data;
+       else
+               return get_unaligned_le16(data);
+}
+
+static int
+ieee80211_parse_adv_t2l(struct ieee80211_sub_if_data *sdata,
+                       const struct ieee80211_ttlm_elem *ttlm,
+                       struct ieee80211_adv_ttlm_info *ttlm_info)
+{
+       /* The element size was already validated in
+        * ieee80211_tid_to_link_map_size_ok()
+        */
+       u8 control, link_map_presence, map_size, tid;
+       u8 *pos;
+
+       memset(ttlm_info, 0, sizeof(*ttlm_info));
+       pos = (void *)ttlm->optional;
+       control = ttlm->control;
+
+       if ((control & IEEE80211_TTLM_CONTROL_DEF_LINK_MAP) ||
+           !(control & IEEE80211_TTLM_CONTROL_SWITCH_TIME_PRESENT))
+               return 0;
+
+       if ((control & IEEE80211_TTLM_CONTROL_DIRECTION) !=
+           IEEE80211_TTLM_DIRECTION_BOTH) {
+               sdata_info(sdata, "Invalid advertised T2L map direction\n");
+               return -EINVAL;
+       }
+
+       link_map_presence = *pos;
+       pos++;
+
+       ttlm_info->switch_time = get_unaligned_le16(pos);
+       pos += 2;
+
+       if (control & IEEE80211_TTLM_CONTROL_EXPECTED_DUR_PRESENT) {
+               ttlm_info->duration = pos[0] | pos[1] << 8 | pos[2] << 16;
+               pos += 3;
+       }
+
+       if (control & IEEE80211_TTLM_CONTROL_LINK_MAP_SIZE)
+               map_size = 1;
+       else
+               map_size = 2;
+
+       /* According to Draft P802.11be_D3.0 clause 35.3.7.1.7, an AP MLD shall
+        * not advertise a TID-to-link mapping that does not map all TIDs to the
+        * same link set, reject frame if not all links have mapping
+        */
+       if (link_map_presence != 0xff) {
+               sdata_info(sdata,
+                          "Invalid advertised T2L mapping presence indicator\n");
+               return -EINVAL;
+       }
+
+       ttlm_info->map = ieee80211_get_ttlm(map_size, pos);
+       if (!ttlm_info->map) {
+               sdata_info(sdata,
+                          "Invalid advertised T2L map for TID 0\n");
+               return -EINVAL;
+       }
+
+       pos += map_size;
+
+       for (tid = 1; tid < 8; tid++) {
+               u16 map = ieee80211_get_ttlm(map_size, pos);
+
+               if (map != ttlm_info->map) {
+                       sdata_info(sdata, "Invalid advertised T2L map for tid %d\n",
+                                  tid);
+                       return -EINVAL;
+               }
+
+               pos += map_size;
+       }
+       return 0;
+}
+
+static void ieee80211_process_adv_ttlm(struct ieee80211_sub_if_data *sdata,
+                                         struct ieee802_11_elems *elems,
+                                         u64 beacon_ts)
+{
+       u8 i;
+       int ret;
+
+       if (!ieee80211_vif_is_mld(&sdata->vif))
+               return;
+
+       if (!elems->ttlm_num) {
+               if (sdata->u.mgd.ttlm_info.switch_time) {
+                       /* if a planned TID-to-link mapping was cancelled -
+                        * abort it
+                        */
+                       wiphy_delayed_work_cancel(sdata->local->hw.wiphy,
+                                                 &sdata->u.mgd.ttlm_work);
+               } else if (sdata->u.mgd.ttlm_info.active) {
+                       /* if no TID-to-link element, set to default mapping in
+                        * which all TIDs are mapped to all setup links
+                        */
+                       ret = ieee80211_vif_set_links(sdata,
+                                                     sdata->vif.valid_links,
+                                                     0);
+                       if (ret) {
+                               sdata_info(sdata, "Failed setting valid/dormant links\n");
+                               return;
+                       }
+                       ieee80211_vif_cfg_change_notify(sdata,
+                                                       BSS_CHANGED_MLD_VALID_LINKS);
+               }
+               memset(&sdata->u.mgd.ttlm_info, 0,
+                      sizeof(sdata->u.mgd.ttlm_info));
+               return;
+       }
+
+       for (i = 0; i < elems->ttlm_num; i++) {
+               struct ieee80211_adv_ttlm_info ttlm_info;
+               u32 res;
+
+               res = ieee80211_parse_adv_t2l(sdata, elems->ttlm[i],
+                                             &ttlm_info);
+
+               if (res) {
+                       __ieee80211_disconnect(sdata);
+                       return;
+               }
+
+               if (ttlm_info.switch_time) {
+                       u32 st_us, delay = 0;
+                       u32 ts_l26 = beacon_ts & GENMASK(25, 0);
+
+                       /* The t2l map switch time is indicated with a partial
+                        * TSF value, convert it to TSF and calc the delay
+                        * to the start time.
+                        */
+                       st_us = ieee80211_tu_to_usec(ttlm_info.switch_time);
+                       if (st_us > ts_l26)
+                               delay = st_us - ts_l26;
+                       else
+                               continue;
+
+                       sdata->u.mgd.ttlm_info = ttlm_info;
+                       wiphy_delayed_work_cancel(sdata->local->hw.wiphy,
+                                                 &sdata->u.mgd.ttlm_work);
+                       wiphy_delayed_work_queue(sdata->local->hw.wiphy,
+                                                &sdata->u.mgd.ttlm_work,
+                                                usecs_to_jiffies(delay));
+                       return;
+               }
+       }
+}
+
 static void ieee80211_rx_mgmt_beacon(struct ieee80211_link_data *link,
                                     struct ieee80211_hdr *hdr, size_t len,
                                     struct ieee80211_rx_status *rx_status)
@@ -6144,6 +6335,8 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_link_data *link,
        }
 
        ieee80211_ml_reconfiguration(sdata, elems);
+       ieee80211_process_adv_ttlm(sdata, elems,
+                                     le64_to_cpu(mgmt->u.beacon.timestamp));
 
        ieee80211_link_info_change_notify(sdata, link, changed);
 free:
@@ -6766,6 +6959,8 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
        timer_setup(&ifmgd->conn_mon_timer, ieee80211_sta_conn_mon_timer, 0);
        wiphy_delayed_work_init(&ifmgd->tx_tspec_wk,
                                ieee80211_sta_handle_tspec_ac_params_wk);
+       wiphy_delayed_work_init(&ifmgd->ttlm_work,
+                               ieee80211_tid_to_link_map_work);
 
        ifmgd->flags = 0;
        ifmgd->powersave = sdata->wdev.ps;
@@ -7840,6 +8035,7 @@ void ieee80211_mgd_stop(struct ieee80211_sub_if_data *sdata)
                                  &ifmgd->tdls_peer_del_work);
        wiphy_delayed_work_cancel(sdata->local->hw.wiphy,
                                  &ifmgd->ml_reconf_work);
+       wiphy_delayed_work_cancel(sdata->local->hw.wiphy, &ifmgd->ttlm_work);
 
        if (ifmgd->assoc_data)
                ieee80211_destroy_assoc_data(sdata, ASSOC_TIMEOUT);