Commit a1efeb82 authored by Yedidya Benshimol's avatar Yedidya Benshimol Committed by Johannes Berg
Browse files

wifi: iwlwifi: mvm: Block EMLSR when a p2p/softAP vif is active



When there's an active link in a non-station vif, the station vif is
not allowed to enter EMLSR

Note that blocking EMLSR by calling iwl_mvm_block_esr() we will schedule
an exit from EMLSR worker, but the worker cannot run before the
activation of the non-BSS link, as ieee80211_remain_on_channel already
holds the wiphy mutex.

Handle that by explicitly calling ieee80211_set_active_links()
to leave EMLSR, and then doing iwl_mvm_block_esr() only for
consistency and to avoid re-entering it before ready.

Note that a call to ieee80211_set_active_links requires to release the
mvm mutex, but that's ok since we still hold the wiphy lock. The only
thing that might race here is the ESR_MODE_NOTIF, so this changes its
handler to run under the wiphy lock.

Signed-off-by: default avatarYedidya Benshimol <yedidya.ben.shimol@intel.com>
Co-developed-by: default avatarMiri Korenblit <miriam.rachel.korenblit@intel.com>
Signed-off-by: default avatarMiri Korenblit <miriam.rachel.korenblit@intel.com>
Link: https://msgid.link/20240505091420.916193759f8a.Idf3a3caf5cdc3e69c81710b7ceb57e87f2de87e4@changeid


Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent eead3591
Loading
Loading
Loading
Loading
+5 −12
Original line number Diff line number Diff line
@@ -1243,7 +1243,7 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
		.data[0] = &d3_cfg_cmd_data,
		.len[0] = sizeof(d3_cfg_cmd_data),
	};
	int ret, primary_link;
	int ret;
	int len __maybe_unused;
	bool unified_image = fw_has_capa(&mvm->fw->ucode_capa,
					 IWL_UCODE_TLV_CAPA_CNSLDTD_D3_D0_IMG);
@@ -1261,18 +1261,11 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
	if (IS_ERR_OR_NULL(vif))
		return 1;

	primary_link = iwl_mvm_get_primary_link(vif);

	/* leave ESR immediately, not only async with iwl_mvm_block_esr() */
	if (ieee80211_vif_is_mld(vif)) {
		ret = ieee80211_set_active_links(vif, BIT(primary_link));
	ret = iwl_mvm_block_esr_sync(mvm, vif, IWL_MVM_ESR_BLOCKED_WOWLAN);
	if (ret)
		return ret;
	}

	mutex_lock(&mvm->mutex);
	/* only additionally block for consistency and to avoid concurrency */
	iwl_mvm_block_esr(mvm, vif, IWL_MVM_ESR_BLOCKED_WOWLAN, primary_link);

	set_bit(IWL_MVM_STATUS_IN_D3, &mvm->status);

@@ -1280,7 +1273,7 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,

	mvmvif = iwl_mvm_vif_from_mac80211(vif);

	mvm_link = mvmvif->link[primary_link];
	mvm_link = mvmvif->link[iwl_mvm_get_primary_link(vif)];
	if (WARN_ON_ONCE(!mvm_link)) {
		ret = -EINVAL;
		goto out_noreset;
+85 −0
Original line number Diff line number Diff line
@@ -108,6 +108,65 @@ int iwl_mvm_add_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
	return iwl_mvm_link_cmd_send(mvm, &cmd, FW_CTXT_ACTION_ADD);
}

struct iwl_mvm_esr_iter_data {
	struct ieee80211_vif *vif;
	unsigned int link_id;
	bool lift_block;
};

static void iwl_mvm_esr_vif_iterator(void *_data, u8 *mac,
				     struct ieee80211_vif *vif)
{
	struct iwl_mvm_esr_iter_data *data = _data;
	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
	int link_id;

	if (ieee80211_vif_type_p2p(vif) == NL80211_IFTYPE_STATION)
		return;

	for_each_mvm_vif_valid_link(mvmvif, link_id) {
		struct iwl_mvm_vif_link_info *link_info =
			mvmvif->link[link_id];
		if (vif == data->vif && link_id == data->link_id)
			continue;
		if (link_info->active)
			data->lift_block = false;
	}
}

int iwl_mvm_esr_non_bss_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
			     unsigned int link_id, bool active)
{
	/* An active link of a non-station vif blocks EMLSR. Upon activation
	 * block EMLSR on the bss vif. Upon deactivation, check if this link
	 * was the last non-station link active, and if so unblock the bss vif
	 */
	struct ieee80211_vif *bss_vif = iwl_mvm_get_bss_vif(mvm);
	struct iwl_mvm_esr_iter_data data = {
		.vif = vif,
		.link_id = link_id,
		.lift_block = true,
	};

	if (IS_ERR_OR_NULL(bss_vif))
		return 0;

	if (active)
		return iwl_mvm_block_esr_sync(mvm, bss_vif,
					      IWL_MVM_ESR_BLOCKED_NON_BSS);

	ieee80211_iterate_active_interfaces(mvm->hw,
					    IEEE80211_IFACE_ITER_NORMAL,
					    iwl_mvm_esr_vif_iterator, &data);
	if (data.lift_block) {
		mutex_lock(&mvm->mutex);
		iwl_mvm_unblock_esr(mvm, bss_vif, IWL_MVM_ESR_BLOCKED_NON_BSS);
		mutex_unlock(&mvm->mutex);
	}

	return 0;
}

int iwl_mvm_link_changed(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
			 struct ieee80211_bss_conf *link_conf,
			 u32 changes, bool active)
