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" 7*3202ea65SDmytro Linkin #define CREATE_TRACE_POINTS 8*3202ea65SDmytro 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 59*3202ea65SDmytro Linkin trace_mlx5_esw_group_qos_config(dev, group, group->tsar_ix, bw_share, max_rate); 60*3202ea65SDmytro 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 96*3202ea65SDmytro Linkin trace_mlx5_esw_vport_qos_config(vport, bw_share, max_rate); 97*3202ea65SDmytro 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 207ad34f02fSDmytro Linkin int mlx5_esw_qos_set_vport_min_rate(struct mlx5_eswitch *esw, 208ad34f02fSDmytro Linkin struct mlx5_vport *evport, 209ad34f02fSDmytro Linkin u32 min_rate, 210ad34f02fSDmytro Linkin struct netlink_ext_ack *extack) 2112d116e3eSDmytro Linkin { 212ad34f02fSDmytro Linkin u32 fw_max_bw_share, previous_min_rate; 2132d116e3eSDmytro Linkin bool min_rate_supported; 2142d116e3eSDmytro Linkin int err; 2152d116e3eSDmytro Linkin 216ad34f02fSDmytro Linkin lockdep_assert_held(&esw->state_lock); 2172d116e3eSDmytro Linkin fw_max_bw_share = MLX5_CAP_QOS(esw->dev, max_tsar_bw_share); 2182d116e3eSDmytro Linkin min_rate_supported = MLX5_CAP_QOS(esw->dev, esw_bw_share) && 2192d116e3eSDmytro Linkin fw_max_bw_share >= MLX5_MIN_BW_SHARE; 220ad34f02fSDmytro Linkin if (min_rate && !min_rate_supported) 2212d116e3eSDmytro Linkin return -EOPNOTSUPP; 2222d116e3eSDmytro Linkin if (min_rate == evport->qos.min_rate) 223ad34f02fSDmytro Linkin return 0; 2242d116e3eSDmytro Linkin 2252d116e3eSDmytro Linkin previous_min_rate = evport->qos.min_rate; 2262d116e3eSDmytro Linkin evport->qos.min_rate = min_rate; 2270fe132eaSDmytro Linkin err = esw_qos_normalize_vports_min_rate(esw, evport->qos.group, extack); 228ad34f02fSDmytro Linkin if (err) 2292d116e3eSDmytro Linkin evport->qos.min_rate = previous_min_rate; 230ad34f02fSDmytro Linkin 2312d116e3eSDmytro Linkin return err; 2322d116e3eSDmytro Linkin } 2332d116e3eSDmytro Linkin 234ad34f02fSDmytro Linkin int mlx5_esw_qos_set_vport_max_rate(struct mlx5_eswitch *esw, 235ad34f02fSDmytro Linkin struct mlx5_vport *evport, 236ad34f02fSDmytro Linkin u32 max_rate, 237ad34f02fSDmytro Linkin struct netlink_ext_ack *extack) 238ad34f02fSDmytro Linkin { 2390fe132eaSDmytro Linkin u32 act_max_rate = max_rate; 240ad34f02fSDmytro Linkin bool max_rate_supported; 241ad34f02fSDmytro Linkin int err; 242ad34f02fSDmytro Linkin 243ad34f02fSDmytro Linkin lockdep_assert_held(&esw->state_lock); 244ad34f02fSDmytro Linkin max_rate_supported = MLX5_CAP_QOS(esw->dev, esw_rate_limit); 245ad34f02fSDmytro Linkin 246ad34f02fSDmytro Linkin if (max_rate && !max_rate_supported) 247ad34f02fSDmytro Linkin return -EOPNOTSUPP; 2482d116e3eSDmytro Linkin if (max_rate == evport->qos.max_rate) 2492d116e3eSDmytro Linkin return 0; 2502d116e3eSDmytro Linkin 2510fe132eaSDmytro Linkin /* If parent group has rate limit need to set to group 2520fe132eaSDmytro Linkin * value when new max rate is 0. 2530fe132eaSDmytro Linkin */ 2540fe132eaSDmytro Linkin if (evport->qos.group && !max_rate) 2550fe132eaSDmytro Linkin act_max_rate = evport->qos.group->max_rate; 2560fe132eaSDmytro Linkin 2570fe132eaSDmytro Linkin err = esw_qos_vport_config(esw, evport, act_max_rate, evport->qos.bw_share, extack); 258f47e04ebSDmytro Linkin 2592d116e3eSDmytro Linkin if (!err) 2602d116e3eSDmytro Linkin evport->qos.max_rate = max_rate; 2612d116e3eSDmytro Linkin 2622d116e3eSDmytro Linkin return err; 2632d116e3eSDmytro Linkin } 2642d116e3eSDmytro Linkin 265f47e04ebSDmytro Linkin static int esw_qos_set_group_min_rate(struct mlx5_eswitch *esw, struct mlx5_esw_rate_group *group, 266f47e04ebSDmytro Linkin u32 min_rate, struct netlink_ext_ack *extack) 267f47e04ebSDmytro Linkin { 268f47e04ebSDmytro Linkin u32 fw_max_bw_share = MLX5_CAP_QOS(esw->dev, max_tsar_bw_share); 269f47e04ebSDmytro Linkin struct mlx5_core_dev *dev = esw->dev; 270f47e04ebSDmytro Linkin u32 previous_min_rate, divider; 271f47e04ebSDmytro Linkin int err; 272f47e04ebSDmytro Linkin 273f47e04ebSDmytro Linkin if (!(MLX5_CAP_QOS(dev, esw_bw_share) && fw_max_bw_share >= MLX5_MIN_BW_SHARE)) 274f47e04ebSDmytro Linkin return -EOPNOTSUPP; 275f47e04ebSDmytro Linkin 276f47e04ebSDmytro Linkin if (min_rate == group->min_rate) 277f47e04ebSDmytro Linkin return 0; 278f47e04ebSDmytro Linkin 279f47e04ebSDmytro Linkin previous_min_rate = group->min_rate; 280f47e04ebSDmytro Linkin group->min_rate = min_rate; 281f47e04ebSDmytro Linkin divider = esw_qos_calculate_min_rate_divider(esw, group, true); 282f47e04ebSDmytro Linkin err = esw_qos_normalize_groups_min_rate(esw, divider, extack); 283f47e04ebSDmytro Linkin if (err) { 284f47e04ebSDmytro Linkin group->min_rate = previous_min_rate; 285f47e04ebSDmytro Linkin NL_SET_ERR_MSG_MOD(extack, "E-Switch group min rate setting failed"); 286f47e04ebSDmytro Linkin 287f47e04ebSDmytro Linkin /* Attempt restoring previous configuration */ 288f47e04ebSDmytro Linkin divider = esw_qos_calculate_min_rate_divider(esw, group, true); 289f47e04ebSDmytro Linkin if (esw_qos_normalize_groups_min_rate(esw, divider, extack)) 290f47e04ebSDmytro Linkin NL_SET_ERR_MSG_MOD(extack, "E-Switch BW share restore failed"); 291f47e04ebSDmytro Linkin } 292f47e04ebSDmytro Linkin 293f47e04ebSDmytro Linkin return err; 294f47e04ebSDmytro Linkin } 295f47e04ebSDmytro Linkin 296f47e04ebSDmytro Linkin static int esw_qos_set_group_max_rate(struct mlx5_eswitch *esw, 297f47e04ebSDmytro Linkin struct mlx5_esw_rate_group *group, 298f47e04ebSDmytro Linkin u32 max_rate, struct netlink_ext_ack *extack) 299f47e04ebSDmytro Linkin { 3000fe132eaSDmytro Linkin struct mlx5_vport *vport; 3010fe132eaSDmytro Linkin unsigned long i; 302f47e04ebSDmytro Linkin int err; 303f47e04ebSDmytro Linkin 304f47e04ebSDmytro Linkin if (group->max_rate == max_rate) 305f47e04ebSDmytro Linkin return 0; 306f47e04ebSDmytro Linkin 307f47e04ebSDmytro Linkin err = esw_qos_group_config(esw, group, max_rate, group->bw_share, extack); 308f47e04ebSDmytro Linkin if (err) 309f47e04ebSDmytro Linkin return err; 310f47e04ebSDmytro Linkin 311f47e04ebSDmytro Linkin group->max_rate = max_rate; 312f47e04ebSDmytro Linkin 3130fe132eaSDmytro Linkin /* Any unlimited vports in the group should be set 3140fe132eaSDmytro Linkin * with the value of the group. 3150fe132eaSDmytro Linkin */ 3160fe132eaSDmytro Linkin mlx5_esw_for_each_vport(esw, i, vport) { 3170fe132eaSDmytro Linkin if (!vport->enabled || !vport->qos.enabled || 3180fe132eaSDmytro Linkin vport->qos.group != group || vport->qos.max_rate) 3190fe132eaSDmytro Linkin continue; 3200fe132eaSDmytro Linkin 3210fe132eaSDmytro Linkin err = esw_qos_vport_config(esw, vport, max_rate, vport->qos.bw_share, extack); 3220fe132eaSDmytro Linkin if (err) 3230fe132eaSDmytro Linkin NL_SET_ERR_MSG_MOD(extack, 3240fe132eaSDmytro Linkin "E-Switch vport implicit rate limit setting failed"); 3250fe132eaSDmytro Linkin } 3260fe132eaSDmytro Linkin 327f47e04ebSDmytro Linkin return err; 328f47e04ebSDmytro Linkin } 329f47e04ebSDmytro Linkin 3300fe132eaSDmytro Linkin static int esw_qos_vport_create_sched_element(struct mlx5_eswitch *esw, 3310fe132eaSDmytro Linkin struct mlx5_vport *vport, 3320fe132eaSDmytro Linkin u32 max_rate, u32 bw_share) 3330fe132eaSDmytro Linkin { 3340fe132eaSDmytro Linkin u32 sched_ctx[MLX5_ST_SZ_DW(scheduling_context)] = {}; 3350fe132eaSDmytro Linkin struct mlx5_esw_rate_group *group = vport->qos.group; 3360fe132eaSDmytro Linkin struct mlx5_core_dev *dev = esw->dev; 3370fe132eaSDmytro Linkin u32 parent_tsar_ix; 3380fe132eaSDmytro Linkin void *vport_elem; 3390fe132eaSDmytro Linkin int err; 3400fe132eaSDmytro Linkin 3410fe132eaSDmytro Linkin parent_tsar_ix = group ? group->tsar_ix : esw->qos.root_tsar_ix; 3420fe132eaSDmytro Linkin MLX5_SET(scheduling_context, sched_ctx, element_type, 3430fe132eaSDmytro Linkin SCHEDULING_CONTEXT_ELEMENT_TYPE_VPORT); 3440fe132eaSDmytro Linkin vport_elem = MLX5_ADDR_OF(scheduling_context, sched_ctx, element_attributes); 3450fe132eaSDmytro Linkin MLX5_SET(vport_element, vport_elem, vport_number, vport->vport); 3460fe132eaSDmytro Linkin MLX5_SET(scheduling_context, sched_ctx, parent_element_id, parent_tsar_ix); 3470fe132eaSDmytro Linkin MLX5_SET(scheduling_context, sched_ctx, max_average_bw, max_rate); 3480fe132eaSDmytro Linkin MLX5_SET(scheduling_context, sched_ctx, bw_share, bw_share); 3490fe132eaSDmytro Linkin 3500fe132eaSDmytro Linkin err = mlx5_create_scheduling_element_cmd(dev, 3510fe132eaSDmytro Linkin SCHEDULING_HIERARCHY_E_SWITCH, 3520fe132eaSDmytro Linkin sched_ctx, 3530fe132eaSDmytro Linkin &vport->qos.esw_tsar_ix); 3540fe132eaSDmytro Linkin if (err) { 3550fe132eaSDmytro Linkin esw_warn(esw->dev, "E-Switch create TSAR vport element failed (vport=%d,err=%d)\n", 3560fe132eaSDmytro Linkin vport->vport, err); 3570fe132eaSDmytro Linkin return err; 3580fe132eaSDmytro Linkin } 3590fe132eaSDmytro Linkin 3600fe132eaSDmytro Linkin return 0; 3610fe132eaSDmytro Linkin } 3620fe132eaSDmytro Linkin 3630fe132eaSDmytro Linkin static int esw_qos_update_group_scheduling_element(struct mlx5_eswitch *esw, 3640fe132eaSDmytro Linkin struct mlx5_vport *vport, 3650fe132eaSDmytro Linkin struct mlx5_esw_rate_group *curr_group, 3660fe132eaSDmytro Linkin struct mlx5_esw_rate_group *new_group, 3670fe132eaSDmytro Linkin struct netlink_ext_ack *extack) 3680fe132eaSDmytro Linkin { 3690fe132eaSDmytro Linkin u32 max_rate; 3700fe132eaSDmytro Linkin int err; 3710fe132eaSDmytro Linkin 3720fe132eaSDmytro Linkin err = mlx5_destroy_scheduling_element_cmd(esw->dev, 3730fe132eaSDmytro Linkin SCHEDULING_HIERARCHY_E_SWITCH, 3740fe132eaSDmytro Linkin vport->qos.esw_tsar_ix); 3750fe132eaSDmytro Linkin if (err) { 3760fe132eaSDmytro Linkin NL_SET_ERR_MSG_MOD(extack, "E-Switch destroy TSAR vport element failed"); 3770fe132eaSDmytro Linkin return err; 3780fe132eaSDmytro Linkin } 3790fe132eaSDmytro Linkin 3800fe132eaSDmytro Linkin vport->qos.group = new_group; 3810fe132eaSDmytro Linkin max_rate = vport->qos.max_rate ? vport->qos.max_rate : new_group->max_rate; 3820fe132eaSDmytro Linkin 3830fe132eaSDmytro Linkin /* If vport is unlimited, we set the group's value. 3840fe132eaSDmytro Linkin * Therefore, if the group is limited it will apply to 3850fe132eaSDmytro Linkin * the vport as well and if not, vport will remain unlimited. 3860fe132eaSDmytro Linkin */ 3870fe132eaSDmytro Linkin err = esw_qos_vport_create_sched_element(esw, vport, max_rate, vport->qos.bw_share); 3880fe132eaSDmytro Linkin if (err) { 3890fe132eaSDmytro Linkin NL_SET_ERR_MSG_MOD(extack, "E-Switch vport group set failed."); 3900fe132eaSDmytro Linkin goto err_sched; 3910fe132eaSDmytro Linkin } 3920fe132eaSDmytro Linkin 3930fe132eaSDmytro Linkin return 0; 3940fe132eaSDmytro Linkin 3950fe132eaSDmytro Linkin err_sched: 3960fe132eaSDmytro Linkin vport->qos.group = curr_group; 3970fe132eaSDmytro Linkin max_rate = vport->qos.max_rate ? vport->qos.max_rate : curr_group->max_rate; 3980fe132eaSDmytro Linkin if (esw_qos_vport_create_sched_element(esw, vport, max_rate, vport->qos.bw_share)) 3990fe132eaSDmytro Linkin esw_warn(esw->dev, "E-Switch vport group restore failed (vport=%d)\n", 4000fe132eaSDmytro Linkin vport->vport); 4010fe132eaSDmytro Linkin 4020fe132eaSDmytro Linkin return err; 4030fe132eaSDmytro Linkin } 4040fe132eaSDmytro Linkin 4050fe132eaSDmytro Linkin static int esw_qos_vport_update_group(struct mlx5_eswitch *esw, 4060fe132eaSDmytro Linkin struct mlx5_vport *vport, 4070fe132eaSDmytro Linkin struct mlx5_esw_rate_group *group, 4080fe132eaSDmytro Linkin struct netlink_ext_ack *extack) 4090fe132eaSDmytro Linkin { 4100fe132eaSDmytro Linkin struct mlx5_esw_rate_group *new_group, *curr_group; 4110fe132eaSDmytro Linkin int err; 4120fe132eaSDmytro Linkin 4130fe132eaSDmytro Linkin if (!vport->enabled) 4140fe132eaSDmytro Linkin return -EINVAL; 4150fe132eaSDmytro Linkin 4160fe132eaSDmytro Linkin curr_group = vport->qos.group; 4170fe132eaSDmytro Linkin new_group = group ?: esw->qos.group0; 4180fe132eaSDmytro Linkin if (curr_group == new_group) 4190fe132eaSDmytro Linkin return 0; 4200fe132eaSDmytro Linkin 4210fe132eaSDmytro Linkin err = esw_qos_update_group_scheduling_element(esw, vport, curr_group, new_group, extack); 4220fe132eaSDmytro Linkin if (err) 4230fe132eaSDmytro Linkin return err; 4240fe132eaSDmytro Linkin 4250fe132eaSDmytro Linkin /* Recalculate bw share weights of old and new groups */ 4260fe132eaSDmytro Linkin if (vport->qos.bw_share) { 4270fe132eaSDmytro Linkin esw_qos_normalize_vports_min_rate(esw, curr_group, extack); 4280fe132eaSDmytro Linkin esw_qos_normalize_vports_min_rate(esw, new_group, extack); 4290fe132eaSDmytro Linkin } 4300fe132eaSDmytro Linkin 4310fe132eaSDmytro Linkin return 0; 4320fe132eaSDmytro Linkin } 4330fe132eaSDmytro Linkin 4341ae258f8SDmytro Linkin static struct mlx5_esw_rate_group * 4351ae258f8SDmytro Linkin esw_qos_create_rate_group(struct mlx5_eswitch *esw, struct netlink_ext_ack *extack) 4361ae258f8SDmytro Linkin { 4371ae258f8SDmytro Linkin u32 tsar_ctx[MLX5_ST_SZ_DW(scheduling_context)] = {}; 4381ae258f8SDmytro Linkin struct mlx5_esw_rate_group *group; 439f47e04ebSDmytro Linkin u32 divider; 4401ae258f8SDmytro Linkin int err; 4411ae258f8SDmytro Linkin 4421ae258f8SDmytro Linkin if (!MLX5_CAP_QOS(esw->dev, log_esw_max_sched_depth)) 4431ae258f8SDmytro Linkin return ERR_PTR(-EOPNOTSUPP); 4441ae258f8SDmytro Linkin 4451ae258f8SDmytro Linkin group = kzalloc(sizeof(*group), GFP_KERNEL); 4461ae258f8SDmytro Linkin if (!group) 4471ae258f8SDmytro Linkin return ERR_PTR(-ENOMEM); 4481ae258f8SDmytro Linkin 4491ae258f8SDmytro Linkin MLX5_SET(scheduling_context, tsar_ctx, parent_element_id, 4501ae258f8SDmytro Linkin esw->qos.root_tsar_ix); 4511ae258f8SDmytro Linkin err = mlx5_create_scheduling_element_cmd(esw->dev, 4521ae258f8SDmytro Linkin SCHEDULING_HIERARCHY_E_SWITCH, 4531ae258f8SDmytro Linkin tsar_ctx, 4541ae258f8SDmytro Linkin &group->tsar_ix); 4551ae258f8SDmytro Linkin if (err) { 4561ae258f8SDmytro Linkin NL_SET_ERR_MSG_MOD(extack, "E-Switch create TSAR for group failed"); 4571ae258f8SDmytro Linkin goto err_sched_elem; 4581ae258f8SDmytro Linkin } 4591ae258f8SDmytro Linkin 460f47e04ebSDmytro Linkin list_add_tail(&group->list, &esw->qos.groups); 461f47e04ebSDmytro Linkin 462f47e04ebSDmytro Linkin divider = esw_qos_calculate_min_rate_divider(esw, group, true); 463f47e04ebSDmytro Linkin if (divider) { 464f47e04ebSDmytro Linkin err = esw_qos_normalize_groups_min_rate(esw, divider, extack); 465f47e04ebSDmytro Linkin if (err) { 466f47e04ebSDmytro Linkin NL_SET_ERR_MSG_MOD(extack, "E-Switch groups normalization failed"); 467f47e04ebSDmytro Linkin goto err_min_rate; 468f47e04ebSDmytro Linkin } 469f47e04ebSDmytro Linkin } 470*3202ea65SDmytro Linkin trace_mlx5_esw_group_qos_create(esw->dev, group, group->tsar_ix); 471f47e04ebSDmytro Linkin 4721ae258f8SDmytro Linkin return group; 4731ae258f8SDmytro Linkin 474f47e04ebSDmytro Linkin err_min_rate: 475f47e04ebSDmytro Linkin list_del(&group->list); 476f47e04ebSDmytro Linkin err = mlx5_destroy_scheduling_element_cmd(esw->dev, 477f47e04ebSDmytro Linkin SCHEDULING_HIERARCHY_E_SWITCH, 478f47e04ebSDmytro Linkin group->tsar_ix); 479f47e04ebSDmytro Linkin if (err) 480f47e04ebSDmytro Linkin NL_SET_ERR_MSG_MOD(extack, "E-Switch destroy TSAR for group failed"); 4811ae258f8SDmytro Linkin err_sched_elem: 4821ae258f8SDmytro Linkin kfree(group); 4831ae258f8SDmytro Linkin return ERR_PTR(err); 4841ae258f8SDmytro Linkin } 4851ae258f8SDmytro Linkin 4861ae258f8SDmytro Linkin static int esw_qos_destroy_rate_group(struct mlx5_eswitch *esw, 4871ae258f8SDmytro Linkin struct mlx5_esw_rate_group *group, 4881ae258f8SDmytro Linkin struct netlink_ext_ack *extack) 4891ae258f8SDmytro Linkin { 490f47e04ebSDmytro Linkin u32 divider; 4911ae258f8SDmytro Linkin int err; 4921ae258f8SDmytro Linkin 493f47e04ebSDmytro Linkin list_del(&group->list); 494f47e04ebSDmytro Linkin 495f47e04ebSDmytro Linkin divider = esw_qos_calculate_min_rate_divider(esw, NULL, true); 496f47e04ebSDmytro Linkin err = esw_qos_normalize_groups_min_rate(esw, divider, extack); 497f47e04ebSDmytro Linkin if (err) 498f47e04ebSDmytro Linkin NL_SET_ERR_MSG_MOD(extack, "E-Switch groups' normalization failed"); 499f47e04ebSDmytro Linkin 5001ae258f8SDmytro Linkin err = mlx5_destroy_scheduling_element_cmd(esw->dev, 5011ae258f8SDmytro Linkin SCHEDULING_HIERARCHY_E_SWITCH, 5021ae258f8SDmytro Linkin group->tsar_ix); 5031ae258f8SDmytro Linkin if (err) 5041ae258f8SDmytro Linkin NL_SET_ERR_MSG_MOD(extack, "E-Switch destroy TSAR_ID failed"); 5051ae258f8SDmytro Linkin 506*3202ea65SDmytro Linkin trace_mlx5_esw_group_qos_destroy(esw->dev, group, group->tsar_ix); 5071ae258f8SDmytro Linkin kfree(group); 5081ae258f8SDmytro Linkin return err; 5091ae258f8SDmytro Linkin } 5101ae258f8SDmytro Linkin 5112d116e3eSDmytro Linkin static bool esw_qos_element_type_supported(struct mlx5_core_dev *dev, int type) 5122d116e3eSDmytro Linkin { 5132d116e3eSDmytro Linkin switch (type) { 5142d116e3eSDmytro Linkin case SCHEDULING_CONTEXT_ELEMENT_TYPE_TSAR: 5152d116e3eSDmytro Linkin return MLX5_CAP_QOS(dev, esw_element_type) & 5162d116e3eSDmytro Linkin ELEMENT_TYPE_CAP_MASK_TASR; 5172d116e3eSDmytro Linkin case SCHEDULING_CONTEXT_ELEMENT_TYPE_VPORT: 5182d116e3eSDmytro Linkin return MLX5_CAP_QOS(dev, esw_element_type) & 5192d116e3eSDmytro Linkin ELEMENT_TYPE_CAP_MASK_VPORT; 5202d116e3eSDmytro Linkin case SCHEDULING_CONTEXT_ELEMENT_TYPE_VPORT_TC: 5212d116e3eSDmytro Linkin return MLX5_CAP_QOS(dev, esw_element_type) & 5222d116e3eSDmytro Linkin ELEMENT_TYPE_CAP_MASK_VPORT_TC; 5232d116e3eSDmytro Linkin case SCHEDULING_CONTEXT_ELEMENT_TYPE_PARA_VPORT_TC: 5242d116e3eSDmytro Linkin return MLX5_CAP_QOS(dev, esw_element_type) & 5252d116e3eSDmytro Linkin ELEMENT_TYPE_CAP_MASK_PARA_VPORT_TC; 5262d116e3eSDmytro Linkin } 5272d116e3eSDmytro Linkin return false; 5282d116e3eSDmytro Linkin } 5292d116e3eSDmytro Linkin 5302d116e3eSDmytro Linkin void mlx5_esw_qos_create(struct mlx5_eswitch *esw) 5312d116e3eSDmytro Linkin { 5322d116e3eSDmytro Linkin u32 tsar_ctx[MLX5_ST_SZ_DW(scheduling_context)] = {}; 5332d116e3eSDmytro Linkin struct mlx5_core_dev *dev = esw->dev; 5342d116e3eSDmytro Linkin __be32 *attr; 5352d116e3eSDmytro Linkin int err; 5362d116e3eSDmytro Linkin 5372d116e3eSDmytro Linkin if (!MLX5_CAP_GEN(dev, qos) || !MLX5_CAP_QOS(dev, esw_scheduling)) 5382d116e3eSDmytro Linkin return; 5392d116e3eSDmytro Linkin 5402d116e3eSDmytro Linkin if (!esw_qos_element_type_supported(dev, SCHEDULING_CONTEXT_ELEMENT_TYPE_TSAR)) 5412d116e3eSDmytro Linkin return; 5422d116e3eSDmytro Linkin 5431ae258f8SDmytro Linkin mutex_lock(&esw->state_lock); 5442d116e3eSDmytro Linkin if (esw->qos.enabled) 5451ae258f8SDmytro Linkin goto unlock; 5462d116e3eSDmytro Linkin 5472d116e3eSDmytro Linkin MLX5_SET(scheduling_context, tsar_ctx, element_type, 5482d116e3eSDmytro Linkin SCHEDULING_CONTEXT_ELEMENT_TYPE_TSAR); 5492d116e3eSDmytro Linkin 5502d116e3eSDmytro Linkin attr = MLX5_ADDR_OF(scheduling_context, tsar_ctx, element_attributes); 5512d116e3eSDmytro Linkin *attr = cpu_to_be32(TSAR_ELEMENT_TSAR_TYPE_DWRR << 16); 5522d116e3eSDmytro Linkin 5532d116e3eSDmytro Linkin err = mlx5_create_scheduling_element_cmd(dev, 5542d116e3eSDmytro Linkin SCHEDULING_HIERARCHY_E_SWITCH, 5552d116e3eSDmytro Linkin tsar_ctx, 5562d116e3eSDmytro Linkin &esw->qos.root_tsar_ix); 5572d116e3eSDmytro Linkin if (err) { 5581ae258f8SDmytro Linkin esw_warn(dev, "E-Switch create root TSAR failed (%d)\n", err); 5591ae258f8SDmytro Linkin goto unlock; 5602d116e3eSDmytro Linkin } 5612d116e3eSDmytro Linkin 562f47e04ebSDmytro Linkin INIT_LIST_HEAD(&esw->qos.groups); 5631ae258f8SDmytro Linkin if (MLX5_CAP_QOS(dev, log_esw_max_sched_depth)) { 5641ae258f8SDmytro Linkin esw->qos.group0 = esw_qos_create_rate_group(esw, NULL); 5651ae258f8SDmytro Linkin if (IS_ERR(esw->qos.group0)) { 5661ae258f8SDmytro Linkin esw_warn(dev, "E-Switch create rate group 0 failed (%ld)\n", 5671ae258f8SDmytro Linkin PTR_ERR(esw->qos.group0)); 5681ae258f8SDmytro Linkin goto err_group0; 5691ae258f8SDmytro Linkin } 5701ae258f8SDmytro Linkin } 5712d116e3eSDmytro Linkin esw->qos.enabled = true; 5721ae258f8SDmytro Linkin unlock: 5731ae258f8SDmytro Linkin mutex_unlock(&esw->state_lock); 5741ae258f8SDmytro Linkin return; 5751ae258f8SDmytro Linkin 5761ae258f8SDmytro Linkin err_group0: 5771ae258f8SDmytro Linkin err = mlx5_destroy_scheduling_element_cmd(esw->dev, 5781ae258f8SDmytro Linkin SCHEDULING_HIERARCHY_E_SWITCH, 5791ae258f8SDmytro Linkin esw->qos.root_tsar_ix); 5801ae258f8SDmytro Linkin if (err) 5811ae258f8SDmytro Linkin esw_warn(esw->dev, "E-Switch destroy root TSAR failed (%d)\n", err); 5821ae258f8SDmytro Linkin mutex_unlock(&esw->state_lock); 5832d116e3eSDmytro Linkin } 5842d116e3eSDmytro Linkin 5852d116e3eSDmytro Linkin void mlx5_esw_qos_destroy(struct mlx5_eswitch *esw) 5862d116e3eSDmytro Linkin { 5871ae258f8SDmytro Linkin struct devlink *devlink = priv_to_devlink(esw->dev); 5882d116e3eSDmytro Linkin int err; 5892d116e3eSDmytro Linkin 5901ae258f8SDmytro Linkin devlink_rate_nodes_destroy(devlink); 5911ae258f8SDmytro Linkin mutex_lock(&esw->state_lock); 5922d116e3eSDmytro Linkin if (!esw->qos.enabled) 5931ae258f8SDmytro Linkin goto unlock; 5941ae258f8SDmytro Linkin 5951ae258f8SDmytro Linkin if (esw->qos.group0) 5961ae258f8SDmytro Linkin esw_qos_destroy_rate_group(esw, esw->qos.group0, NULL); 5972d116e3eSDmytro Linkin 5982d116e3eSDmytro Linkin err = mlx5_destroy_scheduling_element_cmd(esw->dev, 5992d116e3eSDmytro Linkin SCHEDULING_HIERARCHY_E_SWITCH, 6002d116e3eSDmytro Linkin esw->qos.root_tsar_ix); 6012d116e3eSDmytro Linkin if (err) 6021ae258f8SDmytro Linkin esw_warn(esw->dev, "E-Switch destroy root TSAR failed (%d)\n", err); 6032d116e3eSDmytro Linkin 6042d116e3eSDmytro Linkin esw->qos.enabled = false; 6051ae258f8SDmytro Linkin unlock: 6061ae258f8SDmytro Linkin mutex_unlock(&esw->state_lock); 6072d116e3eSDmytro Linkin } 6082d116e3eSDmytro Linkin 6092d116e3eSDmytro Linkin int mlx5_esw_qos_vport_enable(struct mlx5_eswitch *esw, struct mlx5_vport *vport, 6102d116e3eSDmytro Linkin u32 max_rate, u32 bw_share) 6112d116e3eSDmytro Linkin { 6122d116e3eSDmytro Linkin int err; 6132d116e3eSDmytro Linkin 6142d116e3eSDmytro Linkin lockdep_assert_held(&esw->state_lock); 6152d116e3eSDmytro Linkin if (!esw->qos.enabled) 6162d116e3eSDmytro Linkin return 0; 6172d116e3eSDmytro Linkin 6182d116e3eSDmytro Linkin if (vport->qos.enabled) 6192d116e3eSDmytro Linkin return -EEXIST; 6202d116e3eSDmytro Linkin 6210fe132eaSDmytro Linkin vport->qos.group = esw->qos.group0; 6222d116e3eSDmytro Linkin 6230fe132eaSDmytro Linkin err = esw_qos_vport_create_sched_element(esw, vport, max_rate, bw_share); 624*3202ea65SDmytro Linkin if (!err) { 6252d116e3eSDmytro Linkin vport->qos.enabled = true; 626*3202ea65SDmytro Linkin trace_mlx5_esw_vport_qos_create(vport, bw_share, max_rate); 627*3202ea65SDmytro Linkin } 6282d116e3eSDmytro Linkin 6292d116e3eSDmytro Linkin return err; 6302d116e3eSDmytro Linkin } 6312d116e3eSDmytro Linkin 6322d116e3eSDmytro Linkin void mlx5_esw_qos_vport_disable(struct mlx5_eswitch *esw, struct mlx5_vport *vport) 6332d116e3eSDmytro Linkin { 6342d116e3eSDmytro Linkin int err; 6352d116e3eSDmytro Linkin 6362d116e3eSDmytro Linkin lockdep_assert_held(&esw->state_lock); 6372d116e3eSDmytro Linkin if (!esw->qos.enabled || !vport->qos.enabled) 6382d116e3eSDmytro Linkin return; 6390fe132eaSDmytro Linkin WARN(vport->qos.group && vport->qos.group != esw->qos.group0, 6400fe132eaSDmytro Linkin "Disabling QoS on port before detaching it from group"); 6412d116e3eSDmytro Linkin 6422d116e3eSDmytro Linkin err = mlx5_destroy_scheduling_element_cmd(esw->dev, 6432d116e3eSDmytro Linkin SCHEDULING_HIERARCHY_E_SWITCH, 6442d116e3eSDmytro Linkin vport->qos.esw_tsar_ix); 6452d116e3eSDmytro Linkin if (err) 6462d116e3eSDmytro Linkin esw_warn(esw->dev, "E-Switch destroy TSAR vport element failed (vport=%d,err=%d)\n", 6472d116e3eSDmytro Linkin vport->vport, err); 6482d116e3eSDmytro Linkin 6492d116e3eSDmytro Linkin vport->qos.enabled = false; 650*3202ea65SDmytro Linkin trace_mlx5_esw_vport_qos_destroy(vport); 6512d116e3eSDmytro Linkin } 6522d116e3eSDmytro Linkin 6532d116e3eSDmytro Linkin int mlx5_esw_qos_modify_vport_rate(struct mlx5_eswitch *esw, u16 vport_num, u32 rate_mbps) 6542d116e3eSDmytro Linkin { 6552d116e3eSDmytro Linkin u32 ctx[MLX5_ST_SZ_DW(scheduling_context)] = {}; 6562d116e3eSDmytro Linkin struct mlx5_vport *vport; 6572d116e3eSDmytro Linkin u32 bitmask; 6582d116e3eSDmytro Linkin 6592d116e3eSDmytro Linkin vport = mlx5_eswitch_get_vport(esw, vport_num); 6602d116e3eSDmytro Linkin if (IS_ERR(vport)) 6612d116e3eSDmytro Linkin return PTR_ERR(vport); 6622d116e3eSDmytro Linkin 6632d116e3eSDmytro Linkin if (!vport->qos.enabled) 6642d116e3eSDmytro Linkin return -EOPNOTSUPP; 6652d116e3eSDmytro Linkin 6662d116e3eSDmytro Linkin MLX5_SET(scheduling_context, ctx, max_average_bw, rate_mbps); 6672d116e3eSDmytro Linkin bitmask = MODIFY_SCHEDULING_ELEMENT_IN_MODIFY_BITMASK_MAX_AVERAGE_BW; 6682d116e3eSDmytro Linkin 6692d116e3eSDmytro Linkin return mlx5_modify_scheduling_element_cmd(esw->dev, 6702d116e3eSDmytro Linkin SCHEDULING_HIERARCHY_E_SWITCH, 6712d116e3eSDmytro Linkin ctx, 6722d116e3eSDmytro Linkin vport->qos.esw_tsar_ix, 6732d116e3eSDmytro Linkin bitmask); 6742d116e3eSDmytro Linkin } 675ad34f02fSDmytro Linkin 676ad34f02fSDmytro Linkin #define MLX5_LINKSPEED_UNIT 125000 /* 1Mbps in Bps */ 677ad34f02fSDmytro Linkin 678ad34f02fSDmytro Linkin /* Converts bytes per second value passed in a pointer into megabits per 679ad34f02fSDmytro Linkin * second, rewriting last. If converted rate exceed link speed or is not a 680ad34f02fSDmytro Linkin * fraction of Mbps - returns error. 681ad34f02fSDmytro Linkin */ 682ad34f02fSDmytro Linkin static int esw_qos_devlink_rate_to_mbps(struct mlx5_core_dev *mdev, const char *name, 683ad34f02fSDmytro Linkin u64 *rate, struct netlink_ext_ack *extack) 684ad34f02fSDmytro Linkin { 685ad34f02fSDmytro Linkin u32 link_speed_max, reminder; 686ad34f02fSDmytro Linkin u64 value; 687ad34f02fSDmytro Linkin int err; 688ad34f02fSDmytro Linkin 689ad34f02fSDmytro Linkin err = mlx5e_port_max_linkspeed(mdev, &link_speed_max); 690ad34f02fSDmytro Linkin if (err) { 691ad34f02fSDmytro Linkin NL_SET_ERR_MSG_MOD(extack, "Failed to get link maximum speed"); 692ad34f02fSDmytro Linkin return err; 693ad34f02fSDmytro Linkin } 694ad34f02fSDmytro Linkin 695ad34f02fSDmytro Linkin value = div_u64_rem(*rate, MLX5_LINKSPEED_UNIT, &reminder); 696ad34f02fSDmytro Linkin if (reminder) { 697ad34f02fSDmytro Linkin pr_err("%s rate value %lluBps not in link speed units of 1Mbps.\n", 698ad34f02fSDmytro Linkin name, *rate); 699ad34f02fSDmytro Linkin NL_SET_ERR_MSG_MOD(extack, "TX rate value not in link speed units of 1Mbps"); 700ad34f02fSDmytro Linkin return -EINVAL; 701ad34f02fSDmytro Linkin } 702ad34f02fSDmytro Linkin 703ad34f02fSDmytro Linkin if (value > link_speed_max) { 704ad34f02fSDmytro Linkin pr_err("%s rate value %lluMbps exceed link maximum speed %u.\n", 705ad34f02fSDmytro Linkin name, value, link_speed_max); 706ad34f02fSDmytro Linkin NL_SET_ERR_MSG_MOD(extack, "TX rate value exceed link maximum speed"); 707ad34f02fSDmytro Linkin return -EINVAL; 708ad34f02fSDmytro Linkin } 709ad34f02fSDmytro Linkin 710ad34f02fSDmytro Linkin *rate = value; 711ad34f02fSDmytro Linkin return 0; 712ad34f02fSDmytro Linkin } 713ad34f02fSDmytro Linkin 714ad34f02fSDmytro Linkin /* Eswitch devlink rate API */ 715ad34f02fSDmytro Linkin 716ad34f02fSDmytro Linkin int mlx5_esw_devlink_rate_leaf_tx_share_set(struct devlink_rate *rate_leaf, void *priv, 717ad34f02fSDmytro Linkin u64 tx_share, struct netlink_ext_ack *extack) 718ad34f02fSDmytro Linkin { 719ad34f02fSDmytro Linkin struct mlx5_vport *vport = priv; 720ad34f02fSDmytro Linkin struct mlx5_eswitch *esw; 721ad34f02fSDmytro Linkin int err; 722ad34f02fSDmytro Linkin 723ad34f02fSDmytro Linkin esw = vport->dev->priv.eswitch; 724ad34f02fSDmytro Linkin if (!mlx5_esw_allowed(esw)) 725ad34f02fSDmytro Linkin return -EPERM; 726ad34f02fSDmytro Linkin 727ad34f02fSDmytro Linkin err = esw_qos_devlink_rate_to_mbps(vport->dev, "tx_share", &tx_share, extack); 728ad34f02fSDmytro Linkin if (err) 729ad34f02fSDmytro Linkin return err; 730ad34f02fSDmytro Linkin 731ad34f02fSDmytro Linkin mutex_lock(&esw->state_lock); 732ad34f02fSDmytro Linkin err = mlx5_esw_qos_set_vport_min_rate(esw, vport, tx_share, extack); 733ad34f02fSDmytro Linkin mutex_unlock(&esw->state_lock); 734ad34f02fSDmytro Linkin return err; 735ad34f02fSDmytro Linkin } 736ad34f02fSDmytro Linkin 737ad34f02fSDmytro Linkin int mlx5_esw_devlink_rate_leaf_tx_max_set(struct devlink_rate *rate_leaf, void *priv, 738ad34f02fSDmytro Linkin u64 tx_max, struct netlink_ext_ack *extack) 739ad34f02fSDmytro Linkin { 740ad34f02fSDmytro Linkin struct mlx5_vport *vport = priv; 741ad34f02fSDmytro Linkin struct mlx5_eswitch *esw; 742ad34f02fSDmytro Linkin int err; 743ad34f02fSDmytro Linkin 744ad34f02fSDmytro Linkin esw = vport->dev->priv.eswitch; 745ad34f02fSDmytro Linkin if (!mlx5_esw_allowed(esw)) 746ad34f02fSDmytro Linkin return -EPERM; 747ad34f02fSDmytro Linkin 748ad34f02fSDmytro Linkin err = esw_qos_devlink_rate_to_mbps(vport->dev, "tx_max", &tx_max, extack); 749ad34f02fSDmytro Linkin if (err) 750ad34f02fSDmytro Linkin return err; 751ad34f02fSDmytro Linkin 752ad34f02fSDmytro Linkin mutex_lock(&esw->state_lock); 753ad34f02fSDmytro Linkin err = mlx5_esw_qos_set_vport_max_rate(esw, vport, tx_max, extack); 754ad34f02fSDmytro Linkin mutex_unlock(&esw->state_lock); 755ad34f02fSDmytro Linkin return err; 756ad34f02fSDmytro Linkin } 7571ae258f8SDmytro Linkin 758f47e04ebSDmytro Linkin int mlx5_esw_devlink_rate_node_tx_share_set(struct devlink_rate *rate_node, void *priv, 759f47e04ebSDmytro Linkin u64 tx_share, struct netlink_ext_ack *extack) 760f47e04ebSDmytro Linkin { 761f47e04ebSDmytro Linkin struct mlx5_core_dev *dev = devlink_priv(rate_node->devlink); 762f47e04ebSDmytro Linkin struct mlx5_eswitch *esw = dev->priv.eswitch; 763f47e04ebSDmytro Linkin struct mlx5_esw_rate_group *group = priv; 764f47e04ebSDmytro Linkin int err; 765f47e04ebSDmytro Linkin 766f47e04ebSDmytro Linkin err = esw_qos_devlink_rate_to_mbps(dev, "tx_share", &tx_share, extack); 767f47e04ebSDmytro Linkin if (err) 768f47e04ebSDmytro Linkin return err; 769f47e04ebSDmytro Linkin 770f47e04ebSDmytro Linkin mutex_lock(&esw->state_lock); 771f47e04ebSDmytro Linkin err = esw_qos_set_group_min_rate(esw, group, tx_share, extack); 772f47e04ebSDmytro Linkin mutex_unlock(&esw->state_lock); 773f47e04ebSDmytro Linkin return err; 774f47e04ebSDmytro Linkin } 775f47e04ebSDmytro Linkin 776f47e04ebSDmytro Linkin int mlx5_esw_devlink_rate_node_tx_max_set(struct devlink_rate *rate_node, void *priv, 777f47e04ebSDmytro Linkin u64 tx_max, struct netlink_ext_ack *extack) 778f47e04ebSDmytro Linkin { 779f47e04ebSDmytro Linkin struct mlx5_core_dev *dev = devlink_priv(rate_node->devlink); 780f47e04ebSDmytro Linkin struct mlx5_eswitch *esw = dev->priv.eswitch; 781f47e04ebSDmytro Linkin struct mlx5_esw_rate_group *group = priv; 782f47e04ebSDmytro Linkin int err; 783f47e04ebSDmytro Linkin 784f47e04ebSDmytro Linkin err = esw_qos_devlink_rate_to_mbps(dev, "tx_max", &tx_max, extack); 785f47e04ebSDmytro Linkin if (err) 786f47e04ebSDmytro Linkin return err; 787f47e04ebSDmytro Linkin 788f47e04ebSDmytro Linkin mutex_lock(&esw->state_lock); 789f47e04ebSDmytro Linkin err = esw_qos_set_group_max_rate(esw, group, tx_max, extack); 790f47e04ebSDmytro Linkin mutex_unlock(&esw->state_lock); 791f47e04ebSDmytro Linkin return err; 792f47e04ebSDmytro Linkin } 793f47e04ebSDmytro Linkin 7941ae258f8SDmytro Linkin int mlx5_esw_devlink_rate_node_new(struct devlink_rate *rate_node, void **priv, 7951ae258f8SDmytro Linkin struct netlink_ext_ack *extack) 7961ae258f8SDmytro Linkin { 7971ae258f8SDmytro Linkin struct mlx5_esw_rate_group *group; 7981ae258f8SDmytro Linkin struct mlx5_eswitch *esw; 7991ae258f8SDmytro Linkin int err = 0; 8001ae258f8SDmytro Linkin 8011ae258f8SDmytro Linkin esw = mlx5_devlink_eswitch_get(rate_node->devlink); 8021ae258f8SDmytro Linkin if (IS_ERR(esw)) 8031ae258f8SDmytro Linkin return PTR_ERR(esw); 8041ae258f8SDmytro Linkin 8051ae258f8SDmytro Linkin mutex_lock(&esw->state_lock); 8061ae258f8SDmytro Linkin if (esw->mode != MLX5_ESWITCH_OFFLOADS) { 8071ae258f8SDmytro Linkin NL_SET_ERR_MSG_MOD(extack, 8081ae258f8SDmytro Linkin "Rate node creation supported only in switchdev mode"); 8091ae258f8SDmytro Linkin err = -EOPNOTSUPP; 8101ae258f8SDmytro Linkin goto unlock; 8111ae258f8SDmytro Linkin } 8121ae258f8SDmytro Linkin 8131ae258f8SDmytro Linkin group = esw_qos_create_rate_group(esw, extack); 8141ae258f8SDmytro Linkin if (IS_ERR(group)) { 8151ae258f8SDmytro Linkin err = PTR_ERR(group); 8161ae258f8SDmytro Linkin goto unlock; 8171ae258f8SDmytro Linkin } 8181ae258f8SDmytro Linkin 8191ae258f8SDmytro Linkin *priv = group; 8201ae258f8SDmytro Linkin unlock: 8211ae258f8SDmytro Linkin mutex_unlock(&esw->state_lock); 8221ae258f8SDmytro Linkin return err; 8231ae258f8SDmytro Linkin } 8241ae258f8SDmytro Linkin 8251ae258f8SDmytro Linkin int mlx5_esw_devlink_rate_node_del(struct devlink_rate *rate_node, void *priv, 8261ae258f8SDmytro Linkin struct netlink_ext_ack *extack) 8271ae258f8SDmytro Linkin { 8281ae258f8SDmytro Linkin struct mlx5_esw_rate_group *group = priv; 8291ae258f8SDmytro Linkin struct mlx5_eswitch *esw; 8301ae258f8SDmytro Linkin int err; 8311ae258f8SDmytro Linkin 8321ae258f8SDmytro Linkin esw = mlx5_devlink_eswitch_get(rate_node->devlink); 8331ae258f8SDmytro Linkin if (IS_ERR(esw)) 8341ae258f8SDmytro Linkin return PTR_ERR(esw); 8351ae258f8SDmytro Linkin 8361ae258f8SDmytro Linkin mutex_lock(&esw->state_lock); 8371ae258f8SDmytro Linkin err = esw_qos_destroy_rate_group(esw, group, extack); 8381ae258f8SDmytro Linkin mutex_unlock(&esw->state_lock); 8391ae258f8SDmytro Linkin return err; 8401ae258f8SDmytro Linkin } 8410fe132eaSDmytro Linkin 8420fe132eaSDmytro Linkin int mlx5_esw_qos_vport_update_group(struct mlx5_eswitch *esw, 8430fe132eaSDmytro Linkin struct mlx5_vport *vport, 8440fe132eaSDmytro Linkin struct mlx5_esw_rate_group *group, 8450fe132eaSDmytro Linkin struct netlink_ext_ack *extack) 8460fe132eaSDmytro Linkin { 8470fe132eaSDmytro Linkin int err; 8480fe132eaSDmytro Linkin 8490fe132eaSDmytro Linkin mutex_lock(&esw->state_lock); 8500fe132eaSDmytro Linkin err = esw_qos_vport_update_group(esw, vport, group, extack); 8510fe132eaSDmytro Linkin mutex_unlock(&esw->state_lock); 8520fe132eaSDmytro Linkin return err; 8530fe132eaSDmytro Linkin } 8540fe132eaSDmytro Linkin 8550fe132eaSDmytro Linkin int mlx5_esw_devlink_rate_parent_set(struct devlink_rate *devlink_rate, 8560fe132eaSDmytro Linkin struct devlink_rate *parent, 8570fe132eaSDmytro Linkin void *priv, void *parent_priv, 8580fe132eaSDmytro Linkin struct netlink_ext_ack *extack) 8590fe132eaSDmytro Linkin { 8600fe132eaSDmytro Linkin struct mlx5_esw_rate_group *group; 8610fe132eaSDmytro Linkin struct mlx5_vport *vport = priv; 8620fe132eaSDmytro Linkin 8630fe132eaSDmytro Linkin if (!parent) 8640fe132eaSDmytro Linkin return mlx5_esw_qos_vport_update_group(vport->dev->priv.eswitch, 8650fe132eaSDmytro Linkin vport, NULL, extack); 8660fe132eaSDmytro Linkin 8670fe132eaSDmytro Linkin group = parent_priv; 8680fe132eaSDmytro Linkin return mlx5_esw_qos_vport_update_group(vport->dev->priv.eswitch, vport, group, extack); 8690fe132eaSDmytro Linkin } 870