Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/contrib/dev/iwlwifi/mvm/quota.c
48287 views
1
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2
/*
3
* Copyright (C) 2012-2014, 2018, 2021-2022, 2025 Intel Corporation
4
* Copyright (C) 2013-2014 Intel Mobile Communications GmbH
5
* Copyright (C) 2016-2017 Intel Deutschland GmbH
6
*/
7
#include <net/mac80211.h>
8
#include "fw-api.h"
9
#include "mvm.h"
10
11
#define QUOTA_100 IWL_MVM_MAX_QUOTA
12
#define QUOTA_LOWLAT_MIN ((QUOTA_100 * IWL_MVM_LOWLAT_QUOTA_MIN_PERCENT) / 100)
13
14
struct iwl_mvm_quota_iterator_data {
15
int n_interfaces[MAX_BINDINGS];
16
int colors[MAX_BINDINGS];
17
int low_latency[MAX_BINDINGS];
18
#ifdef CONFIG_IWLWIFI_DEBUGFS
19
int dbgfs_min[MAX_BINDINGS];
20
#endif
21
int n_low_latency_bindings;
22
struct ieee80211_vif *disabled_vif;
23
};
24
25
static void iwl_mvm_quota_iterator(void *_data, u8 *mac,
26
struct ieee80211_vif *vif)
27
{
28
struct iwl_mvm_quota_iterator_data *data = _data;
29
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
30
u16 id;
31
32
/* skip disabled interfaces here immediately */
33
if (vif == data->disabled_vif)
34
return;
35
36
if (!mvmvif->deflink.phy_ctxt)
37
return;
38
39
/* currently, PHY ID == binding ID */
40
id = mvmvif->deflink.phy_ctxt->id;
41
42
/* need at least one binding per PHY */
43
BUILD_BUG_ON(NUM_PHY_CTX > MAX_BINDINGS);
44
45
if (WARN_ON_ONCE(id >= MAX_BINDINGS))
46
return;
47
48
switch (vif->type) {
49
case NL80211_IFTYPE_STATION:
50
if (vif->cfg.assoc)
51
break;
52
return;
53
case NL80211_IFTYPE_AP:
54
case NL80211_IFTYPE_ADHOC:
55
if (mvmvif->ap_ibss_active)
56
break;
57
return;
58
case NL80211_IFTYPE_MONITOR:
59
if (mvmvif->monitor_active)
60
break;
61
return;
62
case NL80211_IFTYPE_P2P_DEVICE:
63
return;
64
default:
65
WARN_ON_ONCE(1);
66
return;
67
}
68
69
if (data->colors[id] < 0)
70
data->colors[id] = mvmvif->deflink.phy_ctxt->color;
71
else
72
WARN_ON_ONCE(data->colors[id] !=
73
mvmvif->deflink.phy_ctxt->color);
74
75
data->n_interfaces[id]++;
76
77
#ifdef CONFIG_IWLWIFI_DEBUGFS
78
if (mvmvif->dbgfs_quota_min)
79
data->dbgfs_min[id] = max(data->dbgfs_min[id],
80
mvmvif->dbgfs_quota_min);
81
#endif
82
83
if (iwl_mvm_vif_low_latency(mvmvif) && !data->low_latency[id]) {
84
data->n_low_latency_bindings++;
85
data->low_latency[id] = true;
86
}
87
}
88
89
int iwl_mvm_update_quotas(struct iwl_mvm *mvm,
90
bool force_update,
91
struct ieee80211_vif *disabled_vif)
92
{
93
struct iwl_time_quota_cmd cmd = {};
94
int i, idx, err, num_active_macs, quota, quota_rem, n_non_lowlat;
95
struct iwl_mvm_quota_iterator_data data = {
96
.n_interfaces = {},
97
.colors = { -1, -1, -1, -1 },
98
.disabled_vif = disabled_vif,
99
};
100
struct iwl_time_quota_cmd *last = &mvm->last_quota_cmd;
101
struct iwl_time_quota_data *qdata, *last_data;
102
bool send = false;
103
104
lockdep_assert_held(&mvm->mutex);
105
106
if (fw_has_capa(&mvm->fw->ucode_capa,
107
IWL_UCODE_TLV_CAPA_DYNAMIC_QUOTA))
108
return 0;
109
110
/* update all upon completion */
111
if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))
112
return 0;
113
114
/* iterator data above must match */
115
BUILD_BUG_ON(MAX_BINDINGS != 4);
116
117
ieee80211_iterate_active_interfaces_atomic(
118
mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
119
iwl_mvm_quota_iterator, &data);
120
121
/*
122
* The FW's scheduling session consists of
123
* IWL_MVM_MAX_QUOTA fragments. Divide these fragments
124
* equally between all the bindings that require quota
125
*/
126
num_active_macs = 0;
127
for (i = 0; i < MAX_BINDINGS; i++) {
128
qdata = iwl_mvm_quota_cmd_get_quota(mvm, &cmd, i);
129
qdata->id_and_color = cpu_to_le32(FW_CTXT_INVALID);
130
num_active_macs += data.n_interfaces[i];
131
}
132
133
n_non_lowlat = num_active_macs;
134
135
if (data.n_low_latency_bindings == 1) {
136
for (i = 0; i < MAX_BINDINGS; i++) {
137
if (data.low_latency[i]) {
138
n_non_lowlat -= data.n_interfaces[i];
139
break;
140
}
141
}
142
}
143
144
if (data.n_low_latency_bindings == 1 && n_non_lowlat) {
145
/*
146
* Reserve quota for the low latency binding in case that
147
* there are several data bindings but only a single
148
* low latency one. Split the rest of the quota equally
149
* between the other data interfaces.
150
*/
151
quota = (QUOTA_100 - QUOTA_LOWLAT_MIN) / n_non_lowlat;
152
quota_rem = QUOTA_100 - n_non_lowlat * quota -
153
QUOTA_LOWLAT_MIN;
154
IWL_DEBUG_QUOTA(mvm,
155
"quota: low-latency binding active, remaining quota per other binding: %d\n",
156
quota);
157
} else if (num_active_macs) {
158
/*
159
* There are 0 or more than 1 low latency bindings, or all the
160
* data interfaces belong to the single low latency binding.
161
* Split the quota equally between the data interfaces.
162
*/
163
quota = QUOTA_100 / num_active_macs;
164
quota_rem = QUOTA_100 % num_active_macs;
165
IWL_DEBUG_QUOTA(mvm,
166
"quota: splitting evenly per binding: %d\n",
167
quota);
168
} else {
169
/* values don't really matter - won't be used */
170
quota = 0;
171
quota_rem = 0;
172
}
173
174
for (idx = 0, i = 0; i < MAX_BINDINGS; i++) {
175
if (data.colors[i] < 0)
176
continue;
177
178
qdata = iwl_mvm_quota_cmd_get_quota(mvm, &cmd, idx);
179
180
qdata->id_and_color =
181
cpu_to_le32(FW_CMD_ID_AND_COLOR(i, data.colors[i]));
182
183
if (data.n_interfaces[i] <= 0)
184
qdata->quota = cpu_to_le32(0);
185
#ifdef CONFIG_IWLWIFI_DEBUGFS
186
else if (data.dbgfs_min[i])
187
qdata->quota =
188
cpu_to_le32(data.dbgfs_min[i] * QUOTA_100 / 100);
189
#endif
190
else if (data.n_low_latency_bindings == 1 && n_non_lowlat &&
191
data.low_latency[i])
192
/*
193
* There is more than one binding, but only one of the
194
* bindings is in low latency. For this case, allocate
195
* the minimal required quota for the low latency
196
* binding.
197
*/
198
qdata->quota = cpu_to_le32(QUOTA_LOWLAT_MIN);
199
else
200
qdata->quota =
201
cpu_to_le32(quota * data.n_interfaces[i]);
202
203
WARN_ONCE(le32_to_cpu(qdata->quota) > QUOTA_100,
204
"Binding=%d, quota=%u > max=%u\n",
205
idx, le32_to_cpu(qdata->quota), QUOTA_100);
206
207
qdata->max_duration = cpu_to_le32(0);
208
209
idx++;
210
}
211
212
/* Give the remainder of the session to the first data binding */
213
for (i = 0; i < MAX_BINDINGS; i++) {
214
qdata = iwl_mvm_quota_cmd_get_quota(mvm, &cmd, i);
215
if (le32_to_cpu(qdata->quota) != 0) {
216
le32_add_cpu(&qdata->quota, quota_rem);
217
IWL_DEBUG_QUOTA(mvm,
218
"quota: giving remainder of %d to binding %d\n",
219
quota_rem, i);
220
break;
221
}
222
}
223
224
/* check that we have non-zero quota for all valid bindings */
225
for (i = 0; i < MAX_BINDINGS; i++) {
226
qdata = iwl_mvm_quota_cmd_get_quota(mvm, &cmd, i);
227
last_data = iwl_mvm_quota_cmd_get_quota(mvm, last, i);
228
if (qdata->id_and_color != last_data->id_and_color)
229
send = true;
230
if (qdata->max_duration != last_data->max_duration)
231
send = true;
232
if (abs((int)le32_to_cpu(qdata->quota) -
233
(int)le32_to_cpu(last_data->quota))
234
> IWL_MVM_QUOTA_THRESHOLD)
235
send = true;
236
if (qdata->id_and_color == cpu_to_le32(FW_CTXT_INVALID))
237
continue;
238
WARN_ONCE(qdata->quota == 0,
239
"zero quota on binding %d\n", i);
240
}
241
242
if (!send && !force_update) {
243
/* don't send a practically unchanged command, the firmware has
244
* to re-initialize a lot of state and that can have an adverse
245
* impact on it
246
*/
247
return 0;
248
}
249
250
err = iwl_mvm_send_cmd_pdu(mvm, TIME_QUOTA_CMD, 0,
251
iwl_mvm_quota_cmd_size(mvm), &cmd);
252
253
if (err)
254
IWL_ERR(mvm, "Failed to send quota: %d\n", err);
255
else
256
mvm->last_quota_cmd = cmd;
257
return err;
258
}
259
260