@@ -924,6 +983,32 @@ void iwl_mvm_block_esr(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
	iwl_mvm_exit_esr(mvm, vif, reason, link_to_keep);
}

int iwl_mvm_block_esr_sync(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
			   enum iwl_mvm_esr_state reason)
{
	int primary_link = iwl_mvm_get_primary_link(vif);
	int ret;

	if (!IWL_MVM_AUTO_EML_ENABLE || !ieee80211_vif_is_mld(vif))
		return 0;

	/* This should be called only with blocking reasons */
	if (WARN_ON(!(reason & IWL_MVM_BLOCK_ESR_REASONS)))
		return 0;

	/* leave ESR immediately, not only async with iwl_mvm_block_esr() */
	ret = ieee80211_set_active_links(vif, BIT(primary_link));
	if (ret)
		return ret;

	mutex_lock(&mvm->mutex);
	/* only additionally block for consistency and to avoid concurrency */
	iwl_mvm_block_esr(mvm, vif, reason, primary_link);
	mutex_unlock(&mvm->mutex);

	return 0;
}

static void iwl_mvm_esr_unblocked(struct iwl_mvm *mvm,
				  struct ieee80211_vif *vif)
{
+4 −2
Original line number Diff line number Diff line
@@ -4830,6 +4830,10 @@ int iwl_mvm_roc_common(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
	 */
	flush_work(&mvm->roc_done_wk);

	ret = iwl_mvm_esr_non_bss_link(mvm, vif, 0, true);
	if (ret)
		return ret;

	mutex_lock(&mvm->mutex);

	switch (vif->type) {
@@ -4873,9 +4877,7 @@ int iwl_mvm_cancel_roc(struct ieee80211_hw *hw,

	IWL_DEBUG_MAC80211(mvm, "enter\n");

	mutex_lock(&mvm->mutex);
	iwl_mvm_stop_roc(mvm, vif);
	mutex_unlock(&mvm->mutex);

	IWL_DEBUG_MAC80211(mvm, "leave\n");
	return 0;
+16 −0
Original line number Diff line number Diff line
@@ -391,6 +391,18 @@ static int iwl_mvm_mld_assign_vif_chanctx(struct ieee80211_hw *hw,
	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
	int ret;

	/* update EMLSR mode */
	if (ieee80211_vif_type_p2p(vif) != NL80211_IFTYPE_STATION) {
		ret = iwl_mvm_esr_non_bss_link(mvm, vif, link_conf->link_id,
					       true);
		/*
		 * Don't activate this link if failed to exit EMLSR in
		 * the BSS interface
		 */
		if (ret)
			return ret;
	}

	mutex_lock(&mvm->mutex);
	ret = __iwl_mvm_mld_assign_vif_chanctx(mvm, vif, link_conf, ctx, false);
	mutex_unlock(&mvm->mutex);
@@ -514,6 +526,10 @@ static void iwl_mvm_mld_unassign_vif_chanctx(struct ieee80211_hw *hw,
		iwl_mvm_add_link(mvm, vif, link_conf);
	}
	mutex_unlock(&mvm->mutex);

	/* update EMLSR mode */
	if (ieee80211_vif_type_p2p(vif) != NL80211_IFTYPE_STATION)
		iwl_mvm_esr_non_bss_link(mvm, vif, link_conf->link_id, false);
}

static void
+6 −0
Original line number Diff line number Diff line
@@ -358,6 +358,7 @@ struct iwl_mvm_vif_link_info {
 * @IWL_MVM_ESR_BLOCKED_WOWLAN: WOWLAN is preventing the enablement of EMLSR
 * @IWL_MVM_ESR_BLOCKED_TPT: block EMLSR when there is not enough traffic
 * @IWL_MVM_ESR_BLOCKED_FW: FW didn't recommended/forced exit from EMLSR
 * @IWL_MVM_ESR_BLOCKED_NON_BSS: An active non-bssid link's preventing EMLSR
 * @IWL_MVM_ESR_EXIT_MISSED_BEACON: exited EMLSR due to missed beacons
 * @IWL_MVM_ESR_EXIT_LOW_RSSI: link is deactivated/not allowed for EMLSR
 *	due to low RSSI.
@@ -372,6 +373,7 @@ enum iwl_mvm_esr_state {
	IWL_MVM_ESR_BLOCKED_WOWLAN	= 0x2,
	IWL_MVM_ESR_BLOCKED_TPT		= 0x4,
	IWL_MVM_ESR_BLOCKED_FW		= 0x8,
	IWL_MVM_ESR_BLOCKED_NON_BSS	= 0x10,
	IWL_MVM_ESR_EXIT_MISSED_BEACON	= 0x10000,
	IWL_MVM_ESR_EXIT_LOW_RSSI	= 0x20000,
	IWL_MVM_ESR_EXIT_COEX		= 0x40000,
@@ -2888,6 +2890,8 @@ bool iwl_mvm_vif_has_esr_cap(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
void iwl_mvm_block_esr(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
		       enum iwl_mvm_esr_state reason,
		       u8 link_to_keep);
int iwl_mvm_block_esr_sync(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
			   enum iwl_mvm_esr_state reason);
void iwl_mvm_unblock_esr(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
			 enum iwl_mvm_esr_state reason);
void iwl_mvm_exit_esr(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
@@ -2904,4 +2908,6 @@ iwl_mvm_bt_coex_calculate_esr_mode(struct iwl_mvm *mvm,
				   struct ieee80211_vif *vif,
				   s32 link_rssi,
				   bool primary);
int iwl_mvm_esr_non_bss_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
			     unsigned int link_id, bool active);
#endif /* __IWL_MVM_H__ */
Loading