wifi: iwlwifi: support fast resume

This will allow to suspend / resume the system without resetting the
firmware. This will allow to reduce the resume time.
In case the fast_resume fails, stop the device and bring it up from
scratch.

Raise the timeout for the D3_END notification since in some iterations,
it took 240ms.

Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
Link: https://patch.msgid.link/20240618194245.03b8d2801044.I613d17c712de7a0d611cde4e14f37ebbe0c3c964@changeid
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
Emmanuel Grumbach 2024-06-18 19:44:03 +03:00 committed by Johannes Berg
parent 0091eda014
commit e8bb19c1d5
4 changed files with 130 additions and 11 deletions

View File

@ -2493,6 +2493,9 @@ static void iwl_mvm_parse_wowlan_info_notif(struct iwl_mvm *mvm,
return;
}
if (mvm->fast_resume)
return;
iwl_mvm_convert_key_counters_v5(status, &data->gtk[0].sc);
iwl_mvm_convert_gtk_v3(status, data->gtk);
iwl_mvm_convert_igtk(status, &data->igtk[0]);
@ -3049,7 +3052,7 @@ static bool iwl_mvm_check_rt_status(struct iwl_mvm *mvm,
if (iwl_mvm_rt_status(mvm->trans,
mvm->trans->dbg.lmac_error_event_table[0],
&err_id)) {
if (err_id == RF_KILL_INDICATOR_FOR_WOWLAN) {
if (err_id == RF_KILL_INDICATOR_FOR_WOWLAN && vif) {
struct cfg80211_wowlan_wakeup wakeup = {
.rfkill_release = true,
};
@ -3366,7 +3369,7 @@ static int iwl_mvm_resume_firmware(struct iwl_mvm *mvm, bool test)
return ret;
}
#define IWL_MVM_D3_NOTIF_TIMEOUT (HZ / 5)
#define IWL_MVM_D3_NOTIF_TIMEOUT (HZ / 3)
static int iwl_mvm_d3_notif_wait(struct iwl_mvm *mvm,
struct iwl_d3_data *d3_data)
@ -3377,12 +3380,22 @@ static int iwl_mvm_d3_notif_wait(struct iwl_mvm *mvm,
WIDE_ID(SCAN_GROUP, OFFLOAD_MATCH_INFO_NOTIF),
WIDE_ID(PROT_OFFLOAD_GROUP, D3_END_NOTIFICATION)
};
static const u16 d3_fast_resume_notif[] = {
WIDE_ID(PROT_OFFLOAD_GROUP, D3_END_NOTIFICATION)
};
struct iwl_notification_wait wait_d3_notif;
int ret;
iwl_init_notification_wait(&mvm->notif_wait, &wait_d3_notif,
d3_resume_notif, ARRAY_SIZE(d3_resume_notif),
iwl_mvm_wait_d3_notif, d3_data);
if (mvm->fast_resume)
iwl_init_notification_wait(&mvm->notif_wait, &wait_d3_notif,
d3_fast_resume_notif,
ARRAY_SIZE(d3_fast_resume_notif),
iwl_mvm_wait_d3_notif, d3_data);
else
iwl_init_notification_wait(&mvm->notif_wait, &wait_d3_notif,
d3_resume_notif,
ARRAY_SIZE(d3_resume_notif),
iwl_mvm_wait_d3_notif, d3_data);
ret = iwl_mvm_resume_firmware(mvm, d3_data->test);
if (ret) {
@ -3567,6 +3580,68 @@ void iwl_mvm_set_wakeup(struct ieee80211_hw *hw, bool enabled)
device_set_wakeup_enable(mvm->trans->dev, enabled);
}
void iwl_mvm_fast_suspend(struct iwl_mvm *mvm)
{
struct iwl_d3_manager_config d3_cfg_cmd_data = {};
int ret;
lockdep_assert_held(&mvm->mutex);
IWL_DEBUG_WOWLAN(mvm, "Starting fast suspend flow\n");
mvm->fast_resume = true;
set_bit(IWL_MVM_STATUS_IN_D3, &mvm->status);
WARN_ON(iwl_mvm_power_update_device(mvm));
mvm->trans->system_pm_mode = IWL_PLAT_PM_MODE_D3;
ret = iwl_mvm_send_cmd_pdu(mvm, D3_CONFIG_CMD, CMD_SEND_IN_D3,
sizeof(d3_cfg_cmd_data), &d3_cfg_cmd_data);
if (ret)
IWL_ERR(mvm,
"fast suspend: couldn't send D3_CONFIG_CMD %d\n", ret);
WARN_ON(iwl_mvm_power_update_mac(mvm));
ret = iwl_trans_d3_suspend(mvm->trans, false, false);
if (ret)
IWL_ERR(mvm, "fast suspend: trans_d3_suspend failed %d\n", ret);
}
int iwl_mvm_fast_resume(struct iwl_mvm *mvm)
{
struct iwl_d3_data d3_data = {
.notif_expected =
IWL_D3_NOTIF_D3_END_NOTIF,
};
int ret;
lockdep_assert_held(&mvm->mutex);
IWL_DEBUG_WOWLAN(mvm, "Starting the fast resume flow\n");
mvm->last_reset_or_resume_time_jiffies = jiffies;
iwl_fw_dbg_read_d3_debug_data(&mvm->fwrt);
if (iwl_mvm_check_rt_status(mvm, NULL)) {
set_bit(STATUS_FW_ERROR, &mvm->trans->status);
iwl_mvm_dump_nic_error_log(mvm);
iwl_dbg_tlv_time_point(&mvm->fwrt,
IWL_FW_INI_TIME_POINT_FW_ASSERT, NULL);
iwl_fw_dbg_collect_desc(&mvm->fwrt, &iwl_dump_desc_assert,
false, 0);
return -ENODEV;
}
ret = iwl_mvm_d3_notif_wait(mvm, &d3_data);
clear_bit(IWL_MVM_STATUS_IN_D3, &mvm->status);
mvm->trans->system_pm_mode = IWL_PLAT_PM_MODE_DISABLED;
mvm->fast_resume = false;
if (ret)
IWL_ERR(mvm, "Couldn't get the d3 notif %d\n", ret);
return ret;
}
#ifdef CONFIG_IWLWIFI_DEBUGFS
static int iwl_mvm_d3_test_open(struct inode *inode, struct file *file)
{

View File

@ -1222,6 +1222,7 @@ static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm)
int __iwl_mvm_mac_start(struct iwl_mvm *mvm)
{
bool fast_resume = false;
int ret;
lockdep_assert_held(&mvm->mutex);
@ -1247,6 +1248,30 @@ int __iwl_mvm_mac_start(struct iwl_mvm *mvm)
mvm->nvm_data = NULL;
}
#ifdef CONFIG_PM
/* fast_resume will be cleared by iwl_mvm_fast_resume */
fast_resume = mvm->fast_resume;
if (fast_resume) {
ret = iwl_mvm_fast_resume(mvm);
if (ret) {
iwl_mvm_stop_device(mvm);
/* iwl_mvm_up() will be called further down */
} else {
/*
* We clear IWL_MVM_STATUS_FIRMWARE_RUNNING upon
* mac_down() so that debugfs will stop honoring
* requests after we flush all the workers.
* Set the IWL_MVM_STATUS_FIRMWARE_RUNNING bit again
* now that we are back. This is a bit abusing the
* flag since the firmware wasn't really ever stopped,
* but this still serves the purpose.
*/
set_bit(IWL_MVM_STATUS_FIRMWARE_RUNNING, &mvm->status);
}
}
#endif /* CONFIG_PM */
if (test_bit(IWL_MVM_STATUS_HW_RESTART_REQUESTED, &mvm->status)) {
/*
* Now convert the HW_RESTART_REQUESTED flag to IN_HW_RESTART
@ -1257,7 +1282,10 @@ int __iwl_mvm_mac_start(struct iwl_mvm *mvm)
/* Clean up some internal and mac80211 state on restart */
iwl_mvm_restart_cleanup(mvm);
}
ret = iwl_mvm_up(mvm);
/* we also want to load the firmware if fast_resume failed */
if (!fast_resume || ret)
ret = iwl_mvm_up(mvm);
iwl_dbg_tlv_time_point(&mvm->fwrt, IWL_FW_INI_TIME_POINT_POST_INIT,
NULL);
@ -1340,7 +1368,7 @@ void iwl_mvm_mac_reconfig_complete(struct ieee80211_hw *hw,
}
}
void __iwl_mvm_mac_stop(struct iwl_mvm *mvm)
void __iwl_mvm_mac_stop(struct iwl_mvm *mvm, bool suspend)
{
lockdep_assert_held(&mvm->mutex);
@ -1356,7 +1384,11 @@ void __iwl_mvm_mac_stop(struct iwl_mvm *mvm)
if (!iwl_mvm_has_new_station_api(mvm->fw))
iwl_mvm_rm_aux_sta(mvm);
iwl_mvm_stop_device(mvm);
if (suspend &&
mvm->trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210)
iwl_mvm_fast_suspend(mvm);
else
iwl_mvm_stop_device(mvm);
iwl_mvm_async_handlers_purge(mvm);
/* async_handlers_list is empty and will stay empty: HW is stopped */
@ -1425,7 +1457,7 @@ void iwl_mvm_mac_stop(struct ieee80211_hw *hw, bool suspend)
iwl_mvm_mei_set_sw_rfkill_state(mvm);
mutex_lock(&mvm->mutex);
__iwl_mvm_mac_stop(mvm);
__iwl_mvm_mac_stop(mvm, suspend);
mutex_unlock(&mvm->mutex);
/*

View File

@ -1161,6 +1161,7 @@ struct iwl_mvm {
struct ieee80211_channel **nd_channels;
int n_nd_channels;
bool net_detect;
bool fast_resume;
u8 offload_tid;
#ifdef CONFIG_IWLWIFI_DEBUGFS
bool d3_wake_sysassert;
@ -1739,7 +1740,7 @@ struct iwl_rate_info {
u8 ieee; /* MAC header: IWL_RATE_6M_IEEE, etc. */
};
void __iwl_mvm_mac_stop(struct iwl_mvm *mvm);
void __iwl_mvm_mac_stop(struct iwl_mvm *mvm, bool suspend);
int __iwl_mvm_mac_start(struct iwl_mvm *mvm);
/******************
@ -2261,11 +2262,22 @@ extern const struct file_operations iwl_dbgfs_d3_test_ops;
#ifdef CONFIG_PM
void iwl_mvm_set_last_nonqos_seq(struct iwl_mvm *mvm,
struct ieee80211_vif *vif);
void iwl_mvm_fast_suspend(struct iwl_mvm *mvm);
int iwl_mvm_fast_resume(struct iwl_mvm *mvm);
#else
static inline void
iwl_mvm_set_last_nonqos_seq(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
{
}
static inline void iwl_mvm_fast_suspend(struct iwl_mvm *mvm)
{
}
static inline int iwl_mvm_fast_resume(struct iwl_mvm *mvm)
{
return 0;
}
#endif
void iwl_mvm_set_wowlan_qos_seq(struct iwl_mvm_sta *mvm_ap_sta,
struct iwl_wowlan_config_cmd *cmd);

View File

@ -299,7 +299,7 @@ static void check_exit_ctkill(struct work_struct *work)
ret = iwl_mvm_get_temp(mvm, &temp);
__iwl_mvm_mac_stop(mvm);
__iwl_mvm_mac_stop(mvm, false);
if (ret)
goto reschedule;