cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

quota.c (7987B)


      1// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
      2/*
      3 * Copyright (C) 2012-2014, 2018, 2021 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}