12d116e3eSDmytro Linkin // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB 22d116e3eSDmytro Linkin /* Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved. */ 32d116e3eSDmytro Linkin 42d116e3eSDmytro Linkin #include "eswitch.h" 52d116e3eSDmytro Linkin #include "esw/qos.h" 6ad34f02fSDmytro Linkin #include "en/port.h" 73202ea65SDmytro Linkin #define CREATE_TRACE_POINTS 83202ea65SDmytro Linkin #include "diag/qos_tracepoint.h" 92d116e3eSDmytro Linkin 102d116e3eSDmytro Linkin /* Minimum supported BW share value by the HW is 1 Mbit/sec */ 112d116e3eSDmytro Linkin #define MLX5_MIN_BW_SHARE 1 122d116e3eSDmytro Linkin 132d116e3eSDmytro Linkin #define MLX5_RATE_TO_BW_SHARE(rate, divider, limit) \ 14ad34f02fSDmytro Linkin min_t(u32, max_t(u32, DIV_ROUND_UP(rate, divider), MLX5_MIN_BW_SHARE), limit) 152d116e3eSDmytro Linkin 161ae258f8SDmytro Linkin struct mlx5_esw_rate_group { 171ae258f8SDmytro Linkin u32 tsar_ix; 181ae258f8SDmytro Linkin u32 max_rate; 191ae258f8SDmytro Linkin u32 min_rate; 201ae258f8SDmytro Linkin u32 bw_share; 21f47e04ebSDmytro Linkin struct list_head list; 221ae258f8SDmytro Linkin }; 231ae258f8SDmytro Linkin 24f47e04ebSDmytro Linkin static int esw_qos_tsar_config(struct mlx5_core_dev *dev, u32 *sched_ctx, 25f47e04ebSDmytro Linkin u32 parent_ix, u32 tsar_ix, 26f47e04ebSDmytro Linkin u32 max_rate, u32 bw_share) 27f47e04ebSDmytro Linkin { 28f47e04ebSDmytro Linkin u32 bitmask = 0; 29f47e04ebSDmytro Linkin 30f47e04ebSDmytro Linkin if (!MLX5_CAP_GEN(dev, qos) || !MLX5_CAP_QOS(dev, esw_scheduling)) 31f47e04ebSDmytro Linkin return -EOPNOTSUPP; 32f47e04ebSDmytro Linkin 33f47e04ebSDmytro Linkin MLX5_SET(scheduling_context, sched_ctx, parent_element_id, parent_ix); 34f47e04ebSDmytro Linkin MLX5_SET(scheduling_context, sched_ctx, max_average_bw, max_rate); 35f47e04ebSDmytro Linkin MLX5_SET(scheduling_context, sched_ctx, bw_share, bw_share); 36f47e04ebSDmytro Linkin bitmask |= MODIFY_SCHEDULING_ELEMENT_IN_MODIFY_BITMASK_MAX_AVERAGE_BW; 37f47e04ebSDmytro Linkin bitmask |= MODIFY_SCHEDULING_ELEMENT_IN_MODIFY_BITMASK_BW_SHARE; 38f47e04ebSDmytro Linkin 39f47e04ebSDmytro Linkin return mlx5_modify_scheduling_element_cmd(dev, 40f47e04ebSDmytro Linkin SCHEDULING_HIERARCHY_E_SWITCH, 41f47e04ebSDmytro Linkin sched_ctx, 42f47e04ebSDmytro Linkin tsar_ix, 43f47e04ebSDmytro Linkin bitmask); 44f47e04ebSDmytro Linkin } 45f47e04ebSDmytro Linkin 46f47e04ebSDmytro Linkin static int esw_qos_group_config(struct mlx5_eswitch *esw, struct mlx5_esw_rate_group *group, 47f47e04ebSDmytro Linkin u32 max_rate, u32 bw_share, struct netlink_ext_ack *extack) 48f47e04ebSDmytro Linkin { 49f47e04ebSDmytro Linkin u32 sched_ctx[MLX5_ST_SZ_DW(scheduling_context)] = {}; 50f47e04ebSDmytro Linkin struct mlx5_core_dev *dev = esw->dev; 51f47e04ebSDmytro Linkin int err; 52f47e04ebSDmytro Linkin 53f47e04ebSDmytro Linkin err = esw_qos_tsar_config(dev, sched_ctx, 54f47e04ebSDmytro Linkin esw->qos.root_tsar_ix, group->tsar_ix, 55f47e04ebSDmytro Linkin max_rate, bw_share); 56f47e04ebSDmytro Linkin if (err) 57f47e04ebSDmytro Linkin NL_SET_ERR_MSG_MOD(extack, "E-Switch modify group TSAR element failed"); 58f47e04ebSDmytro Linkin 593202ea65SDmytro Linkin trace_mlx5_esw_group_qos_config(dev, group, group->tsar_ix, bw_share, max_rate); 603202ea65SDmytro Linkin 61f47e04ebSDmytro Linkin return err; 62f47e04ebSDmytro Linkin } 63f47e04ebSDmytro Linkin 642d116e3eSDmytro Linkin static int esw_qos_vport_config(struct mlx5_eswitch *esw, 652d116e3eSDmytro Linkin struct mlx5_vport *vport, 66ad34f02fSDmytro Linkin u32 max_rate, u32 bw_share, 67ad34f02fSDmytro Linkin struct netlink_ext_ack *extack) 682d116e3eSDmytro Linkin { 692d116e3eSDmytro Linkin u32 sched_ctx[MLX5_ST_SZ_DW(scheduling_context)] = {}; 700fe132eaSDmytro Linkin struct mlx5_esw_rate_group *group = vport->qos.group; 712d116e3eSDmytro Linkin struct mlx5_core_dev *dev = esw->dev; 720fe132eaSDmytro Linkin u32 parent_tsar_ix; 732d116e3eSDmytro Linkin void *vport_elem; 742d116e3eSDmytro Linkin int err; 752d116e3eSDmytro Linkin 762d116e3eSDmytro Linkin if (!vport->qos.enabled) 772d116e3eSDmytro Linkin return -EIO; 782d116e3eSDmytro Linkin 790fe132eaSDmytro Linkin parent_tsar_ix = group ? group->tsar_ix : esw->qos.root_tsar_ix; 802d116e3eSDmytro Linkin MLX5_SET(scheduling_context, sched_ctx, element_type, 812d116e3eSDmytro Linkin SCHEDULING_CONTEXT_ELEMENT_TYPE_VPORT); 822d116e3eSDmytro Linkin vport_elem = MLX5_ADDR_OF(scheduling_context, sched_ctx, 832d116e3eSDmytro Linkin element_attributes); 842d116e3eSDmytro Linkin MLX5_SET(vport_element, vport_elem, vport_number, vport->vport); 852d116e3eSDmytro Linkin 860fe132eaSDmytro Linkin err = esw_qos_tsar_config(dev, sched_ctx, parent_tsar_ix, vport->qos.esw_tsar_ix, 87f47e04ebSDmytro Linkin max_rate, bw_share); 882d116e3eSDmytro Linkin if (err) { 89f47e04ebSDmytro Linkin esw_warn(esw->dev, 90f47e04ebSDmytro Linkin "E-Switch modify TSAR vport element failed (vport=%d,err=%d)\n", 912d116e3eSDmytro Linkin vport->vport, err); 92ad34f02fSDmytro Linkin NL_SET_ERR_MSG_MOD(extack, "E-Switch modify TSAR vport element failed"); 932d116e3eSDmytro Linkin return err; 942d116e3eSDmytro Linkin } 952d116e3eSDmytro Linkin 963202ea65SDmytro Linkin trace_mlx5_esw_vport_qos_config(vport, bw_share, max_rate); 973202ea65SDmytro Linkin 982d116e3eSDmytro Linkin return 0; 992d116e3eSDmytro Linkin } 1002d116e3eSDmytro Linkin 101f47e04ebSDmytro Linkin static u32 esw_qos_calculate_min_rate_divider(struct mlx5_eswitch *esw, 102f47e04ebSDmytro Linkin struct mlx5_esw_rate_group *group, 103f47e04ebSDmytro Linkin bool group_level) 1042d116e3eSDmytro Linkin { 1052d116e3eSDmytro Linkin u32 fw_max_bw_share = MLX5_CAP_QOS(esw->dev, max_tsar_bw_share); 1062d116e3eSDmytro Linkin struct mlx5_vport *evport; 1072d116e3eSDmytro Linkin u32 max_guarantee = 0; 1082d116e3eSDmytro Linkin unsigned long i; 1092d116e3eSDmytro Linkin 110f47e04ebSDmytro Linkin if (group_level) { 111f47e04ebSDmytro Linkin struct mlx5_esw_rate_group *group; 112f47e04ebSDmytro Linkin 113f47e04ebSDmytro Linkin list_for_each_entry(group, &esw->qos.groups, list) { 114f47e04ebSDmytro Linkin if (group->min_rate < max_guarantee) 115f47e04ebSDmytro Linkin continue; 116f47e04ebSDmytro Linkin max_guarantee = group->min_rate; 117f47e04ebSDmytro Linkin } 118f47e04ebSDmytro Linkin } else { 1192d116e3eSDmytro Linkin mlx5_esw_for_each_vport(esw, i, evport) { 120f47e04ebSDmytro Linkin if (!evport->enabled || !evport->qos.enabled || 1210fe132eaSDmytro Linkin evport->qos.group != group || evport->qos.min_rate < max_guarantee) 1222d116e3eSDmytro Linkin continue; 1232d116e3eSDmytro Linkin max_guarantee = evport->qos.min_rate; 1242d116e3eSDmytro Linkin } 125f47e04ebSDmytro Linkin } 1262d116e3eSDmytro Linkin 1272d116e3eSDmytro Linkin if (max_guarantee) 1282d116e3eSDmytro Linkin return max_t(u32, max_guarantee / fw_max_bw_share, 1); 1290fe132eaSDmytro Linkin 1300fe132eaSDmytro Linkin /* If vports min rate divider is 0 but their group has bw_share configured, then 1310fe132eaSDmytro Linkin * need to set bw_share for vports to minimal value. 1320fe132eaSDmytro Linkin */ 1330fe132eaSDmytro Linkin if (!group_level && !max_guarantee && group->bw_share) 1340fe132eaSDmytro Linkin return 1; 1352d116e3eSDmytro Linkin return 0; 1362d116e3eSDmytro Linkin } 1372d116e3eSDmytro Linkin 138f47e04ebSDmytro Linkin static u32 esw_qos_calc_bw_share(u32 min_rate, u32 divider, u32 fw_max) 139f47e04ebSDmytro Linkin { 140f47e04ebSDmytro Linkin if (divider) 141f47e04ebSDmytro Linkin return MLX5_RATE_TO_BW_SHARE(min_rate, divider, fw_max); 142f47e04ebSDmytro Linkin 143f47e04ebSDmytro Linkin return 0; 144f47e04ebSDmytro Linkin } 145f47e04ebSDmytro Linkin 146f47e04ebSDmytro Linkin static int esw_qos_normalize_vports_min_rate(struct mlx5_eswitch *esw, 147f47e04ebSDmytro Linkin struct mlx5_esw_rate_group *group, 148f47e04ebSDmytro Linkin struct netlink_ext_ack *extack) 1492d116e3eSDmytro Linkin { 1502d116e3eSDmytro Linkin u32 fw_max_bw_share = MLX5_CAP_QOS(esw->dev, max_tsar_bw_share); 151f47e04ebSDmytro Linkin u32 divider = esw_qos_calculate_min_rate_divider(esw, group, false); 1522d116e3eSDmytro Linkin struct mlx5_vport *evport; 1532d116e3eSDmytro Linkin unsigned long i; 1542d116e3eSDmytro Linkin u32 bw_share; 1552d116e3eSDmytro Linkin int err; 1562d116e3eSDmytro Linkin 1572d116e3eSDmytro Linkin mlx5_esw_for_each_vport(esw, i, evport) { 1580fe132eaSDmytro Linkin if (!evport->enabled || !evport->qos.enabled || evport->qos.group != group) 1592d116e3eSDmytro Linkin continue; 160f47e04ebSDmytro Linkin bw_share = esw_qos_calc_bw_share(evport->qos.min_rate, divider, fw_max_bw_share); 1612d116e3eSDmytro Linkin 1622d116e3eSDmytro Linkin if (bw_share == evport->qos.bw_share) 1632d116e3eSDmytro Linkin continue; 1642d116e3eSDmytro Linkin 165f47e04ebSDmytro Linkin err = esw_qos_vport_config(esw, evport, evport->qos.max_rate, bw_share, extack); 166f47e04ebSDmytro Linkin if (err) 1672d116e3eSDmytro Linkin return err; 168f47e04ebSDmytro Linkin 169f47e04ebSDmytro Linkin evport->qos.bw_share = bw_share; 170f47e04ebSDmytro Linkin } 171f47e04ebSDmytro Linkin 172f47e04ebSDmytro Linkin return 0; 173f47e04ebSDmytro Linkin } 174f47e04ebSDmytro Linkin 175f47e04ebSDmytro Linkin static int esw_qos_normalize_groups_min_rate(struct mlx5_eswitch *esw, u32 divider, 176f47e04ebSDmytro Linkin struct netlink_ext_ack *extack) 177f47e04ebSDmytro Linkin { 178f47e04ebSDmytro Linkin u32 fw_max_bw_share = MLX5_CAP_QOS(esw->dev, max_tsar_bw_share); 179f47e04ebSDmytro Linkin struct mlx5_esw_rate_group *group; 180f47e04ebSDmytro Linkin u32 bw_share; 181f47e04ebSDmytro Linkin int err; 182f47e04ebSDmytro Linkin 183f47e04ebSDmytro Linkin list_for_each_entry(group, &esw->qos.groups, list) { 184f47e04ebSDmytro Linkin bw_share = esw_qos_calc_bw_share(group->min_rate, divider, fw_max_bw_share); 185f47e04ebSDmytro Linkin 186f47e04ebSDmytro Linkin if (bw_share == group->bw_share) 187f47e04ebSDmytro Linkin continue; 188f47e04ebSDmytro Linkin 189f47e04ebSDmytro Linkin err = esw_qos_group_config(esw, group, group->max_rate, bw_share, extack); 190f47e04ebSDmytro Linkin if (err) 191f47e04ebSDmytro Linkin return err; 192f47e04ebSDmytro Linkin 193f47e04ebSDmytro Linkin group->bw_share = bw_share; 1940fe132eaSDmytro Linkin 1950fe132eaSDmytro Linkin /* All the group's vports need to be set with default bw_share 1960fe132eaSDmytro Linkin * to enable them with QOS 1970fe132eaSDmytro Linkin */ 1980fe132eaSDmytro Linkin err = esw_qos_normalize_vports_min_rate(esw, group, extack); 1990fe132eaSDmytro Linkin 2000fe132eaSDmytro Linkin if (err) 2010fe132eaSDmytro Linkin return err; 2022d116e3eSDmytro Linkin } 2032d116e3eSDmytro Linkin 2042d116e3eSDmytro Linkin return 0; 2052d116e3eSDmytro Linkin } 2062d116e3eSDmytro Linkin 207d7df09f5SDmytro Linkin static int esw_qos_set_vport_min_rate(struct mlx5_eswitch *esw, struct mlx5_vport *evport, 208d7df09f5SDmytro Linkin u32 min_rate, struct netlink_ext_ack *extack) 2092d116e3eSDmytro Linkin { 210ad34f02fSDmytro Linkin u32 fw_max_bw_share, previous_min_rate; 2112d116e3eSDmytro Linkin bool min_rate_supported; 2122d116e3eSDmytro Linkin int err; 2132d116e3eSDmytro Linkin 214ad34f02fSDmytro Linkin lockdep_assert_held(&esw->state_lock); 2152d116e3eSDmytro Linkin fw_max_bw_share = MLX5_CAP_QOS(esw->dev, max_tsar_bw_share); 2162d116e3eSDmytro Linkin min_rate_supported = MLX5_CAP_QOS(esw->dev, esw_bw_share) && 2172d116e3eSDmytro Linkin fw_max_bw_share >= MLX5_MIN_BW_SHARE; 218ad34f02fSDmytro Linkin if (min_rate && !min_rate_supported) 2192d116e3eSDmytro Linkin return -EOPNOTSUPP; 2202d116e3eSDmytro Linkin if (min_rate == evport->qos.min_rate) 221ad34f02fSDmytro Linkin return 0; 2222d116e3eSDmytro Linkin 2232d116e3eSDmytro Linkin previous_min_rate = evport->qos.min_rate; 2242d116e3eSDmytro Linkin evport->qos.min_rate = min_rate; 2250fe132eaSDmytro Linkin err = esw_qos_normalize_vports_min_rate(esw, evport->qos.group, extack); 226ad34f02fSDmytro Linkin if (err) 2272d116e3eSDmytro Linkin evport->qos.min_rate = previous_min_rate; 228ad34f02fSDmytro Linkin 2292d116e3eSDmytro Linkin return err; 2302d116e3eSDmytro Linkin } 2312d116e3eSDmytro Linkin 232d7df09f5SDmytro Linkin static int esw_qos_set_vport_max_rate(struct mlx5_eswitch *esw, struct mlx5_vport *evport, 233d7df09f5SDmytro Linkin u32 max_rate, struct netlink_ext_ack *extack) 234ad34f02fSDmytro Linkin { 2350fe132eaSDmytro Linkin u32 act_max_rate = max_rate; 236ad34f02fSDmytro Linkin bool max_rate_supported; 237ad34f02fSDmytro Linkin int err; 238ad34f02fSDmytro Linkin 239ad34f02fSDmytro Linkin lockdep_assert_held(&esw->state_lock); 240ad34f02fSDmytro Linkin max_rate_supported = MLX5_CAP_QOS(esw->dev, esw_rate_limit); 241ad34f02fSDmytro Linkin 242ad34f02fSDmytro Linkin if (max_rate && !max_rate_supported) 243ad34f02fSDmytro Linkin return -EOPNOTSUPP; 2442d116e3eSDmytro Linkin if (max_rate == evport->qos.max_rate) 2452d116e3eSDmytro Linkin return 0; 2462d116e3eSDmytro Linkin 2470fe132eaSDmytro Linkin /* If parent group has rate limit need to set to group 2480fe132eaSDmytro Linkin * value when new max rate is 0. 2490fe132eaSDmytro Linkin */ 2500fe132eaSDmytro Linkin if (evport->qos.group && !max_rate) 2510fe132eaSDmytro Linkin act_max_rate = evport->qos.group->max_rate; 2520fe132eaSDmytro Linkin 2530fe132eaSDmytro Linkin err = esw_qos_vport_config(esw, evport, act_max_rate, evport->qos.bw_share, extack); 254f47e04ebSDmytro Linkin 2552d116e3eSDmytro Linkin if (!err) 2562d116e3eSDmytro Linkin evport->qos.max_rate = max_rate; 2572d116e3eSDmytro Linkin 2582d116e3eSDmytro Linkin return err; 2592d116e3eSDmytro Linkin } 2602d116e3eSDmytro Linkin 261f47e04ebSDmytro Linkin static int esw_qos_set_group_min_rate(struct mlx5_eswitch *esw, struct mlx5_esw_rate_group *group, 262f47e04ebSDmytro Linkin u32 min_rate, struct netlink_ext_ack *extack) 263f47e04ebSDmytro Linkin { 264f47e04ebSDmytro Linkin u32 fw_max_bw_share = MLX5_CAP_QOS(esw->dev, max_tsar_bw_share); 265f47e04ebSDmytro Linkin struct mlx5_core_dev *dev = esw->dev; 266f47e04ebSDmytro Linkin u32 previous_min_rate, divider; 267f47e04ebSDmytro Linkin int err; 268f47e04ebSDmytro Linkin 269f47e04ebSDmytro Linkin if (!(MLX5_CAP_QOS(dev, esw_bw_share) && fw_max_bw_share >= MLX5_MIN_BW_SHARE)) 270f47e04ebSDmytro Linkin return -EOPNOTSUPP; 271f47e04ebSDmytro Linkin 272f47e04ebSDmytro Linkin if (min_rate == group->min_rate) 273f47e04ebSDmytro Linkin return 0; 274f47e04ebSDmytro Linkin 275f47e04ebSDmytro Linkin previous_min_rate = group->min_rate; 276f47e04ebSDmytro Linkin group->min_rate = min_rate; 277f47e04ebSDmytro Linkin divider = esw_qos_calculate_min_rate_divider(esw, group, true); 278f47e04ebSDmytro Linkin err = esw_qos_normalize_groups_min_rate(esw, divider, extack); 279f47e04ebSDmytro Linkin if (err) { 280f47e04ebSDmytro Linkin group->min_rate = previous_min_rate; 281f47e04ebSDmytro Linkin NL_SET_ERR_MSG_MOD(extack, "E-Switch group min rate setting failed"); 282f47e04ebSDmytro Linkin 283f47e04ebSDmytro Linkin /* Attempt restoring previous configuration */ 284f47e04ebSDmytro Linkin divider = esw_qos_calculate_min_rate_divider(esw, group, true); 285f47e04ebSDmytro Linkin if (esw_qos_normalize_groups_min_rate(esw, divider, extack)) 286f47e04ebSDmytro Linkin NL_SET_ERR_MSG_MOD(extack, "E-Switch BW share restore failed"); 287f47e04ebSDmytro Linkin } 288f47e04ebSDmytro Linkin 289f47e04ebSDmytro Linkin return err; 290f47e04ebSDmytro Linkin } 291f47e04ebSDmytro Linkin 292f47e04ebSDmytro Linkin static int esw_qos_set_group_max_rate(struct mlx5_eswitch *esw, 293f47e04ebSDmytro Linkin struct mlx5_esw_rate_group *group, 294f47e04ebSDmytro Linkin u32 max_rate, struct netlink_ext_ack *extack) 295f47e04ebSDmytro Linkin { 2960fe132eaSDmytro Linkin struct mlx5_vport *vport; 2970fe132eaSDmytro Linkin unsigned long i; 298f47e04ebSDmytro Linkin int err; 299f47e04ebSDmytro Linkin 300f47e04ebSDmytro Linkin if (group->max_rate == max_rate) 301f47e04ebSDmytro Linkin return 0; 302f47e04ebSDmytro Linkin 303f47e04ebSDmytro Linkin err = esw_qos_group_config(esw, group, max_rate, group->bw_share, extack); 304f47e04ebSDmytro Linkin if (err) 305f47e04ebSDmytro Linkin return err; 306f47e04ebSDmytro Linkin 307f47e04ebSDmytro Linkin group->max_rate = max_rate; 308f47e04ebSDmytro Linkin 3090fe132eaSDmytro Linkin /* Any unlimited vports in the group should be set 3100fe132eaSDmytro Linkin * with the value of the group. 3110fe132eaSDmytro Linkin */ 3120fe132eaSDmytro Linkin mlx5_esw_for_each_vport(esw, i, vport) { 3130fe132eaSDmytro Linkin if (!vport->enabled || !vport->qos.enabled || 3140fe132eaSDmytro Linkin vport->qos.group != group || vport->qos.max_rate) 3150fe132eaSDmytro Linkin continue; 3160fe132eaSDmytro Linkin 3170fe132eaSDmytro Linkin err = esw_qos_vport_config(esw, vport, max_rate, vport->qos.bw_share, extack); 3180fe132eaSDmytro Linkin if (err) 3190fe132eaSDmytro Linkin NL_SET_ERR_MSG_MOD(extack, 3200fe132eaSDmytro Linkin "E-Switch vport implicit rate limit setting failed"); 3210fe132eaSDmytro Linkin } 3220fe132eaSDmytro Linkin 323f47e04ebSDmytro Linkin return err; 324f47e04ebSDmytro Linkin } 325f47e04ebSDmytro Linkin 3260fe132eaSDmytro Linkin static int esw_qos_vport_create_sched_element(struct mlx5_eswitch *esw, 3270fe132eaSDmytro Linkin struct mlx5_vport *vport, 3280fe132eaSDmytro Linkin u32 max_rate, u32 bw_share) 3290fe132eaSDmytro Linkin { 3300fe132eaSDmytro Linkin u32 sched_ctx[MLX5_ST_SZ_DW(scheduling_context)] = {}; 3310fe132eaSDmytro Linkin struct mlx5_esw_rate_group *group = vport->qos.group; 3320fe132eaSDmytro Linkin struct mlx5_core_dev *dev = esw->dev; 3330fe132eaSDmytro Linkin u32 parent_tsar_ix; 3340fe132eaSDmytro Linkin void *vport_elem; 3350fe132eaSDmytro Linkin int err; 3360fe132eaSDmytro Linkin 3370fe132eaSDmytro Linkin parent_tsar_ix = group ? group->tsar_ix : esw->qos.root_tsar_ix; 3380fe132eaSDmytro Linkin MLX5_SET(scheduling_context, sched_ctx, element_type, 3390fe132eaSDmytro Linkin SCHEDULING_CONTEXT_ELEMENT_TYPE_VPORT); 3400fe132eaSDmytro Linkin vport_elem = MLX5_ADDR_OF(scheduling_context, sched_ctx, element_attributes); 3410fe132eaSDmytro Linkin MLX5_SET(vport_element, vport_elem, vport_number, vport->vport); 3420fe132eaSDmytro Linkin MLX5_SET(scheduling_context, sched_ctx, parent_element_id, parent_tsar_ix); 3430fe132eaSDmytro Linkin MLX5_SET(scheduling_context, sched_ctx, max_average_bw, max_rate); 3440fe132eaSDmytro Linkin MLX5_SET(scheduling_context, sched_ctx, bw_share, bw_share); 3450fe132eaSDmytro Linkin 3460fe132eaSDmytro Linkin err = mlx5_create_scheduling_element_cmd(dev, 3470fe132eaSDmytro Linkin SCHEDULING_HIERARCHY_E_SWITCH, 3480fe132eaSDmytro Linkin sched_ctx, 3490fe132eaSDmytro Linkin &vport->qos.esw_tsar_ix); 3500fe132eaSDmytro Linkin if (err) { 3510fe132eaSDmytro Linkin esw_warn(esw->dev, "E-Switch create TSAR vport element failed (vport=%d,err=%d)\n", 3520fe132eaSDmytro Linkin vport->vport, err); 3530fe132eaSDmytro Linkin return err; 3540fe132eaSDmytro Linkin } 3550fe132eaSDmytro Linkin 3560fe132eaSDmytro Linkin return 0; 3570fe132eaSDmytro Linkin } 3580fe132eaSDmytro Linkin 3590fe132eaSDmytro Linkin static int esw_qos_update_group_scheduling_element(struct mlx5_eswitch *esw, 3600fe132eaSDmytro Linkin struct mlx5_vport *vport, 3610fe132eaSDmytro Linkin struct mlx5_esw_rate_group *curr_group, 3620fe132eaSDmytro Linkin struct mlx5_esw_rate_group *new_group, 3630fe132eaSDmytro Linkin struct netlink_ext_ack *extack) 3640fe132eaSDmytro Linkin { 3650fe132eaSDmytro Linkin u32 max_rate; 3660fe132eaSDmytro Linkin int err; 3670fe132eaSDmytro Linkin 3680fe132eaSDmytro Linkin err = mlx5_destroy_scheduling_element_cmd(esw->dev, 3690fe132eaSDmytro Linkin SCHEDULING_HIERARCHY_E_SWITCH, 3700fe132eaSDmytro Linkin vport->qos.esw_tsar_ix); 3710fe132eaSDmytro Linkin if (err) { 3720fe132eaSDmytro Linkin NL_SET_ERR_MSG_MOD(extack, "E-Switch destroy TSAR vport element failed"); 3730fe132eaSDmytro Linkin return err; 3740fe132eaSDmytro Linkin } 3750fe132eaSDmytro Linkin 3760fe132eaSDmytro Linkin vport->qos.group = new_group; 3770fe132eaSDmytro Linkin max_rate = vport->qos.max_rate ? vport->qos.max_rate : new_group->max_rate; 3780fe132eaSDmytro Linkin 3790fe132eaSDmytro Linkin /* If vport is unlimited, we set the group's value. 3800fe132eaSDmytro Linkin * Therefore, if the group is limited it will apply to 3810fe132eaSDmytro Linkin * the vport as well and if not, vport will remain unlimited. 3820fe132eaSDmytro Linkin */ 3830fe132eaSDmytro Linkin err = esw_qos_vport_create_sched_element(esw, vport, max_rate, vport->qos.bw_share); 3840fe132eaSDmytro Linkin if (err) { 3850fe132eaSDmytro Linkin NL_SET_ERR_MSG_MOD(extack, "E-Switch vport group set failed."); 3860fe132eaSDmytro Linkin goto err_sched; 3870fe132eaSDmytro Linkin } 3880fe132eaSDmytro Linkin 3890fe132eaSDmytro Linkin return 0; 3900fe132eaSDmytro Linkin 3910fe132eaSDmytro Linkin err_sched: 3920fe132eaSDmytro Linkin vport->qos.group = curr_group; 3930fe132eaSDmytro Linkin max_rate = vport->qos.max_rate ? vport->qos.max_rate : curr_group->max_rate; 3940fe132eaSDmytro Linkin if (esw_qos_vport_create_sched_element(esw, vport, max_rate, vport->qos.bw_share)) 3950fe132eaSDmytro Linkin esw_warn(esw->dev, "E-Switch vport group restore failed (vport=%d)\n", 3960fe132eaSDmytro Linkin vport->vport); 3970fe132eaSDmytro Linkin 3980fe132eaSDmytro Linkin return err; 3990fe132eaSDmytro Linkin } 4000fe132eaSDmytro Linkin 4010fe132eaSDmytro Linkin static int esw_qos_vport_update_group(struct mlx5_eswitch *esw, 4020fe132eaSDmytro Linkin struct mlx5_vport *vport, 4030fe132eaSDmytro Linkin struct mlx5_esw_rate_group *group, 4040fe132eaSDmytro Linkin struct netlink_ext_ack *extack) 4050fe132eaSDmytro Linkin { 4060fe132eaSDmytro Linkin struct mlx5_esw_rate_group *new_group, *curr_group; 4070fe132eaSDmytro Linkin int err; 4080fe132eaSDmytro Linkin 4090fe132eaSDmytro Linkin if (!vport->enabled) 4100fe132eaSDmytro Linkin return -EINVAL; 4110fe132eaSDmytro Linkin 4120fe132eaSDmytro Linkin curr_group = vport->qos.group; 4130fe132eaSDmytro Linkin new_group = group ?: esw->qos.group0; 4140fe132eaSDmytro Linkin if (curr_group == new_group) 4150fe132eaSDmytro Linkin return 0; 4160fe132eaSDmytro Linkin 4170fe132eaSDmytro Linkin err = esw_qos_update_group_scheduling_element(esw, vport, curr_group, new_group, extack); 4180fe132eaSDmytro Linkin if (err) 4190fe132eaSDmytro Linkin return err; 4200fe132eaSDmytro Linkin 4210fe132eaSDmytro Linkin /* Recalculate bw share weights of old and new groups */ 4220fe132eaSDmytro Linkin if (vport->qos.bw_share) { 4230fe132eaSDmytro Linkin esw_qos_normalize_vports_min_rate(esw, curr_group, extack); 4240fe132eaSDmytro Linkin esw_qos_normalize_vports_min_rate(esw, new_group, extack); 4250fe132eaSDmytro Linkin } 4260fe132eaSDmytro Linkin 4270fe132eaSDmytro Linkin return 0; 4280fe132eaSDmytro Linkin } 4290fe132eaSDmytro Linkin 4301ae258f8SDmytro Linkin static struct mlx5_esw_rate_group * 431*85c5f7c9SDmytro Linkin __esw_qos_create_rate_group(struct mlx5_eswitch *esw, struct netlink_ext_ack *extack) 4321ae258f8SDmytro Linkin { 4331ae258f8SDmytro Linkin u32 tsar_ctx[MLX5_ST_SZ_DW(scheduling_context)] = {}; 4341ae258f8SDmytro Linkin struct mlx5_esw_rate_group *group; 435f47e04ebSDmytro Linkin u32 divider; 4361ae258f8SDmytro Linkin int err; 4371ae258f8SDmytro Linkin 4381ae258f8SDmytro Linkin group = kzalloc(sizeof(*group), GFP_KERNEL); 4391ae258f8SDmytro Linkin if (!group) 4401ae258f8SDmytro Linkin return ERR_PTR(-ENOMEM); 4411ae258f8SDmytro Linkin 4421ae258f8SDmytro Linkin MLX5_SET(scheduling_context, tsar_ctx, parent_element_id, 4431ae258f8SDmytro Linkin esw->qos.root_tsar_ix); 4441ae258f8SDmytro Linkin err = mlx5_create_scheduling_element_cmd(esw->dev, 4451ae258f8SDmytro Linkin SCHEDULING_HIERARCHY_E_SWITCH, 4461ae258f8SDmytro Linkin tsar_ctx, 4471ae258f8SDmytro Linkin &group->tsar_ix); 4481ae258f8SDmytro Linkin if (err) { 4491ae258f8SDmytro Linkin NL_SET_ERR_MSG_MOD(extack, "E-Switch create TSAR for group failed"); 4501ae258f8SDmytro Linkin goto err_sched_elem; 4511ae258f8SDmytro Linkin } 4521ae258f8SDmytro Linkin 453f47e04ebSDmytro Linkin list_add_tail(&group->list, &esw->qos.groups); 454f47e04ebSDmytro Linkin 455f47e04ebSDmytro Linkin divider = esw_qos_calculate_min_rate_divider(esw, group, true); 456f47e04ebSDmytro Linkin if (divider) { 457f47e04ebSDmytro Linkin err = esw_qos_normalize_groups_min_rate(esw, divider, extack); 458f47e04ebSDmytro Linkin if (err) { 459f47e04ebSDmytro Linkin NL_SET_ERR_MSG_MOD(extack, "E-Switch groups normalization failed"); 460f47e04ebSDmytro Linkin goto err_min_rate; 461f47e04ebSDmytro Linkin } 462f47e04ebSDmytro Linkin } 4633202ea65SDmytro Linkin trace_mlx5_esw_group_qos_create(esw->dev, group, group->tsar_ix); 464f47e04ebSDmytro Linkin 4651ae258f8SDmytro Linkin return group; 4661ae258f8SDmytro Linkin 467f47e04ebSDmytro Linkin err_min_rate: 468f47e04ebSDmytro Linkin list_del(&group->list); 469a6f74333SDmytro Linkin if (mlx5_destroy_scheduling_element_cmd(esw->dev, 470f47e04ebSDmytro Linkin SCHEDULING_HIERARCHY_E_SWITCH, 471a6f74333SDmytro Linkin group->tsar_ix)) 472f47e04ebSDmytro Linkin NL_SET_ERR_MSG_MOD(extack, "E-Switch destroy TSAR for group failed"); 4731ae258f8SDmytro Linkin err_sched_elem: 4741ae258f8SDmytro Linkin kfree(group); 4751ae258f8SDmytro Linkin return ERR_PTR(err); 4761ae258f8SDmytro Linkin } 4771ae258f8SDmytro Linkin 478*85c5f7c9SDmytro Linkin static int esw_qos_get(struct mlx5_eswitch *esw, struct netlink_ext_ack *extack); 479*85c5f7c9SDmytro Linkin static void esw_qos_put(struct mlx5_eswitch *esw); 480*85c5f7c9SDmytro Linkin 481*85c5f7c9SDmytro Linkin static struct mlx5_esw_rate_group * 482*85c5f7c9SDmytro Linkin esw_qos_create_rate_group(struct mlx5_eswitch *esw, struct netlink_ext_ack *extack) 483*85c5f7c9SDmytro Linkin { 484*85c5f7c9SDmytro Linkin struct mlx5_esw_rate_group *group; 485*85c5f7c9SDmytro Linkin int err; 486*85c5f7c9SDmytro Linkin 487*85c5f7c9SDmytro Linkin if (!MLX5_CAP_QOS(esw->dev, log_esw_max_sched_depth)) 488*85c5f7c9SDmytro Linkin return ERR_PTR(-EOPNOTSUPP); 489*85c5f7c9SDmytro Linkin 490*85c5f7c9SDmytro Linkin err = esw_qos_get(esw, extack); 491*85c5f7c9SDmytro Linkin if (err) 492*85c5f7c9SDmytro Linkin return ERR_PTR(err); 493*85c5f7c9SDmytro Linkin 494*85c5f7c9SDmytro Linkin group = __esw_qos_create_rate_group(esw, extack); 495*85c5f7c9SDmytro Linkin if (IS_ERR(group)) 496*85c5f7c9SDmytro Linkin esw_qos_put(esw); 497*85c5f7c9SDmytro Linkin 498*85c5f7c9SDmytro Linkin return group; 499*85c5f7c9SDmytro Linkin } 500*85c5f7c9SDmytro Linkin 501*85c5f7c9SDmytro Linkin static int __esw_qos_destroy_rate_group(struct mlx5_eswitch *esw, 5021ae258f8SDmytro Linkin struct mlx5_esw_rate_group *group, 5031ae258f8SDmytro Linkin struct netlink_ext_ack *extack) 5041ae258f8SDmytro Linkin { 505f47e04ebSDmytro Linkin u32 divider; 5061ae258f8SDmytro Linkin int err; 5071ae258f8SDmytro Linkin 508f47e04ebSDmytro Linkin list_del(&group->list); 509f47e04ebSDmytro Linkin 510f47e04ebSDmytro Linkin divider = esw_qos_calculate_min_rate_divider(esw, NULL, true); 511f47e04ebSDmytro Linkin err = esw_qos_normalize_groups_min_rate(esw, divider, extack); 512f47e04ebSDmytro Linkin if (err) 513f47e04ebSDmytro Linkin NL_SET_ERR_MSG_MOD(extack, "E-Switch groups' normalization failed"); 514f47e04ebSDmytro Linkin 5151ae258f8SDmytro Linkin err = mlx5_destroy_scheduling_element_cmd(esw->dev, 5161ae258f8SDmytro Linkin SCHEDULING_HIERARCHY_E_SWITCH, 5171ae258f8SDmytro Linkin group->tsar_ix); 5181ae258f8SDmytro Linkin if (err) 5191ae258f8SDmytro Linkin NL_SET_ERR_MSG_MOD(extack, "E-Switch destroy TSAR_ID failed"); 5201ae258f8SDmytro Linkin 5213202ea65SDmytro Linkin trace_mlx5_esw_group_qos_destroy(esw->dev, group, group->tsar_ix); 522*85c5f7c9SDmytro Linkin 5231ae258f8SDmytro Linkin kfree(group); 524*85c5f7c9SDmytro Linkin 525*85c5f7c9SDmytro Linkin return err; 526*85c5f7c9SDmytro Linkin } 527*85c5f7c9SDmytro Linkin 528*85c5f7c9SDmytro Linkin static int esw_qos_destroy_rate_group(struct mlx5_eswitch *esw, 529*85c5f7c9SDmytro Linkin struct mlx5_esw_rate_group *group, 530*85c5f7c9SDmytro Linkin struct netlink_ext_ack *extack) 531*85c5f7c9SDmytro Linkin { 532*85c5f7c9SDmytro Linkin int err; 533*85c5f7c9SDmytro Linkin 534*85c5f7c9SDmytro Linkin err = __esw_qos_destroy_rate_group(esw, group, extack); 535*85c5f7c9SDmytro Linkin esw_qos_put(esw); 536*85c5f7c9SDmytro Linkin 5371ae258f8SDmytro Linkin return err; 5381ae258f8SDmytro Linkin } 5391ae258f8SDmytro Linkin 5402d116e3eSDmytro Linkin static bool esw_qos_element_type_supported(struct mlx5_core_dev *dev, int type) 5412d116e3eSDmytro Linkin { 5422d116e3eSDmytro Linkin switch (type) { 5432d116e3eSDmytro Linkin case SCHEDULING_CONTEXT_ELEMENT_TYPE_TSAR: 5442d116e3eSDmytro Linkin return MLX5_CAP_QOS(dev, esw_element_type) & 5452d116e3eSDmytro Linkin ELEMENT_TYPE_CAP_MASK_TASR; 5462d116e3eSDmytro Linkin case SCHEDULING_CONTEXT_ELEMENT_TYPE_VPORT: 5472d116e3eSDmytro Linkin return MLX5_CAP_QOS(dev, esw_element_type) & 5482d116e3eSDmytro Linkin ELEMENT_TYPE_CAP_MASK_VPORT; 5492d116e3eSDmytro Linkin case SCHEDULING_CONTEXT_ELEMENT_TYPE_VPORT_TC: 5502d116e3eSDmytro Linkin return MLX5_CAP_QOS(dev, esw_element_type) & 5512d116e3eSDmytro Linkin ELEMENT_TYPE_CAP_MASK_VPORT_TC; 5522d116e3eSDmytro Linkin case SCHEDULING_CONTEXT_ELEMENT_TYPE_PARA_VPORT_TC: 5532d116e3eSDmytro Linkin return MLX5_CAP_QOS(dev, esw_element_type) & 5542d116e3eSDmytro Linkin ELEMENT_TYPE_CAP_MASK_PARA_VPORT_TC; 5552d116e3eSDmytro Linkin } 5562d116e3eSDmytro Linkin return false; 5572d116e3eSDmytro Linkin } 5582d116e3eSDmytro Linkin 559*85c5f7c9SDmytro Linkin static int esw_qos_create(struct mlx5_eswitch *esw, struct netlink_ext_ack *extack) 5602d116e3eSDmytro Linkin { 5612d116e3eSDmytro Linkin u32 tsar_ctx[MLX5_ST_SZ_DW(scheduling_context)] = {}; 5622d116e3eSDmytro Linkin struct mlx5_core_dev *dev = esw->dev; 5632d116e3eSDmytro Linkin __be32 *attr; 5642d116e3eSDmytro Linkin int err; 5652d116e3eSDmytro Linkin 5662d116e3eSDmytro Linkin if (!MLX5_CAP_GEN(dev, qos) || !MLX5_CAP_QOS(dev, esw_scheduling)) 567*85c5f7c9SDmytro Linkin return -EOPNOTSUPP; 5682d116e3eSDmytro Linkin 5692d116e3eSDmytro Linkin if (!esw_qos_element_type_supported(dev, SCHEDULING_CONTEXT_ELEMENT_TYPE_TSAR)) 570*85c5f7c9SDmytro Linkin return -EOPNOTSUPP; 5712d116e3eSDmytro Linkin 5722d116e3eSDmytro Linkin MLX5_SET(scheduling_context, tsar_ctx, element_type, 5732d116e3eSDmytro Linkin SCHEDULING_CONTEXT_ELEMENT_TYPE_TSAR); 5742d116e3eSDmytro Linkin 5752d116e3eSDmytro Linkin attr = MLX5_ADDR_OF(scheduling_context, tsar_ctx, element_attributes); 5762d116e3eSDmytro Linkin *attr = cpu_to_be32(TSAR_ELEMENT_TSAR_TYPE_DWRR << 16); 5772d116e3eSDmytro Linkin 5782d116e3eSDmytro Linkin err = mlx5_create_scheduling_element_cmd(dev, 5792d116e3eSDmytro Linkin SCHEDULING_HIERARCHY_E_SWITCH, 5802d116e3eSDmytro Linkin tsar_ctx, 5812d116e3eSDmytro Linkin &esw->qos.root_tsar_ix); 5822d116e3eSDmytro Linkin if (err) { 5831ae258f8SDmytro Linkin esw_warn(dev, "E-Switch create root TSAR failed (%d)\n", err); 584*85c5f7c9SDmytro Linkin return err; 5852d116e3eSDmytro Linkin } 5862d116e3eSDmytro Linkin 587f47e04ebSDmytro Linkin INIT_LIST_HEAD(&esw->qos.groups); 5881ae258f8SDmytro Linkin if (MLX5_CAP_QOS(dev, log_esw_max_sched_depth)) { 589*85c5f7c9SDmytro Linkin esw->qos.group0 = __esw_qos_create_rate_group(esw, extack); 5901ae258f8SDmytro Linkin if (IS_ERR(esw->qos.group0)) { 5911ae258f8SDmytro Linkin esw_warn(dev, "E-Switch create rate group 0 failed (%ld)\n", 5921ae258f8SDmytro Linkin PTR_ERR(esw->qos.group0)); 5931ae258f8SDmytro Linkin goto err_group0; 5941ae258f8SDmytro Linkin } 5951ae258f8SDmytro Linkin } 596*85c5f7c9SDmytro Linkin refcount_set(&esw->qos.refcnt, 1); 597*85c5f7c9SDmytro Linkin 598*85c5f7c9SDmytro Linkin return 0; 5991ae258f8SDmytro Linkin 6001ae258f8SDmytro Linkin err_group0: 601*85c5f7c9SDmytro Linkin if (mlx5_destroy_scheduling_element_cmd(esw->dev, SCHEDULING_HIERARCHY_E_SWITCH, 602*85c5f7c9SDmytro Linkin esw->qos.root_tsar_ix)) 603*85c5f7c9SDmytro Linkin esw_warn(esw->dev, "E-Switch destroy root TSAR failed.\n"); 604*85c5f7c9SDmytro Linkin 605*85c5f7c9SDmytro Linkin return err; 6062d116e3eSDmytro Linkin } 6072d116e3eSDmytro Linkin 608*85c5f7c9SDmytro Linkin static void esw_qos_destroy(struct mlx5_eswitch *esw) 6092d116e3eSDmytro Linkin { 6102d116e3eSDmytro Linkin int err; 6112d116e3eSDmytro Linkin 6121ae258f8SDmytro Linkin if (esw->qos.group0) 613*85c5f7c9SDmytro Linkin __esw_qos_destroy_rate_group(esw, esw->qos.group0, NULL); 6142d116e3eSDmytro Linkin 6152d116e3eSDmytro Linkin err = mlx5_destroy_scheduling_element_cmd(esw->dev, 6162d116e3eSDmytro Linkin SCHEDULING_HIERARCHY_E_SWITCH, 6172d116e3eSDmytro Linkin esw->qos.root_tsar_ix); 6182d116e3eSDmytro Linkin if (err) 6191ae258f8SDmytro Linkin esw_warn(esw->dev, "E-Switch destroy root TSAR failed (%d)\n", err); 620*85c5f7c9SDmytro Linkin } 6212d116e3eSDmytro Linkin 622*85c5f7c9SDmytro Linkin static int esw_qos_get(struct mlx5_eswitch *esw, struct netlink_ext_ack *extack) 623*85c5f7c9SDmytro Linkin { 624*85c5f7c9SDmytro Linkin int err = 0; 625*85c5f7c9SDmytro Linkin 626*85c5f7c9SDmytro Linkin lockdep_assert_held(&esw->state_lock); 627*85c5f7c9SDmytro Linkin 628*85c5f7c9SDmytro Linkin if (!refcount_inc_not_zero(&esw->qos.refcnt)) { 629*85c5f7c9SDmytro Linkin /* esw_qos_create() set refcount to 1 only on success. 630*85c5f7c9SDmytro Linkin * No need to decrement on failure. 631*85c5f7c9SDmytro Linkin */ 632*85c5f7c9SDmytro Linkin err = esw_qos_create(esw, extack); 633*85c5f7c9SDmytro Linkin } 634*85c5f7c9SDmytro Linkin 635*85c5f7c9SDmytro Linkin return err; 636*85c5f7c9SDmytro Linkin } 637*85c5f7c9SDmytro Linkin 638*85c5f7c9SDmytro Linkin static void esw_qos_put(struct mlx5_eswitch *esw) 639*85c5f7c9SDmytro Linkin { 640*85c5f7c9SDmytro Linkin lockdep_assert_held(&esw->state_lock); 641*85c5f7c9SDmytro Linkin if (refcount_dec_and_test(&esw->qos.refcnt)) 642*85c5f7c9SDmytro Linkin esw_qos_destroy(esw); 6432d116e3eSDmytro Linkin } 6442d116e3eSDmytro Linkin 645d7df09f5SDmytro Linkin static int esw_qos_vport_enable(struct mlx5_eswitch *esw, struct mlx5_vport *vport, 646*85c5f7c9SDmytro Linkin u32 max_rate, u32 bw_share, struct netlink_ext_ack *extack) 6472d116e3eSDmytro Linkin { 6482d116e3eSDmytro Linkin int err; 6492d116e3eSDmytro Linkin 6502d116e3eSDmytro Linkin lockdep_assert_held(&esw->state_lock); 6512d116e3eSDmytro Linkin if (vport->qos.enabled) 652d7df09f5SDmytro Linkin return 0; 6532d116e3eSDmytro Linkin 654*85c5f7c9SDmytro Linkin err = esw_qos_get(esw, extack); 655*85c5f7c9SDmytro Linkin if (err) 656*85c5f7c9SDmytro Linkin return err; 657*85c5f7c9SDmytro Linkin 6580fe132eaSDmytro Linkin vport->qos.group = esw->qos.group0; 6592d116e3eSDmytro Linkin 6600fe132eaSDmytro Linkin err = esw_qos_vport_create_sched_element(esw, vport, max_rate, bw_share); 661*85c5f7c9SDmytro Linkin if (err) 662*85c5f7c9SDmytro Linkin goto err_out; 663*85c5f7c9SDmytro Linkin 6642d116e3eSDmytro Linkin vport->qos.enabled = true; 6653202ea65SDmytro Linkin trace_mlx5_esw_vport_qos_create(vport, bw_share, max_rate); 666*85c5f7c9SDmytro Linkin 667*85c5f7c9SDmytro Linkin return 0; 668*85c5f7c9SDmytro Linkin 669*85c5f7c9SDmytro Linkin err_out: 670*85c5f7c9SDmytro Linkin esw_qos_put(esw); 6712d116e3eSDmytro Linkin 6722d116e3eSDmytro Linkin return err; 6732d116e3eSDmytro Linkin } 6742d116e3eSDmytro Linkin 6752d116e3eSDmytro Linkin void mlx5_esw_qos_vport_disable(struct mlx5_eswitch *esw, struct mlx5_vport *vport) 6762d116e3eSDmytro Linkin { 6772d116e3eSDmytro Linkin int err; 6782d116e3eSDmytro Linkin 6792d116e3eSDmytro Linkin lockdep_assert_held(&esw->state_lock); 680*85c5f7c9SDmytro Linkin if (!vport->qos.enabled) 6812d116e3eSDmytro Linkin return; 6820fe132eaSDmytro Linkin WARN(vport->qos.group && vport->qos.group != esw->qos.group0, 6830fe132eaSDmytro Linkin "Disabling QoS on port before detaching it from group"); 6842d116e3eSDmytro Linkin 6852d116e3eSDmytro Linkin err = mlx5_destroy_scheduling_element_cmd(esw->dev, 6862d116e3eSDmytro Linkin SCHEDULING_HIERARCHY_E_SWITCH, 6872d116e3eSDmytro Linkin vport->qos.esw_tsar_ix); 6882d116e3eSDmytro Linkin if (err) 6892d116e3eSDmytro Linkin esw_warn(esw->dev, "E-Switch destroy TSAR vport element failed (vport=%d,err=%d)\n", 6902d116e3eSDmytro Linkin vport->vport, err); 6912d116e3eSDmytro Linkin 692d7df09f5SDmytro Linkin memset(&vport->qos, 0, sizeof(vport->qos)); 6933202ea65SDmytro Linkin trace_mlx5_esw_vport_qos_destroy(vport); 694*85c5f7c9SDmytro Linkin 695*85c5f7c9SDmytro Linkin esw_qos_put(esw); 6962d116e3eSDmytro Linkin } 6972d116e3eSDmytro Linkin 698d7df09f5SDmytro Linkin int mlx5_esw_qos_set_vport_rate(struct mlx5_eswitch *esw, struct mlx5_vport *vport, 699d7df09f5SDmytro Linkin u32 min_rate, u32 max_rate) 700d7df09f5SDmytro Linkin { 701d7df09f5SDmytro Linkin int err; 702d7df09f5SDmytro Linkin 703d7df09f5SDmytro Linkin lockdep_assert_held(&esw->state_lock); 704*85c5f7c9SDmytro Linkin err = esw_qos_vport_enable(esw, vport, 0, 0, NULL); 705d7df09f5SDmytro Linkin if (err) 706d7df09f5SDmytro Linkin return err; 707d7df09f5SDmytro Linkin 708d7df09f5SDmytro Linkin err = esw_qos_set_vport_min_rate(esw, vport, min_rate, NULL); 709d7df09f5SDmytro Linkin if (!err) 710d7df09f5SDmytro Linkin err = esw_qos_set_vport_max_rate(esw, vport, max_rate, NULL); 711d7df09f5SDmytro Linkin 712d7df09f5SDmytro Linkin return err; 713d7df09f5SDmytro Linkin } 714d7df09f5SDmytro Linkin 7152d116e3eSDmytro Linkin int mlx5_esw_qos_modify_vport_rate(struct mlx5_eswitch *esw, u16 vport_num, u32 rate_mbps) 7162d116e3eSDmytro Linkin { 7172d116e3eSDmytro Linkin u32 ctx[MLX5_ST_SZ_DW(scheduling_context)] = {}; 7182d116e3eSDmytro Linkin struct mlx5_vport *vport; 7192d116e3eSDmytro Linkin u32 bitmask; 720d7df09f5SDmytro Linkin int err; 7212d116e3eSDmytro Linkin 7222d116e3eSDmytro Linkin vport = mlx5_eswitch_get_vport(esw, vport_num); 7232d116e3eSDmytro Linkin if (IS_ERR(vport)) 7242d116e3eSDmytro Linkin return PTR_ERR(vport); 7252d116e3eSDmytro Linkin 726d7df09f5SDmytro Linkin mutex_lock(&esw->state_lock); 727d7df09f5SDmytro Linkin if (!vport->qos.enabled) { 728d7df09f5SDmytro Linkin /* Eswitch QoS wasn't enabled yet. Enable it and vport QoS. */ 729*85c5f7c9SDmytro Linkin err = esw_qos_vport_enable(esw, vport, rate_mbps, vport->qos.bw_share, NULL); 730d7df09f5SDmytro Linkin } else { 7312d116e3eSDmytro Linkin MLX5_SET(scheduling_context, ctx, max_average_bw, rate_mbps); 7322d116e3eSDmytro Linkin 733d7df09f5SDmytro Linkin bitmask = MODIFY_SCHEDULING_ELEMENT_IN_MODIFY_BITMASK_MAX_AVERAGE_BW; 734d7df09f5SDmytro Linkin err = mlx5_modify_scheduling_element_cmd(esw->dev, 7352d116e3eSDmytro Linkin SCHEDULING_HIERARCHY_E_SWITCH, 7362d116e3eSDmytro Linkin ctx, 7372d116e3eSDmytro Linkin vport->qos.esw_tsar_ix, 7382d116e3eSDmytro Linkin bitmask); 7392d116e3eSDmytro Linkin } 740d7df09f5SDmytro Linkin mutex_unlock(&esw->state_lock); 741d7df09f5SDmytro Linkin 742d7df09f5SDmytro Linkin return err; 743d7df09f5SDmytro Linkin } 744ad34f02fSDmytro Linkin 745ad34f02fSDmytro Linkin #define MLX5_LINKSPEED_UNIT 125000 /* 1Mbps in Bps */ 746ad34f02fSDmytro Linkin 747ad34f02fSDmytro Linkin /* Converts bytes per second value passed in a pointer into megabits per 748ad34f02fSDmytro Linkin * second, rewriting last. If converted rate exceed link speed or is not a 749ad34f02fSDmytro Linkin * fraction of Mbps - returns error. 750ad34f02fSDmytro Linkin */ 751ad34f02fSDmytro Linkin static int esw_qos_devlink_rate_to_mbps(struct mlx5_core_dev *mdev, const char *name, 752ad34f02fSDmytro Linkin u64 *rate, struct netlink_ext_ack *extack) 753ad34f02fSDmytro Linkin { 754ad34f02fSDmytro Linkin u32 link_speed_max, reminder; 755ad34f02fSDmytro Linkin u64 value; 756ad34f02fSDmytro Linkin int err; 757ad34f02fSDmytro Linkin 758ad34f02fSDmytro Linkin err = mlx5e_port_max_linkspeed(mdev, &link_speed_max); 759ad34f02fSDmytro Linkin if (err) { 760ad34f02fSDmytro Linkin NL_SET_ERR_MSG_MOD(extack, "Failed to get link maximum speed"); 761ad34f02fSDmytro Linkin return err; 762ad34f02fSDmytro Linkin } 763ad34f02fSDmytro Linkin 764ad34f02fSDmytro Linkin value = div_u64_rem(*rate, MLX5_LINKSPEED_UNIT, &reminder); 765ad34f02fSDmytro Linkin if (reminder) { 766ad34f02fSDmytro Linkin pr_err("%s rate value %lluBps not in link speed units of 1Mbps.\n", 767ad34f02fSDmytro Linkin name, *rate); 768ad34f02fSDmytro Linkin NL_SET_ERR_MSG_MOD(extack, "TX rate value not in link speed units of 1Mbps"); 769ad34f02fSDmytro Linkin return -EINVAL; 770ad34f02fSDmytro Linkin } 771ad34f02fSDmytro Linkin 772ad34f02fSDmytro Linkin if (value > link_speed_max) { 773ad34f02fSDmytro Linkin pr_err("%s rate value %lluMbps exceed link maximum speed %u.\n", 774ad34f02fSDmytro Linkin name, value, link_speed_max); 775ad34f02fSDmytro Linkin NL_SET_ERR_MSG_MOD(extack, "TX rate value exceed link maximum speed"); 776ad34f02fSDmytro Linkin return -EINVAL; 777ad34f02fSDmytro Linkin } 778ad34f02fSDmytro Linkin 779ad34f02fSDmytro Linkin *rate = value; 780ad34f02fSDmytro Linkin return 0; 781ad34f02fSDmytro Linkin } 782ad34f02fSDmytro Linkin 783ad34f02fSDmytro Linkin /* Eswitch devlink rate API */ 784ad34f02fSDmytro Linkin 785ad34f02fSDmytro Linkin int mlx5_esw_devlink_rate_leaf_tx_share_set(struct devlink_rate *rate_leaf, void *priv, 786ad34f02fSDmytro Linkin u64 tx_share, struct netlink_ext_ack *extack) 787ad34f02fSDmytro Linkin { 788ad34f02fSDmytro Linkin struct mlx5_vport *vport = priv; 789ad34f02fSDmytro Linkin struct mlx5_eswitch *esw; 790ad34f02fSDmytro Linkin int err; 791ad34f02fSDmytro Linkin 792ad34f02fSDmytro Linkin esw = vport->dev->priv.eswitch; 793ad34f02fSDmytro Linkin if (!mlx5_esw_allowed(esw)) 794ad34f02fSDmytro Linkin return -EPERM; 795ad34f02fSDmytro Linkin 796ad34f02fSDmytro Linkin err = esw_qos_devlink_rate_to_mbps(vport->dev, "tx_share", &tx_share, extack); 797ad34f02fSDmytro Linkin if (err) 798ad34f02fSDmytro Linkin return err; 799ad34f02fSDmytro Linkin 800ad34f02fSDmytro Linkin mutex_lock(&esw->state_lock); 801*85c5f7c9SDmytro Linkin err = esw_qos_vport_enable(esw, vport, 0, 0, extack); 802d7df09f5SDmytro Linkin if (err) 803d7df09f5SDmytro Linkin goto unlock; 804d7df09f5SDmytro Linkin 805d7df09f5SDmytro Linkin err = esw_qos_set_vport_min_rate(esw, vport, tx_share, extack); 806d7df09f5SDmytro Linkin unlock: 807ad34f02fSDmytro Linkin mutex_unlock(&esw->state_lock); 808ad34f02fSDmytro Linkin return err; 809ad34f02fSDmytro Linkin } 810ad34f02fSDmytro Linkin 811ad34f02fSDmytro Linkin int mlx5_esw_devlink_rate_leaf_tx_max_set(struct devlink_rate *rate_leaf, void *priv, 812ad34f02fSDmytro Linkin u64 tx_max, struct netlink_ext_ack *extack) 813ad34f02fSDmytro Linkin { 814ad34f02fSDmytro Linkin struct mlx5_vport *vport = priv; 815ad34f02fSDmytro Linkin struct mlx5_eswitch *esw; 816ad34f02fSDmytro Linkin int err; 817ad34f02fSDmytro Linkin 818ad34f02fSDmytro Linkin esw = vport->dev->priv.eswitch; 819ad34f02fSDmytro Linkin if (!mlx5_esw_allowed(esw)) 820ad34f02fSDmytro Linkin return -EPERM; 821ad34f02fSDmytro Linkin 822ad34f02fSDmytro Linkin err = esw_qos_devlink_rate_to_mbps(vport->dev, "tx_max", &tx_max, extack); 823ad34f02fSDmytro Linkin if (err) 824ad34f02fSDmytro Linkin return err; 825ad34f02fSDmytro Linkin 826ad34f02fSDmytro Linkin mutex_lock(&esw->state_lock); 827*85c5f7c9SDmytro Linkin err = esw_qos_vport_enable(esw, vport, 0, 0, extack); 828d7df09f5SDmytro Linkin if (err) 829d7df09f5SDmytro Linkin goto unlock; 830d7df09f5SDmytro Linkin 831d7df09f5SDmytro Linkin err = esw_qos_set_vport_max_rate(esw, vport, tx_max, extack); 832d7df09f5SDmytro Linkin unlock: 833ad34f02fSDmytro Linkin mutex_unlock(&esw->state_lock); 834ad34f02fSDmytro Linkin return err; 835ad34f02fSDmytro Linkin } 8361ae258f8SDmytro Linkin 837f47e04ebSDmytro Linkin int mlx5_esw_devlink_rate_node_tx_share_set(struct devlink_rate *rate_node, void *priv, 838f47e04ebSDmytro Linkin u64 tx_share, struct netlink_ext_ack *extack) 839f47e04ebSDmytro Linkin { 840f47e04ebSDmytro Linkin struct mlx5_core_dev *dev = devlink_priv(rate_node->devlink); 841f47e04ebSDmytro Linkin struct mlx5_eswitch *esw = dev->priv.eswitch; 842f47e04ebSDmytro Linkin struct mlx5_esw_rate_group *group = priv; 843f47e04ebSDmytro Linkin int err; 844f47e04ebSDmytro Linkin 845f47e04ebSDmytro Linkin err = esw_qos_devlink_rate_to_mbps(dev, "tx_share", &tx_share, extack); 846f47e04ebSDmytro Linkin if (err) 847f47e04ebSDmytro Linkin return err; 848f47e04ebSDmytro Linkin 849f47e04ebSDmytro Linkin mutex_lock(&esw->state_lock); 850f47e04ebSDmytro Linkin err = esw_qos_set_group_min_rate(esw, group, tx_share, extack); 851f47e04ebSDmytro Linkin mutex_unlock(&esw->state_lock); 852f47e04ebSDmytro Linkin return err; 853f47e04ebSDmytro Linkin } 854f47e04ebSDmytro Linkin 855f47e04ebSDmytro Linkin int mlx5_esw_devlink_rate_node_tx_max_set(struct devlink_rate *rate_node, void *priv, 856f47e04ebSDmytro Linkin u64 tx_max, struct netlink_ext_ack *extack) 857f47e04ebSDmytro Linkin { 858f47e04ebSDmytro Linkin struct mlx5_core_dev *dev = devlink_priv(rate_node->devlink); 859f47e04ebSDmytro Linkin struct mlx5_eswitch *esw = dev->priv.eswitch; 860f47e04ebSDmytro Linkin struct mlx5_esw_rate_group *group = priv; 861f47e04ebSDmytro Linkin int err; 862f47e04ebSDmytro Linkin 863f47e04ebSDmytro Linkin err = esw_qos_devlink_rate_to_mbps(dev, "tx_max", &tx_max, extack); 864f47e04ebSDmytro Linkin if (err) 865f47e04ebSDmytro Linkin return err; 866f47e04ebSDmytro Linkin 867f47e04ebSDmytro Linkin mutex_lock(&esw->state_lock); 868f47e04ebSDmytro Linkin err = esw_qos_set_group_max_rate(esw, group, tx_max, extack); 869f47e04ebSDmytro Linkin mutex_unlock(&esw->state_lock); 870f47e04ebSDmytro Linkin return err; 871f47e04ebSDmytro Linkin } 872f47e04ebSDmytro Linkin 8731ae258f8SDmytro Linkin int mlx5_esw_devlink_rate_node_new(struct devlink_rate *rate_node, void **priv, 8741ae258f8SDmytro Linkin struct netlink_ext_ack *extack) 8751ae258f8SDmytro Linkin { 8761ae258f8SDmytro Linkin struct mlx5_esw_rate_group *group; 8771ae258f8SDmytro Linkin struct mlx5_eswitch *esw; 8781ae258f8SDmytro Linkin int err = 0; 8791ae258f8SDmytro Linkin 8801ae258f8SDmytro Linkin esw = mlx5_devlink_eswitch_get(rate_node->devlink); 8811ae258f8SDmytro Linkin if (IS_ERR(esw)) 8821ae258f8SDmytro Linkin return PTR_ERR(esw); 8831ae258f8SDmytro Linkin 8841ae258f8SDmytro Linkin mutex_lock(&esw->state_lock); 8851ae258f8SDmytro Linkin if (esw->mode != MLX5_ESWITCH_OFFLOADS) { 8861ae258f8SDmytro Linkin NL_SET_ERR_MSG_MOD(extack, 8871ae258f8SDmytro Linkin "Rate node creation supported only in switchdev mode"); 8881ae258f8SDmytro Linkin err = -EOPNOTSUPP; 8891ae258f8SDmytro Linkin goto unlock; 8901ae258f8SDmytro Linkin } 8911ae258f8SDmytro Linkin 8921ae258f8SDmytro Linkin group = esw_qos_create_rate_group(esw, extack); 8931ae258f8SDmytro Linkin if (IS_ERR(group)) { 8941ae258f8SDmytro Linkin err = PTR_ERR(group); 8951ae258f8SDmytro Linkin goto unlock; 8961ae258f8SDmytro Linkin } 8971ae258f8SDmytro Linkin 8981ae258f8SDmytro Linkin *priv = group; 8991ae258f8SDmytro Linkin unlock: 9001ae258f8SDmytro Linkin mutex_unlock(&esw->state_lock); 9011ae258f8SDmytro Linkin return err; 9021ae258f8SDmytro Linkin } 9031ae258f8SDmytro Linkin 9041ae258f8SDmytro Linkin int mlx5_esw_devlink_rate_node_del(struct devlink_rate *rate_node, void *priv, 9051ae258f8SDmytro Linkin struct netlink_ext_ack *extack) 9061ae258f8SDmytro Linkin { 9071ae258f8SDmytro Linkin struct mlx5_esw_rate_group *group = priv; 9081ae258f8SDmytro Linkin struct mlx5_eswitch *esw; 9091ae258f8SDmytro Linkin int err; 9101ae258f8SDmytro Linkin 9111ae258f8SDmytro Linkin esw = mlx5_devlink_eswitch_get(rate_node->devlink); 9121ae258f8SDmytro Linkin if (IS_ERR(esw)) 9131ae258f8SDmytro Linkin return PTR_ERR(esw); 9141ae258f8SDmytro Linkin 9151ae258f8SDmytro Linkin mutex_lock(&esw->state_lock); 9161ae258f8SDmytro Linkin err = esw_qos_destroy_rate_group(esw, group, extack); 9171ae258f8SDmytro Linkin mutex_unlock(&esw->state_lock); 9181ae258f8SDmytro Linkin return err; 9191ae258f8SDmytro Linkin } 9200fe132eaSDmytro Linkin 9210fe132eaSDmytro Linkin int mlx5_esw_qos_vport_update_group(struct mlx5_eswitch *esw, 9220fe132eaSDmytro Linkin struct mlx5_vport *vport, 9230fe132eaSDmytro Linkin struct mlx5_esw_rate_group *group, 9240fe132eaSDmytro Linkin struct netlink_ext_ack *extack) 9250fe132eaSDmytro Linkin { 9260fe132eaSDmytro Linkin int err; 9270fe132eaSDmytro Linkin 9280fe132eaSDmytro Linkin mutex_lock(&esw->state_lock); 929*85c5f7c9SDmytro Linkin err = esw_qos_vport_enable(esw, vport, 0, 0, extack); 930d7df09f5SDmytro Linkin if (!err) 9310fe132eaSDmytro Linkin err = esw_qos_vport_update_group(esw, vport, group, extack); 9320fe132eaSDmytro Linkin mutex_unlock(&esw->state_lock); 9330fe132eaSDmytro Linkin return err; 9340fe132eaSDmytro Linkin } 9350fe132eaSDmytro Linkin 9360fe132eaSDmytro Linkin int mlx5_esw_devlink_rate_parent_set(struct devlink_rate *devlink_rate, 9370fe132eaSDmytro Linkin struct devlink_rate *parent, 9380fe132eaSDmytro Linkin void *priv, void *parent_priv, 9390fe132eaSDmytro Linkin struct netlink_ext_ack *extack) 9400fe132eaSDmytro Linkin { 9410fe132eaSDmytro Linkin struct mlx5_esw_rate_group *group; 9420fe132eaSDmytro Linkin struct mlx5_vport *vport = priv; 9430fe132eaSDmytro Linkin 9440fe132eaSDmytro Linkin if (!parent) 9450fe132eaSDmytro Linkin return mlx5_esw_qos_vport_update_group(vport->dev->priv.eswitch, 9460fe132eaSDmytro Linkin vport, NULL, extack); 9470fe132eaSDmytro Linkin 9480fe132eaSDmytro Linkin group = parent_priv; 9490fe132eaSDmytro Linkin return mlx5_esw_qos_vport_update_group(vport->dev->priv.eswitch, vport, group, extack); 9500fe132eaSDmytro Linkin } 951