300 lines · c
1// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause2/*3 * Copyright (C) 2012-2014, 2018, 2021-2022 Intel Corporation4 * Copyright (C) 2013-2014 Intel Mobile Communications GmbH5 * Copyright (C) 2016-2017 Intel Deutschland GmbH6 */7#include <net/mac80211.h>8#include "fw-api.h"9#include "mvm.h"10 11#define QUOTA_100 IWL_MVM_MAX_QUOTA12#define QUOTA_LOWLAT_MIN ((QUOTA_100 * IWL_MVM_LOWLAT_QUOTA_MIN_PERCENT) / 100)13 14struct 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_DEBUGFS19 int dbgfs_min[MAX_BINDINGS];20#endif21 int n_low_latency_bindings;22 struct ieee80211_vif *disabled_vif;23};24 25static 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 else72 WARN_ON_ONCE(data->colors[id] !=73 mvmvif->deflink.phy_ctxt->color);74 75 data->n_interfaces[id]++;76 77#ifdef CONFIG_IWLWIFI_DEBUGFS78 if (mvmvif->dbgfs_quota_min)79 data->dbgfs_min[id] = max(data->dbgfs_min[id],80 mvmvif->dbgfs_quota_min);81#endif82 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 89static void iwl_mvm_adjust_quota_for_noa(struct iwl_mvm *mvm,90 struct iwl_time_quota_cmd *cmd)91{92#ifdef CONFIG_NL80211_TESTMODE93 struct iwl_mvm_vif *mvmvif;94 int i, phy_id = -1, beacon_int = 0;95 96 if (!mvm->noa_duration || !mvm->noa_vif)97 return;98 99 mvmvif = iwl_mvm_vif_from_mac80211(mvm->noa_vif);100 if (!mvmvif->ap_ibss_active)101 return;102 103 phy_id = mvmvif->deflink.phy_ctxt->id;104 beacon_int = mvm->noa_vif->bss_conf.beacon_int;105 106 for (i = 0; i < MAX_BINDINGS; i++) {107 struct iwl_time_quota_data *data =108 iwl_mvm_quota_cmd_get_quota(mvm, cmd,109 i);110 u32 id_n_c = le32_to_cpu(data->id_and_color);111 u32 id = (id_n_c & FW_CTXT_ID_MSK) >> FW_CTXT_ID_POS;112 u32 quota = le32_to_cpu(data->quota);113 114 if (id != phy_id)115 continue;116 117 quota *= (beacon_int - mvm->noa_duration);118 quota /= beacon_int;119 120 IWL_DEBUG_QUOTA(mvm, "quota: adjust for NoA from %d to %d\n",121 le32_to_cpu(data->quota), quota);122 123 data->quota = cpu_to_le32(quota);124 }125#endif126}127 128int iwl_mvm_update_quotas(struct iwl_mvm *mvm,129 bool force_update,130 struct ieee80211_vif *disabled_vif)131{132 struct iwl_time_quota_cmd cmd = {};133 int i, idx, err, num_active_macs, quota, quota_rem, n_non_lowlat;134 struct iwl_mvm_quota_iterator_data data = {135 .n_interfaces = {},136 .colors = { -1, -1, -1, -1 },137 .disabled_vif = disabled_vif,138 };139 struct iwl_time_quota_cmd *last = &mvm->last_quota_cmd;140 struct iwl_time_quota_data *qdata, *last_data;141 bool send = false;142 143 lockdep_assert_held(&mvm->mutex);144 145 if (fw_has_capa(&mvm->fw->ucode_capa,146 IWL_UCODE_TLV_CAPA_DYNAMIC_QUOTA))147 return 0;148 149 /* update all upon completion */150 if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))151 return 0;152 153 /* iterator data above must match */154 BUILD_BUG_ON(MAX_BINDINGS != 4);155 156 ieee80211_iterate_active_interfaces_atomic(157 mvm->hw, IEEE80211_IFACE_ITER_NORMAL,158 iwl_mvm_quota_iterator, &data);159 160 /*161 * The FW's scheduling session consists of162 * IWL_MVM_MAX_QUOTA fragments. Divide these fragments163 * equally between all the bindings that require quota164 */165 num_active_macs = 0;166 for (i = 0; i < MAX_BINDINGS; i++) {167 qdata = iwl_mvm_quota_cmd_get_quota(mvm, &cmd, i);168 qdata->id_and_color = cpu_to_le32(FW_CTXT_INVALID);169 num_active_macs += data.n_interfaces[i];170 }171 172 n_non_lowlat = num_active_macs;173 174 if (data.n_low_latency_bindings == 1) {175 for (i = 0; i < MAX_BINDINGS; i++) {176 if (data.low_latency[i]) {177 n_non_lowlat -= data.n_interfaces[i];178 break;179 }180 }181 }182 183 if (data.n_low_latency_bindings == 1 && n_non_lowlat) {184 /*185 * Reserve quota for the low latency binding in case that186 * there are several data bindings but only a single187 * low latency one. Split the rest of the quota equally188 * between the other data interfaces.189 */190 quota = (QUOTA_100 - QUOTA_LOWLAT_MIN) / n_non_lowlat;191 quota_rem = QUOTA_100 - n_non_lowlat * quota -192 QUOTA_LOWLAT_MIN;193 IWL_DEBUG_QUOTA(mvm,194 "quota: low-latency binding active, remaining quota per other binding: %d\n",195 quota);196 } else if (num_active_macs) {197 /*198 * There are 0 or more than 1 low latency bindings, or all the199 * data interfaces belong to the single low latency binding.200 * Split the quota equally between the data interfaces.201 */202 quota = QUOTA_100 / num_active_macs;203 quota_rem = QUOTA_100 % num_active_macs;204 IWL_DEBUG_QUOTA(mvm,205 "quota: splitting evenly per binding: %d\n",206 quota);207 } else {208 /* values don't really matter - won't be used */209 quota = 0;210 quota_rem = 0;211 }212 213 for (idx = 0, i = 0; i < MAX_BINDINGS; i++) {214 if (data.colors[i] < 0)215 continue;216 217 qdata = iwl_mvm_quota_cmd_get_quota(mvm, &cmd, idx);218 219 qdata->id_and_color =220 cpu_to_le32(FW_CMD_ID_AND_COLOR(i, data.colors[i]));221 222 if (data.n_interfaces[i] <= 0)223 qdata->quota = cpu_to_le32(0);224#ifdef CONFIG_IWLWIFI_DEBUGFS225 else if (data.dbgfs_min[i])226 qdata->quota =227 cpu_to_le32(data.dbgfs_min[i] * QUOTA_100 / 100);228#endif229 else if (data.n_low_latency_bindings == 1 && n_non_lowlat &&230 data.low_latency[i])231 /*232 * There is more than one binding, but only one of the233 * bindings is in low latency. For this case, allocate234 * the minimal required quota for the low latency235 * binding.236 */237 qdata->quota = cpu_to_le32(QUOTA_LOWLAT_MIN);238 else239 qdata->quota =240 cpu_to_le32(quota * data.n_interfaces[i]);241 242 WARN_ONCE(le32_to_cpu(qdata->quota) > QUOTA_100,243 "Binding=%d, quota=%u > max=%u\n",244 idx, le32_to_cpu(qdata->quota), QUOTA_100);245 246 qdata->max_duration = cpu_to_le32(0);247 248 idx++;249 }250 251 /* Give the remainder of the session to the first data binding */252 for (i = 0; i < MAX_BINDINGS; i++) {253 qdata = iwl_mvm_quota_cmd_get_quota(mvm, &cmd, i);254 if (le32_to_cpu(qdata->quota) != 0) {255 le32_add_cpu(&qdata->quota, quota_rem);256 IWL_DEBUG_QUOTA(mvm,257 "quota: giving remainder of %d to binding %d\n",258 quota_rem, i);259 break;260 }261 }262 263 iwl_mvm_adjust_quota_for_noa(mvm, &cmd);264 265 /* check that we have non-zero quota for all valid bindings */266 for (i = 0; i < MAX_BINDINGS; i++) {267 qdata = iwl_mvm_quota_cmd_get_quota(mvm, &cmd, i);268 last_data = iwl_mvm_quota_cmd_get_quota(mvm, last, i);269 if (qdata->id_and_color != last_data->id_and_color)270 send = true;271 if (qdata->max_duration != last_data->max_duration)272 send = true;273 if (abs((int)le32_to_cpu(qdata->quota) -274 (int)le32_to_cpu(last_data->quota))275 > IWL_MVM_QUOTA_THRESHOLD)276 send = true;277 if (qdata->id_and_color == cpu_to_le32(FW_CTXT_INVALID))278 continue;279 WARN_ONCE(qdata->quota == 0,280 "zero quota on binding %d\n", i);281 }282 283 if (!send && !force_update) {284 /* don't send a practically unchanged command, the firmware has285 * to re-initialize a lot of state and that can have an adverse286 * impact on it287 */288 return 0;289 }290 291 err = iwl_mvm_send_cmd_pdu(mvm, TIME_QUOTA_CMD, 0,292 iwl_mvm_quota_cmd_size(mvm), &cmd);293 294 if (err)295 IWL_ERR(mvm, "Failed to send quota: %d\n", err);296 else297 mvm->last_quota_cmd = cmd;298 return err;299}300