File Index
Files and directories
Sign in
FILE

mlo.c

File preview with syntax highlighting.

linux-6.19.14/drivers/net/wireless/intel/iwlwifi/mld/mlo.c C
   1// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
   2/*
   3 * Copyright (C) 2024-2025 Intel Corporation
   4 */
   5#include "mlo.h"
   6#include "phy.h"
   7
   8/* Block reasons helper */
   9#define HANDLE_EMLSR_BLOCKED_REASONS(HOW)	\
  10	HOW(PREVENTION)			\
  11	HOW(WOWLAN)			\
  12	HOW(ROC)			\
  13	HOW(NON_BSS)			\
  14	HOW(TMP_NON_BSS)		\
  15	HOW(TPT)
  16
  17static const char *
  18iwl_mld_get_emlsr_blocked_string(enum iwl_mld_emlsr_blocked blocked)
  19{
  20	/* Using switch without "default" will warn about missing entries  */
  21	switch (blocked) {
  22#define REASON_CASE(x) case IWL_MLD_EMLSR_BLOCKED_##x: return #x;
  23	HANDLE_EMLSR_BLOCKED_REASONS(REASON_CASE)
  24#undef REASON_CASE
  25	}
  26
  27	return "ERROR";
  28}
  29
  30static void iwl_mld_print_emlsr_blocked(struct iwl_mld *mld, u32 mask)
  31{
  32#define NAME_FMT(x) "%s"
  33#define NAME_PR(x) (mask & IWL_MLD_EMLSR_BLOCKED_##x) ? "[" #x "]" : "",
  34	IWL_DEBUG_EHT(mld,
  35		      "EMLSR blocked = " HANDLE_EMLSR_BLOCKED_REASONS(NAME_FMT)
  36		      " (0x%x)\n", HANDLE_EMLSR_BLOCKED_REASONS(NAME_PR) mask);
  37#undef NAME_FMT
  38#undef NAME_PR
  39}
  40
  41/* Exit reasons helper */
  42#define HANDLE_EMLSR_EXIT_REASONS(HOW)	\
  43	HOW(BLOCK)			\
  44	HOW(MISSED_BEACON)		\
  45	HOW(FAIL_ENTRY)			\
  46	HOW(CSA)			\
  47	HOW(EQUAL_BAND)			\
  48	HOW(LOW_RSSI)			\
  49	HOW(LINK_USAGE)			\
  50	HOW(BT_COEX)			\
  51	HOW(CHAN_LOAD)			\
  52	HOW(RFI)			\
  53	HOW(FW_REQUEST)			\
  54	HOW(INVALID)
  55
  56static const char *
  57iwl_mld_get_emlsr_exit_string(enum iwl_mld_emlsr_exit exit)
  58{
  59	/* Using switch without "default" will warn about missing entries  */
  60	switch (exit) {
  61#define REASON_CASE(x) case IWL_MLD_EMLSR_EXIT_##x: return #x;
  62	HANDLE_EMLSR_EXIT_REASONS(REASON_CASE)
  63#undef REASON_CASE
  64	}
  65
  66	return "ERROR";
  67}
  68
  69static void iwl_mld_print_emlsr_exit(struct iwl_mld *mld, u32 mask)
  70{
  71#define NAME_FMT(x) "%s"
  72#define NAME_PR(x) (mask & IWL_MLD_EMLSR_EXIT_##x) ? "[" #x "]" : "",
  73	IWL_DEBUG_EHT(mld,
  74		      "EMLSR exit = " HANDLE_EMLSR_EXIT_REASONS(NAME_FMT)
  75		      " (0x%x)\n", HANDLE_EMLSR_EXIT_REASONS(NAME_PR) mask);
  76#undef NAME_FMT
  77#undef NAME_PR
  78}
  79
  80void iwl_mld_emlsr_prevent_done_wk(struct wiphy *wiphy, struct wiphy_work *wk)
  81{
  82	struct iwl_mld_vif *mld_vif = container_of(wk, struct iwl_mld_vif,
  83						   emlsr.prevent_done_wk.work);
  84	struct ieee80211_vif *vif =
  85		container_of((void *)mld_vif, struct ieee80211_vif, drv_priv);
  86
  87	if (WARN_ON(!(mld_vif->emlsr.blocked_reasons &
  88		      IWL_MLD_EMLSR_BLOCKED_PREVENTION)))
  89		return;
  90
  91	iwl_mld_unblock_emlsr(mld_vif->mld, vif,
  92			      IWL_MLD_EMLSR_BLOCKED_PREVENTION);
  93}
  94
  95void iwl_mld_emlsr_tmp_non_bss_done_wk(struct wiphy *wiphy,
  96				       struct wiphy_work *wk)
  97{
  98	struct iwl_mld_vif *mld_vif = container_of(wk, struct iwl_mld_vif,
  99						   emlsr.tmp_non_bss_done_wk.work);
 100	struct ieee80211_vif *vif =
 101		container_of((void *)mld_vif, struct ieee80211_vif, drv_priv);
 102
 103	if (WARN_ON(!(mld_vif->emlsr.blocked_reasons &
 104		      IWL_MLD_EMLSR_BLOCKED_TMP_NON_BSS)))
 105		return;
 106
 107	iwl_mld_unblock_emlsr(mld_vif->mld, vif,
 108			      IWL_MLD_EMLSR_BLOCKED_TMP_NON_BSS);
 109}
 110
 111#define IWL_MLD_TRIGGER_LINK_SEL_TIME	(HZ * IWL_MLD_TRIGGER_LINK_SEL_TIME_SEC)
 112#define IWL_MLD_SCAN_EXPIRE_TIME	(HZ * IWL_MLD_SCAN_EXPIRE_TIME_SEC)
 113
 114/* Exit reasons that can cause longer EMLSR prevention */
 115#define IWL_MLD_PREVENT_EMLSR_REASONS	(IWL_MLD_EMLSR_EXIT_MISSED_BEACON	| \
 116					 IWL_MLD_EMLSR_EXIT_LINK_USAGE		| \
 117					 IWL_MLD_EMLSR_EXIT_FW_REQUEST)
 118#define IWL_MLD_PREVENT_EMLSR_TIMEOUT	(HZ * 400)
 119
 120#define IWL_MLD_EMLSR_PREVENT_SHORT	(HZ * 300)
 121#define IWL_MLD_EMLSR_PREVENT_LONG	(HZ * 600)
 122
 123static void iwl_mld_check_emlsr_prevention(struct iwl_mld *mld,
 124					   struct iwl_mld_vif *mld_vif,
 125					   enum iwl_mld_emlsr_exit reason)
 126{
 127	unsigned long delay;
 128
 129	/*
 130	 * Reset the counter if more than 400 seconds have passed between one
 131	 * exit and the other, or if we exited due to a different reason.
 132	 * Will also reset the counter after the long prevention is done.
 133	 */
 134	if (time_after(jiffies, mld_vif->emlsr.last_exit_ts +
 135				IWL_MLD_PREVENT_EMLSR_TIMEOUT) ||
 136	    mld_vif->emlsr.last_exit_reason != reason)
 137		mld_vif->emlsr.exit_repeat_count = 0;
 138
 139	mld_vif->emlsr.last_exit_reason = reason;
 140	mld_vif->emlsr.last_exit_ts = jiffies;
 141	mld_vif->emlsr.exit_repeat_count++;
 142
 143	/*
 144	 * Do not add a prevention when the reason was a block. For a block,
 145	 * EMLSR will be enabled again on unblock.
 146	 */
 147	if (reason == IWL_MLD_EMLSR_EXIT_BLOCK)
 148		return;
 149
 150	/* Set prevention for a minimum of 30 seconds */
 151	mld_vif->emlsr.blocked_reasons |= IWL_MLD_EMLSR_BLOCKED_PREVENTION;
 152	delay = IWL_MLD_TRIGGER_LINK_SEL_TIME;
 153
 154	/* Handle repeats for reasons that can cause long prevention */
 155	if (mld_vif->emlsr.exit_repeat_count > 1 &&
 156	    reason & IWL_MLD_PREVENT_EMLSR_REASONS) {
 157		if (mld_vif->emlsr.exit_repeat_count == 2)
 158			delay = IWL_MLD_EMLSR_PREVENT_SHORT;
 159		else
 160			delay = IWL_MLD_EMLSR_PREVENT_LONG;
 161
 162		/*
 163		 * The timeouts are chosen so that this will not happen, i.e.
 164		 * IWL_MLD_EMLSR_PREVENT_LONG > IWL_MLD_PREVENT_EMLSR_TIMEOUT
 165		 */
 166		WARN_ON(mld_vif->emlsr.exit_repeat_count > 3);
 167	}
 168
 169	IWL_DEBUG_EHT(mld,
 170		      "Preventing EMLSR for %ld seconds due to %u exits with the reason = %s (0x%x)\n",
 171		      delay / HZ, mld_vif->emlsr.exit_repeat_count,
 172		      iwl_mld_get_emlsr_exit_string(reason), reason);
 173
 174	wiphy_delayed_work_queue(mld->wiphy,
 175				 &mld_vif->emlsr.prevent_done_wk, delay);
 176}
 177
 178static void iwl_mld_clear_avg_chan_load_iter(struct ieee80211_hw *hw,
 179					     struct ieee80211_chanctx_conf *ctx,
 180					     void *dat)
 181{
 182	struct iwl_mld_phy *phy = iwl_mld_phy_from_mac80211(ctx);
 183
 184	/* It is ok to do it for all chanctx (and not only for the ones that
 185	 * belong to the EMLSR vif) since EMLSR is not allowed if there is
 186	 * another vif.
 187	 */
 188	phy->avg_channel_load_not_by_us = 0;
 189}
 190
 191static int _iwl_mld_exit_emlsr(struct iwl_mld *mld, struct ieee80211_vif *vif,
 192			       enum iwl_mld_emlsr_exit exit, u8 link_to_keep,
 193			       bool sync)
 194{
 195	struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
 196	u16 new_active_links;
 197	int ret = 0;
 198
 199	lockdep_assert_wiphy(mld->wiphy);
 200
 201	/* On entry failure need to exit anyway, even if entered from debugfs */
 202	if (exit != IWL_MLD_EMLSR_EXIT_FAIL_ENTRY && !IWL_MLD_AUTO_EML_ENABLE)
 203		return 0;
 204
 205	/* Ignore exit request if EMLSR is not active */
 206	if (!iwl_mld_emlsr_active(vif))
 207		return 0;
 208
 209	if (WARN_ON(!ieee80211_vif_is_mld(vif) || !mld_vif->authorized))
 210		return 0;
 211
 212	if (WARN_ON(!(vif->active_links & BIT(link_to_keep))))
 213		link_to_keep = __ffs(vif->active_links);
 214
 215	new_active_links = BIT(link_to_keep);
 216	IWL_DEBUG_EHT(mld,
 217		      "Exiting EMLSR. reason = %s (0x%x). Current active links=0x%x, new active links = 0x%x\n",
 218		      iwl_mld_get_emlsr_exit_string(exit), exit,
 219		      vif->active_links, new_active_links);
 220
 221	if (sync)
 222		ret = ieee80211_set_active_links(vif, new_active_links);
 223	else
 224		ieee80211_set_active_links_async(vif, new_active_links);
 225
 226	/* Update latest exit reason and check EMLSR prevention */
 227	iwl_mld_check_emlsr_prevention(mld, mld_vif, exit);
 228
 229	/* channel_load_not_by_us is invalid when in EMLSR.
 230	 * Clear it so wrong values won't be used.
 231	 */
 232	ieee80211_iter_chan_contexts_atomic(mld->hw,
 233					    iwl_mld_clear_avg_chan_load_iter,
 234					    NULL);
 235
 236	return ret;
 237}
 238
 239void iwl_mld_exit_emlsr(struct iwl_mld *mld, struct ieee80211_vif *vif,
 240			enum iwl_mld_emlsr_exit exit, u8 link_to_keep)
 241{
 242	_iwl_mld_exit_emlsr(mld, vif, exit, link_to_keep, false);
 243}
 244
 245static int _iwl_mld_emlsr_block(struct iwl_mld *mld, struct ieee80211_vif *vif,
 246				enum iwl_mld_emlsr_blocked reason,
 247				u8 link_to_keep, bool sync)
 248{
 249	struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
 250
 251	lockdep_assert_wiphy(mld->wiphy);
 252
 253	if (!IWL_MLD_AUTO_EML_ENABLE || !iwl_mld_vif_has_emlsr_cap(vif))
 254		return 0;
 255
 256	if (mld_vif->emlsr.blocked_reasons & reason)
 257		return 0;
 258
 259	mld_vif->emlsr.blocked_reasons |= reason;
 260
 261	IWL_DEBUG_EHT(mld, "Blocking EMLSR mode. reason = %s (0x%x)\n",
 262		      iwl_mld_get_emlsr_blocked_string(reason), reason);
 263	iwl_mld_print_emlsr_blocked(mld, mld_vif->emlsr.blocked_reasons);
 264
 265	if (reason == IWL_MLD_EMLSR_BLOCKED_TPT)
 266		wiphy_delayed_work_cancel(mld_vif->mld->wiphy,
 267					  &mld_vif->emlsr.check_tpt_wk);
 268
 269	return _iwl_mld_exit_emlsr(mld, vif, IWL_MLD_EMLSR_EXIT_BLOCK,
 270				   link_to_keep, sync);
 271}
 272
 273void iwl_mld_block_emlsr(struct iwl_mld *mld, struct ieee80211_vif *vif,
 274			 enum iwl_mld_emlsr_blocked reason, u8 link_to_keep)
 275{
 276	_iwl_mld_emlsr_block(mld, vif, reason, link_to_keep, false);
 277}
 278
 279int iwl_mld_block_emlsr_sync(struct iwl_mld *mld, struct ieee80211_vif *vif,
 280			     enum iwl_mld_emlsr_blocked reason, u8 link_to_keep)
 281{
 282	return _iwl_mld_emlsr_block(mld, vif, reason, link_to_keep, true);
 283}
 284
 285#define IWL_MLD_EMLSR_BLOCKED_TMP_NON_BSS_TIMEOUT (10 * HZ)
 286
 287static void iwl_mld_vif_iter_emlsr_block_tmp_non_bss(void *_data, u8 *mac,
 288						     struct ieee80211_vif *vif)
 289{
 290	struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
 291	int ret;
 292
 293	if (!iwl_mld_vif_has_emlsr_cap(vif))
 294		return;
 295
 296	ret = iwl_mld_block_emlsr_sync(mld_vif->mld, vif,
 297				       IWL_MLD_EMLSR_BLOCKED_TMP_NON_BSS,
 298				       iwl_mld_get_primary_link(vif));
 299	if (ret)
 300		return;
 301
 302	wiphy_delayed_work_queue(mld_vif->mld->wiphy,
 303				 &mld_vif->emlsr.tmp_non_bss_done_wk,
 304				 IWL_MLD_EMLSR_BLOCKED_TMP_NON_BSS_TIMEOUT);
 305}
 306
 307void iwl_mld_emlsr_block_tmp_non_bss(struct iwl_mld *mld)
 308{
 309	ieee80211_iterate_active_interfaces_mtx(mld->hw,
 310						IEEE80211_IFACE_ITER_NORMAL,
 311						iwl_mld_vif_iter_emlsr_block_tmp_non_bss,
 312						NULL);
 313}
 314
 315static void _iwl_mld_select_links(struct iwl_mld *mld,
 316				  struct ieee80211_vif *vif);
 317
 318void iwl_mld_unblock_emlsr(struct iwl_mld *mld, struct ieee80211_vif *vif,
 319			   enum iwl_mld_emlsr_blocked reason)
 320{
 321	struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
 322
 323	lockdep_assert_wiphy(mld->wiphy);
 324
 325	if (!IWL_MLD_AUTO_EML_ENABLE || !iwl_mld_vif_has_emlsr_cap(vif))
 326		return;
 327
 328	if (!(mld_vif->emlsr.blocked_reasons & reason))
 329		return;
 330
 331	mld_vif->emlsr.blocked_reasons &= ~reason;
 332
 333	IWL_DEBUG_EHT(mld, "Unblocking EMLSR mode. reason = %s (0x%x)\n",
 334		      iwl_mld_get_emlsr_blocked_string(reason), reason);
 335	iwl_mld_print_emlsr_blocked(mld, mld_vif->emlsr.blocked_reasons);
 336
 337	if (reason == IWL_MLD_EMLSR_BLOCKED_TPT)
 338		wiphy_delayed_work_queue(mld_vif->mld->wiphy,
 339					 &mld_vif->emlsr.check_tpt_wk,
 340					 round_jiffies_relative(IWL_MLD_TPT_COUNT_WINDOW));
 341
 342	if (mld_vif->emlsr.blocked_reasons)
 343		return;
 344
 345	IWL_DEBUG_EHT(mld, "EMLSR is unblocked\n");
 346	iwl_mld_int_mlo_scan(mld, vif);
 347}
 348
 349static void
 350iwl_mld_vif_iter_emlsr_mode_notif(void *data, u8 *mac,
 351				  struct ieee80211_vif *vif)
 352{
 353	const struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
 354	const struct iwl_esr_mode_notif *notif = (void *)data;
 355	enum iwl_mvm_fw_esr_recommendation action = le32_to_cpu(notif->action);
 356
 357	if (!iwl_mld_vif_has_emlsr_cap(vif))
 358		return;
 359
 360	switch (action) {
 361	case ESR_RECOMMEND_LEAVE:
 362		IWL_DEBUG_EHT(mld_vif->mld,
 363			      "FW recommend leave reason = 0x%x\n",
 364			      le32_to_cpu(notif->leave_reason_mask));
 365
 366		iwl_mld_exit_emlsr(mld_vif->mld, vif,
 367				   IWL_MLD_EMLSR_EXIT_FW_REQUEST,
 368				   iwl_mld_get_primary_link(vif));
 369		break;
 370	case ESR_FORCE_LEAVE:
 371		IWL_DEBUG_EHT(mld_vif->mld, "FW force leave reason = 0x%x\n",
 372			      le32_to_cpu(notif->leave_reason_mask));
 373		fallthrough;
 374	case ESR_RECOMMEND_ENTER:
 375	default:
 376		IWL_WARN(mld_vif->mld, "Unexpected EMLSR notification: %d\n",
 377			 action);
 378	}
 379}
 380
 381void iwl_mld_handle_emlsr_mode_notif(struct iwl_mld *mld,
 382				     struct iwl_rx_packet *pkt)
 383{
 384	ieee80211_iterate_active_interfaces_mtx(mld->hw,
 385						IEEE80211_IFACE_ITER_NORMAL,
 386						iwl_mld_vif_iter_emlsr_mode_notif,
 387						pkt->data);
 388}
 389
 390static void
 391iwl_mld_vif_iter_disconnect_emlsr(void *data, u8 *mac,
 392				  struct ieee80211_vif *vif)
 393{
 394	if (!iwl_mld_vif_has_emlsr_cap(vif))
 395		return;
 396
 397	ieee80211_connection_loss(vif);
 398}
 399
 400void iwl_mld_handle_emlsr_trans_fail_notif(struct iwl_mld *mld,
 401					   struct iwl_rx_packet *pkt)
 402{
 403	const struct iwl_esr_trans_fail_notif *notif = (const void *)pkt->data;
 404	u32 fw_link_id = le32_to_cpu(notif->link_id);
 405	struct ieee80211_bss_conf *bss_conf =
 406		iwl_mld_fw_id_to_link_conf(mld, fw_link_id);
 407
 408	IWL_DEBUG_EHT(mld,
 409		      "Failed to %s EMLSR on link %d (FW: %d), reason %d\n",
 410		      le32_to_cpu(notif->activation) ? "enter" : "exit",
 411		      bss_conf ? bss_conf->link_id : -1,
 412		      le32_to_cpu(notif->link_id),
 413		      le32_to_cpu(notif->err_code));
 414
 415	if (IWL_FW_CHECK(mld, !bss_conf,
 416			 "FW reported failure to %sactivate EMLSR on a non-existing link: %d\n",
 417			 le32_to_cpu(notif->activation) ? "" : "de",
 418			 fw_link_id)) {
 419		ieee80211_iterate_active_interfaces_mtx(
 420			mld->hw, IEEE80211_IFACE_ITER_NORMAL,
 421			iwl_mld_vif_iter_disconnect_emlsr, NULL);
 422		return;
 423	}
 424
 425	/* Disconnect if we failed to deactivate a link */
 426	if (!le32_to_cpu(notif->activation)) {
 427		ieee80211_connection_loss(bss_conf->vif);
 428		return;
 429	}
 430
 431	/*
 432	 * We failed to activate the second link, go back to the link specified
 433	 * by the firmware as that is the one that is still valid now.
 434	 */
 435	iwl_mld_exit_emlsr(mld, bss_conf->vif, IWL_MLD_EMLSR_EXIT_FAIL_ENTRY,
 436			   bss_conf->link_id);
 437}
 438
 439/* Active non-station link tracking */
 440static void iwl_mld_count_non_bss_links(void *_data, u8 *mac,
 441					struct ieee80211_vif *vif)
 442{
 443	struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
 444	int *count = _data;
 445
 446	if (ieee80211_vif_type_p2p(vif) == NL80211_IFTYPE_STATION)
 447		return;
 448
 449	*count += iwl_mld_count_active_links(mld_vif->mld, vif);
 450}
 451
 452struct iwl_mld_update_emlsr_block_data {
 453	bool block;
 454	int result;
 455};
 456
 457static void
 458iwl_mld_vif_iter_update_emlsr_non_bss_block(void *_data, u8 *mac,
 459					    struct ieee80211_vif *vif)
 460{
 461	struct iwl_mld_update_emlsr_block_data *data = _data;
 462	struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
 463	int ret;
 464
 465	if (data->block) {
 466		ret = iwl_mld_block_emlsr_sync(mld_vif->mld, vif,
 467					       IWL_MLD_EMLSR_BLOCKED_NON_BSS,
 468					       iwl_mld_get_primary_link(vif));
 469		if (ret)
 470			data->result = ret;
 471	} else {
 472		iwl_mld_unblock_emlsr(mld_vif->mld, vif,
 473				      IWL_MLD_EMLSR_BLOCKED_NON_BSS);
 474	}
 475}
 476
 477int iwl_mld_emlsr_check_non_bss_block(struct iwl_mld *mld,
 478				      int pending_link_changes)
 479{
 480	/* An active link of a non-station vif blocks EMLSR. Upon activation
 481	 * block EMLSR on the bss vif. Upon deactivation, check if this link
 482	 * was the last non-station link active, and if so unblock the bss vif
 483	 */
 484	struct iwl_mld_update_emlsr_block_data block_data = {};
 485	int count = pending_link_changes;
 486
 487	/* No need to count if we are activating a non-BSS link */
 488	if (count <= 0)
 489		ieee80211_iterate_active_interfaces_mtx(mld->hw,
 490							IEEE80211_IFACE_ITER_NORMAL,
 491							iwl_mld_count_non_bss_links,
 492							&count);
 493
 494	/*
 495	 * We could skip updating it if the block change did not change (and
 496	 * pending_link_changes is non-zero).
 497	 */
 498	block_data.block = !!count;
 499
 500	ieee80211_iterate_active_interfaces_mtx(mld->hw,
 501						IEEE80211_IFACE_ITER_NORMAL,
 502						iwl_mld_vif_iter_update_emlsr_non_bss_block,
 503						&block_data);
 504
 505	return block_data.result;
 506}
 507
 508#define EMLSR_SEC_LINK_MIN_PERC 10
 509#define EMLSR_MIN_TX 3000
 510#define EMLSR_MIN_RX 400
 511
 512void iwl_mld_emlsr_check_tpt(struct wiphy *wiphy, struct wiphy_work *wk)
 513{
 514	struct iwl_mld_vif *mld_vif = container_of(wk, struct iwl_mld_vif,
 515						   emlsr.check_tpt_wk.work);
 516	struct ieee80211_vif *vif =
 517		container_of((void *)mld_vif, struct ieee80211_vif, drv_priv);
 518	struct iwl_mld *mld = mld_vif->mld;
 519	struct iwl_mld_sta *mld_sta;
 520	struct iwl_mld_link *sec_link;
 521	unsigned long total_tx = 0, total_rx = 0;
 522	unsigned long sec_link_tx = 0, sec_link_rx = 0;
 523	u8 sec_link_tx_perc, sec_link_rx_perc;
 524	s8 sec_link_id;
 525
 526	if (!iwl_mld_vif_has_emlsr_cap(vif) || !mld_vif->ap_sta)
 527		return;
 528
 529	mld_sta = iwl_mld_sta_from_mac80211(mld_vif->ap_sta);
 530
 531	/* We only count for the AP sta in a MLO connection */
 532	if (!mld_sta->mpdu_counters)
 533		return;
 534
 535	/* This wk should only run when the TPT blocker isn't set.
 536	 * When the blocker is set, the decision to remove it, as well as
 537	 * clearing the counters is done in DP (to avoid having a wk every
 538	 * 5 seconds when idle. When the blocker is unset, we are not idle anyway)
 539	 */
 540	if (WARN_ON(mld_vif->emlsr.blocked_reasons & IWL_MLD_EMLSR_BLOCKED_TPT))
 541		return;
 542	/*
 543	 * TPT is unblocked, need to check if the TPT criteria is still met.
 544	 *
 545	 * If EMLSR is active for at least 5 seconds, then we also
 546	 * need to check the secondary link requirements.
 547	 */
 548	if (iwl_mld_emlsr_active(vif) &&
 549	    time_is_before_jiffies(mld_vif->emlsr.last_entry_ts +
 550				   IWL_MLD_TPT_COUNT_WINDOW)) {
 551		sec_link_id = iwl_mld_get_other_link(vif, iwl_mld_get_primary_link(vif));
 552		sec_link = iwl_mld_link_dereference_check(mld_vif, sec_link_id);
 553		if (WARN_ON_ONCE(!sec_link))
 554			return;
 555		/* We need the FW ID here */
 556		sec_link_id = sec_link->fw_id;
 557	} else {
 558		sec_link_id = -1;
 559	}
 560
 561	/* Sum up RX and TX MPDUs from the different queues/links */
 562	for (int q = 0; q < mld->trans->info.num_rxqs; q++) {
 563		struct iwl_mld_per_q_mpdu_counter *queue_counter =
 564			&mld_sta->mpdu_counters[q];
 565
 566		spin_lock_bh(&queue_counter->lock);
 567
 568		/* The link IDs that doesn't exist will contain 0 */
 569		for (int link = 0;
 570		     link < ARRAY_SIZE(queue_counter->per_link);
 571		     link++) {
 572			total_tx += queue_counter->per_link[link].tx;
 573			total_rx += queue_counter->per_link[link].rx;
 574		}
 575
 576		if (sec_link_id != -1) {
 577			sec_link_tx += queue_counter->per_link[sec_link_id].tx;
 578			sec_link_rx += queue_counter->per_link[sec_link_id].rx;
 579		}
 580
 581		memset(queue_counter->per_link, 0,
 582		       sizeof(queue_counter->per_link));
 583
 584		spin_unlock_bh(&queue_counter->lock);
 585	}
 586
 587	IWL_DEBUG_EHT(mld, "total Tx MPDUs: %ld. total Rx MPDUs: %ld\n",
 588		      total_tx, total_rx);
 589
 590	/* If we don't have enough MPDUs - exit EMLSR */
 591	if (total_tx < IWL_MLD_ENTER_EMLSR_TPT_THRESH &&
 592	    total_rx < IWL_MLD_ENTER_EMLSR_TPT_THRESH) {
 593		iwl_mld_block_emlsr(mld, vif, IWL_MLD_EMLSR_BLOCKED_TPT,
 594				    iwl_mld_get_primary_link(vif));
 595		return;
 596	}
 597
 598	/* EMLSR is not active */
 599	if (sec_link_id == -1)
 600		goto schedule;
 601
 602	IWL_DEBUG_EHT(mld, "Secondary Link %d: Tx MPDUs: %ld. Rx MPDUs: %ld\n",
 603		      sec_link_id, sec_link_tx, sec_link_rx);
 604
 605	/* Calculate the percentage of the secondary link TX/RX */
 606	sec_link_tx_perc = total_tx ? sec_link_tx * 100 / total_tx : 0;
 607	sec_link_rx_perc = total_rx ? sec_link_rx * 100 / total_rx : 0;
 608
 609	/*
 610	 * The TX/RX percentage is checked only if it exceeds the required
 611	 * minimum. In addition, RX is checked only if the TX check failed.
 612	 */
 613	if ((total_tx > EMLSR_MIN_TX &&
 614	     sec_link_tx_perc < EMLSR_SEC_LINK_MIN_PERC) ||
 615	    (total_rx > EMLSR_MIN_RX &&
 616	     sec_link_rx_perc < EMLSR_SEC_LINK_MIN_PERC)) {
 617		iwl_mld_exit_emlsr(mld, vif, IWL_MLD_EMLSR_EXIT_LINK_USAGE,
 618				   iwl_mld_get_primary_link(vif));
 619		return;
 620	}
 621
 622schedule:
 623	/* Check again when the next window ends  */
 624	wiphy_delayed_work_queue(mld_vif->mld->wiphy,
 625				 &mld_vif->emlsr.check_tpt_wk,
 626				 round_jiffies_relative(IWL_MLD_TPT_COUNT_WINDOW));
 627}
 628
 629void iwl_mld_emlsr_unblock_tpt_wk(struct wiphy *wiphy, struct wiphy_work *wk)
 630{
 631	struct iwl_mld_vif *mld_vif = container_of(wk, struct iwl_mld_vif,
 632						   emlsr.unblock_tpt_wk);
 633	struct ieee80211_vif *vif =
 634		container_of((void *)mld_vif, struct ieee80211_vif, drv_priv);
 635
 636	iwl_mld_unblock_emlsr(mld_vif->mld, vif, IWL_MLD_EMLSR_BLOCKED_TPT);
 637}
 638
 639/*
 640 * Link selection
 641 */
 642
 643s8 iwl_mld_get_emlsr_rssi_thresh(struct iwl_mld *mld,
 644				 const struct cfg80211_chan_def *chandef,
 645				 bool low)
 646{
 647	if (WARN_ON(chandef->chan->band != NL80211_BAND_2GHZ &&
 648		    chandef->chan->band != NL80211_BAND_5GHZ &&
 649		    chandef->chan->band != NL80211_BAND_6GHZ))
 650		return S8_MAX;
 651
 652#define RSSI_THRESHOLD(_low, _bw)			\
 653	(_low) ? IWL_MLD_LOW_RSSI_THRESH_##_bw##MHZ	\
 654	       : IWL_MLD_HIGH_RSSI_THRESH_##_bw##MHZ
 655
 656	switch (chandef->width) {
 657	case NL80211_CHAN_WIDTH_20_NOHT:
 658	case NL80211_CHAN_WIDTH_20:
 659	/* 320 MHz has the same thresholds as 20 MHz */
 660	case NL80211_CHAN_WIDTH_320:
 661		return RSSI_THRESHOLD(low, 20);
 662	case NL80211_CHAN_WIDTH_40:
 663		return RSSI_THRESHOLD(low, 40);
 664	case NL80211_CHAN_WIDTH_80:
 665		return RSSI_THRESHOLD(low, 80);
 666	case NL80211_CHAN_WIDTH_160:
 667		return RSSI_THRESHOLD(low, 160);
 668	default:
 669		WARN_ON(1);
 670		return S8_MAX;
 671	}
 672#undef RSSI_THRESHOLD
 673}
 674
 675static u32
 676iwl_mld_emlsr_disallowed_with_link(struct iwl_mld *mld,
 677				   struct ieee80211_vif *vif,
 678				   struct iwl_mld_link_sel_data *link,
 679				   bool primary)
 680{
 681	struct wiphy *wiphy = mld->wiphy;
 682	struct ieee80211_bss_conf *conf;
 683	u32 ret = 0;
 684
 685	conf = wiphy_dereference(wiphy, vif->link_conf[link->link_id]);
 686	if (WARN_ON_ONCE(!conf))
 687		return IWL_MLD_EMLSR_EXIT_INVALID;
 688
 689	if (link->chandef->chan->band == NL80211_BAND_2GHZ && mld->bt_is_active)
 690		ret |= IWL_MLD_EMLSR_EXIT_BT_COEX;
 691
 692	if (link->signal <
 693	    iwl_mld_get_emlsr_rssi_thresh(mld, link->chandef, false))
 694		ret |= IWL_MLD_EMLSR_EXIT_LOW_RSSI;
 695
 696	if (conf->csa_active)
 697		ret |= IWL_MLD_EMLSR_EXIT_CSA;
 698
 699	if (ret) {
 700		IWL_DEBUG_EHT(mld, "Link %d is not allowed for EMLSR as %s\n",
 701			      link->link_id, primary ? "primary" : "secondary");
 702		iwl_mld_print_emlsr_exit(mld, ret);
 703	}
 704
 705	return ret;
 706}
 707
 708static u8
 709iwl_mld_set_link_sel_data(struct iwl_mld *mld,
 710			  struct ieee80211_vif *vif,
 711			  struct iwl_mld_link_sel_data *data,
 712			  unsigned long usable_links,
 713			  u8 *best_link_idx)
 714{
 715	u8 n_data = 0;
 716	u16 max_grade = 0;
 717	unsigned long link_id;
 718
 719	for_each_set_bit(link_id, &usable_links, IEEE80211_MLD_MAX_NUM_LINKS) {
 720		struct ieee80211_bss_conf *link_conf =
 721			link_conf_dereference_protected(vif, link_id);
 722
 723		if (WARN_ON_ONCE(!link_conf))
 724			continue;
 725
 726		/* Ignore any BSS that was not seen in the last MLO scan */
 727		if (ktime_before(link_conf->bss->ts_boottime,
 728				 mld->scan.last_mlo_scan_start_time))
 729			continue;
 730
 731		data[n_data].link_id = link_id;
 732		data[n_data].chandef = &link_conf->chanreq.oper;
 733		data[n_data].signal = MBM_TO_DBM(link_conf->bss->signal);
 734		data[n_data].grade = iwl_mld_get_link_grade(mld, link_conf);
 735
 736		if (n_data == 0 || data[n_data].grade > max_grade) {
 737			max_grade = data[n_data].grade;
 738			*best_link_idx = n_data;
 739		}
 740		n_data++;
 741	}
 742
 743	return n_data;
 744}
 745
 746static u32
 747iwl_mld_get_min_chan_load_thresh(struct ieee80211_chanctx_conf *chanctx)
 748{
 749	const struct iwl_mld_phy *phy = iwl_mld_phy_from_mac80211(chanctx);
 750
 751	switch (phy->chandef.width) {
 752	case NL80211_CHAN_WIDTH_320:
 753	case NL80211_CHAN_WIDTH_160:
 754		return 5;
 755	case NL80211_CHAN_WIDTH_80:
 756		return 7;
 757	default:
 758		break;
 759	}
 760	return 10;
 761}
 762
 763static bool
 764iwl_mld_channel_load_allows_emlsr(struct iwl_mld *mld,
 765				  struct ieee80211_vif *vif,
 766				  const struct iwl_mld_link_sel_data *a,
 767				  const struct iwl_mld_link_sel_data *b)
 768{
 769	struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
 770	struct iwl_mld_link *link_a =
 771		iwl_mld_link_dereference_check(mld_vif, a->link_id);
 772	struct ieee80211_chanctx_conf *chanctx_a = NULL;
 773	u32 bw_a, bw_b, ratio;
 774	u32 primary_load_perc;
 775
 776	if (!link_a || !link_a->active) {
 777		IWL_DEBUG_EHT(mld, "Primary link is not active. Can't enter EMLSR\n");
 778		return false;
 779	}
 780
 781	chanctx_a = wiphy_dereference(mld->wiphy, link_a->chan_ctx);
 782
 783	if (WARN_ON(!chanctx_a))
 784		return false;
 785
 786	primary_load_perc =
 787		iwl_mld_phy_from_mac80211(chanctx_a)->avg_channel_load_not_by_us;
 788
 789	IWL_DEBUG_EHT(mld, "Average channel load not by us: %u\n", primary_load_perc);
 790
 791	if (primary_load_perc < iwl_mld_get_min_chan_load_thresh(chanctx_a)) {
 792		IWL_DEBUG_EHT(mld, "Channel load is below the minimum threshold\n");
 793		return false;
 794	}
 795
 796	if (iwl_mld_vif_low_latency(mld_vif)) {
 797		IWL_DEBUG_EHT(mld, "Low latency vif, EMLSR is allowed\n");
 798		return true;
 799	}
 800
 801	if (a->chandef->width <= b->chandef->width)
 802		return true;
 803
 804	bw_a = cfg80211_chandef_get_width(a->chandef);
 805	bw_b = cfg80211_chandef_get_width(b->chandef);
 806	ratio = bw_a / bw_b;
 807
 808	switch (ratio) {
 809	case 2:
 810		return primary_load_perc > 25;
 811	case 4:
 812		return primary_load_perc > 40;
 813	case 8:
 814	case 16:
 815		return primary_load_perc > 50;
 816	}
 817
 818	return false;
 819}
 820
 821VISIBLE_IF_IWLWIFI_KUNIT u32
 822iwl_mld_emlsr_pair_state(struct ieee80211_vif *vif,
 823			 struct iwl_mld_link_sel_data *a,
 824			 struct iwl_mld_link_sel_data *b)
 825{
 826	struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
 827	struct iwl_mld *mld = mld_vif->mld;
 828	u32 reason_mask = 0;
 829
 830	/* Per-link considerations */
 831	reason_mask = iwl_mld_emlsr_disallowed_with_link(mld, vif, a, true);
 832	if (reason_mask)
 833		return reason_mask;
 834
 835	reason_mask = iwl_mld_emlsr_disallowed_with_link(mld, vif, b, false);
 836	if (reason_mask)
 837		return reason_mask;
 838
 839	if (a->chandef->chan->band == b->chandef->chan->band) {
 840		const struct cfg80211_chan_def *c_low = a->chandef;
 841		const struct cfg80211_chan_def *c_high = b->chandef;
 842		u32 c_low_upper_edge, c_high_lower_edge;
 843
 844		if (c_low->chan->center_freq > c_high->chan->center_freq)
 845			swap(c_low, c_high);
 846
 847		c_low_upper_edge = c_low->center_freq1 +
 848				   cfg80211_chandef_get_width(c_low) / 2;
 849		c_high_lower_edge = c_high->center_freq1 -
 850				    cfg80211_chandef_get_width(c_high) / 2;
 851
 852		if (a->chandef->chan->band == NL80211_BAND_5GHZ &&
 853		    c_low_upper_edge <= 5330 && c_high_lower_edge >= 5490) {
 854			/* This case is fine - HW/FW can deal with it, there's
 855			 * enough separation between the two channels.
 856			 */
 857		} else {
 858			reason_mask |= IWL_MLD_EMLSR_EXIT_EQUAL_BAND;
 859		}
 860	}
 861	if (!iwl_mld_channel_load_allows_emlsr(mld, vif, a, b))
 862		reason_mask |= IWL_MLD_EMLSR_EXIT_CHAN_LOAD;
 863
 864	if (reason_mask) {
 865		IWL_DEBUG_EHT(mld,
 866			      "Links %d and %d are not a valid pair for EMLSR\n",
 867			      a->link_id, b->link_id);
 868		IWL_DEBUG_EHT(mld, "Links bandwidth are: %d and %d\n",
 869			      nl80211_chan_width_to_mhz(a->chandef->width),
 870			      nl80211_chan_width_to_mhz(b->chandef->width));
 871		iwl_mld_print_emlsr_exit(mld, reason_mask);
 872	}
 873
 874	return reason_mask;
 875}
 876EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_emlsr_pair_state);
 877
 878/* Calculation is done with fixed-point with a scaling factor of 1/256 */
 879#define SCALE_FACTOR 256
 880
 881/*
 882 * Returns the combined grade of two given links.
 883 * Returns 0 if EMLSR is not allowed with these 2 links.
 884 */
 885static
 886unsigned int iwl_mld_get_emlsr_grade(struct iwl_mld *mld,
 887				     struct ieee80211_vif *vif,
 888				     struct iwl_mld_link_sel_data *a,
 889				     struct iwl_mld_link_sel_data *b,
 890				     u8 *primary_id)
 891{
 892	struct ieee80211_bss_conf *primary_conf;
 893	struct wiphy *wiphy = ieee80211_vif_to_wdev(vif)->wiphy;
 894	unsigned int primary_load;
 895
 896	lockdep_assert_wiphy(wiphy);
 897
 898	/* a is always primary, b is always secondary */
 899	if (b->grade > a->grade)
 900		swap(a, b);
 901
 902	*primary_id = a->link_id;
 903
 904	if (iwl_mld_emlsr_pair_state(vif, a, b))
 905		return 0;
 906
 907	primary_conf = wiphy_dereference(wiphy, vif->link_conf[*primary_id]);
 908
 909	if (WARN_ON_ONCE(!primary_conf))
 910		return 0;
 911
 912	primary_load = iwl_mld_get_chan_load(mld, primary_conf);
 913
 914	/* The more the primary link is loaded, the more worthwhile EMLSR becomes */
 915	return a->grade + ((b->grade * primary_load) / SCALE_FACTOR);
 916}
 917
 918static void _iwl_mld_select_links(struct iwl_mld *mld,
 919				  struct ieee80211_vif *vif)
 920{
 921	struct iwl_mld_link_sel_data data[IEEE80211_MLD_MAX_NUM_LINKS];
 922	struct iwl_mld_link_sel_data *best_link;
 923	struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
 924	int max_active_links = iwl_mld_max_active_links(mld, vif);
 925	u16 new_active, usable_links = ieee80211_vif_usable_links(vif);
 926	u8 best_idx, new_primary, n_data;
 927	u16 max_grade;
 928
 929	lockdep_assert_wiphy(mld->wiphy);
 930
 931	if (!mld_vif->authorized || hweight16(usable_links) <= 1)
 932		return;
 933
 934	if (WARN(ktime_before(mld->scan.last_mlo_scan_start_time,
 935			      ktime_sub_ns(ktime_get_boottime_ns(),
 936					   5ULL * NSEC_PER_SEC)),
 937		"Last MLO scan was too long ago, can't select links\n"))
 938		return;
 939
 940	/* The logic below is simple and not suited for more than 2 links */
 941	WARN_ON_ONCE(max_active_links > 2);
 942
 943	n_data = iwl_mld_set_link_sel_data(mld, vif, data, usable_links,
 944					   &best_idx);
 945
 946	if (!n_data) {
 947		IWL_DEBUG_EHT(mld,
 948			      "Couldn't find a valid grade for any link!\n");
 949		return;
 950	}
 951
 952	/* Default to selecting the single best link */
 953	best_link = &data[best_idx];
 954	new_primary = best_link->link_id;
 955	new_active = BIT(best_link->link_id);
 956	max_grade = best_link->grade;
 957
 958	/* If EMLSR is not possible, activate the best link */
 959	if (max_active_links == 1 || n_data == 1 ||
 960	    !iwl_mld_vif_has_emlsr_cap(vif) || !IWL_MLD_AUTO_EML_ENABLE ||
 961	    mld_vif->emlsr.blocked_reasons)
 962		goto set_active;
 963
 964	/* Try to find the best link combination */
 965	for (u8 a = 0; a < n_data; a++) {
 966		for (u8 b = a + 1; b < n_data; b++) {
 967			u8 best_in_pair;
 968			u16 emlsr_grade =
 969				iwl_mld_get_emlsr_grade(mld, vif,
 970							&data[a], &data[b],
 971							&best_in_pair);
 972
 973			/*
 974			 * Prefer (new) EMLSR combination to prefer EMLSR over
 975			 * a single link.
 976			 */
 977			if (emlsr_grade < max_grade)
 978				continue;
 979
 980			max_grade = emlsr_grade;
 981			new_primary = best_in_pair;
 982			new_active = BIT(data[a].link_id) |
 983				     BIT(data[b].link_id);
 984		}
 985	}
 986
 987set_active:
 988	IWL_DEBUG_EHT(mld, "Link selection result: 0x%x. Primary = %d\n",
 989		      new_active, new_primary);
 990
 991	mld_vif->emlsr.selected_primary = new_primary;
 992	mld_vif->emlsr.selected_links = new_active;
 993
 994	ieee80211_set_active_links_async(vif, new_active);
 995}
 996
 997static void iwl_mld_vif_iter_select_links(void *_data, u8 *mac,
 998					  struct ieee80211_vif *vif)
 999{
1000	struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
1001	struct iwl_mld *mld = mld_vif->mld;
1002
1003	_iwl_mld_select_links(mld, vif);
1004}
1005
1006void iwl_mld_select_links(struct iwl_mld *mld)
1007{
1008	ieee80211_iterate_active_interfaces_mtx(mld->hw,
1009						IEEE80211_IFACE_ITER_NORMAL,
1010						iwl_mld_vif_iter_select_links,
1011						NULL);
1012}
1013
1014static void iwl_mld_emlsr_check_bt_iter(void *_data, u8 *mac,
1015					struct ieee80211_vif *vif)
1016{
1017	struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
1018	struct iwl_mld *mld = mld_vif->mld;
1019	struct ieee80211_bss_conf *link;
1020	unsigned int link_id;
1021
1022	if (!iwl_mld_vif_has_emlsr_cap(vif))
1023		return;
1024
1025	if (!mld->bt_is_active) {
1026		iwl_mld_retry_emlsr(mld, vif);
1027		return;
1028	}
1029
1030	/* BT is turned ON but we are not in EMLSR, nothing to do */
1031	if (!iwl_mld_emlsr_active(vif))
1032		return;
1033
1034	/* In EMLSR and BT is turned ON */
1035
1036	for_each_vif_active_link(vif, link, link_id) {
1037		if (WARN_ON(!link->chanreq.oper.chan))
1038			continue;
1039
1040		if (link->chanreq.oper.chan->band == NL80211_BAND_2GHZ) {
1041			iwl_mld_exit_emlsr(mld, vif, IWL_MLD_EMLSR_EXIT_BT_COEX,
1042					   iwl_mld_get_primary_link(vif));
1043			return;
1044		}
1045	}
1046}
1047
1048void iwl_mld_emlsr_check_bt(struct iwl_mld *mld)
1049{
1050	ieee80211_iterate_active_interfaces_mtx(mld->hw,
1051						IEEE80211_IFACE_ITER_NORMAL,
1052						iwl_mld_emlsr_check_bt_iter,
1053						NULL);
1054}
1055
1056struct iwl_mld_chan_load_data {
1057	struct iwl_mld_phy *phy;
1058	u32 prev_chan_load_not_by_us;
1059};
1060
1061static void iwl_mld_chan_load_update_iter(void *_data, u8 *mac,
1062					  struct ieee80211_vif *vif)
1063{
1064	struct iwl_mld_chan_load_data *data = _data;
1065	const struct iwl_mld_phy *phy = data->phy;
1066	struct ieee80211_chanctx_conf *chanctx =
1067		container_of((const void *)phy, struct ieee80211_chanctx_conf,
1068			     drv_priv);
1069	struct iwl_mld *mld = iwl_mld_vif_from_mac80211(vif)->mld;
1070	struct ieee80211_bss_conf *prim_link;
1071	unsigned int prim_link_id;
1072
1073	prim_link_id = iwl_mld_get_primary_link(vif);
1074	prim_link = link_conf_dereference_protected(vif, prim_link_id);
1075
1076	if (WARN_ON(!prim_link))
1077		return;
1078
1079	if (chanctx != rcu_access_pointer(prim_link->chanctx_conf))
1080		return;
1081
1082	if (iwl_mld_emlsr_active(vif)) {
1083		int chan_load = iwl_mld_get_chan_load_by_others(mld, prim_link,
1084								true);
1085
1086		if (chan_load < 0)
1087			return;
1088
1089		/* chan_load is in range [0,255] */
1090		if (chan_load < NORMALIZE_PERCENT_TO_255(IWL_MLD_EXIT_EMLSR_CHAN_LOAD))
1091			iwl_mld_exit_emlsr(mld, vif,
1092					   IWL_MLD_EMLSR_EXIT_CHAN_LOAD,
1093					   prim_link_id);
1094	} else {
1095		u32 old_chan_load = data->prev_chan_load_not_by_us;
1096		u32 new_chan_load = phy->avg_channel_load_not_by_us;
1097		u32 min_thresh = iwl_mld_get_min_chan_load_thresh(chanctx);
1098
1099#define THRESHOLD_CROSSED(threshold) \
1100	(old_chan_load <= (threshold) && new_chan_load > (threshold))
1101
1102		if (THRESHOLD_CROSSED(min_thresh) || THRESHOLD_CROSSED(25) ||
1103		    THRESHOLD_CROSSED(40) || THRESHOLD_CROSSED(50))
1104			iwl_mld_retry_emlsr(mld, vif);
1105#undef THRESHOLD_CROSSED
1106	}
1107}
1108
1109void iwl_mld_emlsr_check_chan_load(struct ieee80211_hw *hw,
1110				   struct iwl_mld_phy *phy,
1111				   u32 prev_chan_load_not_by_us)
1112{
1113	struct iwl_mld_chan_load_data data = {
1114		.phy = phy,
1115		.prev_chan_load_not_by_us = prev_chan_load_not_by_us,
1116	};
1117
1118	ieee80211_iterate_active_interfaces_mtx(hw,
1119						IEEE80211_IFACE_ITER_NORMAL,
1120						iwl_mld_chan_load_update_iter,
1121						&data);
1122}
1123
1124void iwl_mld_retry_emlsr(struct iwl_mld *mld, struct ieee80211_vif *vif)
1125{
1126	struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
1127
1128	if (!IWL_MLD_AUTO_EML_ENABLE || !iwl_mld_vif_has_emlsr_cap(vif) ||
1129	    iwl_mld_emlsr_active(vif) || mld_vif->emlsr.blocked_reasons)
1130		return;
1131
1132	iwl_mld_int_mlo_scan(mld, vif);
1133}
1134
1135static void iwl_mld_ignore_tpt_iter(void *data, u8 *mac,
1136				    struct ieee80211_vif *vif)
1137{
1138	struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
1139	struct iwl_mld *mld = mld_vif->mld;
1140	struct iwl_mld_sta *mld_sta;
1141	bool *start = (void *)data;
1142
1143	/* check_tpt_wk is only used when TPT block isn't set */
1144	if (mld_vif->emlsr.blocked_reasons & IWL_MLD_EMLSR_BLOCKED_TPT ||
1145	    !IWL_MLD_AUTO_EML_ENABLE || !mld_vif->ap_sta)
1146		return;
1147
1148	mld_sta = iwl_mld_sta_from_mac80211(mld_vif->ap_sta);
1149
1150	/* We only count for the AP sta in a MLO connection */
1151	if (!mld_sta->mpdu_counters)
1152		return;
1153
1154	if (*start) {
1155		wiphy_delayed_work_cancel(mld_vif->mld->wiphy,
1156					  &mld_vif->emlsr.check_tpt_wk);
1157		IWL_DEBUG_EHT(mld, "TPT check disabled\n");
1158		return;
1159	}
1160
1161	/* Clear the counters so we start from the beginning */
1162	for (int q = 0; q < mld->trans->info.num_rxqs; q++) {
1163		struct iwl_mld_per_q_mpdu_counter *queue_counter =
1164			&mld_sta->mpdu_counters[q];
1165
1166		spin_lock_bh(&queue_counter->lock);
1167
1168		memset(queue_counter->per_link, 0,
1169		       sizeof(queue_counter->per_link));
1170
1171		spin_unlock_bh(&queue_counter->lock);
1172	}
1173
1174	/* Schedule the check in 5 seconds */
1175	wiphy_delayed_work_queue(mld_vif->mld->wiphy,
1176				 &mld_vif->emlsr.check_tpt_wk,
1177				 round_jiffies_relative(IWL_MLD_TPT_COUNT_WINDOW));
1178	IWL_DEBUG_EHT(mld, "TPT check enabled\n");
1179}
1180
1181void iwl_mld_start_ignoring_tpt_updates(struct iwl_mld *mld)
1182{
1183	bool start = true;
1184
1185	ieee80211_iterate_active_interfaces_mtx(mld->hw,
1186						IEEE80211_IFACE_ITER_NORMAL,
1187						iwl_mld_ignore_tpt_iter,
1188						&start);
1189}
1190
1191void iwl_mld_stop_ignoring_tpt_updates(struct iwl_mld *mld)
1192{
1193	bool start = false;
1194
1195	ieee80211_iterate_active_interfaces_mtx(mld->hw,
1196						IEEE80211_IFACE_ITER_NORMAL,
1197						iwl_mld_ignore_tpt_iter,
1198						&start);
1199}
~/ Read-only file preview.