Commit 2f33561e authored by Miri Korenblit's avatar Miri Korenblit Committed by Johannes Berg
Browse files

wifi: iwlwifi: mvm: trigger link selection after exiting EMLSR



If the reason for exiting EMLSR was a blocking reason, wait for the
corresponding unblocking event:
- if there is an ongoing scan - do nothing. Link selection will be
  triggered at the end of it.
- If more than 30 seconds passed since the exit, trigger MLO scan, which
  will trigger link selection
- If less then 30 seconds passed since exit, reuse the latest link
  selection result

If the reason for exiting EMLSR was an exit reason (IWL_MVM_EXIT_*),
schedule MLO scan in 30 seconds.

Signed-off-by: default avatarMiri Korenblit <miriam.rachel.korenblit@intel.com>
Reviewed-by: default avatarIlan Peer <ilan.peer@intel.com>
Link: https://msgid.link/20240505091420.6a808c4ae8f5.Ia79605838eb6deee9358bec633ef537f2653db92@changeid


Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent 72c19df2
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@
#define IWL_MVM_BT_COEX_DISABLE_ESR_THRESH	69
#define IWL_MVM_BT_COEX_ENABLE_ESR_THRESH	63
#define IWL_MVM_BT_COEX_WIFI_LOSS_THRESH	0
#define IWL_MVM_TRIGGER_LINK_SEL_TIME_SEC	30

