Commit 07bf5297 authored by Miri Korenblit's avatar Miri Korenblit Committed by Johannes Berg

wifi: iwlwifi: mvm: Implement new link selection algorithm

Replaces the current logic with a new algorithm based on the link
grading introduced in a previous patch.

The new selection algorithm will be invoked upon successful scan to ensure
it has the necessary updated data it needs.

This update delegates the selection logic as the primary link
determiner in EMLSR mode, storing it in mvmvif to avoid repeated
calculations, as the result may vary.

Additionally, includes tests for iwl_mvm_valid_link_pair to validate
link pairs for EMLSR.
Signed-off-by: default avatarMiri Korenblit <miriam.rachel.korenblit@intel.com>
Reviewed-by: default avatarJohannes Berg <johannes.berg@intel.com>
Link: https://msgid.link/20240416134215.309fb1b3fe44.I5baf0c293c89a5a28bd1a6386bf9ca6d2bf61ab8@changeidSigned-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent 1b9b7d37
......@@ -282,7 +282,7 @@ static void iwl_mvm_bt_coex_enable_esr(struct iwl_mvm *mvm,
static bool
iwl_mvm_bt_coex_calculate_esr_mode(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
int link_id, int primary_link)
int link_id)
{
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
struct iwl_mvm_vif_link_info *link_info = mvmvif->link[link_id];
......@@ -298,7 +298,7 @@ iwl_mvm_bt_coex_calculate_esr_mode(struct iwl_mvm *mvm,
return true;
/* If LB link is the primary one we should always disable eSR */
if (link_id == primary_link)
if (link_id == iwl_mvm_get_primary_link(vif))
return false;
/* The feature is not supported */
......@@ -340,17 +340,13 @@ void iwl_mvm_bt_coex_update_link_esr(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
int link_id)
{
unsigned long usable_links = ieee80211_vif_usable_links(vif);
int primary_link = iwl_mvm_mld_get_primary_link(mvm, vif,
usable_links);
bool enable;
/* Not assoc, not MLD vif or only one usable link */
if (primary_link < 0)
if (!ieee80211_vif_is_mld(vif) ||
!iwl_mvm_vif_from_mac80211(vif)->authorized)
return;
enable = iwl_mvm_bt_coex_calculate_esr_mode(mvm, vif, link_id,
primary_link);
enable = iwl_mvm_bt_coex_calculate_esr_mode(mvm, vif, link_id);
iwl_mvm_bt_coex_enable_esr(mvm, vif, enable);
}
......
......@@ -1261,31 +1261,22 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
if (IS_ERR_OR_NULL(vif))
return 1;
if (hweight16(vif->active_links) > 1) {
mutex_lock(&mvm->mutex);
primary_link = iwl_mvm_get_primary_link(vif);
if (ieee80211_vif_is_mld(vif) && vif->cfg.assoc &&
mvmvif->esr_active) {
/*
* Select the 'best' link.
* May need to revisit, it seems better to not optimize
* for throughput but rather range, reliability and
* power here - and select 2.4 GHz ...
* Select the 'best' link. May need to revisit, it seems
* better to not optimize for throughput but rather
* range, reliability and power here - and select
* 2.4 GHz ...
*/
primary_link = iwl_mvm_mld_get_primary_link(mvm, vif,
vif->active_links);
if (WARN_ONCE(primary_link < 0, "no primary link in 0x%x\n",
vif->active_links))
primary_link = __ffs(vif->active_links);
ret = ieee80211_set_active_links(vif, BIT(primary_link));
if (ret)
return ret;
} else if (vif->active_links) {
primary_link = __ffs(vif->active_links);
} else {
primary_link = 0;
}
mutex_lock(&mvm->mutex);
set_bit(IWL_MVM_STATUS_IN_D3, &mvm->status);
synchronize_net();
......
......@@ -1350,6 +1350,7 @@ void iwl_mvm_mac_stop(struct ieee80211_hw *hw)
iwl_mvm_scan_stop(mvm, IWL_MVM_SCAN_INT_MLO, false);
mutex_unlock(&mvm->mutex);
wiphy_work_flush(mvm->hw->wiphy, &mvm->async_handlers_wiphy_wk);
flush_work(&mvm->async_handlers_wk);
flush_work(&mvm->add_stream_wk);
......@@ -3883,6 +3884,9 @@ iwl_mvm_sta_state_assoc_to_authorized(struct iwl_mvm *mvm,
WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif));
mvmvif->authorized = 1;
mvmvif->link_selection_res = 0;
mvmvif->link_selection_primary =
vif->active_links ? __ffs(vif->active_links) : 0;
callbacks->mac_ctxt_changed(mvm, vif, false);
iwl_mvm_mei_host_associated(mvm, vif, mvm_sta);
......@@ -3891,11 +3895,11 @@ iwl_mvm_sta_state_assoc_to_authorized(struct iwl_mvm *mvm,
iwl_mvm_bt_coex_update_vif_esr(mvm, vif);
/* when client is authorized (AP station marked as such),
* try to enable more links
* try to enable the best link(s).
*/
if (vif->type == NL80211_IFTYPE_STATION &&
!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))
iwl_mvm_mld_select_links(mvm, vif, false);
iwl_mvm_select_links(mvm, vif);
}
mvm_sta->authorized = true;
......@@ -3939,6 +3943,7 @@ iwl_mvm_sta_state_authorized_to_assoc(struct iwl_mvm *mvm,
* time.
*/
mvmvif->authorized = 0;
mvmvif->link_selection_res = 0;
/* disable beacon filtering */
iwl_mvm_disable_beacon_filter(mvm, vif);
......
......@@ -232,6 +232,12 @@ static int iwl_mvm_esr_mode_active(struct iwl_mvm *mvm,
link->phy_ctxt->rlc_disabled = true;
}
if (vif->active_links == mvmvif->link_selection_res &&
!WARN_ON(!(vif->active_links & BIT(mvmvif->link_selection_primary))))
mvmvif->primary_link = mvmvif->link_selection_primary;
else
mvmvif->primary_link = __ffs(vif->active_links);
return ret;
}
......@@ -689,9 +695,6 @@ iwl_mvm_mld_link_info_changed_station(struct iwl_mvm *mvm,
if (ret)
IWL_ERR(mvm, "failed to update MAC %pM\n", vif->addr);
if (changes & BSS_CHANGED_MLD_VALID_LINKS)
iwl_mvm_mld_select_links(mvm, vif, true);
memcpy(mvmvif->link[link_conf->link_id]->bssid, link_conf->bssid,
ETH_ALEN);
......@@ -1112,6 +1115,14 @@ iwl_mvm_mld_change_vif_links(struct ieee80211_hw *hw,
if (new_links == 0) {
mvmvif->link[0] = &mvmvif->deflink;
err = iwl_mvm_add_link(mvm, vif, &vif->bss_conf);
if (err == 0)
mvmvif->primary_link = 0;
} else if (!(new_links & BIT(mvmvif->primary_link))) {
/*
* Ensure we always have a valid primary_link, the real
* decision happens later when PHY is activated.
*/
mvmvif->primary_link = BIT(__ffs(new_links));
}
out_err:
......@@ -1144,27 +1155,22 @@ void iwl_mvm_recalc_esr(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
{
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
bool enable = !mvmvif->esr_disable_reason;
int link_id;
u16 new_active_links;
/* Nothing to do */
if (mvmvif->esr_active == enable)
return;
if (enable) {
/* Try to re-enable eSR */
iwl_mvm_mld_select_links(mvm, vif, false);
/* The next link selection will enter eSR if possible */
if (enable)
return;
}
/*
* Find the primary link, as we want to switch to it and drop the
* secondary one.
*/
link_id = iwl_mvm_mld_get_primary_link(mvm, vif, vif->active_links);
WARN_ON(link_id < 0);
ieee80211_set_active_links_async(vif,
vif->active_links & BIT(link_id));
new_active_links = BIT(iwl_mvm_get_primary_link(vif));
ieee80211_set_active_links_async(vif, new_active_links);
}
bool iwl_mvm_esr_allowed_on_vif(struct iwl_mvm *mvm,
......@@ -1200,12 +1206,13 @@ static bool iwl_mvm_can_enter_esr(struct iwl_mvm *mvm,
unsigned long desired_links)
{
struct iwl_mvm_link_sel_data data[IEEE80211_MLD_MAX_NUM_LINKS];
u8 n_data;
u8 best_link, n_data;
if (!iwl_mvm_esr_allowed_on_vif(mvm, vif))
return false;
n_data = iwl_mvm_set_link_selection_data(vif, data, desired_links);
n_data = iwl_mvm_set_link_selection_data(vif, data, desired_links,
&best_link);
if (n_data != 2)
return false;
......
......@@ -388,6 +388,12 @@ enum iwl_mvm_esr_disable_reason {
* @esr_active: indicates eSR mode is active
* @esr_disable_reason: a bitmap of enum iwl_mvm_esr_disable_reason
* @pm_enabled: indicates powersave is enabled
* @link_selection_res: bitmap of active links as it was decided in the last
* link selection. Valid only for a MLO vif after assoc. 0 if there wasn't
* any link selection yet.
* @link_selection_primary: primary link selected by link selection
* @primary_link: primary link in eSR. Valid only for an associated MLD vif,
* and in eSR mode. Valid only for a STA.
*/
struct iwl_mvm_vif {
struct iwl_mvm *mvm;
......@@ -478,6 +484,9 @@ struct iwl_mvm_vif {
struct ieee80211_key_conf __rcu *keys[2];
} bcn_prot;
u16 link_selection_res;
u8 link_selection_primary;
u8 primary_link;
struct iwl_mvm_vif_link_info deflink;
struct iwl_mvm_vif_link_info *link[IEEE80211_MLD_MAX_NUM_LINKS];
};
......@@ -1944,24 +1953,27 @@ int iwl_mvm_remove_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
int iwl_mvm_disable_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
struct ieee80211_bss_conf *link_conf);
void iwl_mvm_mld_select_links(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
bool valid_links_changed);
int iwl_mvm_mld_get_primary_link(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
unsigned long usable_links);
void iwl_mvm_select_links(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
u8 iwl_mvm_get_primary_link(struct ieee80211_vif *vif);
#if IS_ENABLED(CONFIG_IWLWIFI_KUNIT_TESTS)
unsigned int iwl_mvm_get_link_grade(struct ieee80211_bss_conf *link_conf);
#endif
struct iwl_mvm_link_sel_data {
u8 link_id;
enum nl80211_band band;
enum nl80211_chan_width width;
bool active;
u16 grade;
};
u8 iwl_mvm_set_link_selection_data(struct ieee80211_vif *vif,
struct iwl_mvm_link_sel_data *data,
unsigned long usable_links);
unsigned long usable_links,
u8 *best_link_idx);
bool iwl_mvm_mld_valid_link_pair(struct ieee80211_vif *vif,
struct iwl_mvm_link_sel_data *a,
struct iwl_mvm_link_sel_data *b);
const struct iwl_mvm_link_sel_data *a,
const struct iwl_mvm_link_sel_data *b);
/* AP and IBSS */
bool iwl_mvm_start_ap_ibss_common(struct ieee80211_hw *hw,
struct ieee80211_vif *vif, int *ret);
......@@ -2461,7 +2473,6 @@ u32 iwl_mvm_get_sec_flags(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
struct ieee80211_key_conf *keyconf);
unsigned int iwl_mvm_get_link_grade(struct ieee80211_bss_conf *link_conf);
bool iwl_rfi_supported(struct iwl_mvm *mvm);
int iwl_rfi_send_config_cmd(struct iwl_mvm *mvm,
......
......@@ -365,7 +365,8 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = {
iwl_mvm_rx_scan_match_found,
RX_HANDLER_SYNC),
RX_HANDLER(SCAN_COMPLETE_UMAC, iwl_mvm_rx_umac_scan_complete_notif,
RX_HANDLER_ASYNC_LOCKED, struct iwl_umac_scan_complete),
RX_HANDLER_ASYNC_LOCKED_WIPHY,
struct iwl_umac_scan_complete),
RX_HANDLER(SCAN_ITERATION_COMPLETE_UMAC,
iwl_mvm_rx_umac_scan_iter_complete_notif, RX_HANDLER_SYNC,
struct iwl_umac_scan_iter_complete_notif),
......
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
* Copyright (C) 2012-2014, 2018-2023 Intel Corporation
* Copyright (C) 2012-2014, 2018-2024 Intel Corporation
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
* Copyright (C) 2016-2017 Intel Deutschland GmbH
*/
......@@ -3177,6 +3177,23 @@ int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm,
return ret;
}
static void iwl_mvm_find_link_selection_vif(void *_data, u8 *mac,
struct ieee80211_vif *vif)
{
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
if (ieee80211_vif_is_mld(vif) && mvmvif->authorized)
iwl_mvm_select_links(mvmvif->mvm, vif);
}
static void iwl_mvm_post_scan_link_selection(struct iwl_mvm *mvm)
{
ieee80211_iterate_active_interfaces(mvm->hw,
IEEE80211_IFACE_ITER_NORMAL,
iwl_mvm_find_link_selection_vif,
NULL);
}
void iwl_mvm_rx_umac_scan_complete_notif(struct iwl_mvm *mvm,
struct iwl_rx_cmd_buffer *rxb)
{
......@@ -3236,6 +3253,9 @@ void iwl_mvm_rx_umac_scan_complete_notif(struct iwl_mvm *mvm,
mvm->last_ebs_successful = false;
mvm->scan_uid_status[uid] = 0;
if (notif->status == IWL_SCAN_OFFLOAD_COMPLETED)
iwl_mvm_post_scan_link_selection(mvm);
}
void iwl_mvm_rx_umac_scan_iter_complete_notif(struct iwl_mvm *mvm,
......
......@@ -208,3 +208,77 @@ static struct kunit_suite link_grading = {
};
kunit_test_suite(link_grading);
static const struct valid_link_pair_case {
const char *desc;
u32 esr_disable_reason;
enum nl80211_band band_a;
enum nl80211_band band_b;
bool valid;
} valid_link_pair_cases[] = {
{
.desc = "HB + UHB, valid.",
.band_a = NL80211_BAND_5GHZ,
.band_b = NL80211_BAND_6GHZ,
.valid = true,
},
{
.desc = "LB + HB, no BT.",
.band_a = NL80211_BAND_2GHZ,
.band_b = NL80211_BAND_5GHZ,
.valid = true,
},
{
.desc = "LB + HB, with BT.",
.esr_disable_reason = 0x1,
.band_a = NL80211_BAND_2GHZ,
.band_b = NL80211_BAND_5GHZ,
.valid = false,
},
{
.desc = "Same band",
.band_a = NL80211_BAND_2GHZ,
.band_b = NL80211_BAND_2GHZ,
.valid = false,
},
};
KUNIT_ARRAY_PARAM_DESC(valid_link_pair, valid_link_pair_cases, desc)
static void test_valid_link_pair(struct kunit *test)
{
const struct valid_link_pair_case *params = test->param_value;
size_t vif_size = sizeof(struct ieee80211_vif) +
sizeof(struct iwl_mvm_vif);
struct ieee80211_vif *vif = kunit_kzalloc(test, vif_size, GFP_KERNEL);
struct iwl_mvm_link_sel_data link_a = {
.band = params->band_a,
};
struct iwl_mvm_link_sel_data link_b = {
.band = params->band_b,
};
bool result;
KUNIT_ASSERT_NOT_NULL(test, vif);
iwl_mvm_vif_from_mac80211(vif)->esr_disable_reason =
params->esr_disable_reason;
result = iwl_mvm_mld_valid_link_pair(vif, &link_a, &link_b);
KUNIT_EXPECT_EQ(test, result, params->valid);
kunit_kfree(test, vif);
}
static struct kunit_case valid_link_pair_test_cases[] = {
KUNIT_CASE_PARAM(test_valid_link_pair, valid_link_pair_gen_params),
{},
};
static struct kunit_suite valid_link_pair = {
.name = "iwlmvm-valid-link-pair",
.test_cases = valid_link_pair_test_cases,
};
kunit_test_suite(valid_link_pair);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment