Commit 2f876f91 authored by Johannes Berg's avatar Johannes Berg

wifi: iwlwifi: mvm: exit EMLSR when CSA happens

If CSA is happening, then exit EMLSR to keep the better link,
which is the primary link unless that's doing the CSA with
quiet. This is done because we can't transmit the OMN frame
on a quiet link, but want to exit EMLSR during CSA for better
beacon reception, so we can follow the switch accurately.
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
Signed-off-by: default avatarMiri Korenblit <miriam.rachel.korenblit@intel.com>
Link: https://msgid.link/20240505091420.3ffff9577f08.I2620971fa5aef789e0d4a588def4c2621e8bed5b@changeidSigned-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent ae7fe563
......@@ -597,9 +597,15 @@ iwl_mvm_esr_disallowed_with_link(struct ieee80211_vif *vif,
{
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
struct iwl_mvm *mvm = mvmvif->mvm;
struct wiphy *wiphy = mvm->hw->wiphy;
struct ieee80211_bss_conf *conf;
enum iwl_mvm_esr_state ret = 0;
s8 thresh;
conf = wiphy_dereference(wiphy, vif->link_conf[link->link_id]);
if (WARN_ON_ONCE(!conf))
return false;
/* BT Coex effects eSR mode only if one of the links is on LB */
if (link->chandef->chan->band == NL80211_BAND_2GHZ &&
(!iwl_mvm_bt_coex_calculate_esr_mode(mvm, vif, link->signal,
......@@ -612,6 +618,9 @@ iwl_mvm_esr_disallowed_with_link(struct ieee80211_vif *vif,
if (link->signal < thresh)
ret |= IWL_MVM_ESR_EXIT_LOW_RSSI;
if (conf->csa_active)
ret |= IWL_MVM_ESR_EXIT_CSA;
if (ret)
IWL_DEBUG_INFO(mvm,
"Link %d is not allowed for esr. Reason: 0x%x\n",
......
......@@ -5570,17 +5570,16 @@ static void iwl_mvm_csa_block_txqs(void *data, struct ieee80211_sta *sta)
}
#define IWL_MAX_CSA_BLOCK_TX 1500
int iwl_mvm_pre_channel_switch(struct ieee80211_hw *hw,
int iwl_mvm_pre_channel_switch(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
struct ieee80211_channel_switch *chsw)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
struct ieee80211_vif *csa_vif;
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
struct iwl_mvm_txq *mvmtxq;
int ret;
mutex_lock(&mvm->mutex);
lockdep_assert_held(&mvm->mutex);
mvmvif->csa_failed = false;
mvmvif->csa_blocks_tx = false;
......@@ -5598,25 +5597,19 @@ int iwl_mvm_pre_channel_switch(struct ieee80211_hw *hw,
rcu_dereference_protected(mvm->csa_vif,
lockdep_is_held(&mvm->mutex));
if (WARN_ONCE(csa_vif && csa_vif->bss_conf.csa_active,
"Another CSA is already in progress")) {
ret = -EBUSY;
goto out_unlock;
}
"Another CSA is already in progress"))
return -EBUSY;
/* we still didn't unblock tx. prevent new CS meanwhile */
if (rcu_dereference_protected(mvm->csa_tx_blocked_vif,
lockdep_is_held(&mvm->mutex))) {
ret = -EBUSY;
goto out_unlock;
}
lockdep_is_held(&mvm->mutex)))
return -EBUSY;
rcu_assign_pointer(mvm->csa_vif, vif);
if (WARN_ONCE(mvmvif->csa_countdown,
"Previous CSA countdown didn't complete")) {
ret = -EBUSY;
goto out_unlock;
}
"Previous CSA countdown didn't complete"))
return -EBUSY;
mvmvif->csa_target_freq = chsw->chandef.chan->center_freq;
......@@ -5650,10 +5643,8 @@ int iwl_mvm_pre_channel_switch(struct ieee80211_hw *hw,
* we don't know the dtim period. In this case, the firmware can't
* track the beacons.
*/
if (!vif->cfg.assoc || !vif->bss_conf.dtim_period) {
ret = -EBUSY;
goto out_unlock;
}
if (!vif->cfg.assoc || !vif->bss_conf.dtim_period)
return -EBUSY;
if (chsw->delay > IWL_MAX_CSA_BLOCK_TX &&
hweight16(vif->valid_links) <= 1)
......@@ -5675,7 +5666,7 @@ int iwl_mvm_pre_channel_switch(struct ieee80211_hw *hw,
IWL_UCODE_TLV_CAPA_CHANNEL_SWITCH_CMD)) {
ret = iwl_mvm_old_pre_chan_sw_sta(mvm, vif, chsw);
if (ret)
goto out_unlock;
return ret;
} else {
iwl_mvm_schedule_client_csa(mvm, vif, chsw);
}
......@@ -5691,12 +5682,23 @@ int iwl_mvm_pre_channel_switch(struct ieee80211_hw *hw,
ret = iwl_mvm_power_update_ps(mvm);
if (ret)
goto out_unlock;
return ret;
/* we won't be on this channel any longer */
iwl_mvm_teardown_tdls_peers(mvm);
out_unlock:
return ret;
}
static int iwl_mvm_mac_pre_channel_switch(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_channel_switch *chsw)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
int ret;
mutex_lock(&mvm->mutex);
ret = iwl_mvm_pre_channel_switch(mvm, vif, chsw);
mutex_unlock(&mvm->mutex);
return ret;
......@@ -6482,7 +6484,7 @@ const struct ieee80211_ops iwl_mvm_hw_ops = {
.set_tim = iwl_mvm_set_tim,
.channel_switch = iwl_mvm_channel_switch,
.pre_channel_switch = iwl_mvm_pre_channel_switch,
.pre_channel_switch = iwl_mvm_mac_pre_channel_switch,
.post_channel_switch = iwl_mvm_post_channel_switch,
.abort_channel_switch = iwl_mvm_abort_channel_switch,
.channel_switch_rx_beacon = iwl_mvm_channel_switch_rx_beacon,
......
......@@ -1290,6 +1290,45 @@ iwl_mvm_mld_can_neg_ttlm(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
return NEG_TTLM_RES_ACCEPT;
}
static int
iwl_mvm_mld_mac_pre_channel_switch(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_channel_switch *chsw)
{
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
int ret;
mutex_lock(&mvm->mutex);
if (mvmvif->esr_active) {
u8 primary = iwl_mvm_get_primary_link(vif);
int selected;
/* prefer primary unless quiet CSA on it */
if (chsw->link_id == primary && chsw->block_tx)
selected = iwl_mvm_get_other_link(vif, primary);
else
selected = primary;
iwl_mvm_exit_esr(mvm, vif, IWL_MVM_ESR_EXIT_CSA, selected);
mutex_unlock(&mvm->mutex);
/*
* If we've not kept the link active that's doing the CSA
* then we don't need to do anything else, just return.
*/
if (selected != chsw->link_id)
return 0;
mutex_lock(&mvm->mutex);
}
ret = iwl_mvm_pre_channel_switch(mvm, vif, chsw);
mutex_unlock(&mvm->mutex);
return ret;
}
const struct ieee80211_ops iwl_mvm_mld_hw_ops = {
.tx = iwl_mvm_mac_tx,
.wake_tx_queue = iwl_mvm_mac_wake_tx_queue,
......@@ -1343,7 +1382,7 @@ const struct ieee80211_ops iwl_mvm_mld_hw_ops = {
.tx_last_beacon = iwl_mvm_tx_last_beacon,
.channel_switch = iwl_mvm_channel_switch,
.pre_channel_switch = iwl_mvm_pre_channel_switch,
.pre_channel_switch = iwl_mvm_mld_mac_pre_channel_switch,
.post_channel_switch = iwl_mvm_post_channel_switch,
.abort_channel_switch = iwl_mvm_abort_channel_switch,
.channel_switch_rx_beacon = iwl_mvm_channel_switch_rx_beacon,
......
......@@ -365,6 +365,7 @@ struct iwl_mvm_vif_link_info {
* due to BT Coex.
* @IWL_MVM_ESR_EXIT_BANDWIDTH: Bandwidths of primary and secondry links
* preventing the enablement of EMLSR
* @IWL_MVM_ESR_EXIT_CSA: CSA happened, so exit EMLSR
*/
enum iwl_mvm_esr_state {
IWL_MVM_ESR_BLOCKED_PREVENTION = 0x1,
......@@ -375,6 +376,7 @@ enum iwl_mvm_esr_state {
IWL_MVM_ESR_EXIT_LOW_RSSI = 0x20000,
IWL_MVM_ESR_EXIT_COEX = 0x40000,
IWL_MVM_ESR_EXIT_BANDWIDTH = 0x80000,
IWL_MVM_ESR_EXIT_CSA = 0x100000,
};
#define IWL_MVM_BLOCK_ESR_REASONS 0xffff
......@@ -2818,7 +2820,7 @@ void iwl_mvm_change_chanctx(struct ieee80211_hw *hw,
int iwl_mvm_tx_last_beacon(struct ieee80211_hw *hw);
void iwl_mvm_channel_switch(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_channel_switch *chsw);
int iwl_mvm_pre_channel_switch(struct ieee80211_hw *hw,
int iwl_mvm_pre_channel_switch(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
struct ieee80211_channel_switch *chsw);
void iwl_mvm_abort_channel_switch(struct ieee80211_hw *hw,
......
......@@ -10,6 +10,14 @@
MODULE_IMPORT_NS(EXPORTED_FOR_KUNIT_TESTING);
static struct wiphy wiphy = {
.mtx = __MUTEX_INITIALIZER(wiphy.mtx),
};
static struct ieee80211_hw hw = {
.wiphy = &wiphy,
};
static struct ieee80211_channel chan_5ghz = {
.band = NL80211_BAND_5GHZ,
};
......@@ -50,7 +58,10 @@ static struct iwl_fw fw = {
},
};
static struct iwl_mvm mvm = {.fw = &fw};
static struct iwl_mvm mvm = {
.hw = &hw,
.fw = &fw,
};
static const struct link_grading_case {
const char *desc;
......@@ -237,6 +248,7 @@ static const struct valid_link_pair_case {
enum nl80211_chan_width cw_b;
s32 sig_a;
s32 sig_b;
bool csa_a;
bool valid;
} valid_link_pair_cases[] = {
{
......@@ -335,6 +347,17 @@ static const struct valid_link_pair_case {
.cw_b = NL80211_CHAN_WIDTH_160,
.valid = true,
},
{
.desc = "CSA active",
.chan_a = &chan_6ghz,
.cw_a = NL80211_CHAN_WIDTH_160,
.sig_a = -5,
.chan_b = &chan_5ghz,
.cw_b = NL80211_CHAN_WIDTH_160,
.valid = false,
/* same as previous entry with valid=true except for CSA */
.csa_a = true,
},
};
KUNIT_ARRAY_PARAM_DESC(valid_link_pair, valid_link_pair_cases, desc)
......@@ -358,6 +381,7 @@ static void test_valid_link_pair(struct kunit *test)
.link_id = 5,
.signal = params->sig_b,
};
struct ieee80211_bss_conf *conf;
bool result;
KUNIT_ASSERT_NOT_NULL(test, vif);
......@@ -377,7 +401,20 @@ static void test_valid_link_pair(struct kunit *test)
mvm.last_bt_notif.wifi_loss_low_rssi = params->bt;
mvmvif->mvm = &mvm;
conf = kunit_kzalloc(test, sizeof(*vif->link_conf[0]), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, conf);
conf->chanreq.oper = chandef_a;
conf->csa_active = params->csa_a;
vif->link_conf[link_a.link_id] = (void __rcu *)conf;
conf = kunit_kzalloc(test, sizeof(*vif->link_conf[0]), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, conf);
conf->chanreq.oper = chandef_b;
vif->link_conf[link_b.link_id] = (void __rcu *)conf;
wiphy_lock(&wiphy);
result = iwl_mvm_mld_valid_link_pair(vif, &link_a, &link_b);
wiphy_unlock(&wiphy);
KUNIT_EXPECT_EQ(test, result, params->valid);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment