/* Update SF - Disable if needed. if this fails, SF might still be on * while many macs are bound, which is forbidden - so fail the binding.
*/ if (iwl_mvm_sf_update(mvm, vif, false)) return -EINVAL;
int iwl_mvm_esr_non_bss_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif, unsignedint 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);
if (WARN_ON_ONCE(!link_info ||
link_info->fw_link_id == IWL_MVM_FW_LINK_ID_INVALID)) return -EINVAL;
if (changes & LINK_CONTEXT_MODIFY_ACTIVE) { /* When activating a link, phy context should be valid; * when deactivating a link, it also should be valid since * the link was active before. So, do nothing in this case. * Since a link is added first with FW_CTXT_INVALID, then we * can get here in case it's removed before it was activated.
*/ if (!link_info->phy_ctxt) return 0;
/* Catch early if driver tries to activate or deactivate a link * twice.
*/
WARN_ON_ONCE(active == link_info->active);
/* When deactivating a link session protection should * be stopped. Also let the firmware know if we can't Tx.
*/ if (!active && vif->type == NL80211_IFTYPE_STATION) {
iwl_mvm_stop_session_protection(mvm, vif); if (link_info->csa_block_tx) {
cmd.block_tx = 1;
link_info->csa_block_tx = false;
}
}
}
cmd.link_id = cpu_to_le32(link_info->fw_link_id);
/* The phy_id, link address and listen_lmac can be modified only until * the link becomes active, otherwise they will be ignored.
*/
phyctxt = link_info->phy_ctxt; if (phyctxt)
cmd.phy_id = cpu_to_le32(phyctxt->id); else
cmd.phy_id = cpu_to_le32(FW_CTXT_INVALID);
cmd.mac_id = cpu_to_le32(mvmvif->id);
/* The fw does not distinguish between ht and fat */
ht_flag = LINK_PROT_FLG_HT_PROT | LINK_PROT_FLG_FAT_PROT;
iwl_mvm_set_fw_protection_flags(mvm, vif, link_conf,
&cmd.protection_flags,
ht_flag, LINK_PROT_FLG_TGG_PROTECT);
ret = iwl_mvm_link_cmd_send(mvm, &cmd, FW_CTXT_ACTION_REMOVE);
if (!ret && iwl_mvm_sf_update(mvm, vif, true))
IWL_ERR(mvm, "Failed to update SF state\n");
return ret;
}
/* link should be deactivated before removal, so in most cases we need to * perform these two operations together
*/ int iwl_mvm_disable_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct ieee80211_bss_conf *link_conf)
{ int ret;
ret = iwl_mvm_link_changed(mvm, vif, link_conf,
LINK_CONTEXT_MODIFY_ACTIVE, false); if (ret) return ret;
ret = iwl_mvm_remove_link(mvm, vif, link_conf); if (ret) return ret;
/* No puncturing, no penalty */ if (mhz < 80) return SCALE_FACTOR;
/* total number of subchannels */
n_subchannels = mhz / 20; /* how many of these are punctured */
n_punctured = hweight16(link_conf->chanreq.oper.punctured);
/* If there isn't BSS Load element, take the defaults */ if (!bss_load_elem ||
bss_load_elem->datalen != sizeof(*bss_load)) {
rcu_read_unlock(); switch (band) { case NL80211_BAND_2GHZ:
chan_load = DEFAULT_CHAN_LOAD_LB; break; case NL80211_BAND_5GHZ:
chan_load = DEFAULT_CHAN_LOAD_HB; break; case NL80211_BAND_6GHZ:
chan_load = DEFAULT_CHAN_LOAD_UHB; break; default:
chan_load = 0; break;
} /* The defaults are given in percentage */ return NORMALIZE_PERCENT_TO_255(chan_load);
}
bss_load = (constvoid *)bss_load_elem->data; /* Channel util is in range 0-255 */
chan_load = bss_load->channel_util;
rcu_read_unlock();
if (!mvm_link || !mvm_link->active) return chan_load;
if (WARN_ONCE(!mvm_link->phy_ctxt, "Active link (%u) without phy ctxt assigned!\n",
link_conf->link_id)) return chan_load;
/* channel load by us is given in percentage */
chan_load_by_us =
NORMALIZE_PERCENT_TO_255(mvm_link->phy_ctxt->channel_load_by_us);
/* Use only values that firmware sends that can possibly be valid */ if (chan_load_by_us <= chan_load)
chan_load -= chan_load_by_us;
/* This function calculates the grade of a link. Returns 0 in error case */
VISIBLE_IF_IWLWIFI_KUNIT unsignedint iwl_mvm_get_link_grade(struct ieee80211_bss_conf *link_conf)
{ enum nl80211_band band; int i, rssi_idx;
s32 link_rssi; unsignedint grade = MAX_GRADE;
if (WARN_ON_ONCE(!link_conf)) return 0;
band = link_conf->chanreq.oper.chan->band; if (WARN_ONCE(band != NL80211_BAND_2GHZ &&
band != NL80211_BAND_5GHZ &&
band != NL80211_BAND_6GHZ, "Invalid band (%u)\n", band)) return 0;
link_rssi = MBM_TO_DBM(link_conf->bss->signal); /* * For 6 GHz the RSSI of the beacons is lower than * the RSSI of the data.
*/ if (band == NL80211_BAND_6GHZ)
link_rssi += 4;
rssi_idx = band == NL80211_BAND_2GHZ ? 0 : 1;
/* No valid RSSI - take the lowest grade */ if (!link_rssi)
link_rssi = rssi_to_grade_map[0].rssi[rssi_idx];
/* Get grade based on RSSI */ for (i = 0; i < ARRAY_SIZE(rssi_to_grade_map); i++) { conststruct iwl_mvm_rssi_to_grade *line =
&rssi_to_grade_map[i];
if (link_rssi > line->rssi[rssi_idx]) continue;
grade = line->grade; break;
}
conf = wiphy_dereference(wiphy, vif->link_conf[link->link_id]); if (WARN_ON_ONCE(!conf)) returnfalse;
/* 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,
primary)))
ret |= IWL_MVM_ESR_EXIT_COEX;
/* Per-link considerations */ if (iwl_mvm_esr_disallowed_with_link(mvm, vif, a, true) ||
iwl_mvm_esr_disallowed_with_link(mvm, vif, b, false)) returnfalse;
if (a->chandef->chan->band == b->chandef->chan->band ||
a->chandef->width != b->chandef->width)
ret |= IWL_MVM_ESR_EXIT_BANDWIDTH;
if (ret) {
IWL_DEBUG_INFO(mvm, "Links %d and %d are not a valid pair for EMLSR\n",
a->link_id, b->link_id);
iwl_mvm_print_esr_state(mvm, ret); returnfalse;
}
/* eSR is not supported/blocked, or only one usable link */ if (max_active_links == 1 || !iwl_mvm_vif_has_esr_cap(mvm, vif) ||
mvmvif->esr_disable_reason || n_data == 1) goto set_active;
for (u8 a = 0; a < n_data; a++) for (u8 b = a + 1; b < n_data; b++) {
u16 esr_grade = iwl_mvm_get_esr_grade(vif, &data[a],
&data[b],
&best_in_pair);
/* relevant data is written with both locks held, so read with either */
lockdep_assert(lockdep_is_held(&mvmvif->mvm->mutex) ||
lockdep_is_held(&mvmvif->mvm->hw->wiphy->mtx));
if (!ieee80211_vif_is_mld(vif)) return 0;
/* In AP mode, there is no primary link */ if (vif->type == NL80211_IFTYPE_AP) return __ffs(vif->active_links);
if (mvmvif->esr_active &&
!WARN_ON(!(BIT(mvmvif->primary_link) & vif->active_links))) return mvmvif->primary_link;
return __ffs(vif->active_links);
}
/* * For non-MLO/single link, this will return the deflink/single active link, * respectively
*/
u8 iwl_mvm_get_other_link(struct ieee80211_vif *vif, u8 link_id)
{ switch (hweight16(vif->active_links)) { case 0: return 0; default:
WARN_ON(1);
fallthrough; case 1: return __ffs(vif->active_links); case 2: return __ffs(vif->active_links & ~BIT(link_id));
}
}
/* Reasons that can cause esr prevention */ #define IWL_MVM_ESR_PREVENT_REASONS IWL_MVM_ESR_EXIT_MISSED_BEACON #define IWL_MVM_PREVENT_ESR_TIMEOUT (HZ * 400) #define IWL_MVM_ESR_PREVENT_SHORT (HZ * 300) #define IWL_MVM_ESR_PREVENT_LONG (HZ * 600)
/* Only handle reasons that can cause prevention */ if (!(reason & IWL_MVM_ESR_PREVENT_REASONS)) returnfalse;
/* * 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; returnfalse;
}
mvmvif->exit_same_reason_count++; if (WARN_ON(mvmvif->exit_same_reason_count < 2 ||
mvmvif->exit_same_reason_count > 3)) returnfalse;
/* * 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;
IWL_DEBUG_INFO(mvm, "Preventing EMLSR for %ld seconds due to %u exits with the reason = %s (0x%x)\n",
delay / HZ, mvmvif->exit_same_reason_count,
iwl_get_esr_state_string(reason), 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));
}
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);
if (!ieee80211_vif_is_mld(vif) || !mvmvif->authorized ||
mvmvif->esr_active) return;
IWL_DEBUG_INFO(mvm, "EMLSR is unblocked\n");
/* If we exited due to an EXIT reason, and the exit was in less than * 30 seconds, then a MLO scan was scheduled already.
*/ if (!need_new_sel &&
!(mvmvif->last_esr_exit.reason & IWL_MVM_BLOCK_ESR_REASONS)) {
IWL_DEBUG_INFO(mvm, "Wait for MLO scan\n"); return;
}
/* * 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);
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.