wifi: iwlwifi: mvm: d3: implement suspend with MLO

When using MLO, we need to have only a single link active
when entering suspend and of course most of the code also
needs to be adjusted to not use deflink, apart from older
code that's not used with MLO-capable firmware. Implement
that.

Note that the link selection currently prefers the "best"
link, which might really not be the best for D3, but that
can be fixed later once we agree.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Reviewed-by: Gregory Greenman <gregory.greenman@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
Link: https://msgid.link/20240131091413.38f0fd4d2db0.I27c7a1d08aecc5da0af2c351212f22e92ed70219@changeid
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
Johannes Berg 2024-01-31 10:24:35 +02:00
parent c868a189ec
commit 8c9bef26e9

View File

@ -450,9 +450,9 @@ static void iwl_mvm_wowlan_get_rsc_v5_data(struct ieee80211_hw *hw,
} }
static int iwl_mvm_wowlan_config_rsc_tsc(struct iwl_mvm *mvm, static int iwl_mvm_wowlan_config_rsc_tsc(struct iwl_mvm *mvm,
struct ieee80211_vif *vif) struct ieee80211_vif *vif,
struct iwl_mvm_vif_link_info *mvm_link)
{ {
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
int ver = iwl_fw_lookup_cmd_ver(mvm->fw, WOWLAN_TSC_RSC_PARAM, int ver = iwl_fw_lookup_cmd_ver(mvm->fw, WOWLAN_TSC_RSC_PARAM,
IWL_FW_CMD_VER_UNKNOWN); IWL_FW_CMD_VER_UNKNOWN);
int ret; int ret;
@ -470,7 +470,7 @@ static int iwl_mvm_wowlan_config_rsc_tsc(struct iwl_mvm *mvm,
for (i = 0; i < ARRAY_SIZE(data.rsc->mcast_key_id_map); i++) for (i = 0; i < ARRAY_SIZE(data.rsc->mcast_key_id_map); i++)
data.rsc->mcast_key_id_map[i] = data.rsc->mcast_key_id_map[i] =
IWL_MCAST_KEY_MAP_INVALID; IWL_MCAST_KEY_MAP_INVALID;
data.rsc->sta_id = cpu_to_le32(mvmvif->deflink.ap_sta_id); data.rsc->sta_id = cpu_to_le32(mvm_link->ap_sta_id);
ieee80211_iter_keys(mvm->hw, vif, ieee80211_iter_keys(mvm->hw, vif,
iwl_mvm_wowlan_get_rsc_v5_data, iwl_mvm_wowlan_get_rsc_v5_data,
@ -494,7 +494,7 @@ static int iwl_mvm_wowlan_config_rsc_tsc(struct iwl_mvm *mvm,
if (ver == 4) { if (ver == 4) {
size = sizeof(*data.rsc_tsc); size = sizeof(*data.rsc_tsc);
data.rsc_tsc->sta_id = data.rsc_tsc->sta_id =
cpu_to_le32(mvmvif->deflink.ap_sta_id); cpu_to_le32(mvm_link->ap_sta_id);
} else { } else {
/* ver == 2 || ver == IWL_FW_CMD_VER_UNKNOWN */ /* ver == 2 || ver == IWL_FW_CMD_VER_UNKNOWN */
size = sizeof(data.rsc_tsc->params); size = sizeof(data.rsc_tsc->params);
@ -668,10 +668,9 @@ static int iwl_mvm_send_patterns_v1(struct iwl_mvm *mvm,
} }
static int iwl_mvm_send_patterns(struct iwl_mvm *mvm, static int iwl_mvm_send_patterns(struct iwl_mvm *mvm,
struct ieee80211_vif *vif, struct iwl_mvm_vif_link_info *mvm_link,
struct cfg80211_wowlan *wowlan) struct cfg80211_wowlan *wowlan)
{ {
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
struct iwl_wowlan_patterns_cmd *pattern_cmd; struct iwl_wowlan_patterns_cmd *pattern_cmd;
struct iwl_host_cmd cmd = { struct iwl_host_cmd cmd = {
.id = WOWLAN_PATTERNS, .id = WOWLAN_PATTERNS,
@ -693,7 +692,7 @@ static int iwl_mvm_send_patterns(struct iwl_mvm *mvm,
pattern_cmd->n_patterns = wowlan->n_patterns; pattern_cmd->n_patterns = wowlan->n_patterns;
if (ver >= 3) if (ver >= 3)
pattern_cmd->sta_id = mvmvif->deflink.ap_sta_id; pattern_cmd->sta_id = mvm_link->ap_sta_id;
for (i = 0; i < wowlan->n_patterns; i++) { for (i = 0; i < wowlan->n_patterns; i++) {
int mask_len = DIV_ROUND_UP(wowlan->patterns[i].pattern_len, 8); int mask_len = DIV_ROUND_UP(wowlan->patterns[i].pattern_len, 8);
@ -730,7 +729,8 @@ static int iwl_mvm_d3_reprogram(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
struct iwl_time_quota_data *quota; struct iwl_time_quota_data *quota;
u32 status; u32 status;
if (WARN_ON_ONCE(iwl_mvm_is_cdb_supported(mvm))) if (WARN_ON_ONCE(iwl_mvm_is_cdb_supported(mvm) ||
ieee80211_vif_is_mld(vif)))
return -EINVAL; return -EINVAL;
/* add back the PHY */ /* add back the PHY */
@ -987,7 +987,8 @@ iwl_mvm_get_wowlan_config(struct iwl_mvm *mvm,
} }
static int iwl_mvm_wowlan_config_key_params(struct iwl_mvm *mvm, static int iwl_mvm_wowlan_config_key_params(struct iwl_mvm *mvm,
struct ieee80211_vif *vif) struct ieee80211_vif *vif,
struct iwl_mvm_vif_link_info *mvm_link)
{ {
bool unified = fw_has_capa(&mvm->fw->ucode_capa, bool unified = fw_has_capa(&mvm->fw->ucode_capa,
IWL_UCODE_TLV_CAPA_CNSLDTD_D3_D0_IMG); IWL_UCODE_TLV_CAPA_CNSLDTD_D3_D0_IMG);
@ -1016,7 +1017,7 @@ static int iwl_mvm_wowlan_config_key_params(struct iwl_mvm *mvm,
return -EIO; return -EIO;
} }
ret = iwl_mvm_wowlan_config_rsc_tsc(mvm, vif); ret = iwl_mvm_wowlan_config_rsc_tsc(mvm, vif, mvm_link);
if (ret) if (ret)
return ret; return ret;
@ -1030,7 +1031,7 @@ static int iwl_mvm_wowlan_config_key_params(struct iwl_mvm *mvm,
if (ver == 2) { if (ver == 2) {
size = sizeof(tkip_data.tkip); size = sizeof(tkip_data.tkip);
tkip_data.tkip.sta_id = tkip_data.tkip.sta_id =
cpu_to_le32(mvmvif->deflink.ap_sta_id); cpu_to_le32(mvm_link->ap_sta_id);
} else if (ver == 1 || ver == IWL_FW_CMD_VER_UNKNOWN) { } else if (ver == 1 || ver == IWL_FW_CMD_VER_UNKNOWN) {
size = sizeof(struct iwl_wowlan_tkip_params_cmd_ver_1); size = sizeof(struct iwl_wowlan_tkip_params_cmd_ver_1);
} else { } else {
@ -1079,7 +1080,7 @@ static int iwl_mvm_wowlan_config_key_params(struct iwl_mvm *mvm,
kek_kck_cmd.kek_len = cpu_to_le16(mvmvif->rekey_data.kek_len); kek_kck_cmd.kek_len = cpu_to_le16(mvmvif->rekey_data.kek_len);
kek_kck_cmd.replay_ctr = mvmvif->rekey_data.replay_ctr; kek_kck_cmd.replay_ctr = mvmvif->rekey_data.replay_ctr;
kek_kck_cmd.akm = cpu_to_le32(mvmvif->rekey_data.akm); kek_kck_cmd.akm = cpu_to_le32(mvmvif->rekey_data.akm);
kek_kck_cmd.sta_id = cpu_to_le32(mvmvif->deflink.ap_sta_id); kek_kck_cmd.sta_id = cpu_to_le32(mvm_link->ap_sta_id);
if (cmd_ver == 4) { if (cmd_ver == 4) {
cmd_size = sizeof(struct iwl_wowlan_kek_kck_material_cmd_v4); cmd_size = sizeof(struct iwl_wowlan_kek_kck_material_cmd_v4);
@ -1112,6 +1113,7 @@ iwl_mvm_wowlan_config(struct iwl_mvm *mvm,
struct cfg80211_wowlan *wowlan, struct cfg80211_wowlan *wowlan,
struct iwl_wowlan_config_cmd *wowlan_config_cmd, struct iwl_wowlan_config_cmd *wowlan_config_cmd,
struct ieee80211_vif *vif, struct iwl_mvm_vif *mvmvif, struct ieee80211_vif *vif, struct iwl_mvm_vif *mvmvif,
struct iwl_mvm_vif_link_info *mvm_link,
struct ieee80211_sta *ap_sta) struct ieee80211_sta *ap_sta)
{ {
int ret; int ret;
@ -1130,7 +1132,7 @@ iwl_mvm_wowlan_config(struct iwl_mvm *mvm,
return ret; return ret;
} }
ret = iwl_mvm_wowlan_config_key_params(mvm, vif); ret = iwl_mvm_wowlan_config_key_params(mvm, vif, mvm_link);
if (ret) if (ret)
return ret; return ret;
@ -1142,7 +1144,7 @@ iwl_mvm_wowlan_config(struct iwl_mvm *mvm,
if (fw_has_api(&mvm->fw->ucode_capa, if (fw_has_api(&mvm->fw->ucode_capa,
IWL_UCODE_TLV_API_WOWLAN_TCP_SYN_WAKE)) IWL_UCODE_TLV_API_WOWLAN_TCP_SYN_WAKE))
ret = iwl_mvm_send_patterns(mvm, vif, wowlan); ret = iwl_mvm_send_patterns(mvm, mvm_link, wowlan);
else else
ret = iwl_mvm_send_patterns_v1(mvm, wowlan); ret = iwl_mvm_send_patterns_v1(mvm, wowlan);
if (ret) if (ret)
@ -1223,6 +1225,7 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
struct ieee80211_vif *vif = NULL; struct ieee80211_vif *vif = NULL;
struct iwl_mvm_vif *mvmvif = NULL; struct iwl_mvm_vif *mvmvif = NULL;
struct ieee80211_sta *ap_sta = NULL; struct ieee80211_sta *ap_sta = NULL;
struct iwl_mvm_vif_link_info *mvm_link;
struct iwl_d3_manager_config d3_cfg_cmd_data = { struct iwl_d3_manager_config d3_cfg_cmd_data = {
/* /*
* Program the minimum sleep time to 10 seconds, as many * Program the minimum sleep time to 10 seconds, as many
@ -1237,7 +1240,7 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
.data[0] = &d3_cfg_cmd_data, .data[0] = &d3_cfg_cmd_data,
.len[0] = sizeof(d3_cfg_cmd_data), .len[0] = sizeof(d3_cfg_cmd_data),
}; };
int ret; int ret, primary_link;
int len __maybe_unused; int len __maybe_unused;
bool unified_image = fw_has_capa(&mvm->fw->ucode_capa, bool unified_image = fw_has_capa(&mvm->fw->ucode_capa,
IWL_UCODE_TLV_CAPA_CNSLDTD_D3_D0_IMG); IWL_UCODE_TLV_CAPA_CNSLDTD_D3_D0_IMG);
@ -1251,21 +1254,44 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
return -EINVAL; return -EINVAL;
} }
vif = iwl_mvm_get_bss_vif(mvm);
if (IS_ERR_OR_NULL(vif))
return 1;
if (ieee80211_vif_is_mld(vif) && vif->cfg.assoc) {
/*
* 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 {
primary_link = 0;
}
mutex_lock(&mvm->mutex); mutex_lock(&mvm->mutex);
set_bit(IWL_MVM_STATUS_IN_D3, &mvm->status); set_bit(IWL_MVM_STATUS_IN_D3, &mvm->status);
synchronize_net(); synchronize_net();
vif = iwl_mvm_get_bss_vif(mvm);
if (IS_ERR_OR_NULL(vif)) {
ret = 1;
goto out_noreset;
}
mvmvif = iwl_mvm_vif_from_mac80211(vif); mvmvif = iwl_mvm_vif_from_mac80211(vif);
if (mvmvif->deflink.ap_sta_id == IWL_MVM_INVALID_STA) { mvm_link = mvmvif->link[primary_link];
if (WARN_ON_ONCE(!mvm_link))
return -EINVAL;
if (mvm_link->ap_sta_id == IWL_MVM_INVALID_STA) {
/* if we're not associated, this must be netdetect */ /* if we're not associated, this must be netdetect */
if (!wowlan->nd_config) { if (!wowlan->nd_config) {
ret = 1; ret = 1;
@ -1281,10 +1307,10 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
} else { } else {
struct iwl_wowlan_config_cmd wowlan_config_cmd = {}; struct iwl_wowlan_config_cmd wowlan_config_cmd = {};
wowlan_config_cmd.sta_id = mvmvif->deflink.ap_sta_id; wowlan_config_cmd.sta_id = mvm_link->ap_sta_id;
ap_sta = rcu_dereference_protected( ap_sta = rcu_dereference_protected(
mvm->fw_id_to_mac_id[mvmvif->deflink.ap_sta_id], mvm->fw_id_to_mac_id[mvm_link->ap_sta_id],
lockdep_is_held(&mvm->mutex)); lockdep_is_held(&mvm->mutex));
if (IS_ERR_OR_NULL(ap_sta)) { if (IS_ERR_OR_NULL(ap_sta)) {
ret = -EINVAL; ret = -EINVAL;
@ -1296,7 +1322,7 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
if (ret) if (ret)
goto out_noreset; goto out_noreset;
ret = iwl_mvm_wowlan_config(mvm, wowlan, &wowlan_config_cmd, ret = iwl_mvm_wowlan_config(mvm, wowlan, &wowlan_config_cmd,
vif, mvmvif, ap_sta); vif, mvmvif, mvm_link, ap_sta);
if (ret) if (ret)
goto out; goto out;
@ -2846,6 +2872,9 @@ iwl_mvm_choose_query_wakeup_reasons(struct iwl_mvm *mvm,
u8 sta_id = mvm->net_detect ? IWL_MVM_INVALID_STA : u8 sta_id = mvm->net_detect ? IWL_MVM_INVALID_STA :
mvmvif->deflink.ap_sta_id; mvmvif->deflink.ap_sta_id;
/* bug - FW with MLO has status notification */
WARN_ON(ieee80211_vif_is_mld(vif));
d3_data->status = iwl_mvm_send_wowlan_get_status(mvm, sta_id); d3_data->status = iwl_mvm_send_wowlan_get_status(mvm, sta_id);
} }