#define IWL_MVM_DEFAULT_PS_TX_DATA_TIMEOUT	(100 * USEC_PER_MSEC)
#define IWL_MVM_DEFAULT_PS_RX_DATA_TIMEOUT	(100 * USEC_PER_MSEC)
+1 −25
Original line number Diff line number Diff line
@@ -712,31 +712,7 @@ static ssize_t iwl_dbgfs_int_mlo_scan_write(struct ieee80211_vif *vif,
	if (!action) {
		ret = iwl_mvm_scan_stop(mvm, IWL_MVM_SCAN_INT_MLO, false);
	} else if (action == 1) {
		struct ieee80211_channel *channels[IEEE80211_MLD_MAX_NUM_LINKS];
		unsigned long usable_links = ieee80211_vif_usable_links(vif);
		size_t n_channels = 0;
		u8 link_id;

		rcu_read_lock();

		for_each_set_bit(link_id, &usable_links,
				 IEEE80211_MLD_MAX_NUM_LINKS) {
			struct ieee80211_bss_conf *link_conf =
				rcu_dereference(vif->link_conf[link_id]);

			if (WARN_ON_ONCE(!link_conf))
				continue;

			channels[n_channels++] = link_conf->chanreq.oper.chan;
		}

		rcu_read_unlock();

		if (n_channels)
			ret = iwl_mvm_int_mlo_scan_start(mvm, vif, channels,
							 n_channels);
		else
			ret = -EINVAL;
		ret = iwl_mvm_int_mlo_scan(mvm, vif);
	} else {
		ret = -EINVAL;
	}
+84 −18
Original line number Diff line number Diff line
@@ -787,37 +787,42 @@ u8 iwl_mvm_get_other_link(struct ieee80211_vif *vif, u8 link_id)
#define IWL_MVM_ESR_PREVENT_SHORT	(HZ * 300)
#define IWL_MVM_ESR_PREVENT_LONG	(HZ * 600)

static void iwl_mvm_recalc_esr_prevention(struct iwl_mvm *mvm,
static bool iwl_mvm_check_esr_prevention(struct iwl_mvm *mvm,
					 struct iwl_mvm_vif *mvmvif,
					 enum iwl_mvm_esr_state reason)
{
	unsigned long now = jiffies;
	unsigned long delay;
	bool timeout_expired =
		time_after(now, mvmvif->last_esr_exit.ts +
	bool timeout_expired = time_after(jiffies,
					  mvmvif->last_esr_exit.ts +
					  IWL_MVM_PREVENT_ESR_TIMEOUT);

	if (WARN_ON(!(IWL_MVM_ESR_PREVENT_REASONS & reason)))
		return;
	unsigned long delay;

	lockdep_assert_held(&mvm->mutex);

	mvmvif->last_esr_exit.ts = now;
	/* Only handle reasons that can cause prevention */
	if (!(reason & IWL_MVM_ESR_PREVENT_REASONS))
		return false;

	if (timeout_expired ||
	    mvmvif->last_esr_exit.reason != reason) {
		mvmvif->last_esr_exit.reason = reason;
	/*
	 * Reset the counter if more than 400 seconds have passed between one
	 * exit and the other, or if we exited due to a different reason.
	 * Will also reset the counter after the long prevention is done.
	 */
	if (timeout_expired || mvmvif->last_esr_exit.reason != reason) {
		mvmvif->exit_same_reason_count = 1;
		return;
		return false;
	}

	mvmvif->exit_same_reason_count++;
	if (WARN_ON(mvmvif->exit_same_reason_count < 2 ||
		    mvmvif->exit_same_reason_count > 3))
		return;
		return false;

	mvmvif->esr_disable_reason |= IWL_MVM_ESR_BLOCKED_PREVENTION;

	/*
	 * For the second exit, use a short prevention, and for the third one,
	 * use a long prevention.
	 */
	delay = mvmvif->exit_same_reason_count == 2 ?
		IWL_MVM_ESR_PREVENT_SHORT :
		IWL_MVM_ESR_PREVENT_LONG;
@@ -828,8 +833,11 @@ static void iwl_mvm_recalc_esr_prevention(struct iwl_mvm *mvm,

	wiphy_delayed_work_queue(mvm->hw->wiphy,
				 &mvmvif->prevent_esr_done_wk, delay);
	return true;
}

#define IWL_MVM_TRIGGER_LINK_SEL_TIME (IWL_MVM_TRIGGER_LINK_SEL_TIME_SEC * HZ)

/* API to exit eSR mode */
void iwl_mvm_exit_esr(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
		      enum iwl_mvm_esr_state reason,
@@ -837,6 +845,7 @@ void iwl_mvm_exit_esr(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
{
	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
	u16 new_active_links;
	bool prevented;

	lockdep_assert_held(&mvm->mutex);

@@ -857,8 +866,25 @@ void iwl_mvm_exit_esr(struct iwl_mvm *mvm, struct ieee80211_vif *vif,

	ieee80211_set_active_links_async(vif, new_active_links);

	if (IWL_MVM_ESR_PREVENT_REASONS & reason)
		iwl_mvm_recalc_esr_prevention(mvm, mvmvif, reason);
	/* Prevent EMLSR if needed */
	prevented = iwl_mvm_check_esr_prevention(mvm, mvmvif, reason);

	/* Remember why and when we exited EMLSR */
	mvmvif->last_esr_exit.ts = jiffies;
	mvmvif->last_esr_exit.reason = reason;

	/*
	 * If EMLSR is prevented now - don't try to get back to EMLSR.
	 * If we exited due to a blocking event, we will try to get back to
	 * EMLSR when the corresponding unblocking event will happen.
	 */
	if (prevented || reason & IWL_MVM_BLOCK_ESR_REASONS)
		return;

	/* If EMLSR is not blocked - try enabling it again in 30 seconds */
	wiphy_delayed_work_queue(mvm->hw->wiphy,
				 &mvmvif->mlo_int_scan_wk,
				 round_jiffies_relative(IWL_MVM_TRIGGER_LINK_SEL_TIME));
}

void iwl_mvm_block_esr(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
@@ -882,6 +908,43 @@ void iwl_mvm_block_esr(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
	iwl_mvm_exit_esr(mvm, vif, reason, link_to_keep);
}

static void iwl_mvm_esr_unblocked(struct iwl_mvm *mvm,
				  struct ieee80211_vif *vif)
{
	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
	bool need_new_sel = time_after(jiffies, mvmvif->last_esr_exit.ts +
						IWL_MVM_TRIGGER_LINK_SEL_TIME);

	lockdep_assert_held(&mvm->mutex);

	if (!ieee80211_vif_is_mld(vif) || !mvmvif->authorized ||
	    mvmvif->esr_active)
		return;

	IWL_DEBUG_INFO(mvm, "EMLSR is unblocked\n");

	/*
	 * If EMLSR was blocked for more than 30 seconds, or the last link
	 * selection decided to not enter EMLSR, trigger a new scan.
	 */
	if (need_new_sel || hweight16(mvmvif->link_selection_res) < 2) {
		IWL_DEBUG_INFO(mvm, "Trigger MLO scan\n");
		wiphy_delayed_work_queue(mvm->hw->wiphy,
					 &mvmvif->mlo_int_scan_wk, 0);
	/*
	 * If EMLSR was blocked for less than 30 seconds, and the last link
	 * selection decided to use EMLSR, activate EMLSR using the previous
	 * link selection result.
	 */
	} else {
		IWL_DEBUG_INFO(mvm,
			       "Use the latest link selection result: 0x%x\n",
			       mvmvif->link_selection_res);
		ieee80211_set_active_links_async(vif,
						 mvmvif->link_selection_res);
	}
}

void iwl_mvm_unblock_esr(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
			 enum iwl_mvm_esr_state reason)
{
@@ -898,4 +961,7 @@ void iwl_mvm_unblock_esr(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
			       reason);

	mvmvif->esr_disable_reason &= ~reason;

	if (!mvmvif->esr_disable_reason)
		iwl_mvm_esr_unblocked(mvm, vif);
}
+24 −2
Original line number Diff line number Diff line
@@ -1625,6 +1625,20 @@ static void iwl_mvm_prevent_esr_done_wk(struct wiphy *wiphy,
	mutex_unlock(&mvm->mutex);
}

static void iwl_mvm_mlo_int_scan_wk(struct wiphy *wiphy, struct wiphy_work *wk)
{
	struct iwl_mvm_vif *mvmvif = container_of(wk, struct iwl_mvm_vif,
						  mlo_int_scan_wk.work);
	struct ieee80211_vif *vif =
		container_of((void *)mvmvif, struct ieee80211_vif, drv_priv);

	mutex_lock(&mvmvif->mvm->mutex);

	iwl_mvm_int_mlo_scan(mvmvif->mvm, vif);

	mutex_unlock(&mvmvif->mvm->mutex);
}

void iwl_mvm_mac_init_mvmvif(struct iwl_mvm *mvm, struct iwl_mvm_vif *mvmvif)
{
	lockdep_assert_held(&mvm->mutex);
@@ -1637,6 +1651,9 @@ void iwl_mvm_mac_init_mvmvif(struct iwl_mvm *mvm, struct iwl_mvm_vif *mvmvif)

	wiphy_delayed_work_init(&mvmvif->prevent_esr_done_wk,
				iwl_mvm_prevent_esr_done_wk);

	wiphy_delayed_work_init(&mvmvif->mlo_int_scan_wk,
				iwl_mvm_mlo_int_scan_wk);
}

static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
@@ -1783,6 +1800,9 @@ void iwl_mvm_prepare_mac_removal(struct iwl_mvm *mvm,
	wiphy_delayed_work_cancel(mvm->hw->wiphy,
				  &mvmvif->prevent_esr_done_wk);

	wiphy_delayed_work_cancel(mvm->hw->wiphy,
				  &mvmvif->mlo_int_scan_wk);

	cancel_delayed_work_sync(&mvmvif->csa_work);
}

@@ -3877,7 +3897,6 @@ iwl_mvm_sta_state_auth_to_assoc(struct ieee80211_hw *hw,
	return callbacks->update_sta(mvm, vif, sta);
}


static int
iwl_mvm_sta_state_assoc_to_authorized(struct iwl_mvm *mvm,
				      struct ieee80211_vif *vif,
@@ -3901,7 +3920,7 @@ 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_res = vif->active_links;
		mvmvif->link_selection_primary =
			vif->active_links ? __ffs(vif->active_links) : 0;

@@ -3968,6 +3987,9 @@ iwl_mvm_sta_state_authorized_to_assoc(struct iwl_mvm *mvm,
		wiphy_delayed_work_cancel(mvm->hw->wiphy,
					  &mvmvif->prevent_esr_done_wk);

		wiphy_delayed_work_cancel(mvm->hw->wiphy,
					  &mvmvif->mlo_int_scan_wk);

		/* No need for the periodic statistics anymore */
		if (ieee80211_vif_is_mld(vif) && mvmvif->esr_active)
			iwl_mvm_request_periodic_system_statistics(mvm, false);
+3 −3
Original line number Diff line number Diff line
@@ -429,6 +429,7 @@ struct iwl_mvm_esr_exit {
 *	@last_esr_exit::reason, only counting exits due to
 *	&IWL_MVM_ESR_PREVENT_REASONS.
 * @prevent_esr_done_wk: work that should be done when esr prevention ends.
 * @mlo_int_scan_wk: work for the internal MLO scan.
 */
struct iwl_mvm_vif {
	struct iwl_mvm *mvm;
@@ -525,6 +526,7 @@ struct iwl_mvm_vif {
	struct iwl_mvm_esr_exit last_esr_exit;
	u8 exit_same_reason_count;
	struct wiphy_delayed_work prevent_esr_done_wk;
	struct wiphy_delayed_work mlo_int_scan_wk;

	struct iwl_mvm_vif_link_info deflink;
	struct iwl_mvm_vif_link_info *link[IEEE80211_MLD_MAX_NUM_LINKS];
@@ -2089,13 +2091,11 @@ int iwl_mvm_reg_scan_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
			   struct ieee80211_scan_ies *ies);
size_t iwl_mvm_scan_size(struct iwl_mvm *mvm);
int iwl_mvm_scan_stop(struct iwl_mvm *mvm, int type, bool notify);
int iwl_mvm_int_mlo_scan_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
			       struct ieee80211_channel **channels,
			       size_t n_channels);

int iwl_mvm_max_scan_ie_len(struct iwl_mvm *mvm);
void iwl_mvm_report_scan_aborted(struct iwl_mvm *mvm);
void iwl_mvm_scan_timeout_wk(struct work_struct *work);
int iwl_mvm_int_mlo_scan(struct iwl_mvm *mvm, struct ieee80211_vif *vif);

/* Scheduled scan */
void iwl_mvm_rx_lmac_scan_complete_notif(struct iwl_mvm *mvm,
Loading