From: Miri Korenblit Date: Fri, 15 May 2026 12:09:38 +0000 (+0300) Subject: wifi: iwlwifi: mld: evacuate NAN channels on link switch X-Git-Url: http://git.ipfire.org/gitweb/index.cgi?a=commitdiff_plain;h=5aee72298cb969bac5358d40bb94ac878503cd2d;p=thirdparty%2Flinux.git wifi: iwlwifi: mld: evacuate NAN channels on link switch The FW API doesn't allow a station (of certain types, including STATION_TYPE_PEER) to not be correlated to any link. Therefore, when switching links, mac80211 first adds the new link and then removes the old one. In case we have a NAN interface that operates on 3 links and a BSS interface that operates on the 4th link, there won't be any room to perform a link switch that temporarily needs two links for the BSS interface, but the firmware only has four total. To mitigate this, try to evacuate a NAN channel in this scenario: First try to evacuate a NAN channel using the same phy as the BSS link being deactivated, since we expect NAN to follow the BSS channel configuration. If that doesn't work, try to evacuate any NAN channel. Reviewed-by: Johannes Berg Link: https://patch.msgid.link/20260515150751.a193c0f41b1f.I1d56c8d8d61d110422271971843b71a93f5ec354@changeid Signed-off-by: Miri Korenblit --- diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c index 9dc540720ecd..92858b8f7395 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c @@ -2585,15 +2585,108 @@ iwl_mld_mac80211_mgd_protect_tdls_discover(struct ieee80211_hw *hw, ret); } +static int iwl_mld_count_free_link_ids(struct iwl_mld *mld) +{ + int free_count = 0; + + for (int i = 0; i < mld->fw->ucode_capa.num_links; i++) { + if (!rcu_access_pointer(mld->fw_id_to_bss_conf[i])) + free_count++; + } + + return free_count; +} + +static bool +iwl_mld_chanctx_used_by_other_vif(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_chanctx_conf *chanctx_conf) +{ + struct ieee80211_bss_conf *iter_link; + struct ieee80211_vif *iter_vif; + int link_id; + + for_each_active_interface(iter_vif, hw) { + if (vif == iter_vif) + continue; + + /* NAN doesn't have active links, so we don't count NAN users */ + for_each_vif_active_link(iter_vif, iter_link, link_id) { + if (rcu_access_pointer(iter_link->chanctx_conf) == + chanctx_conf) + return true; + } + } + + return false; +} + static bool iwl_mld_can_activate_links(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 desired_links) { struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); int n_links = hweight16(desired_links); + int n_add = hweight16(desired_links & ~vif->active_links); + unsigned long to_deactivate = vif->active_links & ~desired_links; + int free_link_ids; + int i; /* Check if HW supports the wanted number of links */ - return n_links <= iwl_mld_max_active_links(mld, vif); + if (n_links > iwl_mld_max_active_links(mld, vif)) + return false; + + /* + * During link switch, mac80211 first adds the new links, then removes + * the old ones. This means we temporarily need extra link objects + * during the transition. Check if we have enough free link IDs. + */ + + free_link_ids = iwl_mld_count_free_link_ids(mld); + + if (free_link_ids >= n_add) + return true; + + if (!mld->nan_device_vif) + return false; + + /* + * Not enough free link IDs. First try to evacuate NAN from the + * channel context of a link that is going to be deactivated. + */ + for_each_set_bit(i, &to_deactivate, IEEE80211_MLD_MAX_NUM_LINKS) { + struct ieee80211_bss_conf *link_conf; + struct ieee80211_chanctx_conf *chanctx_conf; + + link_conf = link_conf_dereference_protected(vif, i); + if (!link_conf) + continue; + + chanctx_conf = wiphy_dereference(mld->wiphy, link_conf->chanctx_conf); + if (!chanctx_conf) + continue; + + if (iwl_mld_chanctx_used_by_other_vif(hw, vif, chanctx_conf)) + continue; + + if (ieee80211_nan_try_evacuate(hw, chanctx_conf)) { + free_link_ids = iwl_mld_count_free_link_ids(mld); + /* + * Evacuation of one channel should do the job. If not, + * something bad is happening. Don't try to evacuate more + */ + return free_link_ids >= n_add; + } + } + + /* Couldn't find/evacuate any channel going to go unused, try any */ + if (ieee80211_nan_try_evacuate(hw, NULL)) { + free_link_ids = iwl_mld_count_free_link_ids(mld); + if (free_link_ids >= n_add) + return true; + } + + return false; } static int