Commit 4d7236f9 authored by Miri Korenblit's avatar Miri Korenblit Committed by Johannes Berg
Browse files

wifi: iwlwifi: mld: remove IWL_MLD_EMLSR_BLOCKED_FW



The channel load logic moves from the FW to the driver.
- Implement the logic: allow EMLSR only if the candidate primary link is
  active and if its average channel load exceeds the threshold.
- Remove IWL_MLD_EMLSR_BLOCKED_FW. Instead, treat ESR_RECOMMEND_LEAVE in
  the EMLSR_RECOMMENDATION notif as an EXIT reason.

Signed-off-by: default avatarMiri Korenblit <miriam.rachel.korenblit@intel.com>
Reviewed-by: default avatarEmmanuel Grumbach <emmanuel.grumbach@intel.com>
Link: https://patch.msgid.link/20250313002008.6729a8d67815.Iab39bf0982d8cdbb0db701d31854101c2fcf3b64@changeid


Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent b611cf6b
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -68,7 +68,7 @@
#define IWL_MLD_LOW_RSSI_THRESH_160MHZ		-72

#define IWL_MLD_ENTER_EMLSR_TPT_THRESH		400
#define IWL_MLD_CHAN_LOAD_THRESH		2 /* in percentage */
#define IWL_MLD_EXIT_EMLSR_CHAN_LOAD		2 /* in percentage */

