xref: /openbmc/linux/drivers/net/wireless/intel/iwlwifi/mvm/quota.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
18e99ea8dSJohannes Berg // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
28e99ea8dSJohannes Berg /*
3f276e20bSJohannes Berg  * Copyright (C) 2012-2014, 2018, 2021-2022 Intel Corporation
48e99ea8dSJohannes Berg  * Copyright (C) 2013-2014 Intel Mobile Communications GmbH
58e99ea8dSJohannes Berg  * Copyright (C) 2016-2017 Intel Deutschland GmbH
68e99ea8dSJohannes Berg  */
7e705c121SKalle Valo #include <net/mac80211.h>
8e705c121SKalle Valo #include "fw-api.h"
9e705c121SKalle Valo #include "mvm.h"
10e705c121SKalle Valo 
11e705c121SKalle Valo #define QUOTA_100	IWL_MVM_MAX_QUOTA
12e705c121SKalle Valo #define QUOTA_LOWLAT_MIN ((QUOTA_100 * IWL_MVM_LOWLAT_QUOTA_MIN_PERCENT) / 100)
13e705c121SKalle Valo 
14e705c121SKalle Valo struct iwl_mvm_quota_iterator_data {
15e705c121SKalle Valo 	int n_interfaces[MAX_BINDINGS];
16e705c121SKalle Valo 	int colors[MAX_BINDINGS];
17e705c121SKalle Valo 	int low_latency[MAX_BINDINGS];
18a80c1cf9SJohannes Berg #ifdef CONFIG_IWLWIFI_DEBUGFS
19a80c1cf9SJohannes Berg 	int dbgfs_min[MAX_BINDINGS];
20a80c1cf9SJohannes Berg #endif
21e705c121SKalle Valo 	int n_low_latency_bindings;
22e705c121SKalle Valo 	struct ieee80211_vif *disabled_vif;
23e705c121SKalle Valo };
24e705c121SKalle Valo 
iwl_mvm_quota_iterator(void * _data,u8 * mac,struct ieee80211_vif * vif)25e705c121SKalle Valo static void iwl_mvm_quota_iterator(void *_data, u8 *mac,
26e705c121SKalle Valo 				   struct ieee80211_vif *vif)
27e705c121SKalle Valo {
28e705c121SKalle Valo 	struct iwl_mvm_quota_iterator_data *data = _data;
29e705c121SKalle Valo 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
30e705c121SKalle Valo 	u16 id;
31e705c121SKalle Valo 
32e705c121SKalle Valo 	/* skip disabled interfaces here immediately */
33e705c121SKalle Valo 	if (vif == data->disabled_vif)
34e705c121SKalle Valo 		return;
35e705c121SKalle Valo 
36*650cadb7SGregory Greenman 	if (!mvmvif->deflink.phy_ctxt)
37e705c121SKalle Valo 		return;
38e705c121SKalle Valo 
39e705c121SKalle Valo 	/* currently, PHY ID == binding ID */
40*650cadb7SGregory Greenman 	id = mvmvif->deflink.phy_ctxt->id;
41e705c121SKalle Valo 
42e705c121SKalle Valo 	/* need at least one binding per PHY */
43e705c121SKalle Valo 	BUILD_BUG_ON(NUM_PHY_CTX > MAX_BINDINGS);
44e705c121SKalle Valo 
45e705c121SKalle Valo 	if (WARN_ON_ONCE(id >= MAX_BINDINGS))
46e705c121SKalle Valo 		return;
47e705c121SKalle Valo 
48e705c121SKalle Valo 	switch (vif->type) {
49e705c121SKalle Valo 	case NL80211_IFTYPE_STATION:
50f276e20bSJohannes Berg 		if (vif->cfg.assoc)
51e705c121SKalle Valo 			break;
52e705c121SKalle Valo 		return;
53e705c121SKalle Valo 	case NL80211_IFTYPE_AP:
54e705c121SKalle Valo 	case NL80211_IFTYPE_ADHOC:
55e705c121SKalle Valo 		if (mvmvif->ap_ibss_active)
56e705c121SKalle Valo 			break;
57e705c121SKalle Valo 		return;
58e705c121SKalle Valo 	case NL80211_IFTYPE_MONITOR:
59e705c121SKalle Valo 		if (mvmvif->monitor_active)
60e705c121SKalle Valo 			break;
61e705c121SKalle Valo 		return;
62e705c121SKalle Valo 	case NL80211_IFTYPE_P2P_DEVICE:
63e705c121SKalle Valo 		return;
64e705c121SKalle Valo 	default:
65e705c121SKalle Valo 		WARN_ON_ONCE(1);
66e705c121SKalle Valo 		return;
67e705c121SKalle Valo 	}
68e705c121SKalle Valo 
69e705c121SKalle Valo 	if (data->colors[id] < 0)
70*650cadb7SGregory Greenman 		data->colors[id] = mvmvif->deflink.phy_ctxt->color;
71e705c121SKalle Valo 	else
72*650cadb7SGregory Greenman 		WARN_ON_ONCE(data->colors[id] !=
73*650cadb7SGregory Greenman 			     mvmvif->deflink.phy_ctxt->color);
74e705c121SKalle Valo 
75e705c121SKalle Valo 	data->n_interfaces[id]++;
76e705c121SKalle Valo 
77a80c1cf9SJohannes Berg #ifdef CONFIG_IWLWIFI_DEBUGFS
78a80c1cf9SJohannes Berg 	if (mvmvif->dbgfs_quota_min)
79a80c1cf9SJohannes Berg 		data->dbgfs_min[id] = max(data->dbgfs_min[id],
80a80c1cf9SJohannes Berg 					  mvmvif->dbgfs_quota_min);
81a80c1cf9SJohannes Berg #endif
82a80c1cf9SJohannes Berg 
83e705c121SKalle Valo 	if (iwl_mvm_vif_low_latency(mvmvif) && !data->low_latency[id]) {
84e705c121SKalle Valo 		data->n_low_latency_bindings++;
85e705c121SKalle Valo 		data->low_latency[id] = true;
86e705c121SKalle Valo 	}
87e705c121SKalle Valo }
88e705c121SKalle Valo 
iwl_mvm_adjust_quota_for_noa(struct iwl_mvm * mvm,struct iwl_time_quota_cmd * cmd)89e705c121SKalle Valo static void iwl_mvm_adjust_quota_for_noa(struct iwl_mvm *mvm,
90e705c121SKalle Valo 					 struct iwl_time_quota_cmd *cmd)
91e705c121SKalle Valo {
92e705c121SKalle Valo #ifdef CONFIG_NL80211_TESTMODE
93e705c121SKalle Valo 	struct iwl_mvm_vif *mvmvif;
94e705c121SKalle Valo 	int i, phy_id = -1, beacon_int = 0;
95e705c121SKalle Valo 
96e705c121SKalle Valo 	if (!mvm->noa_duration || !mvm->noa_vif)
97e705c121SKalle Valo 		return;
98e705c121SKalle Valo 
99e705c121SKalle Valo 	mvmvif = iwl_mvm_vif_from_mac80211(mvm->noa_vif);
100e705c121SKalle Valo 	if (!mvmvif->ap_ibss_active)
101e705c121SKalle Valo 		return;
102e705c121SKalle Valo 
103*650cadb7SGregory Greenman 	phy_id = mvmvif->deflink.phy_ctxt->id;
104e705c121SKalle Valo 	beacon_int = mvm->noa_vif->bss_conf.beacon_int;
105e705c121SKalle Valo 
106e705c121SKalle Valo 	for (i = 0; i < MAX_BINDINGS; i++) {
10772cbb73eSDavid Spinadel 		struct iwl_time_quota_data *data =
10872cbb73eSDavid Spinadel 					iwl_mvm_quota_cmd_get_quota(mvm, cmd,
10972cbb73eSDavid Spinadel 								    i);
11072cbb73eSDavid Spinadel 		u32 id_n_c = le32_to_cpu(data->id_and_color);
111e705c121SKalle Valo 		u32 id = (id_n_c & FW_CTXT_ID_MSK) >> FW_CTXT_ID_POS;
11272cbb73eSDavid Spinadel 		u32 quota = le32_to_cpu(data->quota);
113e705c121SKalle Valo 
114e705c121SKalle Valo 		if (id != phy_id)
115e705c121SKalle Valo 			continue;
116e705c121SKalle Valo 
117e705c121SKalle Valo 		quota *= (beacon_int - mvm->noa_duration);
118e705c121SKalle Valo 		quota /= beacon_int;
119e705c121SKalle Valo 
120e705c121SKalle Valo 		IWL_DEBUG_QUOTA(mvm, "quota: adjust for NoA from %d to %d\n",
12172cbb73eSDavid Spinadel 				le32_to_cpu(data->quota), quota);
122e705c121SKalle Valo 
12372cbb73eSDavid Spinadel 		data->quota = cpu_to_le32(quota);
124e705c121SKalle Valo 	}
125e705c121SKalle Valo #endif
126e705c121SKalle Valo }
127e705c121SKalle Valo 
iwl_mvm_update_quotas(struct iwl_mvm * mvm,bool force_update,struct ieee80211_vif * disabled_vif)128e705c121SKalle Valo int iwl_mvm_update_quotas(struct iwl_mvm *mvm,
129e705c121SKalle Valo 			  bool force_update,
130e705c121SKalle Valo 			  struct ieee80211_vif *disabled_vif)
131e705c121SKalle Valo {
132e705c121SKalle Valo 	struct iwl_time_quota_cmd cmd = {};
133e705c121SKalle Valo 	int i, idx, err, num_active_macs, quota, quota_rem, n_non_lowlat;
134e705c121SKalle Valo 	struct iwl_mvm_quota_iterator_data data = {
135e705c121SKalle Valo 		.n_interfaces = {},
136e705c121SKalle Valo 		.colors = { -1, -1, -1, -1 },
137e705c121SKalle Valo 		.disabled_vif = disabled_vif,
138e705c121SKalle Valo 	};
139e705c121SKalle Valo 	struct iwl_time_quota_cmd *last = &mvm->last_quota_cmd;
14072cbb73eSDavid Spinadel 	struct iwl_time_quota_data *qdata, *last_data;
141e705c121SKalle Valo 	bool send = false;
142e705c121SKalle Valo 
143e705c121SKalle Valo 	lockdep_assert_held(&mvm->mutex);
144e705c121SKalle Valo 
145dad3340fSShaul Triebitz 	if (fw_has_capa(&mvm->fw->ucode_capa,
146dad3340fSShaul Triebitz 			IWL_UCODE_TLV_CAPA_DYNAMIC_QUOTA))
147dad3340fSShaul Triebitz 		return 0;
148dad3340fSShaul Triebitz 
149e705c121SKalle Valo 	/* update all upon completion */
150e705c121SKalle Valo 	if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))
151e705c121SKalle Valo 		return 0;
152e705c121SKalle Valo 
153e705c121SKalle Valo 	/* iterator data above must match */
154e705c121SKalle Valo 	BUILD_BUG_ON(MAX_BINDINGS != 4);
155e705c121SKalle Valo 
156e705c121SKalle Valo 	ieee80211_iterate_active_interfaces_atomic(
157e705c121SKalle Valo 		mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
158e705c121SKalle Valo 		iwl_mvm_quota_iterator, &data);
159e705c121SKalle Valo 
160e705c121SKalle Valo 	/*
161e705c121SKalle Valo 	 * The FW's scheduling session consists of
162e705c121SKalle Valo 	 * IWL_MVM_MAX_QUOTA fragments. Divide these fragments
163e705c121SKalle Valo 	 * equally between all the bindings that require quota
164e705c121SKalle Valo 	 */
165e705c121SKalle Valo 	num_active_macs = 0;
166e705c121SKalle Valo 	for (i = 0; i < MAX_BINDINGS; i++) {
16772cbb73eSDavid Spinadel 		qdata = iwl_mvm_quota_cmd_get_quota(mvm, &cmd, i);
16872cbb73eSDavid Spinadel 		qdata->id_and_color = cpu_to_le32(FW_CTXT_INVALID);
169e705c121SKalle Valo 		num_active_macs += data.n_interfaces[i];
170e705c121SKalle Valo 	}
171e705c121SKalle Valo 
172e705c121SKalle Valo 	n_non_lowlat = num_active_macs;
173e705c121SKalle Valo 
174e705c121SKalle Valo 	if (data.n_low_latency_bindings == 1) {
175e705c121SKalle Valo 		for (i = 0; i < MAX_BINDINGS; i++) {
176e705c121SKalle Valo 			if (data.low_latency[i]) {
177e705c121SKalle Valo 				n_non_lowlat -= data.n_interfaces[i];
178e705c121SKalle Valo 				break;
179e705c121SKalle Valo 			}
180e705c121SKalle Valo 		}
181e705c121SKalle Valo 	}
182e705c121SKalle Valo 
183e705c121SKalle Valo 	if (data.n_low_latency_bindings == 1 && n_non_lowlat) {
184e705c121SKalle Valo 		/*
185e705c121SKalle Valo 		 * Reserve quota for the low latency binding in case that
186e705c121SKalle Valo 		 * there are several data bindings but only a single
187e705c121SKalle Valo 		 * low latency one. Split the rest of the quota equally
188e705c121SKalle Valo 		 * between the other data interfaces.
189e705c121SKalle Valo 		 */
190e705c121SKalle Valo 		quota = (QUOTA_100 - QUOTA_LOWLAT_MIN) / n_non_lowlat;
191e705c121SKalle Valo 		quota_rem = QUOTA_100 - n_non_lowlat * quota -
192e705c121SKalle Valo 			    QUOTA_LOWLAT_MIN;
193e705c121SKalle Valo 		IWL_DEBUG_QUOTA(mvm,
194e705c121SKalle Valo 				"quota: low-latency binding active, remaining quota per other binding: %d\n",
195e705c121SKalle Valo 				quota);
196e705c121SKalle Valo 	} else if (num_active_macs) {
197e705c121SKalle Valo 		/*
198e705c121SKalle Valo 		 * There are 0 or more than 1 low latency bindings, or all the
199e705c121SKalle Valo 		 * data interfaces belong to the single low latency binding.
200e705c121SKalle Valo 		 * Split the quota equally between the data interfaces.
201e705c121SKalle Valo 		 */
202e705c121SKalle Valo 		quota = QUOTA_100 / num_active_macs;
203e705c121SKalle Valo 		quota_rem = QUOTA_100 % num_active_macs;
204e705c121SKalle Valo 		IWL_DEBUG_QUOTA(mvm,
205e705c121SKalle Valo 				"quota: splitting evenly per binding: %d\n",
206e705c121SKalle Valo 				quota);
207e705c121SKalle Valo 	} else {
208e705c121SKalle Valo 		/* values don't really matter - won't be used */
209e705c121SKalle Valo 		quota = 0;
210e705c121SKalle Valo 		quota_rem = 0;
211e705c121SKalle Valo 	}
212e705c121SKalle Valo 
213e705c121SKalle Valo 	for (idx = 0, i = 0; i < MAX_BINDINGS; i++) {
214e705c121SKalle Valo 		if (data.colors[i] < 0)
215e705c121SKalle Valo 			continue;
216e705c121SKalle Valo 
21772cbb73eSDavid Spinadel 		qdata = iwl_mvm_quota_cmd_get_quota(mvm, &cmd, idx);
21872cbb73eSDavid Spinadel 
21972cbb73eSDavid Spinadel 		qdata->id_and_color =
220e705c121SKalle Valo 			cpu_to_le32(FW_CMD_ID_AND_COLOR(i, data.colors[i]));
221e705c121SKalle Valo 
222e705c121SKalle Valo 		if (data.n_interfaces[i] <= 0)
22372cbb73eSDavid Spinadel 			qdata->quota = cpu_to_le32(0);
224a80c1cf9SJohannes Berg #ifdef CONFIG_IWLWIFI_DEBUGFS
225a80c1cf9SJohannes Berg 		else if (data.dbgfs_min[i])
22672cbb73eSDavid Spinadel 			qdata->quota =
227a80c1cf9SJohannes Berg 				cpu_to_le32(data.dbgfs_min[i] * QUOTA_100 / 100);
228a80c1cf9SJohannes Berg #endif
229e705c121SKalle Valo 		else if (data.n_low_latency_bindings == 1 && n_non_lowlat &&
230e705c121SKalle Valo 			 data.low_latency[i])
231e705c121SKalle Valo 			/*
232e705c121SKalle Valo 			 * There is more than one binding, but only one of the
233e705c121SKalle Valo 			 * bindings is in low latency. For this case, allocate
234e705c121SKalle Valo 			 * the minimal required quota for the low latency
235e705c121SKalle Valo 			 * binding.
236e705c121SKalle Valo 			 */
23772cbb73eSDavid Spinadel 			qdata->quota = cpu_to_le32(QUOTA_LOWLAT_MIN);
238e705c121SKalle Valo 		else
23972cbb73eSDavid Spinadel 			qdata->quota =
240e705c121SKalle Valo 				cpu_to_le32(quota * data.n_interfaces[i]);
241e705c121SKalle Valo 
24272cbb73eSDavid Spinadel 		WARN_ONCE(le32_to_cpu(qdata->quota) > QUOTA_100,
243e705c121SKalle Valo 			  "Binding=%d, quota=%u > max=%u\n",
24472cbb73eSDavid Spinadel 			  idx, le32_to_cpu(qdata->quota), QUOTA_100);
245e705c121SKalle Valo 
24672cbb73eSDavid Spinadel 		qdata->max_duration = cpu_to_le32(0);
247e705c121SKalle Valo 
248e705c121SKalle Valo 		idx++;
249e705c121SKalle Valo 	}
250e705c121SKalle Valo 
251e705c121SKalle Valo 	/* Give the remainder of the session to the first data binding */
252e705c121SKalle Valo 	for (i = 0; i < MAX_BINDINGS; i++) {
25372cbb73eSDavid Spinadel 		qdata = iwl_mvm_quota_cmd_get_quota(mvm, &cmd, i);
25472cbb73eSDavid Spinadel 		if (le32_to_cpu(qdata->quota) != 0) {
25572cbb73eSDavid Spinadel 			le32_add_cpu(&qdata->quota, quota_rem);
256e705c121SKalle Valo 			IWL_DEBUG_QUOTA(mvm,
257e705c121SKalle Valo 					"quota: giving remainder of %d to binding %d\n",
258e705c121SKalle Valo 					quota_rem, i);
259e705c121SKalle Valo 			break;
260e705c121SKalle Valo 		}
261e705c121SKalle Valo 	}
262e705c121SKalle Valo 
263e705c121SKalle Valo 	iwl_mvm_adjust_quota_for_noa(mvm, &cmd);
264e705c121SKalle Valo 
265e705c121SKalle Valo 	/* check that we have non-zero quota for all valid bindings */
266e705c121SKalle Valo 	for (i = 0; i < MAX_BINDINGS; i++) {
26772cbb73eSDavid Spinadel 		qdata = iwl_mvm_quota_cmd_get_quota(mvm, &cmd, i);
26872cbb73eSDavid Spinadel 		last_data = iwl_mvm_quota_cmd_get_quota(mvm, last, i);
26972cbb73eSDavid Spinadel 		if (qdata->id_and_color != last_data->id_and_color)
270e705c121SKalle Valo 			send = true;
27172cbb73eSDavid Spinadel 		if (qdata->max_duration != last_data->max_duration)
272e705c121SKalle Valo 			send = true;
27372cbb73eSDavid Spinadel 		if (abs((int)le32_to_cpu(qdata->quota) -
27472cbb73eSDavid Spinadel 			(int)le32_to_cpu(last_data->quota))
275e705c121SKalle Valo 						> IWL_MVM_QUOTA_THRESHOLD)
276e705c121SKalle Valo 			send = true;
27772cbb73eSDavid Spinadel 		if (qdata->id_and_color == cpu_to_le32(FW_CTXT_INVALID))
278e705c121SKalle Valo 			continue;
27972cbb73eSDavid Spinadel 		WARN_ONCE(qdata->quota == 0,
280e705c121SKalle Valo 			  "zero quota on binding %d\n", i);
281e705c121SKalle Valo 	}
282e705c121SKalle Valo 
283e705c121SKalle Valo 	if (!send && !force_update) {
284e705c121SKalle Valo 		/* don't send a practically unchanged command, the firmware has
285e705c121SKalle Valo 		 * to re-initialize a lot of state and that can have an adverse
286e705c121SKalle Valo 		 * impact on it
287e705c121SKalle Valo 		 */
288e705c121SKalle Valo 		return 0;
289e705c121SKalle Valo 	}
290e705c121SKalle Valo 
29172cbb73eSDavid Spinadel 	err = iwl_mvm_send_cmd_pdu(mvm, TIME_QUOTA_CMD, 0,
29272cbb73eSDavid Spinadel 				   iwl_mvm_quota_cmd_size(mvm), &cmd);
293e705c121SKalle Valo 
294e705c121SKalle Valo 	if (err)
295e705c121SKalle Valo 		IWL_ERR(mvm, "Failed to send quota: %d\n", err);
296e705c121SKalle Valo 	else
297e705c121SKalle Valo 		mvm->last_quota_cmd = cmd;
298e705c121SKalle Valo 	return err;
299e705c121SKalle Valo }
300