1d1e879ecSMiri Korenblit // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2d1e879ecSMiri Korenblit /* 3d1e879ecSMiri Korenblit * Copyright (C) 2024-2025 Intel Corporation 4d1e879ecSMiri Korenblit */ 5d1e879ecSMiri Korenblit #include "mld.h" 6d1e879ecSMiri Korenblit #include "iface.h" 7d1e879ecSMiri Korenblit #include "low_latency.h" 8d1e879ecSMiri Korenblit #include "hcmd.h" 9d1e879ecSMiri Korenblit #include "power.h" 10*c008fadbSMiri Korenblit #include "mlo.h" 11d1e879ecSMiri Korenblit 12d1e879ecSMiri Korenblit #define MLD_LL_WK_INTERVAL_MSEC 500 13d1e879ecSMiri Korenblit #define MLD_LL_PERIOD (HZ * MLD_LL_WK_INTERVAL_MSEC / 1000) 14d1e879ecSMiri Korenblit #define MLD_LL_ACTIVE_WK_PERIOD (HZ * 10) 15d1e879ecSMiri Korenblit 16d1e879ecSMiri Korenblit /* packets/MLD_LL_WK_PERIOD seconds */ 17d1e879ecSMiri Korenblit #define MLD_LL_ENABLE_THRESH 100 18d1e879ecSMiri Korenblit 19d1e879ecSMiri Korenblit static bool iwl_mld_calc_low_latency(struct iwl_mld *mld, 20d1e879ecSMiri Korenblit unsigned long timestamp) 21d1e879ecSMiri Korenblit { 22d1e879ecSMiri Korenblit struct iwl_mld_low_latency *ll = &mld->low_latency; 23d1e879ecSMiri Korenblit bool global_low_latency = false; 24d1e879ecSMiri Korenblit u8 num_rx_q = mld->trans->num_rx_queues; 25d1e879ecSMiri Korenblit 26d1e879ecSMiri Korenblit for (int mac_id = 0; mac_id < NUM_MAC_INDEX_DRIVER; mac_id++) { 27d1e879ecSMiri Korenblit u32 total_vo_vi_pkts = 0; 28d1e879ecSMiri Korenblit bool ll_period_expired; 29d1e879ecSMiri Korenblit 30d1e879ecSMiri Korenblit /* If it's not initialized yet, it means we have not yet 31d1e879ecSMiri Korenblit * received/transmitted any vo/vi packet on this MAC. 32d1e879ecSMiri Korenblit */ 33d1e879ecSMiri Korenblit if (!ll->window_start[mac_id]) 34d1e879ecSMiri Korenblit continue; 35d1e879ecSMiri Korenblit 36d1e879ecSMiri Korenblit ll_period_expired = 37d1e879ecSMiri Korenblit time_after(timestamp, ll->window_start[mac_id] + 38d1e879ecSMiri Korenblit MLD_LL_ACTIVE_WK_PERIOD); 39d1e879ecSMiri Korenblit 40d1e879ecSMiri Korenblit if (ll_period_expired) 41d1e879ecSMiri Korenblit ll->window_start[mac_id] = timestamp; 42d1e879ecSMiri Korenblit 43d1e879ecSMiri Korenblit for (int q = 0; q < num_rx_q; q++) { 44d1e879ecSMiri Korenblit struct iwl_mld_low_latency_packets_counters *counters = 45d1e879ecSMiri Korenblit &mld->low_latency.pkts_counters[q]; 46d1e879ecSMiri Korenblit 47d1e879ecSMiri Korenblit spin_lock_bh(&counters->lock); 48d1e879ecSMiri Korenblit 49d1e879ecSMiri Korenblit total_vo_vi_pkts += counters->vo_vi[mac_id]; 50d1e879ecSMiri Korenblit 51d1e879ecSMiri Korenblit if (ll_period_expired) 52d1e879ecSMiri Korenblit counters->vo_vi[mac_id] = 0; 53d1e879ecSMiri Korenblit 54d1e879ecSMiri Korenblit spin_unlock_bh(&counters->lock); 55d1e879ecSMiri Korenblit } 56d1e879ecSMiri Korenblit 57d1e879ecSMiri Korenblit /* enable immediately with enough packets but defer 58d1e879ecSMiri Korenblit * disabling only if the low-latency period expired and 59d1e879ecSMiri Korenblit * below threshold. 60d1e879ecSMiri Korenblit */ 61d1e879ecSMiri Korenblit if (total_vo_vi_pkts > MLD_LL_ENABLE_THRESH) 62d1e879ecSMiri Korenblit mld->low_latency.result[mac_id] = true; 63d1e879ecSMiri Korenblit else if (ll_period_expired) 64d1e879ecSMiri Korenblit mld->low_latency.result[mac_id] = false; 65d1e879ecSMiri Korenblit 66d1e879ecSMiri Korenblit global_low_latency |= mld->low_latency.result[mac_id]; 67d1e879ecSMiri Korenblit } 68d1e879ecSMiri Korenblit 69d1e879ecSMiri Korenblit return global_low_latency; 70d1e879ecSMiri Korenblit } 71d1e879ecSMiri Korenblit 72d1e879ecSMiri Korenblit static void iwl_mld_low_latency_iter(void *_data, u8 *mac, 73d1e879ecSMiri Korenblit struct ieee80211_vif *vif) 74d1e879ecSMiri Korenblit { 75d1e879ecSMiri Korenblit struct iwl_mld *mld = _data; 76d1e879ecSMiri Korenblit struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); 77d1e879ecSMiri Korenblit bool prev = mld_vif->low_latency_causes & LOW_LATENCY_TRAFFIC; 78d1e879ecSMiri Korenblit bool low_latency; 79d1e879ecSMiri Korenblit 80d1e879ecSMiri Korenblit if (WARN_ON(mld_vif->fw_id >= ARRAY_SIZE(mld->low_latency.result))) 81d1e879ecSMiri Korenblit return; 82d1e879ecSMiri Korenblit 83d1e879ecSMiri Korenblit low_latency = mld->low_latency.result[mld_vif->fw_id]; 84d1e879ecSMiri Korenblit 85d1e879ecSMiri Korenblit if (prev != low_latency) 86d1e879ecSMiri Korenblit iwl_mld_vif_update_low_latency(mld, vif, low_latency, 87d1e879ecSMiri Korenblit LOW_LATENCY_TRAFFIC); 88d1e879ecSMiri Korenblit } 89d1e879ecSMiri Korenblit 90d1e879ecSMiri Korenblit static void iwl_mld_low_latency_wk(struct wiphy *wiphy, struct wiphy_work *wk) 91d1e879ecSMiri Korenblit { 92d1e879ecSMiri Korenblit struct iwl_mld *mld = container_of(wk, struct iwl_mld, 93d1e879ecSMiri Korenblit low_latency.work.work); 94d1e879ecSMiri Korenblit unsigned long timestamp = jiffies; 95d1e879ecSMiri Korenblit bool low_latency_active; 96d1e879ecSMiri Korenblit 97d1e879ecSMiri Korenblit if (mld->fw_status.in_hw_restart) 98d1e879ecSMiri Korenblit return; 99d1e879ecSMiri Korenblit 100d1e879ecSMiri Korenblit /* It is assumed that the work was scheduled only after checking 101d1e879ecSMiri Korenblit * at least MLD_LL_PERIOD has passed since the last update. 102d1e879ecSMiri Korenblit */ 103d1e879ecSMiri Korenblit 104d1e879ecSMiri Korenblit low_latency_active = iwl_mld_calc_low_latency(mld, timestamp); 105d1e879ecSMiri Korenblit 106d1e879ecSMiri Korenblit /* Update the timestamp now after the low-latency calculation */ 107d1e879ecSMiri Korenblit mld->low_latency.timestamp = timestamp; 108d1e879ecSMiri Korenblit 109d1e879ecSMiri Korenblit /* If low-latency is active we need to force re-evaluation after 110d1e879ecSMiri Korenblit * 10 seconds, so that we can disable low-latency when 111d1e879ecSMiri Korenblit * the low-latency traffic ends. 112d1e879ecSMiri Korenblit * 113d1e879ecSMiri Korenblit * Otherwise, we don't need to run the work because there is nothing to 114d1e879ecSMiri Korenblit * disable. 115d1e879ecSMiri Korenblit * 116d1e879ecSMiri Korenblit * Note that this has no impact on the regular scheduling of the 117d1e879ecSMiri Korenblit * updates triggered by traffic - those happen whenever the 118d1e879ecSMiri Korenblit * MLD_LL_PERIOD timeout expire. 119d1e879ecSMiri Korenblit */ 120d1e879ecSMiri Korenblit if (low_latency_active) 121d1e879ecSMiri Korenblit wiphy_delayed_work_queue(mld->wiphy, &mld->low_latency.work, 122d1e879ecSMiri Korenblit MLD_LL_ACTIVE_WK_PERIOD); 123d1e879ecSMiri Korenblit 12429b0ca82SMiri Korenblit ieee80211_iterate_active_interfaces_mtx(mld->hw, 125d1e879ecSMiri Korenblit IEEE80211_IFACE_ITER_NORMAL, 126d1e879ecSMiri Korenblit iwl_mld_low_latency_iter, mld); 127d1e879ecSMiri Korenblit } 128d1e879ecSMiri Korenblit 129d1e879ecSMiri Korenblit int iwl_mld_low_latency_init(struct iwl_mld *mld) 130d1e879ecSMiri Korenblit { 131d1e879ecSMiri Korenblit struct iwl_mld_low_latency *ll = &mld->low_latency; 132d1e879ecSMiri Korenblit unsigned long ts = jiffies; 133d1e879ecSMiri Korenblit 134d1e879ecSMiri Korenblit ll->pkts_counters = kcalloc(mld->trans->num_rx_queues, 135d1e879ecSMiri Korenblit sizeof(*ll->pkts_counters), GFP_KERNEL); 136d1e879ecSMiri Korenblit if (!ll->pkts_counters) 137d1e879ecSMiri Korenblit return -ENOMEM; 138d1e879ecSMiri Korenblit 139d1e879ecSMiri Korenblit for (int q = 0; q < mld->trans->num_rx_queues; q++) 140d1e879ecSMiri Korenblit spin_lock_init(&ll->pkts_counters[q].lock); 141d1e879ecSMiri Korenblit 142d1e879ecSMiri Korenblit wiphy_delayed_work_init(&ll->work, iwl_mld_low_latency_wk); 143d1e879ecSMiri Korenblit 144d1e879ecSMiri Korenblit ll->timestamp = ts; 145d1e879ecSMiri Korenblit 146d1e879ecSMiri Korenblit /* The low-latency window_start will be initialized per-MAC on 147d1e879ecSMiri Korenblit * the first vo/vi packet received/transmitted. 148d1e879ecSMiri Korenblit */ 149d1e879ecSMiri Korenblit 150d1e879ecSMiri Korenblit return 0; 151d1e879ecSMiri Korenblit } 152d1e879ecSMiri Korenblit 153d1e879ecSMiri Korenblit void iwl_mld_low_latency_free(struct iwl_mld *mld) 154d1e879ecSMiri Korenblit { 155d1e879ecSMiri Korenblit struct iwl_mld_low_latency *ll = &mld->low_latency; 156d1e879ecSMiri Korenblit 157d1e879ecSMiri Korenblit kfree(ll->pkts_counters); 158d1e879ecSMiri Korenblit ll->pkts_counters = NULL; 159d1e879ecSMiri Korenblit } 160d1e879ecSMiri Korenblit 161d1e879ecSMiri Korenblit void iwl_mld_low_latency_restart_cleanup(struct iwl_mld *mld) 162d1e879ecSMiri Korenblit { 163d1e879ecSMiri Korenblit struct iwl_mld_low_latency *ll = &mld->low_latency; 164d1e879ecSMiri Korenblit 165d1e879ecSMiri Korenblit ll->timestamp = jiffies; 166d1e879ecSMiri Korenblit 167d1e879ecSMiri Korenblit memset(ll->window_start, 0, sizeof(ll->window_start)); 168d1e879ecSMiri Korenblit memset(ll->result, 0, sizeof(ll->result)); 169d1e879ecSMiri Korenblit 170d1e879ecSMiri Korenblit for (int q = 0; q < mld->trans->num_rx_queues; q++) 171d1e879ecSMiri Korenblit memset(ll->pkts_counters[q].vo_vi, 0, 172d1e879ecSMiri Korenblit sizeof(ll->pkts_counters[q].vo_vi)); 173d1e879ecSMiri Korenblit } 174d1e879ecSMiri Korenblit 175d1e879ecSMiri Korenblit static int iwl_mld_send_low_latency_cmd(struct iwl_mld *mld, bool low_latency, 176d1e879ecSMiri Korenblit u16 mac_id) 177d1e879ecSMiri Korenblit { 178d1e879ecSMiri Korenblit struct iwl_mac_low_latency_cmd cmd = { 179d1e879ecSMiri Korenblit .mac_id = cpu_to_le32(mac_id) 180d1e879ecSMiri Korenblit }; 181d1e879ecSMiri Korenblit u16 cmd_id = WIDE_ID(MAC_CONF_GROUP, LOW_LATENCY_CMD); 182d1e879ecSMiri Korenblit int ret; 183d1e879ecSMiri Korenblit 184d1e879ecSMiri Korenblit if (low_latency) { 185d1e879ecSMiri Korenblit /* Currently we don't care about the direction */ 186d1e879ecSMiri Korenblit cmd.low_latency_rx = 1; 187d1e879ecSMiri Korenblit cmd.low_latency_tx = 1; 188d1e879ecSMiri Korenblit } 189d1e879ecSMiri Korenblit 190d1e879ecSMiri Korenblit ret = iwl_mld_send_cmd_pdu(mld, cmd_id, &cmd); 191d1e879ecSMiri Korenblit if (ret) 192d1e879ecSMiri Korenblit IWL_ERR(mld, "Failed to send low latency command\n"); 193d1e879ecSMiri Korenblit 194d1e879ecSMiri Korenblit return ret; 195d1e879ecSMiri Korenblit } 196d1e879ecSMiri Korenblit 197d1e879ecSMiri Korenblit static void iwl_mld_vif_set_low_latency(struct iwl_mld_vif *mld_vif, bool set, 198d1e879ecSMiri Korenblit enum iwl_mld_low_latency_cause cause) 199d1e879ecSMiri Korenblit { 200d1e879ecSMiri Korenblit if (set) 201d1e879ecSMiri Korenblit mld_vif->low_latency_causes |= cause; 202d1e879ecSMiri Korenblit else 203d1e879ecSMiri Korenblit mld_vif->low_latency_causes &= ~cause; 204d1e879ecSMiri Korenblit } 205d1e879ecSMiri Korenblit 206d1e879ecSMiri Korenblit void iwl_mld_vif_update_low_latency(struct iwl_mld *mld, 207d1e879ecSMiri Korenblit struct ieee80211_vif *vif, 208d1e879ecSMiri Korenblit bool low_latency, 209d1e879ecSMiri Korenblit enum iwl_mld_low_latency_cause cause) 210d1e879ecSMiri Korenblit { 211d1e879ecSMiri Korenblit struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); 212d1e879ecSMiri Korenblit bool prev; 213d1e879ecSMiri Korenblit 214d1e879ecSMiri Korenblit prev = iwl_mld_vif_low_latency(mld_vif); 215d1e879ecSMiri Korenblit iwl_mld_vif_set_low_latency(mld_vif, low_latency, cause); 216d1e879ecSMiri Korenblit 217d1e879ecSMiri Korenblit low_latency = iwl_mld_vif_low_latency(mld_vif); 218d1e879ecSMiri Korenblit if (low_latency == prev) 219d1e879ecSMiri Korenblit return; 220d1e879ecSMiri Korenblit 221d1e879ecSMiri Korenblit if (iwl_mld_send_low_latency_cmd(mld, low_latency, mld_vif->fw_id)) { 222d1e879ecSMiri Korenblit /* revert to previous low-latency state */ 223d1e879ecSMiri Korenblit iwl_mld_vif_set_low_latency(mld_vif, prev, cause); 224d1e879ecSMiri Korenblit return; 225d1e879ecSMiri Korenblit } 226d1e879ecSMiri Korenblit 227d1e879ecSMiri Korenblit if (low_latency) 228d1e879ecSMiri Korenblit iwl_mld_leave_omi_bw_reduction(mld); 229d1e879ecSMiri Korenblit 230d1e879ecSMiri Korenblit if (ieee80211_vif_type_p2p(vif) != NL80211_IFTYPE_P2P_CLIENT) 231d1e879ecSMiri Korenblit return; 232d1e879ecSMiri Korenblit 233d1e879ecSMiri Korenblit iwl_mld_update_mac_power(mld, vif, false); 234*c008fadbSMiri Korenblit 235*c008fadbSMiri Korenblit if (low_latency) 236*c008fadbSMiri Korenblit iwl_mld_retry_emlsr(mld, vif); 237d1e879ecSMiri Korenblit } 238d1e879ecSMiri Korenblit 239d1e879ecSMiri Korenblit static bool iwl_mld_is_vo_vi_pkt(struct ieee80211_hdr *hdr) 240d1e879ecSMiri Korenblit { 241d1e879ecSMiri Korenblit u8 tid; 242d1e879ecSMiri Korenblit static const u8 tid_to_mac80211_ac[] = { 243d1e879ecSMiri Korenblit IEEE80211_AC_BE, 244d1e879ecSMiri Korenblit IEEE80211_AC_BK, 245d1e879ecSMiri Korenblit IEEE80211_AC_BK, 246d1e879ecSMiri Korenblit IEEE80211_AC_BE, 247d1e879ecSMiri Korenblit IEEE80211_AC_VI, 248d1e879ecSMiri Korenblit IEEE80211_AC_VI, 249d1e879ecSMiri Korenblit IEEE80211_AC_VO, 250d1e879ecSMiri Korenblit IEEE80211_AC_VO, 251d1e879ecSMiri Korenblit }; 252d1e879ecSMiri Korenblit 253d1e879ecSMiri Korenblit if (!hdr || !ieee80211_is_data_qos(hdr->frame_control)) 254d1e879ecSMiri Korenblit return false; 255d1e879ecSMiri Korenblit 256d1e879ecSMiri Korenblit tid = ieee80211_get_tid(hdr); 257d1e879ecSMiri Korenblit if (tid >= IWL_MAX_TID_COUNT) 258d1e879ecSMiri Korenblit return false; 259d1e879ecSMiri Korenblit 260d1e879ecSMiri Korenblit return tid_to_mac80211_ac[tid] < IEEE80211_AC_VI; 261d1e879ecSMiri Korenblit } 262d1e879ecSMiri Korenblit 263d1e879ecSMiri Korenblit void iwl_mld_low_latency_update_counters(struct iwl_mld *mld, 264d1e879ecSMiri Korenblit struct ieee80211_hdr *hdr, 265d1e879ecSMiri Korenblit struct ieee80211_sta *sta, 266d1e879ecSMiri Korenblit u8 queue) 267d1e879ecSMiri Korenblit { 268d1e879ecSMiri Korenblit struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta); 269d1e879ecSMiri Korenblit struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(mld_sta->vif); 270d1e879ecSMiri Korenblit struct iwl_mld_low_latency_packets_counters *counters; 271d1e879ecSMiri Korenblit unsigned long ts = jiffies ? jiffies : 1; 272d1e879ecSMiri Korenblit u8 fw_id = mld_vif->fw_id; 273d1e879ecSMiri Korenblit 274d1e879ecSMiri Korenblit /* we should have failed op mode init if NULL */ 275d1e879ecSMiri Korenblit if (WARN_ON_ONCE(!mld->low_latency.pkts_counters)) 276d1e879ecSMiri Korenblit return; 277d1e879ecSMiri Korenblit 278d1e879ecSMiri Korenblit if (WARN_ON_ONCE(fw_id >= ARRAY_SIZE(counters->vo_vi) || 279d1e879ecSMiri Korenblit queue >= mld->trans->num_rx_queues)) 280d1e879ecSMiri Korenblit return; 281d1e879ecSMiri Korenblit 282d1e879ecSMiri Korenblit if (mld->low_latency.stopped) 283d1e879ecSMiri Korenblit return; 284d1e879ecSMiri Korenblit 285d1e879ecSMiri Korenblit if (!iwl_mld_is_vo_vi_pkt(hdr)) 286d1e879ecSMiri Korenblit return; 287d1e879ecSMiri Korenblit 288d1e879ecSMiri Korenblit counters = &mld->low_latency.pkts_counters[queue]; 289d1e879ecSMiri Korenblit 290d1e879ecSMiri Korenblit spin_lock_bh(&counters->lock); 291d1e879ecSMiri Korenblit counters->vo_vi[fw_id]++; 292d1e879ecSMiri Korenblit spin_unlock_bh(&counters->lock); 293d1e879ecSMiri Korenblit 294d1e879ecSMiri Korenblit /* Initialize the window_start on the first vo/vi packet */ 295d1e879ecSMiri Korenblit if (!mld->low_latency.window_start[fw_id]) 296d1e879ecSMiri Korenblit mld->low_latency.window_start[fw_id] = ts; 297d1e879ecSMiri Korenblit 298d1e879ecSMiri Korenblit if (time_is_before_jiffies(mld->low_latency.timestamp + MLD_LL_PERIOD)) 299d1e879ecSMiri Korenblit wiphy_delayed_work_queue(mld->wiphy, &mld->low_latency.work, 300d1e879ecSMiri Korenblit 0); 301d1e879ecSMiri Korenblit } 302d1e879ecSMiri Korenblit 303d1e879ecSMiri Korenblit void iwl_mld_low_latency_stop(struct iwl_mld *mld) 304d1e879ecSMiri Korenblit { 305d1e879ecSMiri Korenblit lockdep_assert_wiphy(mld->wiphy); 306d1e879ecSMiri Korenblit 307d1e879ecSMiri Korenblit mld->low_latency.stopped = true; 308d1e879ecSMiri Korenblit 309d1e879ecSMiri Korenblit wiphy_delayed_work_cancel(mld->wiphy, &mld->low_latency.work); 310d1e879ecSMiri Korenblit } 311d1e879ecSMiri Korenblit 312d1e879ecSMiri Korenblit void iwl_mld_low_latency_restart(struct iwl_mld *mld) 313d1e879ecSMiri Korenblit { 314d1e879ecSMiri Korenblit struct iwl_mld_low_latency *ll = &mld->low_latency; 315d1e879ecSMiri Korenblit bool low_latency = false; 316d1e879ecSMiri Korenblit unsigned long ts = jiffies; 317d1e879ecSMiri Korenblit 318d1e879ecSMiri Korenblit lockdep_assert_wiphy(mld->wiphy); 319d1e879ecSMiri Korenblit 320d1e879ecSMiri Korenblit ll->timestamp = ts; 321d1e879ecSMiri Korenblit mld->low_latency.stopped = false; 322d1e879ecSMiri Korenblit 323d1e879ecSMiri Korenblit for (int mac = 0; mac < NUM_MAC_INDEX_DRIVER; mac++) { 324d1e879ecSMiri Korenblit ll->window_start[mac] = 0; 325d1e879ecSMiri Korenblit low_latency |= ll->result[mac]; 326d1e879ecSMiri Korenblit 327d1e879ecSMiri Korenblit for (int q = 0; q < mld->trans->num_rx_queues; q++) { 328d1e879ecSMiri Korenblit spin_lock_bh(&ll->pkts_counters[q].lock); 329d1e879ecSMiri Korenblit ll->pkts_counters[q].vo_vi[mac] = 0; 330d1e879ecSMiri Korenblit spin_unlock_bh(&ll->pkts_counters[q].lock); 331d1e879ecSMiri Korenblit } 332d1e879ecSMiri Korenblit } 333d1e879ecSMiri Korenblit 334d1e879ecSMiri Korenblit /* if low latency is active, force re-evaluation to cover the case of 335d1e879ecSMiri Korenblit * no traffic. 336d1e879ecSMiri Korenblit */ 337d1e879ecSMiri Korenblit if (low_latency) 338d1e879ecSMiri Korenblit wiphy_delayed_work_queue(mld->wiphy, &ll->work, MLD_LL_PERIOD); 339d1e879ecSMiri Korenblit } 340