linux/drivers/net/wireless/intel/iwlwifi/mvm/quota.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
   2/*
   3 * Copyright (C) 2012-2014, 2018 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
  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_DEBUGFS
  19        int dbgfs_min[MAX_BINDINGS];
  20#endif
  21        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->phy_ctxt)
  37                return;
  38
  39        /* currently, PHY ID == binding ID */
  40        id = mvmvif->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->bss_conf.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->phy_ctxt->color;
  71        else
  72                WARN_ON_ONCE(data->colors[id] != mvmvif->phy_ctxt->color);
  73
  74        data->n_interfaces[id]++;
  75
  76#ifdef CONFIG_IWLWIFI_DEBUGFS
  77        if (mvmvif->dbgfs_quota_min)
  78                data->dbgfs_min[id] = max(data->dbgfs_min[id],
  79                                          mvmvif->dbgfs_quota_min);
  80#endif
  81
  82        if (iwl_mvm_vif_low_latency(mvmvif) && !data->low_latency[id]) {
  83                data->n_low_latency_bindings++;
  84                data->low_latency[id] = true;
  85        }
  86}
  87
  88static void iwl_mvm_adjust_quota_for_noa(struct iwl_mvm *mvm,
  89                                         struct iwl_time_quota_cmd *cmd)
  90{
  91#ifdef CONFIG_NL80211_TESTMODE
  92        struct iwl_mvm_vif *mvmvif;
  93        int i, phy_id = -1, beacon_int = 0;
  94
  95        if (!mvm->noa_duration || !mvm->noa_vif)
  96                return;
  97
  98        mvmvif = iwl_mvm_vif_from_mac80211(mvm->noa_vif);
  99        if (!mvmvif->ap_ibss_active)
 100                return;
 101
 102        phy_id = mvmvif->phy_ctxt->id;
 103        beacon_int = mvm->noa_vif->bss_conf.beacon_int;
 104
 105        for (i = 0; i < MAX_BINDINGS; i++) {
 106                struct iwl_time_quota_data *data =
 107                                        iwl_mvm_quota_cmd_get_quota(mvm, cmd,
 108                                                                    i);
 109                u32 id_n_c = le32_to_cpu(data->id_and_color);
 110                u32 id = (id_n_c & FW_CTXT_ID_MSK) >> FW_CTXT_ID_POS;
 111                u32 quota = le32_to_cpu(data->quota);
 112
 113                if (id != phy_id)
 114                        continue;
 115
 116                quota *= (beacon_int - mvm->noa_duration);
 117                quota /= beacon_int;
 118
 119                IWL_DEBUG_QUOTA(mvm, "quota: adjust for NoA from %d to %d\n",
 120                                le32_to_cpu(data->quota), quota);
 121
 122                data->quota = cpu_to_le32(quota);
 123        }
 124#endif
 125}
 126
 127int iwl_mvm_update_quotas(struct iwl_mvm *mvm,
 128                          bool force_update,
 129                          struct ieee80211_vif *disabled_vif)
 130{
 131        struct iwl_time_quota_cmd cmd = {};
 132        int i, idx, err, num_active_macs, quota, quota_rem, n_non_lowlat;
 133        struct iwl_mvm_quota_iterator_data data = {
 134                .n_interfaces = {},
 135                .colors = { -1, -1, -1, -1 },
 136                .disabled_vif = disabled_vif,
 137        };
 138        struct iwl_time_quota_cmd *last = &mvm->last_quota_cmd;
 139        struct iwl_time_quota_data *qdata, *last_data;
 140        bool send = false;
 141
 142        lockdep_assert_held(&mvm->mutex);
 143
 144        if (fw_has_capa(&mvm->fw->ucode_capa,
 145                        IWL_UCODE_TLV_CAPA_DYNAMIC_QUOTA))
 146                return 0;
 147
 148        /* update all upon completion */
 149        if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))
 150                return 0;
 151
 152        /* iterator data above must match */
 153        BUILD_BUG_ON(MAX_BINDINGS != 4);
 154
 155        ieee80211_iterate_active_interfaces_atomic(
 156                mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
 157                iwl_mvm_quota_iterator, &data);
 158
 159        /*
 160         * The FW's scheduling session consists of
 161         * IWL_MVM_MAX_QUOTA fragments. Divide these fragments
 162         * equally between all the bindings that require quota
 163         */
 164        num_active_macs = 0;
 165        for (i = 0; i < MAX_BINDINGS; i++) {
 166                qdata = iwl_mvm_quota_cmd_get_quota(mvm, &cmd, i);
 167                qdata->id_and_color = cpu_to_le32(FW_CTXT_INVALID);
 168                num_active_macs += data.n_interfaces[i];
 169        }
 170
 171        n_non_lowlat = num_active_macs;
 172
 173        if (data.n_low_latency_bindings == 1) {
 174                for (i = 0; i < MAX_BINDINGS; i++) {
 175                        if (data.low_latency[i]) {
 176                                n_non_lowlat -= data.n_interfaces[i];
 177                                break;
 178                        }
 179                }
 180        }
 181
 182        if (data.n_low_latency_bindings == 1 && n_non_lowlat) {
 183                /*
 184                 * Reserve quota for the low latency binding in case that
 185                 * there are several data bindings but only a single
 186                 * low latency one. Split the rest of the quota equally
 187                 * between the other data interfaces.
 188                 */
 189                quota = (QUOTA_100 - QUOTA_LOWLAT_MIN) / n_non_lowlat;
 190                quota_rem = QUOTA_100 - n_non_lowlat * quota -
 191                            QUOTA_LOWLAT_MIN;
 192                IWL_DEBUG_QUOTA(mvm,
 193                                "quota: low-latency binding active, remaining quota per other binding: %d\n",
 194                                quota);
 195        } else if (num_active_macs) {
 196                /*
 197                 * There are 0 or more than 1 low latency bindings, or all the
 198                 * data interfaces belong to the single low latency binding.
 199                 * Split the quota equally between the data interfaces.
 200                 */
 201                quota = QUOTA_100 / num_active_macs;
 202                quota_rem = QUOTA_100 % num_active_macs;
 203                IWL_DEBUG_QUOTA(mvm,
 204                                "quota: splitting evenly per binding: %d\n",
 205                                quota);
 206        } else {
 207                /* values don't really matter - won't be used */
 208                quota = 0;
 209                quota_rem = 0;
 210        }
 211
 212        for (idx = 0, i = 0; i < MAX_BINDINGS; i++) {
 213                if (data.colors[i] < 0)
 214                        continue;
 215
 216                qdata = iwl_mvm_quota_cmd_get_quota(mvm, &cmd, idx);
 217
 218                qdata->id_and_color =
 219                        cpu_to_le32(FW_CMD_ID_AND_COLOR(i, data.colors[i]));
 220
 221                if (data.n_interfaces[i] <= 0)
 222                        qdata->quota = cpu_to_le32(0);
 223#ifdef CONFIG_IWLWIFI_DEBUGFS
 224                else if (data.dbgfs_min[i])
 225                        qdata->quota =
 226                                cpu_to_le32(data.dbgfs_min[i] * QUOTA_100 / 100);
 227#endif
 228                else if (data.n_low_latency_bindings == 1 && n_non_lowlat &&
 229                         data.low_latency[i])
 230                        /*
 231                         * There is more than one binding, but only one of the
 232                         * bindings is in low latency. For this case, allocate
 233                         * the minimal required quota for the low latency
 234                         * binding.
 235                         */
 236                        qdata->quota = cpu_to_le32(QUOTA_LOWLAT_MIN);
 237                else
 238                        qdata->quota =
 239                                cpu_to_le32(quota * data.n_interfaces[i]);
 240
 241                WARN_ONCE(le32_to_cpu(qdata->quota) > QUOTA_100,
 242                          "Binding=%d, quota=%u > max=%u\n",
 243                          idx, le32_to_cpu(qdata->quota), QUOTA_100);
 244
 245                qdata->max_duration = cpu_to_le32(0);
 246
 247                idx++;
 248        }
 249
 250        /* Give the remainder of the session to the first data binding */
 251        for (i = 0; i < MAX_BINDINGS; i++) {
 252                qdata = iwl_mvm_quota_cmd_get_quota(mvm, &cmd, i);
 253                if (le32_to_cpu(qdata->quota) != 0) {
 254                        le32_add_cpu(&qdata->quota, quota_rem);
 255                        IWL_DEBUG_QUOTA(mvm,
 256                                        "quota: giving remainder of %d to binding %d\n",
 257                                        quota_rem, i);
 258                        break;
 259                }
 260        }
 261
 262        iwl_mvm_adjust_quota_for_noa(mvm, &cmd);
 263
 264        /* check that we have non-zero quota for all valid bindings */
 265        for (i = 0; i < MAX_BINDINGS; i++) {
 266                qdata = iwl_mvm_quota_cmd_get_quota(mvm, &cmd, i);
 267                last_data = iwl_mvm_quota_cmd_get_quota(mvm, last, i);
 268                if (qdata->id_and_color != last_data->id_and_color)
 269                        send = true;
 270                if (qdata->max_duration != last_data->max_duration)
 271                        send = true;
 272                if (abs((int)le32_to_cpu(qdata->quota) -
 273                        (int)le32_to_cpu(last_data->quota))
 274                                                > IWL_MVM_QUOTA_THRESHOLD)
 275                        send = true;
 276                if (qdata->id_and_color == cpu_to_le32(FW_CTXT_INVALID))
 277                        continue;
 278                WARN_ONCE(qdata->quota == 0,
 279                          "zero quota on binding %d\n", i);
 280        }
 281
 282        if (!send && !force_update) {
 283                /* don't send a practically unchanged command, the firmware has
 284                 * to re-initialize a lot of state and that can have an adverse
 285                 * impact on it
 286                 */
 287                return 0;
 288        }
 289
 290        err = iwl_mvm_send_cmd_pdu(mvm, TIME_QUOTA_CMD, 0,
 291                                   iwl_mvm_quota_cmd_size(mvm), &cmd);
 292
 293        if (err)
 294                IWL_ERR(mvm, "Failed to send quota: %d\n", err);
 295        else
 296                mvm->last_quota_cmd = cmd;
 297        return err;
 298}
 299