brintos

brintos / linux-shallow public Read only

0
0
Text · 10.0 KiB · 542bc0b Raw
407 lines · c
1// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB2/* 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 init13 * @vsi: vsi pointer14 * @user_pri: user priority15 * @node_type: Type of node, leaf or parent16 * @parent: parent node pointer17 */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 node67 * @vsi: VSI stricture of device68 * @node: Pointer to node to free69 */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 cmd85 * @vsi: vsi pointer86 * @node: pointer to node87 * @cmd: add, remove or modify88 */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	else99		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 TC121 * @parent: parent node of First VSI or TC node122 * @match_val: value to match123 * @type: match type VSI/TC124 */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 use153 * @vsi: vsi pointer154 * @user_pri: user priority155 */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 priority167	 * is in use by any other user priority. If so, nothing left to do168	 */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 unconditionally183 * @vsi: vsi pointer184 * @user_pri: user priority185 */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_handle233 * @vsi: vsi pointer234 * @user_pri: user priority235 */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 node325		 */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 have342	 * 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 tree381 * @vsi: vsi pointer382 * @user_pri: user priority383 */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 tree396 * @vsi: vsi pointer397 */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}407