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

ws.c (10232B)


      1// SPDX-License-Identifier: GPL-2.0 or Linux-OpenIB
      2/* Copyright (c) 2017 - 2021 Intel Corporation */
      3#include "osdep.h"
      4#include "hmc.h"
      5#include "defs.h"
      6#include "type.h"
      7#include "protos.h"
      8
      9#include "ws.h"
     10
     11/**
     12 * irdma_alloc_node - Allocate a WS node and init
     13 * @vsi: vsi pointer
     14 * @user_pri: user priority
     15 * @node_type: Type of node, leaf or parent
     16 * @parent: parent node pointer
     17 */
     18static struct irdma_ws_node *irdma_alloc_node(struct irdma_sc_vsi *vsi,
     19					      u8 user_pri,
     20					      enum irdma_ws_node_type node_type,
     21					      struct irdma_ws_node *parent)
     22{
     23	struct irdma_virt_mem ws_mem;
     24	struct irdma_ws_node *node;
     25	u16 node_index = 0;
     26
     27	ws_mem.size = sizeof(struct irdma_ws_node);
     28	ws_mem.va = kzalloc(ws_mem.size, GFP_KERNEL);
     29	if (!ws_mem.va)
     30		return NULL;
     31
     32	if (parent) {
     33		node_index = irdma_alloc_ws_node_id(vsi->dev);
     34		if (node_index == IRDMA_WS_NODE_INVALID) {
     35			kfree(ws_mem.va);
     36			return NULL;
     37		}
     38	}
     39
     40	node = ws_mem.va;
     41	node->index = node_index;
     42	node->vsi_index = vsi->vsi_idx;
     43	INIT_LIST_HEAD(&node->child_list_head);
     44	if (node_type == WS_NODE_TYPE_LEAF) {
     45		node->type_leaf = true;
     46		node->traffic_class = vsi->qos[user_pri].traffic_class;
     47		node->user_pri = user_pri;
     48		node->rel_bw = vsi->qos[user_pri].rel_bw;
     49		if (!node->rel_bw)
     50			node->rel_bw = 1;
     51
     52		node->lan_qs_handle = vsi->qos[user_pri].lan_qos_handle;
     53		node->prio_type = IRDMA_PRIO_WEIGHTED_RR;
     54	} else {
     55		node->rel_bw = 1;
     56		node->prio_type = IRDMA_PRIO_WEIGHTED_RR;
     57		node->enable = true;
     58	}
     59
     60	node->parent = parent;
     61
     62	return node;
     63}
     64
     65/**
     66 * irdma_free_node - Free a WS node
     67 * @vsi: VSI stricture of device
     68 * @node: Pointer to node to free
     69 */
     70static void irdma_free_node(struct irdma_sc_vsi *vsi,
     71			    struct irdma_ws_node *node)
     72{
     73	struct irdma_virt_mem ws_mem;
     74
     75	if (node->index)
     76		irdma_free_ws_node_id(vsi->dev, node->index);
     77
     78	ws_mem.va = node;
     79	ws_mem.size = sizeof(struct irdma_ws_node);
     80	kfree(ws_mem.va);
     81}
     82
     83/**
     84 * irdma_ws_cqp_cmd - Post CQP work scheduler node cmd
     85 * @vsi: vsi pointer
     86 * @node: pointer to node
     87 * @cmd: add, remove or modify
     88 */
     89static int irdma_ws_cqp_cmd(struct irdma_sc_vsi *vsi,
     90			    struct irdma_ws_node *node, u8 cmd)
     91{
     92	struct irdma_ws_node_info node_info = {};
     93
     94	node_info.id = node->index;
     95	node_info.vsi = node->vsi_index;
     96	if (node->parent)
     97		node_info.parent_id = node->parent->index;
     98	else
     99		node_info.parent_id = node_info.id;
    100
    101	node_info.weight = node->rel_bw;
    102	node_info.tc = node->traffic_class;
    103	node_info.prio_type = node->prio_type;
    104	node_info.type_leaf = node->type_leaf;
    105	node_info.enable = node->enable;
    106	if (irdma_cqp_ws_node_cmd(vsi->dev, cmd, &node_info)) {
    107		ibdev_dbg(to_ibdev(vsi->dev), "WS: CQP WS CMD failed\n");
    108		return -ENOMEM;
    109	}
    110
    111	if (node->type_leaf && cmd == IRDMA_OP_WS_ADD_NODE) {
    112		node->qs_handle = node_info.qs_handle;
    113		vsi->qos[node->user_pri].qs_handle = node_info.qs_handle;
    114	}
    115
    116	return 0;
    117}
    118
    119/**
    120 * ws_find_node - Find SC WS node based on VSI id or TC
    121 * @parent: parent node of First VSI or TC node
    122 * @match_val: value to match
    123 * @type: match type VSI/TC
    124 */
    125static struct irdma_ws_node *ws_find_node(struct irdma_ws_node *parent,
    126					  u16 match_val,
    127					  enum irdma_ws_match_type type)
    128{
    129	struct irdma_ws_node *node;
    130
    131	switch (type) {
    132	case WS_MATCH_TYPE_VSI:
    133		list_for_each_entry(node, &parent->child_list_head, siblings) {
    134			if (node->vsi_index == match_val)
    135				return node;
    136		}
    137		break;
    138	case WS_MATCH_TYPE_TC:
    139		list_for_each_entry(node, &parent->child_list_head, siblings) {
    140			if (node->traffic_class == match_val)
    141				return node;
    142		}
    143		break;
    144	default:
    145		break;
    146	}
    147
    148	return NULL;
    149}
    150
    151/**
    152 * irdma_tc_in_use - Checks to see if a leaf node is in use
    153 * @vsi: vsi pointer
    154 * @user_pri: user priority
    155 */
    156static bool irdma_tc_in_use(struct irdma_sc_vsi *vsi, u8 user_pri)
    157{
    158	int i;
    159
    160	mutex_lock(&vsi->qos[user_pri].qos_mutex);
    161	if (!list_empty(&vsi->qos[user_pri].qplist)) {
    162		mutex_unlock(&vsi->qos[user_pri].qos_mutex);
    163		return true;
    164	}
    165
    166	/* Check if the traffic class associated with the given user priority
    167	 * is in use by any other user priority. If so, nothing left to do
    168	 */
    169	for (i = 0; i < IRDMA_MAX_USER_PRIORITY; i++) {
    170		if (vsi->qos[i].traffic_class == vsi->qos[user_pri].traffic_class &&
    171		    !list_empty(&vsi->qos[i].qplist)) {
    172			mutex_unlock(&vsi->qos[user_pri].qos_mutex);
    173			return true;
    174		}
    175	}
    176	mutex_unlock(&vsi->qos[user_pri].qos_mutex);
    177
    178	return false;
    179}
    180
    181/**
    182 * irdma_remove_leaf - Remove leaf node unconditionally
    183 * @vsi: vsi pointer
    184 * @user_pri: user priority
    185 */
    186static void irdma_remove_leaf(struct irdma_sc_vsi *vsi, u8 user_pri)
    187{
    188	struct irdma_ws_node *ws_tree_root, *vsi_node, *tc_node;
    189	int i;
    190	u16 traffic_class;
    191
    192	traffic_class = vsi->qos[user_pri].traffic_class;
    193	for (i = 0; i < IRDMA_MAX_USER_PRIORITY; i++)
    194		if (vsi->qos[i].traffic_class == traffic_class)
    195			vsi->qos[i].valid = false;
    196
    197	ws_tree_root = vsi->dev->ws_tree_root;
    198	if (!ws_tree_root)
    199		return;
    200
    201	vsi_node = ws_find_node(ws_tree_root, vsi->vsi_idx,
    202				WS_MATCH_TYPE_VSI);
    203	if (!vsi_node)
    204		return;
    205
    206	tc_node = ws_find_node(vsi_node,
    207			       vsi->qos[user_pri].traffic_class,
    208			       WS_MATCH_TYPE_TC);
    209	if (!tc_node)
    210		return;
    211
    212	irdma_ws_cqp_cmd(vsi, tc_node, IRDMA_OP_WS_DELETE_NODE);
    213	vsi->unregister_qset(vsi, tc_node);
    214	list_del(&tc_node->siblings);
    215	irdma_free_node(vsi, tc_node);
    216	/* Check if VSI node can be freed */
    217	if (list_empty(&vsi_node->child_list_head)) {
    218		irdma_ws_cqp_cmd(vsi, vsi_node, IRDMA_OP_WS_DELETE_NODE);
    219		list_del(&vsi_node->siblings);
    220		irdma_free_node(vsi, vsi_node);
    221		/* Free head node there are no remaining VSI nodes */
    222		if (list_empty(&ws_tree_root->child_list_head)) {
    223			irdma_ws_cqp_cmd(vsi, ws_tree_root,
    224					 IRDMA_OP_WS_DELETE_NODE);
    225			irdma_free_node(vsi, ws_tree_root);
    226			vsi->dev->ws_tree_root = NULL;
    227		}
    228	}
    229}
    230
    231/**
    232 * irdma_ws_add - Build work scheduler tree, set RDMA qs_handle
    233 * @vsi: vsi pointer
    234 * @user_pri: user priority
    235 */
    236int irdma_ws_add(struct irdma_sc_vsi *vsi, u8 user_pri)
    237{
    238	struct irdma_ws_node *ws_tree_root;
    239	struct irdma_ws_node *vsi_node;
    240	struct irdma_ws_node *tc_node;
    241	u16 traffic_class;
    242	int ret = 0;
    243	int i;
    244
    245	mutex_lock(&vsi->dev->ws_mutex);
    246	if (vsi->tc_change_pending) {
    247		ret = -EBUSY;
    248		goto exit;
    249	}
    250
    251	if (vsi->qos[user_pri].valid)
    252		goto exit;
    253
    254	ws_tree_root = vsi->dev->ws_tree_root;
    255	if (!ws_tree_root) {
    256		ibdev_dbg(to_ibdev(vsi->dev), "WS: Creating root node\n");
    257		ws_tree_root = irdma_alloc_node(vsi, user_pri,
    258						WS_NODE_TYPE_PARENT, NULL);
    259		if (!ws_tree_root) {
    260			ret = -ENOMEM;
    261			goto exit;
    262		}
    263
    264		ret = irdma_ws_cqp_cmd(vsi, ws_tree_root, IRDMA_OP_WS_ADD_NODE);
    265		if (ret) {
    266			irdma_free_node(vsi, ws_tree_root);
    267			goto exit;
    268		}
    269
    270		vsi->dev->ws_tree_root = ws_tree_root;
    271	}
    272
    273	/* Find a second tier node that matches the VSI */
    274	vsi_node = ws_find_node(ws_tree_root, vsi->vsi_idx,
    275				WS_MATCH_TYPE_VSI);
    276
    277	/* If VSI node doesn't exist, add one */
    278	if (!vsi_node) {
    279		ibdev_dbg(to_ibdev(vsi->dev),
    280			  "WS: Node not found matching VSI %d\n",
    281			  vsi->vsi_idx);
    282		vsi_node = irdma_alloc_node(vsi, user_pri, WS_NODE_TYPE_PARENT,
    283					    ws_tree_root);
    284		if (!vsi_node) {
    285			ret = -ENOMEM;
    286			goto vsi_add_err;
    287		}
    288
    289		ret = irdma_ws_cqp_cmd(vsi, vsi_node, IRDMA_OP_WS_ADD_NODE);
    290		if (ret) {
    291			irdma_free_node(vsi, vsi_node);
    292			goto vsi_add_err;
    293		}
    294
    295		list_add(&vsi_node->siblings, &ws_tree_root->child_list_head);
    296	}
    297
    298	ibdev_dbg(to_ibdev(vsi->dev),
    299		  "WS: Using node %d which represents VSI %d\n",
    300		  vsi_node->index, vsi->vsi_idx);
    301	traffic_class = vsi->qos[user_pri].traffic_class;
    302	tc_node = ws_find_node(vsi_node, traffic_class,
    303			       WS_MATCH_TYPE_TC);
    304	if (!tc_node) {
    305		/* Add leaf node */
    306		ibdev_dbg(to_ibdev(vsi->dev),
    307			  "WS: Node not found matching VSI %d and TC %d\n",
    308			  vsi->vsi_idx, traffic_class);
    309		tc_node = irdma_alloc_node(vsi, user_pri, WS_NODE_TYPE_LEAF,
    310					   vsi_node);
    311		if (!tc_node) {
    312			ret = -ENOMEM;
    313			goto leaf_add_err;
    314		}
    315
    316		ret = irdma_ws_cqp_cmd(vsi, tc_node, IRDMA_OP_WS_ADD_NODE);
    317		if (ret) {
    318			irdma_free_node(vsi, tc_node);
    319			goto leaf_add_err;
    320		}
    321
    322		list_add(&tc_node->siblings, &vsi_node->child_list_head);
    323		/*
    324		 * callback to LAN to update the LAN tree with our node
    325		 */
    326		ret = vsi->register_qset(vsi, tc_node);
    327		if (ret)
    328			goto reg_err;
    329
    330		tc_node->enable = true;
    331		ret = irdma_ws_cqp_cmd(vsi, tc_node, IRDMA_OP_WS_MODIFY_NODE);
    332		if (ret) {
    333			vsi->unregister_qset(vsi, tc_node);
    334			goto reg_err;
    335		}
    336	}
    337	ibdev_dbg(to_ibdev(vsi->dev),
    338		  "WS: Using node %d which represents VSI %d TC %d\n",
    339		  tc_node->index, vsi->vsi_idx, traffic_class);
    340	/*
    341	 * Iterate through other UPs and update the QS handle if they have
    342	 * a matching traffic class.
    343	 */
    344	for (i = 0; i < IRDMA_MAX_USER_PRIORITY; i++) {
    345		if (vsi->qos[i].traffic_class == traffic_class) {
    346			vsi->qos[i].qs_handle = tc_node->qs_handle;
    347			vsi->qos[i].lan_qos_handle = tc_node->lan_qs_handle;
    348			vsi->qos[i].l2_sched_node_id = tc_node->l2_sched_node_id;
    349			vsi->qos[i].valid = true;
    350		}
    351	}
    352	goto exit;
    353
    354reg_err:
    355	irdma_ws_cqp_cmd(vsi, tc_node, IRDMA_OP_WS_DELETE_NODE);
    356	list_del(&tc_node->siblings);
    357	irdma_free_node(vsi, tc_node);
    358leaf_add_err:
    359	if (list_empty(&vsi_node->child_list_head)) {
    360		if (irdma_ws_cqp_cmd(vsi, vsi_node, IRDMA_OP_WS_DELETE_NODE))
    361			goto exit;
    362		list_del(&vsi_node->siblings);
    363		irdma_free_node(vsi, vsi_node);
    364	}
    365
    366vsi_add_err:
    367	/* Free head node there are no remaining VSI nodes */
    368	if (list_empty(&ws_tree_root->child_list_head)) {
    369		irdma_ws_cqp_cmd(vsi, ws_tree_root, IRDMA_OP_WS_DELETE_NODE);
    370		vsi->dev->ws_tree_root = NULL;
    371		irdma_free_node(vsi, ws_tree_root);
    372	}
    373
    374exit:
    375	mutex_unlock(&vsi->dev->ws_mutex);
    376	return ret;
    377}
    378
    379/**
    380 * irdma_ws_remove - Free WS scheduler node, update WS tree
    381 * @vsi: vsi pointer
    382 * @user_pri: user priority
    383 */
    384void irdma_ws_remove(struct irdma_sc_vsi *vsi, u8 user_pri)
    385{
    386	mutex_lock(&vsi->dev->ws_mutex);
    387	if (irdma_tc_in_use(vsi, user_pri))
    388		goto exit;
    389	irdma_remove_leaf(vsi, user_pri);
    390exit:
    391	mutex_unlock(&vsi->dev->ws_mutex);
    392}
    393
    394/**
    395 * irdma_ws_reset - Reset entire WS tree
    396 * @vsi: vsi pointer
    397 */
    398void irdma_ws_reset(struct irdma_sc_vsi *vsi)
    399{
    400	u8 i;
    401
    402	mutex_lock(&vsi->dev->ws_mutex);
    403	for (i = 0; i < IRDMA_MAX_USER_PRIORITY; ++i)
    404		irdma_remove_leaf(vsi, i);
    405	mutex_unlock(&vsi->dev->ws_mutex);
    406}