Path: blob/main/sys/contrib/dev/iwlwifi/mld/low_latency.c
48285 views
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause1/*2* Copyright (C) 2024-2025 Intel Corporation3*/4#include "mld.h"5#include "iface.h"6#include "low_latency.h"7#include "hcmd.h"8#include "power.h"9#include "mlo.h"1011#define MLD_LL_WK_INTERVAL_MSEC 50012#define MLD_LL_PERIOD (HZ * MLD_LL_WK_INTERVAL_MSEC / 1000)13#define MLD_LL_ACTIVE_WK_PERIOD (HZ * 10)1415/* packets/MLD_LL_WK_PERIOD seconds */16#define MLD_LL_ENABLE_THRESH 1001718static bool iwl_mld_calc_low_latency(struct iwl_mld *mld,19unsigned long timestamp)20{21struct iwl_mld_low_latency *ll = &mld->low_latency;22bool global_low_latency = false;23u8 num_rx_q = mld->trans->info.num_rxqs;2425for (int mac_id = 0; mac_id < NUM_MAC_INDEX_DRIVER; mac_id++) {26u32 total_vo_vi_pkts = 0;27bool ll_period_expired;2829/* If it's not initialized yet, it means we have not yet30* received/transmitted any vo/vi packet on this MAC.31*/32if (!ll->window_start[mac_id])33continue;3435ll_period_expired =36time_after(timestamp, ll->window_start[mac_id] +37MLD_LL_ACTIVE_WK_PERIOD);3839if (ll_period_expired)40ll->window_start[mac_id] = timestamp;4142for (int q = 0; q < num_rx_q; q++) {43struct iwl_mld_low_latency_packets_counters *counters =44&mld->low_latency.pkts_counters[q];4546spin_lock_bh(&counters->lock);4748total_vo_vi_pkts += counters->vo_vi[mac_id];4950if (ll_period_expired)51counters->vo_vi[mac_id] = 0;5253spin_unlock_bh(&counters->lock);54}5556/* enable immediately with enough packets but defer57* disabling only if the low-latency period expired and58* below threshold.59*/60if (total_vo_vi_pkts > MLD_LL_ENABLE_THRESH)61mld->low_latency.result[mac_id] = true;62else if (ll_period_expired)63mld->low_latency.result[mac_id] = false;6465global_low_latency |= mld->low_latency.result[mac_id];66}6768return global_low_latency;69}7071static void iwl_mld_low_latency_iter(void *_data, u8 *mac,72struct ieee80211_vif *vif)73{74struct iwl_mld *mld = _data;75struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);76bool prev = mld_vif->low_latency_causes & LOW_LATENCY_TRAFFIC;77bool low_latency;7879if (WARN_ON(mld_vif->fw_id >= ARRAY_SIZE(mld->low_latency.result)))80return;8182low_latency = mld->low_latency.result[mld_vif->fw_id];8384if (prev != low_latency)85iwl_mld_vif_update_low_latency(mld, vif, low_latency,86LOW_LATENCY_TRAFFIC);87}8889static void iwl_mld_low_latency_wk(struct wiphy *wiphy, struct wiphy_work *wk)90{91struct iwl_mld *mld = container_of(wk, struct iwl_mld,92low_latency.work.work);93unsigned long timestamp = jiffies;94bool low_latency_active;9596if (mld->fw_status.in_hw_restart)97return;9899/* It is assumed that the work was scheduled only after checking100* at least MLD_LL_PERIOD has passed since the last update.101*/102103low_latency_active = iwl_mld_calc_low_latency(mld, timestamp);104105/* Update the timestamp now after the low-latency calculation */106mld->low_latency.timestamp = timestamp;107108/* If low-latency is active we need to force re-evaluation after109* 10 seconds, so that we can disable low-latency when110* the low-latency traffic ends.111*112* Otherwise, we don't need to run the work because there is nothing to113* disable.114*115* Note that this has no impact on the regular scheduling of the116* updates triggered by traffic - those happen whenever the117* MLD_LL_PERIOD timeout expire.118*/119if (low_latency_active)120wiphy_delayed_work_queue(mld->wiphy, &mld->low_latency.work,121MLD_LL_ACTIVE_WK_PERIOD);122123ieee80211_iterate_active_interfaces_mtx(mld->hw,124IEEE80211_IFACE_ITER_NORMAL,125iwl_mld_low_latency_iter, mld);126}127128int iwl_mld_low_latency_init(struct iwl_mld *mld)129{130struct iwl_mld_low_latency *ll = &mld->low_latency;131unsigned long ts = jiffies;132133ll->pkts_counters = kcalloc(mld->trans->info.num_rxqs,134sizeof(*ll->pkts_counters), GFP_KERNEL);135if (!ll->pkts_counters)136return -ENOMEM;137138for (int q = 0; q < mld->trans->info.num_rxqs; q++)139spin_lock_init(&ll->pkts_counters[q].lock);140141wiphy_delayed_work_init(&ll->work, iwl_mld_low_latency_wk);142143ll->timestamp = ts;144145/* The low-latency window_start will be initialized per-MAC on146* the first vo/vi packet received/transmitted.147*/148149return 0;150}151152void iwl_mld_low_latency_free(struct iwl_mld *mld)153{154struct iwl_mld_low_latency *ll = &mld->low_latency;155156kfree(ll->pkts_counters);157ll->pkts_counters = NULL;158}159160void iwl_mld_low_latency_restart_cleanup(struct iwl_mld *mld)161{162struct iwl_mld_low_latency *ll = &mld->low_latency;163164ll->timestamp = jiffies;165166memset(ll->window_start, 0, sizeof(ll->window_start));167memset(ll->result, 0, sizeof(ll->result));168169for (int q = 0; q < mld->trans->info.num_rxqs; q++)170memset(ll->pkts_counters[q].vo_vi, 0,171sizeof(ll->pkts_counters[q].vo_vi));172}173174static int iwl_mld_send_low_latency_cmd(struct iwl_mld *mld, bool low_latency,175u16 mac_id)176{177struct iwl_mac_low_latency_cmd cmd = {178.mac_id = cpu_to_le32(mac_id)179};180u16 cmd_id = WIDE_ID(MAC_CONF_GROUP, LOW_LATENCY_CMD);181int ret;182183if (low_latency) {184/* Currently we don't care about the direction */185cmd.low_latency_rx = 1;186cmd.low_latency_tx = 1;187}188189ret = iwl_mld_send_cmd_pdu(mld, cmd_id, &cmd);190if (ret)191IWL_ERR(mld, "Failed to send low latency command\n");192193return ret;194}195196static void iwl_mld_vif_set_low_latency(struct iwl_mld_vif *mld_vif, bool set,197enum iwl_mld_low_latency_cause cause)198{199if (set)200mld_vif->low_latency_causes |= cause;201else202mld_vif->low_latency_causes &= ~cause;203}204205void iwl_mld_vif_update_low_latency(struct iwl_mld *mld,206struct ieee80211_vif *vif,207bool low_latency,208enum iwl_mld_low_latency_cause cause)209{210struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);211bool prev;212213prev = iwl_mld_vif_low_latency(mld_vif);214iwl_mld_vif_set_low_latency(mld_vif, low_latency, cause);215216low_latency = iwl_mld_vif_low_latency(mld_vif);217if (low_latency == prev)218return;219220if (iwl_mld_send_low_latency_cmd(mld, low_latency, mld_vif->fw_id)) {221/* revert to previous low-latency state */222iwl_mld_vif_set_low_latency(mld_vif, prev, cause);223return;224}225226if (ieee80211_vif_type_p2p(vif) != NL80211_IFTYPE_P2P_CLIENT)227return;228229iwl_mld_update_mac_power(mld, vif, false);230231if (low_latency)232iwl_mld_retry_emlsr(mld, vif);233}234235static bool iwl_mld_is_vo_vi_pkt(struct ieee80211_hdr *hdr)236{237u8 tid;238static const u8 tid_to_mac80211_ac[] = {239IEEE80211_AC_BE,240IEEE80211_AC_BK,241IEEE80211_AC_BK,242IEEE80211_AC_BE,243IEEE80211_AC_VI,244IEEE80211_AC_VI,245IEEE80211_AC_VO,246IEEE80211_AC_VO,247};248249if (!hdr || !ieee80211_is_data_qos(hdr->frame_control))250return false;251252tid = ieee80211_get_tid(hdr);253if (tid >= IWL_MAX_TID_COUNT)254return false;255256return tid_to_mac80211_ac[tid] < IEEE80211_AC_VI;257}258259void iwl_mld_low_latency_update_counters(struct iwl_mld *mld,260struct ieee80211_hdr *hdr,261struct ieee80211_sta *sta,262u8 queue)263{264struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta);265struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(mld_sta->vif);266struct iwl_mld_low_latency_packets_counters *counters;267unsigned long ts = jiffies ? jiffies : 1;268u8 fw_id = mld_vif->fw_id;269270/* we should have failed op mode init if NULL */271if (WARN_ON_ONCE(!mld->low_latency.pkts_counters))272return;273274if (WARN_ON_ONCE(fw_id >= ARRAY_SIZE(counters->vo_vi) ||275queue >= mld->trans->info.num_rxqs))276return;277278if (mld->low_latency.stopped)279return;280281if (!iwl_mld_is_vo_vi_pkt(hdr))282return;283284counters = &mld->low_latency.pkts_counters[queue];285286spin_lock_bh(&counters->lock);287counters->vo_vi[fw_id]++;288spin_unlock_bh(&counters->lock);289290/* Initialize the window_start on the first vo/vi packet */291if (!mld->low_latency.window_start[fw_id])292mld->low_latency.window_start[fw_id] = ts;293294if (time_is_before_jiffies(mld->low_latency.timestamp + MLD_LL_PERIOD))295wiphy_delayed_work_queue(mld->wiphy, &mld->low_latency.work,2960);297}298299void iwl_mld_low_latency_stop(struct iwl_mld *mld)300{301lockdep_assert_wiphy(mld->wiphy);302303mld->low_latency.stopped = true;304305wiphy_delayed_work_cancel(mld->wiphy, &mld->low_latency.work);306}307308void iwl_mld_low_latency_restart(struct iwl_mld *mld)309{310struct iwl_mld_low_latency *ll = &mld->low_latency;311bool low_latency = false;312unsigned long ts = jiffies;313314lockdep_assert_wiphy(mld->wiphy);315316ll->timestamp = ts;317mld->low_latency.stopped = false;318319for (int mac = 0; mac < NUM_MAC_INDEX_DRIVER; mac++) {320ll->window_start[mac] = 0;321low_latency |= ll->result[mac];322323for (int q = 0; q < mld->trans->info.num_rxqs; q++) {324spin_lock_bh(&ll->pkts_counters[q].lock);325ll->pkts_counters[q].vo_vi[mac] = 0;326spin_unlock_bh(&ll->pkts_counters[q].lock);327}328}329330/* if low latency is active, force re-evaluation to cover the case of331* no traffic.332*/333if (low_latency)334wiphy_delayed_work_queue(mld->wiphy, &ll->work, MLD_LL_PERIOD);335}336337338