Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0 or Linux-OpenIB
0002 /* Copyright (c) 2017 - 2021 Intel Corporation */
0003 #include "osdep.h"
0004 #include "hmc.h"
0005 #include "defs.h"
0006 #include "type.h"
0007 #include "protos.h"
0008 
0009 #include "ws.h"
0010 
0011 /**
0012  * irdma_alloc_node - Allocate a WS node and init
0013  * @vsi: vsi pointer
0014  * @user_pri: user priority
0015  * @node_type: Type of node, leaf or parent
0016  * @parent: parent node pointer
0017  */
0018 static struct irdma_ws_node *irdma_alloc_node(struct irdma_sc_vsi *vsi,
0019                           u8 user_pri,
0020                           enum irdma_ws_node_type node_type,
0021                           struct irdma_ws_node *parent)
0022 {
0023     struct irdma_virt_mem ws_mem;
0024     struct irdma_ws_node *node;
0025     u16 node_index = 0;
0026 
0027     ws_mem.size = sizeof(struct irdma_ws_node);
0028     ws_mem.va = kzalloc(ws_mem.size, GFP_KERNEL);
0029     if (!ws_mem.va)
0030         return NULL;
0031 
0032     if (parent) {
0033         node_index = irdma_alloc_ws_node_id(vsi->dev);
0034         if (node_index == IRDMA_WS_NODE_INVALID) {
0035             kfree(ws_mem.va);
0036             return NULL;
0037         }
0038     }
0039 
0040     node = ws_mem.va;
0041     node->index = node_index;
0042     node->vsi_index = vsi->vsi_idx;
0043     INIT_LIST_HEAD(&node->child_list_head);
0044     if (node_type == WS_NODE_TYPE_LEAF) {
0045         node->type_leaf = true;
0046         node->traffic_class = vsi->qos[user_pri].traffic_class;
0047         node->user_pri = user_pri;
0048         node->rel_bw = vsi->qos[user_pri].rel_bw;
0049         if (!node->rel_bw)
0050             node->rel_bw = 1;
0051 
0052         node->lan_qs_handle = vsi->qos[user_pri].lan_qos_handle;
0053         node->prio_type = IRDMA_PRIO_WEIGHTED_RR;
0054     } else {
0055         node->rel_bw = 1;
0056         node->prio_type = IRDMA_PRIO_WEIGHTED_RR;
0057         node->enable = true;
0058     }
0059 
0060     node->parent = parent;
0061 
0062     return node;
0063 }
0064 
0065 /**
0066  * irdma_free_node - Free a WS node
0067  * @vsi: VSI stricture of device
0068  * @node: Pointer to node to free
0069  */
0070 static void irdma_free_node(struct irdma_sc_vsi *vsi,
0071                 struct irdma_ws_node *node)
0072 {
0073     struct irdma_virt_mem ws_mem;
0074 
0075     if (node->index)
0076         irdma_free_ws_node_id(vsi->dev, node->index);
0077 
0078     ws_mem.va = node;
0079     ws_mem.size = sizeof(struct irdma_ws_node);
0080     kfree(ws_mem.va);
0081 }
0082 
0083 /**
0084  * irdma_ws_cqp_cmd - Post CQP work scheduler node cmd
0085  * @vsi: vsi pointer
0086  * @node: pointer to node
0087  * @cmd: add, remove or modify
0088  */
0089 static int irdma_ws_cqp_cmd(struct irdma_sc_vsi *vsi,
0090                 struct irdma_ws_node *node, u8 cmd)
0091 {
0092     struct irdma_ws_node_info node_info = {};
0093 
0094     node_info.id = node->index;
0095     node_info.vsi = node->vsi_index;
0096     if (node->parent)
0097         node_info.parent_id = node->parent->index;
0098     else
0099         node_info.parent_id = node_info.id;
0100 
0101     node_info.weight = node->rel_bw;
0102     node_info.tc = node->traffic_class;
0103     node_info.prio_type = node->prio_type;
0104     node_info.type_leaf = node->type_leaf;
0105     node_info.enable = node->enable;
0106     if (irdma_cqp_ws_node_cmd(vsi->dev, cmd, &node_info)) {
0107         ibdev_dbg(to_ibdev(vsi->dev), "WS: CQP WS CMD failed\n");
0108         return -ENOMEM;
0109     }
0110 
0111     if (node->type_leaf && cmd == IRDMA_OP_WS_ADD_NODE) {
0112         node->qs_handle = node_info.qs_handle;
0113         vsi->qos[node->user_pri].qs_handle = node_info.qs_handle;
0114     }
0115 
0116     return 0;
0117 }
0118 
0119 /**
0120  * ws_find_node - Find SC WS node based on VSI id or TC
0121  * @parent: parent node of First VSI or TC node
0122  * @match_val: value to match
0123  * @type: match type VSI/TC
0124  */
0125 static struct irdma_ws_node *ws_find_node(struct irdma_ws_node *parent,
0126                       u16 match_val,
0127                       enum irdma_ws_match_type type)
0128 {
0129     struct irdma_ws_node *node;
0130 
0131     switch (type) {
0132     case WS_MATCH_TYPE_VSI:
0133         list_for_each_entry(node, &parent->child_list_head, siblings) {
0134             if (node->vsi_index == match_val)
0135                 return node;
0136         }
0137         break;
0138     case WS_MATCH_TYPE_TC:
0139         list_for_each_entry(node, &parent->child_list_head, siblings) {
0140             if (node->traffic_class == match_val)
0141                 return node;
0142         }
0143         break;
0144     default:
0145         break;
0146     }
0147 
0148     return NULL;
0149 }
0150 
0151 /**
0152  * irdma_tc_in_use - Checks to see if a leaf node is in use
0153  * @vsi: vsi pointer
0154  * @user_pri: user priority
0155  */
0156 static bool irdma_tc_in_use(struct irdma_sc_vsi *vsi, u8 user_pri)
0157 {
0158     int i;
0159 
0160     mutex_lock(&vsi->qos[user_pri].qos_mutex);
0161     if (!list_empty(&vsi->qos[user_pri].qplist)) {
0162         mutex_unlock(&vsi->qos[user_pri].qos_mutex);
0163         return true;
0164     }
0165 
0166     /* Check if the traffic class associated with the given user priority
0167      * is in use by any other user priority. If so, nothing left to do
0168      */
0169     for (i = 0; i < IRDMA_MAX_USER_PRIORITY; i++) {
0170         if (vsi->qos[i].traffic_class == vsi->qos[user_pri].traffic_class &&
0171             !list_empty(&vsi->qos[i].qplist)) {
0172             mutex_unlock(&vsi->qos[user_pri].qos_mutex);
0173             return true;
0174         }
0175     }
0176     mutex_unlock(&vsi->qos[user_pri].qos_mutex);
0177 
0178     return false;
0179 }
0180 
0181 /**
0182  * irdma_remove_leaf - Remove leaf node unconditionally
0183  * @vsi: vsi pointer
0184  * @user_pri: user priority
0185  */
0186 static void irdma_remove_leaf(struct irdma_sc_vsi *vsi, u8 user_pri)
0187 {
0188     struct irdma_ws_node *ws_tree_root, *vsi_node, *tc_node;
0189     int i;
0190     u16 traffic_class;
0191 
0192     traffic_class = vsi->qos[user_pri].traffic_class;
0193     for (i = 0; i < IRDMA_MAX_USER_PRIORITY; i++)
0194         if (vsi->qos[i].traffic_class == traffic_class)
0195             vsi->qos[i].valid = false;
0196 
0197     ws_tree_root = vsi->dev->ws_tree_root;
0198     if (!ws_tree_root)
0199         return;
0200 
0201     vsi_node = ws_find_node(ws_tree_root, vsi->vsi_idx,
0202                 WS_MATCH_TYPE_VSI);
0203     if (!vsi_node)
0204         return;
0205 
0206     tc_node = ws_find_node(vsi_node,
0207                    vsi->qos[user_pri].traffic_class,
0208                    WS_MATCH_TYPE_TC);
0209     if (!tc_node)
0210         return;
0211 
0212     irdma_ws_cqp_cmd(vsi, tc_node, IRDMA_OP_WS_DELETE_NODE);
0213     vsi->unregister_qset(vsi, tc_node);
0214     list_del(&tc_node->siblings);
0215     irdma_free_node(vsi, tc_node);
0216     /* Check if VSI node can be freed */
0217     if (list_empty(&vsi_node->child_list_head)) {
0218         irdma_ws_cqp_cmd(vsi, vsi_node, IRDMA_OP_WS_DELETE_NODE);
0219         list_del(&vsi_node->siblings);
0220         irdma_free_node(vsi, vsi_node);
0221         /* Free head node there are no remaining VSI nodes */
0222         if (list_empty(&ws_tree_root->child_list_head)) {
0223             irdma_ws_cqp_cmd(vsi, ws_tree_root,
0224                      IRDMA_OP_WS_DELETE_NODE);
0225             irdma_free_node(vsi, ws_tree_root);
0226             vsi->dev->ws_tree_root = NULL;
0227         }
0228     }
0229 }
0230 
0231 /**
0232  * irdma_ws_add - Build work scheduler tree, set RDMA qs_handle
0233  * @vsi: vsi pointer
0234  * @user_pri: user priority
0235  */
0236 int irdma_ws_add(struct irdma_sc_vsi *vsi, u8 user_pri)
0237 {
0238     struct irdma_ws_node *ws_tree_root;
0239     struct irdma_ws_node *vsi_node;
0240     struct irdma_ws_node *tc_node;
0241     u16 traffic_class;
0242     int ret = 0;
0243     int i;
0244 
0245     mutex_lock(&vsi->dev->ws_mutex);
0246     if (vsi->tc_change_pending) {
0247         ret = -EBUSY;
0248         goto exit;
0249     }
0250 
0251     if (vsi->qos[user_pri].valid)
0252         goto exit;
0253 
0254     ws_tree_root = vsi->dev->ws_tree_root;
0255     if (!ws_tree_root) {
0256         ibdev_dbg(to_ibdev(vsi->dev), "WS: Creating root node\n");
0257         ws_tree_root = irdma_alloc_node(vsi, user_pri,
0258                         WS_NODE_TYPE_PARENT, NULL);
0259         if (!ws_tree_root) {
0260             ret = -ENOMEM;
0261             goto exit;
0262         }
0263 
0264         ret = irdma_ws_cqp_cmd(vsi, ws_tree_root, IRDMA_OP_WS_ADD_NODE);
0265         if (ret) {
0266             irdma_free_node(vsi, ws_tree_root);
0267             goto exit;
0268         }
0269 
0270         vsi->dev->ws_tree_root = ws_tree_root;
0271     }
0272 
0273     /* Find a second tier node that matches the VSI */
0274     vsi_node = ws_find_node(ws_tree_root, vsi->vsi_idx,
0275                 WS_MATCH_TYPE_VSI);
0276 
0277     /* If VSI node doesn't exist, add one */
0278     if (!vsi_node) {
0279         ibdev_dbg(to_ibdev(vsi->dev),
0280               "WS: Node not found matching VSI %d\n",
0281               vsi->vsi_idx);
0282         vsi_node = irdma_alloc_node(vsi, user_pri, WS_NODE_TYPE_PARENT,
0283                         ws_tree_root);
0284         if (!vsi_node) {
0285             ret = -ENOMEM;
0286             goto vsi_add_err;
0287         }
0288 
0289         ret = irdma_ws_cqp_cmd(vsi, vsi_node, IRDMA_OP_WS_ADD_NODE);
0290         if (ret) {
0291             irdma_free_node(vsi, vsi_node);
0292             goto vsi_add_err;
0293         }
0294 
0295         list_add(&vsi_node->siblings, &ws_tree_root->child_list_head);
0296     }
0297 
0298     ibdev_dbg(to_ibdev(vsi->dev),
0299           "WS: Using node %d which represents VSI %d\n",
0300           vsi_node->index, vsi->vsi_idx);
0301     traffic_class = vsi->qos[user_pri].traffic_class;
0302     tc_node = ws_find_node(vsi_node, traffic_class,
0303                    WS_MATCH_TYPE_TC);
0304     if (!tc_node) {
0305         /* Add leaf node */
0306         ibdev_dbg(to_ibdev(vsi->dev),
0307               "WS: Node not found matching VSI %d and TC %d\n",
0308               vsi->vsi_idx, traffic_class);
0309         tc_node = irdma_alloc_node(vsi, user_pri, WS_NODE_TYPE_LEAF,
0310                        vsi_node);
0311         if (!tc_node) {
0312             ret = -ENOMEM;
0313             goto leaf_add_err;
0314         }
0315 
0316         ret = irdma_ws_cqp_cmd(vsi, tc_node, IRDMA_OP_WS_ADD_NODE);
0317         if (ret) {
0318             irdma_free_node(vsi, tc_node);
0319             goto leaf_add_err;
0320         }
0321 
0322         list_add(&tc_node->siblings, &vsi_node->child_list_head);
0323         /*
0324          * callback to LAN to update the LAN tree with our node
0325          */
0326         ret = vsi->register_qset(vsi, tc_node);
0327         if (ret)
0328             goto reg_err;
0329 
0330         tc_node->enable = true;
0331         ret = irdma_ws_cqp_cmd(vsi, tc_node, IRDMA_OP_WS_MODIFY_NODE);
0332         if (ret) {
0333             vsi->unregister_qset(vsi, tc_node);
0334             goto reg_err;
0335         }
0336     }
0337     ibdev_dbg(to_ibdev(vsi->dev),
0338           "WS: Using node %d which represents VSI %d TC %d\n",
0339           tc_node->index, vsi->vsi_idx, traffic_class);
0340     /*
0341      * Iterate through other UPs and update the QS handle if they have
0342      * a matching traffic class.
0343      */
0344     for (i = 0; i < IRDMA_MAX_USER_PRIORITY; i++) {
0345         if (vsi->qos[i].traffic_class == traffic_class) {
0346             vsi->qos[i].qs_handle = tc_node->qs_handle;
0347             vsi->qos[i].lan_qos_handle = tc_node->lan_qs_handle;
0348             vsi->qos[i].l2_sched_node_id = tc_node->l2_sched_node_id;
0349             vsi->qos[i].valid = true;
0350         }
0351     }
0352     goto exit;
0353 
0354 reg_err:
0355     irdma_ws_cqp_cmd(vsi, tc_node, IRDMA_OP_WS_DELETE_NODE);
0356     list_del(&tc_node->siblings);
0357     irdma_free_node(vsi, tc_node);
0358 leaf_add_err:
0359     if (list_empty(&vsi_node->child_list_head)) {
0360         if (irdma_ws_cqp_cmd(vsi, vsi_node, IRDMA_OP_WS_DELETE_NODE))
0361             goto exit;
0362         list_del(&vsi_node->siblings);
0363         irdma_free_node(vsi, vsi_node);
0364     }
0365 
0366 vsi_add_err:
0367     /* Free head node there are no remaining VSI nodes */
0368     if (list_empty(&ws_tree_root->child_list_head)) {
0369         irdma_ws_cqp_cmd(vsi, ws_tree_root, IRDMA_OP_WS_DELETE_NODE);
0370         vsi->dev->ws_tree_root = NULL;
0371         irdma_free_node(vsi, ws_tree_root);
0372     }
0373 
0374 exit:
0375     mutex_unlock(&vsi->dev->ws_mutex);
0376     return ret;
0377 }
0378 
0379 /**
0380  * irdma_ws_remove - Free WS scheduler node, update WS tree
0381  * @vsi: vsi pointer
0382  * @user_pri: user priority
0383  */
0384 void irdma_ws_remove(struct irdma_sc_vsi *vsi, u8 user_pri)
0385 {
0386     mutex_lock(&vsi->dev->ws_mutex);
0387     if (irdma_tc_in_use(vsi, user_pri))
0388         goto exit;
0389     irdma_remove_leaf(vsi, user_pri);
0390 exit:
0391     mutex_unlock(&vsi->dev->ws_mutex);
0392 }
0393 
0394 /**
0395  * irdma_ws_reset - Reset entire WS tree
0396  * @vsi: vsi pointer
0397  */
0398 void irdma_ws_reset(struct irdma_sc_vsi *vsi)
0399 {
0400     u8 i;
0401 
0402     mutex_lock(&vsi->dev->ws_mutex);
0403     for (i = 0; i < IRDMA_MAX_USER_PRIORITY; ++i)
0404         irdma_remove_leaf(vsi, i);
0405     mutex_unlock(&vsi->dev->ws_mutex);
0406 }