#define IWL_MLD_FTM_INITIATOR_ALGO		IWL_TOF_ALGO_TYPE_MAX_LIKE
#define IWL_MLD_FTM_INITIATOR_DYNACK		true
+6 −6
Original line number Diff line number Diff line
@@ -24,7 +24,6 @@ enum iwl_mld_cca_40mhz_wa_status {
 *
 * @IWL_MLD_EMLSR_BLOCKED_PREVENTION: Prevent repeated EMLSR enter/exit
 * @IWL_MLD_EMLSR_BLOCKED_WOWLAN: WOWLAN is preventing EMLSR
 * @IWL_MLD_EMLSR_BLOCKED_FW: FW did not recommend MLO
 * @IWL_MLD_EMLSR_BLOCKED_ROC: remain-on-channel is preventing EMLSR
 * @IWL_MLD_EMLSR_BLOCKED_NON_BSS: An active non-BSS interface's link is
 *      preventing EMLSR
@@ -36,11 +35,10 @@ enum iwl_mld_cca_40mhz_wa_status {
enum iwl_mld_emlsr_blocked {
	IWL_MLD_EMLSR_BLOCKED_PREVENTION	= 0x1,
	IWL_MLD_EMLSR_BLOCKED_WOWLAN		= 0x2,
	IWL_MLD_EMLSR_BLOCKED_FW		= 0x4,
	IWL_MLD_EMLSR_BLOCKED_ROC		= 0x8,
	IWL_MLD_EMLSR_BLOCKED_NON_BSS		= 0x10,
	IWL_MLD_EMLSR_BLOCKED_TMP_NON_BSS	= 0x20,
	IWL_MLD_EMLSR_BLOCKED_TPT		= 0x40,
	IWL_MLD_EMLSR_BLOCKED_ROC		= 0x4,
	IWL_MLD_EMLSR_BLOCKED_NON_BSS		= 0x8,
	IWL_MLD_EMLSR_BLOCKED_TMP_NON_BSS	= 0x10,
	IWL_MLD_EMLSR_BLOCKED_TPT		= 0x20,
};

/**
@@ -62,6 +60,7 @@ enum iwl_mld_emlsr_blocked {
 * @IWL_MLD_EMLSR_EXIT_CHAN_LOAD: Exit EMLSR because the primary channel is not
 *	loaded enough to justify EMLSR.
 * @IWL_MLD_EMLSR_EXIT_RFI: Exit EMLSR due to RFI
 * @IWL_MLD_EMLSR_EXIT_FW_REQUEST: Exit EMLSR because the FW requested it
 */
enum iwl_mld_emlsr_exit {
	IWL_MLD_EMLSR_EXIT_BLOCK		= 0x1,
@@ -75,6 +74,7 @@ enum iwl_mld_emlsr_exit {
	IWL_MLD_EMLSR_EXIT_BT_COEX		= 0x100,
	IWL_MLD_EMLSR_EXIT_CHAN_LOAD		= 0x200,
	IWL_MLD_EMLSR_EXIT_RFI			= 0x400,
	IWL_MLD_EMLSR_EXIT_FW_REQUEST		= 0x800,
};

/**
+7 −6
Original line number Diff line number Diff line
@@ -1189,8 +1189,13 @@ iwl_mld_mac80211_link_info_changed_sta(struct iwl_mld *mld,
		iwl_mld_omi_ap_changed_bw(mld, link_conf, bw);
	}

	if (changes & BSS_CHANGED_BANDWIDTH)
	if (changes & BSS_CHANGED_BANDWIDTH) {
		if (iwl_mld_emlsr_active(vif))
			iwl_mld_emlsr_check_equal_bw(mld, vif, link_conf);
		else
			/* Channel load threshold may have changed */
			iwl_mld_retry_emlsr(mld, vif);
	}
}

static int iwl_mld_update_mu_groups(struct iwl_mld *mld,
@@ -1712,10 +1717,6 @@ static int iwl_mld_move_sta_state_up(struct iwl_mld *mld,
						    IWL_MLD_EMLSR_BLOCKED_TPT,
						    0);

			/* Wait for the FW to send a recommendation */
			iwl_mld_block_emlsr(mld, vif,
					    IWL_MLD_EMLSR_BLOCKED_FW, 0);

			/* clear COEX_HIGH_PRIORITY_ENABLE */
			ret = iwl_mld_mac_fw_action(mld, vif,
						    FW_CTXT_ACTION_MODIFY);
+101 −29
Original line number Diff line number Diff line
@@ -9,7 +9,6 @@
#define HANDLE_EMLSR_BLOCKED_REASONS(HOW)	\
	HOW(PREVENTION)			\
	HOW(WOWLAN)			\
	HOW(FW)				\
	HOW(ROC)			\
	HOW(NON_BSS)			\
	HOW(TMP_NON_BSS)		\
@@ -53,7 +52,8 @@ static void iwl_mld_print_emlsr_blocked(struct iwl_mld *mld, u32 mask)
	HOW(LINK_USAGE)			\
	HOW(BT_COEX)			\
	HOW(CHAN_LOAD)			\
	HOW(RFI)
	HOW(RFI)			\
	HOW(FW_REQUEST)

static const char *
iwl_mld_get_emlsr_exit_string(enum iwl_mld_emlsr_exit exit)
@@ -332,18 +332,14 @@ iwl_mld_vif_iter_emlsr_mode_notif(void *data, u8 *mac,
		return;

	switch (le32_to_cpu(notif->action)) {
	case ESR_RECOMMEND_ENTER:
		iwl_mld_unblock_emlsr(mld_vif->mld, vif,
				      IWL_MLD_EMLSR_BLOCKED_FW);
		break;
	case ESR_RECOMMEND_LEAVE:
		iwl_mld_block_emlsr(mld_vif->mld, vif,
				    IWL_MLD_EMLSR_BLOCKED_FW,
		iwl_mld_exit_emlsr(mld_vif->mld, vif,
				   IWL_MLD_EMLSR_EXIT_FW_REQUEST,
				   iwl_mld_get_primary_link(vif));
		break;
	case ESR_RECOMMEND_ENTER:
	case ESR_FORCE_LEAVE:
	default:
		/* ESR_FORCE_LEAVE should not happen at this point */
		IWL_WARN(mld_vif->mld, "Unexpected EMLSR notification: %d\n",
			 le32_to_cpu(notif->action));
	}
@@ -724,6 +720,53 @@ iwl_mld_set_link_sel_data(struct iwl_mld *mld,
	return n_data;
}

static u32
iwl_mld_get_min_chan_load_thresh(struct ieee80211_chanctx_conf *chanctx)
{
	const struct iwl_mld_phy *phy = iwl_mld_phy_from_mac80211(chanctx);

	switch (phy->chandef.width) {
	case NL80211_CHAN_WIDTH_320:
	case NL80211_CHAN_WIDTH_160:
		return 5;
	case NL80211_CHAN_WIDTH_80:
		return 7;
	default:
		break;
	}
	return 10;
}

static bool
iwl_mld_channel_load_allows_emlsr(struct iwl_mld *mld,
				  struct ieee80211_vif *vif,
				  const struct iwl_mld_link_sel_data *a,
				  const struct iwl_mld_link_sel_data *b)
{
	struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
	struct iwl_mld_link *link_a =
		iwl_mld_link_dereference_check(mld_vif, a->link_id);
	struct ieee80211_chanctx_conf *chanctx_a = NULL;
	u32 primary_load_perc;

	if (!link_a || !link_a->active) {
		IWL_DEBUG_EHT(mld, "Primary link is not active. Can't enter EMLSR\n");
		return false;
	}

	chanctx_a = wiphy_dereference(mld->wiphy, link_a->chan_ctx);

	if (WARN_ON(!chanctx_a))
		return false;

	primary_load_perc =
		iwl_mld_phy_from_mac80211(chanctx_a)->avg_channel_load_not_by_us;

	IWL_DEBUG_EHT(mld, "Average channel load not by us: %u\n", primary_load_perc);

	return primary_load_perc > iwl_mld_get_min_chan_load_thresh(chanctx_a);
}

static bool
iwl_mld_valid_emlsr_pair(struct ieee80211_vif *vif,
			 struct iwl_mld_link_sel_data *a,
@@ -746,6 +789,8 @@ iwl_mld_valid_emlsr_pair(struct ieee80211_vif *vif,
		 */
		reason_mask |= IWL_MLD_EMLSR_EXIT_BANDWIDTH;
	}
	if (!iwl_mld_channel_load_allows_emlsr(mld, vif, a, b))
		reason_mask |= IWL_MLD_EMLSR_EXIT_CHAN_LOAD;

	if (reason_mask) {
		IWL_DEBUG_INFO(mld,
@@ -904,7 +949,6 @@ void iwl_mld_emlsr_check_equal_bw(struct iwl_mld *mld,
		link_conf_dereference_check(vif, other_link_id);

	if (!ieee80211_vif_link_active(vif, link->link_id) ||
	    !iwl_mld_emlsr_active(vif) ||
	    WARN_ON(link->link_id == other_link_id || !other_link))
		return;

@@ -952,39 +996,67 @@ void iwl_mld_emlsr_check_bt(struct iwl_mld *mld)
						NULL);
}

static void iwl_mld_emlsr_check_chan_load_iter(void *_data, u8 *mac,
struct iwl_mld_chan_load_data {
	struct iwl_mld_phy *phy;
	u32 prev_chan_load_not_by_us;
};

static void iwl_mld_chan_load_update_iter(void *_data, u8 *mac,
					  struct ieee80211_vif *vif)
{
	struct iwl_mld *mld = (struct iwl_mld *)_data;
	struct iwl_mld_chan_load_data *data = _data;
	const struct iwl_mld_phy *phy = data->phy;
	struct ieee80211_chanctx_conf *chanctx =
		container_of((const void *)phy, struct ieee80211_chanctx_conf,
			     drv_priv);
	struct iwl_mld *mld = iwl_mld_vif_from_mac80211(vif)->mld;
	struct ieee80211_bss_conf *prim_link;
	unsigned int prim_link_id;
	int chan_load;

	if (!iwl_mld_emlsr_active(vif))
		return;

	prim_link_id = iwl_mld_get_primary_link(vif);
	prim_link = link_conf_dereference_protected(vif, prim_link_id);

	if (WARN_ON(!prim_link))
		return;

	chan_load = iwl_mld_get_chan_load_by_others(mld, prim_link, true);
	if (chanctx != rcu_access_pointer(prim_link->chanctx_conf))
		return;

	if (iwl_mld_emlsr_active(vif)) {
		int chan_load = iwl_mld_get_chan_load_by_others(mld, prim_link,
								true);

		if (chan_load < 0)
			return;

		/* chan_load is in range [0,255] */
	if (chan_load < NORMALIZE_PERCENT_TO_255(IWL_MLD_CHAN_LOAD_THRESH))
		iwl_mld_exit_emlsr(mld, vif, IWL_MLD_EMLSR_EXIT_CHAN_LOAD,
		if (chan_load < NORMALIZE_PERCENT_TO_255(IWL_MLD_EXIT_EMLSR_CHAN_LOAD))
			iwl_mld_exit_emlsr(mld, vif,
					   IWL_MLD_EMLSR_EXIT_CHAN_LOAD,
					   prim_link_id);
	} else {
		u32 old_chan_load = data->prev_chan_load_not_by_us;
		u32 new_chan_load = phy->avg_channel_load_not_by_us;
		u32 thresh = iwl_mld_get_min_chan_load_thresh(chanctx);

		if (old_chan_load <= thresh && new_chan_load > thresh)
			iwl_mld_retry_emlsr(mld, vif);
	}
}

void iwl_mld_emlsr_check_chan_load(struct iwl_mld *mld)
void iwl_mld_emlsr_check_chan_load(struct ieee80211_hw *hw,
				   struct iwl_mld_phy *phy,
				   u32 prev_chan_load_not_by_us)
{
	ieee80211_iterate_active_interfaces_mtx(mld->hw,
	struct iwl_mld_chan_load_data data = {
		.phy = phy,
		.prev_chan_load_not_by_us = prev_chan_load_not_by_us,
	};

	ieee80211_iterate_active_interfaces_mtx(hw,
						IEEE80211_IFACE_ITER_NORMAL,
						iwl_mld_emlsr_check_chan_load_iter,
						(void *)(uintptr_t)mld);
						iwl_mld_chan_load_update_iter,
						&data);
}

void iwl_mld_retry_emlsr(struct iwl_mld *mld, struct ieee80211_vif *vif)
+4 −1
Original line number Diff line number Diff line
@@ -11,6 +11,7 @@
#include "iwl-config.h"
#include "iwl-trans.h"
#include "iface.h"
#include "phy.h"

struct iwl_mld;

@@ -139,7 +140,9 @@ void iwl_mld_emlsr_check_equal_bw(struct iwl_mld *mld,

void iwl_mld_emlsr_check_bt(struct iwl_mld *mld);

void iwl_mld_emlsr_check_chan_load(struct iwl_mld *mld);
void iwl_mld_emlsr_check_chan_load(struct ieee80211_hw *hw,
				   struct iwl_mld_phy *phy,
				   u32 prev_chan_load_not_by_us);

/**
 * iwl_mld_retry_emlsr - Retry entering EMLSR
Loading