xref: /linux/drivers/net/wireless/intel/iwlwifi/mld/low_latency.c (revision 4f9786035f9e519db41375818e1d0b5f20da2f10)
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