Path: blob/main/sys/contrib/dev/iwlwifi/mvm/quota.c
48287 views
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause1/*2* Copyright (C) 2012-2014, 2018, 2021-2022, 2025 Intel Corporation3* Copyright (C) 2013-2014 Intel Mobile Communications GmbH4* Copyright (C) 2016-2017 Intel Deutschland GmbH5*/6#include <net/mac80211.h>7#include "fw-api.h"8#include "mvm.h"910#define QUOTA_100 IWL_MVM_MAX_QUOTA11#define QUOTA_LOWLAT_MIN ((QUOTA_100 * IWL_MVM_LOWLAT_QUOTA_MIN_PERCENT) / 100)1213struct iwl_mvm_quota_iterator_data {14int n_interfaces[MAX_BINDINGS];15int colors[MAX_BINDINGS];16int low_latency[MAX_BINDINGS];17#ifdef CONFIG_IWLWIFI_DEBUGFS18int dbgfs_min[MAX_BINDINGS];19#endif20int n_low_latency_bindings;21struct ieee80211_vif *disabled_vif;22};2324static void iwl_mvm_quota_iterator(void *_data, u8 *mac,25struct ieee80211_vif *vif)26{27struct iwl_mvm_quota_iterator_data *data = _data;28struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);29u16 id;3031/* skip disabled interfaces here immediately */32if (vif == data->disabled_vif)33return;3435if (!mvmvif->deflink.phy_ctxt)36return;3738/* currently, PHY ID == binding ID */39id = mvmvif->deflink.phy_ctxt->id;4041/* need at least one binding per PHY */42BUILD_BUG_ON(NUM_PHY_CTX > MAX_BINDINGS);4344if (WARN_ON_ONCE(id >= MAX_BINDINGS))45return;4647switch (vif->type) {48case NL80211_IFTYPE_STATION:49if (vif->cfg.assoc)50break;51return;52case NL80211_IFTYPE_AP:53case NL80211_IFTYPE_ADHOC:54if (mvmvif->ap_ibss_active)55break;56return;57case NL80211_IFTYPE_MONITOR:58if (mvmvif->monitor_active)59break;60return;61case NL80211_IFTYPE_P2P_DEVICE:62return;63default:64WARN_ON_ONCE(1);65return;66}6768if (data->colors[id] < 0)69data->colors[id] = mvmvif->deflink.phy_ctxt->color;70else71WARN_ON_ONCE(data->colors[id] !=72mvmvif->deflink.phy_ctxt->color);7374data->n_interfaces[id]++;7576#ifdef CONFIG_IWLWIFI_DEBUGFS77if (mvmvif->dbgfs_quota_min)78data->dbgfs_min[id] = max(data->dbgfs_min[id],79mvmvif->dbgfs_quota_min);80#endif8182if (iwl_mvm_vif_low_latency(mvmvif) && !data->low_latency[id]) {83data->n_low_latency_bindings++;84data->low_latency[id] = true;85}86}8788int iwl_mvm_update_quotas(struct iwl_mvm *mvm,89bool force_update,90struct ieee80211_vif *disabled_vif)91{92struct iwl_time_quota_cmd cmd = {};93int i, idx, err, num_active_macs, quota, quota_rem, n_non_lowlat;94struct iwl_mvm_quota_iterator_data data = {95.n_interfaces = {},96.colors = { -1, -1, -1, -1 },97.disabled_vif = disabled_vif,98};99struct iwl_time_quota_cmd *last = &mvm->last_quota_cmd;100struct iwl_time_quota_data *qdata, *last_data;101bool send = false;102103lockdep_assert_held(&mvm->mutex);104105if (fw_has_capa(&mvm->fw->ucode_capa,106IWL_UCODE_TLV_CAPA_DYNAMIC_QUOTA))107return 0;108109/* update all upon completion */110if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))111return 0;112113/* iterator data above must match */114BUILD_BUG_ON(MAX_BINDINGS != 4);115116ieee80211_iterate_active_interfaces_atomic(117mvm->hw, IEEE80211_IFACE_ITER_NORMAL,118iwl_mvm_quota_iterator, &data);119120/*121* The FW's scheduling session consists of122* IWL_MVM_MAX_QUOTA fragments. Divide these fragments123* equally between all the bindings that require quota124*/125num_active_macs = 0;126for (i = 0; i < MAX_BINDINGS; i++) {127qdata = iwl_mvm_quota_cmd_get_quota(mvm, &cmd, i);128qdata->id_and_color = cpu_to_le32(FW_CTXT_INVALID);129num_active_macs += data.n_interfaces[i];130}131132n_non_lowlat = num_active_macs;133134if (data.n_low_latency_bindings == 1) {135for (i = 0; i < MAX_BINDINGS; i++) {136if (data.low_latency[i]) {137n_non_lowlat -= data.n_interfaces[i];138break;139}140}141}142143if (data.n_low_latency_bindings == 1 && n_non_lowlat) {144/*145* Reserve quota for the low latency binding in case that146* there are several data bindings but only a single147* low latency one. Split the rest of the quota equally148* between the other data interfaces.149*/150quota = (QUOTA_100 - QUOTA_LOWLAT_MIN) / n_non_lowlat;151quota_rem = QUOTA_100 - n_non_lowlat * quota -152QUOTA_LOWLAT_MIN;153IWL_DEBUG_QUOTA(mvm,154"quota: low-latency binding active, remaining quota per other binding: %d\n",155quota);156} else if (num_active_macs) {157/*158* There are 0 or more than 1 low latency bindings, or all the159* data interfaces belong to the single low latency binding.160* Split the quota equally between the data interfaces.161*/162quota = QUOTA_100 / num_active_macs;163quota_rem = QUOTA_100 % num_active_macs;164IWL_DEBUG_QUOTA(mvm,165"quota: splitting evenly per binding: %d\n",166quota);167} else {168/* values don't really matter - won't be used */169quota = 0;170quota_rem = 0;171}172173for (idx = 0, i = 0; i < MAX_BINDINGS; i++) {174if (data.colors[i] < 0)175continue;176177qdata = iwl_mvm_quota_cmd_get_quota(mvm, &cmd, idx);178179qdata->id_and_color =180cpu_to_le32(FW_CMD_ID_AND_COLOR(i, data.colors[i]));181182if (data.n_interfaces[i] <= 0)183qdata->quota = cpu_to_le32(0);184#ifdef CONFIG_IWLWIFI_DEBUGFS185else if (data.dbgfs_min[i])186qdata->quota =187cpu_to_le32(data.dbgfs_min[i] * QUOTA_100 / 100);188#endif189else if (data.n_low_latency_bindings == 1 && n_non_lowlat &&190data.low_latency[i])191/*192* There is more than one binding, but only one of the193* bindings is in low latency. For this case, allocate194* the minimal required quota for the low latency195* binding.196*/197qdata->quota = cpu_to_le32(QUOTA_LOWLAT_MIN);198else199qdata->quota =200cpu_to_le32(quota * data.n_interfaces[i]);201202WARN_ONCE(le32_to_cpu(qdata->quota) > QUOTA_100,203"Binding=%d, quota=%u > max=%u\n",204idx, le32_to_cpu(qdata->quota), QUOTA_100);205206qdata->max_duration = cpu_to_le32(0);207208idx++;209}210211/* Give the remainder of the session to the first data binding */212for (i = 0; i < MAX_BINDINGS; i++) {213qdata = iwl_mvm_quota_cmd_get_quota(mvm, &cmd, i);214if (le32_to_cpu(qdata->quota) != 0) {215le32_add_cpu(&qdata->quota, quota_rem);216IWL_DEBUG_QUOTA(mvm,217"quota: giving remainder of %d to binding %d\n",218quota_rem, i);219break;220}221}222223/* check that we have non-zero quota for all valid bindings */224for (i = 0; i < MAX_BINDINGS; i++) {225qdata = iwl_mvm_quota_cmd_get_quota(mvm, &cmd, i);226last_data = iwl_mvm_quota_cmd_get_quota(mvm, last, i);227if (qdata->id_and_color != last_data->id_and_color)228send = true;229if (qdata->max_duration != last_data->max_duration)230send = true;231if (abs((int)le32_to_cpu(qdata->quota) -232(int)le32_to_cpu(last_data->quota))233> IWL_MVM_QUOTA_THRESHOLD)234send = true;235if (qdata->id_and_color == cpu_to_le32(FW_CTXT_INVALID))236continue;237WARN_ONCE(qdata->quota == 0,238"zero quota on binding %d\n", i);239}240241if (!send && !force_update) {242/* don't send a practically unchanged command, the firmware has243* to re-initialize a lot of state and that can have an adverse244* impact on it245*/246return 0;247}248249err = iwl_mvm_send_cmd_pdu(mvm, TIME_QUOTA_CMD, 0,250iwl_mvm_quota_cmd_size(mvm), &cmd);251252if (err)253IWL_ERR(mvm, "Failed to send quota: %d\n", err);254else255mvm->last_quota_cmd = cmd;256return err;257}258259260