/* * 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 (time_after(jiffies, mld_vif->emlsr.last_exit_ts +
IWL_MLD_PREVENT_EMLSR_TIMEOUT) ||
mld_vif->emlsr.last_exit_reason != reason)
mld_vif->emlsr.exit_repeat_count = 0;
/* * Do not add a prevention when the reason was a block. For a block, * EMLSR will be enabled again on unblock.
*/ if (reason == IWL_MLD_EMLSR_EXIT_BLOCK) return;
/* Set prevention for a minimum of 30 seconds */
mld_vif->emlsr.blocked_reasons |= IWL_MLD_EMLSR_BLOCKED_PREVENTION;
delay = IWL_MLD_TRIGGER_LINK_SEL_TIME;
/* Handle repeats for reasons that can cause long prevention */ if (mld_vif->emlsr.exit_repeat_count > 1 &&
reason & IWL_MLD_PREVENT_EMLSR_REASONS) { if (mld_vif->emlsr.exit_repeat_count == 2)
delay = IWL_MLD_EMLSR_PREVENT_SHORT; else
delay = IWL_MLD_EMLSR_PREVENT_LONG;
/* * The timeouts are chosen so that this will not happen, i.e. * IWL_MLD_EMLSR_PREVENT_LONG > IWL_MLD_PREVENT_EMLSR_TIMEOUT
*/
WARN_ON(mld_vif->emlsr.exit_repeat_count > 3);
}
IWL_DEBUG_INFO(mld, "Preventing EMLSR for %ld seconds due to %u exits with the reason = %s (0x%x)\n",
delay / HZ, mld_vif->emlsr.exit_repeat_count,
iwl_mld_get_emlsr_exit_string(reason), reason);
/* It is ok to do it for all chanctx (and not only for the ones that * belong to the EMLSR vif) since EMLSR is not allowed if there is * another vif.
*/
phy->avg_channel_load_not_by_us = 0;
}
/* On entry failure need to exit anyway, even if entered from debugfs */ if (exit != IWL_MLD_EMLSR_EXIT_FAIL_ENTRY && !IWL_MLD_AUTO_EML_ENABLE) return 0;
/* Ignore exit request if EMLSR is not active */ if (!iwl_mld_emlsr_active(vif)) return 0;
if (WARN_ON(!ieee80211_vif_is_mld(vif) || !mld_vif->authorized)) return 0;
if (WARN_ON(!(vif->active_links & BIT(link_to_keep))))
link_to_keep = __ffs(vif->active_links);
new_active_links = BIT(link_to_keep);
IWL_DEBUG_INFO(mld, "Exiting EMLSR. reason = %s (0x%x). Current active links=0x%x, new active links = 0x%x\n",
iwl_mld_get_emlsr_exit_string(exit), exit,
vif->active_links, new_active_links);
if (sync)
ret = ieee80211_set_active_links(vif, new_active_links); else
ieee80211_set_active_links_async(vif, new_active_links);
/* channel_load_not_by_us is invalid when in EMLSR. * Clear it so wrong values won't be used.
*/
ieee80211_iter_chan_contexts_atomic(mld->hw,
iwl_mld_clear_avg_chan_load_iter,
NULL);
IWL_DEBUG_INFO(mld, "Failed to %s EMLSR on link %d (FW: %d), reason %d\n",
le32_to_cpu(notif->activation) ? "enter" : "exit",
bss_conf ? bss_conf->link_id : -1,
le32_to_cpu(notif->link_id),
le32_to_cpu(notif->err_code));
if (IWL_FW_CHECK(mld, !bss_conf, "FW reported failure to %sactivate EMLSR on a non-existing link: %d\n",
le32_to_cpu(notif->activation) ? "" : "de",
fw_link_id)) {
ieee80211_iterate_active_interfaces_mtx(
mld->hw, IEEE80211_IFACE_ITER_NORMAL,
iwl_mld_vif_iter_disconnect_emlsr, NULL); return;
}
/* Disconnect if we failed to deactivate a link */ if (!le32_to_cpu(notif->activation)) {
ieee80211_connection_loss(bss_conf->vif); return;
}
/* * We failed to activate the second link, go back to the link specified * by the firmware as that is the one that is still valid now.
*/
iwl_mld_exit_emlsr(mld, bss_conf->vif, IWL_MLD_EMLSR_EXIT_FAIL_ENTRY,
bss_conf->link_id);
}
/* Active non-station link tracking */ staticvoid iwl_mld_count_non_bss_links(void *_data, u8 *mac, struct ieee80211_vif *vif)
{ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); int *count = _data;
if (ieee80211_vif_type_p2p(vif) == NL80211_IFTYPE_STATION) return;
if (data->block) {
ret = iwl_mld_block_emlsr_sync(mld_vif->mld, vif,
IWL_MLD_EMLSR_BLOCKED_NON_BSS,
iwl_mld_get_primary_link(vif)); if (ret)
data->result = ret;
} else {
iwl_mld_unblock_emlsr(mld_vif->mld, vif,
IWL_MLD_EMLSR_BLOCKED_NON_BSS);
}
}
int iwl_mld_emlsr_check_non_bss_block(struct iwl_mld *mld, int pending_link_changes)
{ /* 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 iwl_mld_update_emlsr_block_data block_data = {}; int count = pending_link_changes;
/* No need to count if we are activating a non-BSS link */ if (count <= 0)
ieee80211_iterate_active_interfaces_mtx(mld->hw,
IEEE80211_IFACE_ITER_NORMAL,
iwl_mld_count_non_bss_links,
&count);
/* * We could skip updating it if the block change did not change (and * pending_link_changes is non-zero).
*/
block_data.block = !!count;
/* We only count for the AP sta in a MLO connection */ if (!mld_sta->mpdu_counters) return;
/* This wk should only run when the TPT blocker isn't set. * When the blocker is set, the decision to remove it, as well as * clearing the counters is done in DP (to avoid having a wk every * 5 seconds when idle. When the blocker is unset, we are not idle anyway)
*/ if (WARN_ON(mld_vif->emlsr.blocked_reasons & IWL_MLD_EMLSR_BLOCKED_TPT)) return; /* * TPT is unblocked, need to check if the TPT criteria is still met. * * If EMLSR is active for at least 5 seconds, then we also * need to check the secondary link requirements.
*/ if (iwl_mld_emlsr_active(vif) &&
time_is_before_jiffies(mld_vif->emlsr.last_entry_ts +
IWL_MLD_TPT_COUNT_WINDOW)) {
sec_link_id = iwl_mld_get_other_link(vif, iwl_mld_get_primary_link(vif));
sec_link = iwl_mld_link_dereference_check(mld_vif, sec_link_id); if (WARN_ON_ONCE(!sec_link)) return; /* We need the FW ID here */
sec_link_id = sec_link->fw_id;
} else {
sec_link_id = -1;
}
/* Sum up RX and TX MPDUs from the different queues/links */ for (int q = 0; q < mld->trans->info.num_rxqs; q++) { struct iwl_mld_per_q_mpdu_counter *queue_counter =
&mld_sta->mpdu_counters[q];
spin_lock_bh(&queue_counter->lock);
/* The link IDs that doesn't exist will contain 0 */ for (int link = 0;
link < ARRAY_SIZE(queue_counter->per_link);
link++) {
total_tx += queue_counter->per_link[link].tx;
total_rx += queue_counter->per_link[link].rx;
}
/* Calculate the percentage of the secondary link TX/RX */
sec_link_tx_perc = total_tx ? sec_link_tx * 100 / total_tx : 0;
sec_link_rx_perc = total_rx ? sec_link_rx * 100 / total_rx : 0;
/* * The TX/RX percentage is checked only if it exceeds the required * minimum. In addition, RX is checked only if the TX check failed.
*/ if ((total_tx > EMLSR_MIN_TX &&
sec_link_tx_perc < EMLSR_SEC_LINK_MIN_PERC) ||
(total_rx > EMLSR_MIN_RX &&
sec_link_rx_perc < EMLSR_SEC_LINK_MIN_PERC)) {
iwl_mld_exit_emlsr(mld, vif, IWL_MLD_EMLSR_EXIT_LINK_USAGE,
iwl_mld_get_primary_link(vif)); return;
}
/* Check again when the next window ends */
wiphy_delayed_work_queue(mld_vif->mld->wiphy,
&mld_vif->emlsr.check_tpt_wk,
round_jiffies_relative(IWL_MLD_TPT_COUNT_WINDOW));
}
switch (chandef->width) { case NL80211_CHAN_WIDTH_20_NOHT: case NL80211_CHAN_WIDTH_20: /* 320 MHz has the same thresholds as 20 MHz */ case NL80211_CHAN_WIDTH_320: return RSSI_THRESHOLD(low, 20); case NL80211_CHAN_WIDTH_40: return RSSI_THRESHOLD(low, 40); case NL80211_CHAN_WIDTH_80: return RSSI_THRESHOLD(low, 80); case NL80211_CHAN_WIDTH_160: return RSSI_THRESHOLD(low, 160); default:
WARN_ON(1); return S8_MAX;
} #undef RSSI_THRESHOLD
}
conf = wiphy_dereference(wiphy, vif->link_conf[link->link_id]); if (WARN_ON_ONCE(!conf)) return IWL_MLD_EMLSR_EXIT_INVALID;
if (link->chandef->chan->band == NL80211_BAND_2GHZ && mld->bt_is_active)
ret |= IWL_MLD_EMLSR_EXIT_BT_COEX;
if (link->signal <
iwl_mld_get_emlsr_rssi_thresh(mld, link->chandef, false))
ret |= IWL_MLD_EMLSR_EXIT_LOW_RSSI;
if (conf->csa_active)
ret |= IWL_MLD_EMLSR_EXIT_CSA;
if (ret) {
IWL_DEBUG_INFO(mld, "Link %d is not allowed for EMLSR as %s\n",
link->link_id,
primary ? "primary" : "secondary");
iwl_mld_print_emlsr_exit(mld, ret);
}
/* * TODO: don't select links that weren't discovered in the last scan * This requires mac80211 (or cfg80211) changes to forward/track when * a BSS was last updated. cfg80211 already tracks this information but * it is not exposed within the kernel.
*/
for_each_set_bit(link_id, &usable_links, IEEE80211_MLD_MAX_NUM_LINKS) { struct ieee80211_bss_conf *link_conf =
link_conf_dereference_protected(vif, link_id);
if (WARN_ON_ONCE(!link_conf)) continue;
/* Ignore any BSS that was not seen in the last MLO scan */ if (ktime_before(link_conf->bss->ts_boottime,
mld->scan.last_mlo_scan_time)) continue;
IWL_DEBUG_EHT(mld, "Average channel load not by us: %u\n", primary_load_perc);
if (primary_load_perc < iwl_mld_get_min_chan_load_thresh(chanctx_a)) {
IWL_DEBUG_EHT(mld, "Channel load is below the minimum threshold\n"); returnfalse;
}
if (iwl_mld_vif_low_latency(mld_vif)) {
IWL_DEBUG_EHT(mld, "Low latency vif, EMLSR is allowed\n"); returntrue;
}
if (a->chandef->width <= b->chandef->width) returntrue;
if (a->chandef->chan->band == NL80211_BAND_5GHZ &&
c_low_upper_edge <= 5330 && c_high_lower_edge >= 5490) { /* This case is fine - HW/FW can deal with it, there's * enough separation between the two channels.
*/
} else {
reason_mask |= IWL_MLD_EMLSR_EXIT_EQUAL_BAND;
}
} 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, "Links %d and %d are not a valid pair for EMLSR\n",
a->link_id, b->link_id);
IWL_DEBUG_INFO(mld, "Links bandwidth are: %d and %d\n",
nl80211_chan_width_to_mhz(a->chandef->width),
nl80211_chan_width_to_mhz(b->chandef->width));
iwl_mld_print_emlsr_exit(mld, reason_mask);
}
if (!mld_vif->authorized || hweight16(usable_links) <= 1) return;
if (WARN(ktime_before(mld->scan.last_mlo_scan_time,
ktime_sub_ns(ktime_get_boottime_ns(),
5ULL * NSEC_PER_SEC)), "Last MLO scan was too long ago, can't select links\n")) return;
/* The logic below is simple and not suited for more than 2 links */
WARN_ON_ONCE(max_active_links > 2);
if (!n_data) {
IWL_DEBUG_EHT(mld, "Couldn't find a valid grade for any link!\n"); return;
}
/* Default to selecting the single best link */
best_link = &data[best_idx];
new_primary = best_link->link_id;
new_active = BIT(best_link->link_id);
max_grade = best_link->grade;
/* If EMLSR is not possible, activate the best link */ if (max_active_links == 1 || n_data == 1 ||
!iwl_mld_vif_has_emlsr_cap(vif) || !IWL_MLD_AUTO_EML_ENABLE ||
mld_vif->emlsr.blocked_reasons) goto set_active;
/* Try to find the best link combination */ for (u8 a = 0; a < n_data; a++) { for (u8 b = a + 1; b < n_data; b++) {
u8 best_in_pair;
u16 emlsr_grade =
iwl_mld_get_emlsr_grade(mld, vif,
&data[a], &data[b],
&best_in_pair);
/* * Prefer (new) EMLSR combination to prefer EMLSR over * a single link.
*/ if (emlsr_grade < max_grade) continue;
/* check_tpt_wk is only used when TPT block isn't set */ if (mld_vif->emlsr.blocked_reasons & IWL_MLD_EMLSR_BLOCKED_TPT ||
!IWL_MLD_AUTO_EML_ENABLE || !mld_vif->ap_sta) return;
/* Clear the counters so we start from the beginning */ for (int q = 0; q < mld->trans->info.num_rxqs; q++) { struct iwl_mld_per_q_mpdu_counter *queue_counter =
&mld_sta->mpdu_counters[q];
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.