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 207*d7df09f5SDmytro Linkin static int esw_qos_set_vport_min_rate(struct mlx5_eswitch *esw, struct mlx5_vport *evport, 208*d7df09f5SDmytro 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 232*d7df09f5SDmytro Linkin static int esw_qos_set_vport_max_rate(struct mlx5_eswitch *esw, struct mlx5_vport *evport, 233*d7df09f5SDmytro 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 * 4311ae258f8SDmytro 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 if (!MLX5_CAP_QOS(esw->dev, log_esw_max_sched_depth)) 4391ae258f8SDmytro Linkin return ERR_PTR(-EOPNOTSUPP); 4401ae258f8SDmytro Linkin 4411ae258f8SDmytro Linkin group = kzalloc(sizeof(*group), GFP_KERNEL); 4421ae258f8SDmytro Linkin if (!group) 4431ae258f8SDmytro Linkin return ERR_PTR(-ENOMEM); 4441ae258f8SDmytro Linkin 4451ae258f8SDmytro Linkin MLX5_SET(scheduling_context, tsar_ctx, parent_element_id, 4461ae258f8SDmytro Linkin esw->qos.root_tsar_ix); 4471ae258f8SDmytro Linkin err = mlx5_create_scheduling_element_cmd(esw->dev, 4481ae258f8SDmytro Linkin SCHEDULING_HIERARCHY_E_SWITCH, 4491ae258f8SDmytro Linkin tsar_ctx, 4501ae258f8SDmytro Linkin &group->tsar_ix); 4511ae258f8SDmytro Linkin if (err) { 4521ae258f8SDmytro Linkin NL_SET_ERR_MSG_MOD(extack, "E-Switch create TSAR for group failed"); 4531ae258f8SDmytro Linkin goto err_sched_elem; 4541ae258f8SDmytro Linkin } 4551ae258f8SDmytro Linkin 456f47e04ebSDmytro Linkin list_add_tail(&group->list, &esw->qos.groups); 457f47e04ebSDmytro Linkin 458f47e04ebSDmytro Linkin divider = esw_qos_calculate_min_rate_divider(esw, group, true); 459f47e04ebSDmytro Linkin if (divider) { 460f47e04ebSDmytro Linkin err = esw_qos_normalize_groups_min_rate(esw, divider, extack); 461f47e04ebSDmytro Linkin if (err) { 462f47e04ebSDmytro Linkin NL_SET_ERR_MSG_MOD(extack, "E-Switch groups normalization failed"); 463f47e04ebSDmytro Linkin goto err_min_rate; 464f47e04ebSDmytro Linkin } 465f47e04ebSDmytro Linkin } 4663202ea65SDmytro Linkin trace_mlx5_esw_group_qos_create(esw->dev, group, group->tsar_ix); 467f47e04ebSDmytro Linkin 4681ae258f8SDmytro Linkin return group; 4691ae258f8SDmytro Linkin 470f47e04ebSDmytro Linkin err_min_rate: 471f47e04ebSDmytro Linkin list_del(&group->list); 472a6f74333SDmytro Linkin if (mlx5_destroy_scheduling_element_cmd(esw->dev, 473f47e04ebSDmytro Linkin SCHEDULING_HIERARCHY_E_SWITCH, 474a6f74333SDmytro Linkin group->tsar_ix)) 475f47e04ebSDmytro Linkin NL_SET_ERR_MSG_MOD(extack, "E-Switch destroy TSAR for group failed"); 4761ae258f8SDmytro Linkin err_sched_elem: 4771ae258f8SDmytro Linkin kfree(group); 4781ae258f8SDmytro Linkin return ERR_PTR(err); 4791ae258f8SDmytro Linkin } 4801ae258f8SDmytro Linkin 4811ae258f8SDmytro Linkin static int esw_qos_destroy_rate_group(struct mlx5_eswitch *esw, 4821ae258f8SDmytro Linkin struct mlx5_esw_rate_group *group, 4831ae258f8SDmytro Linkin struct netlink_ext_ack *extack) 4841ae258f8SDmytro Linkin { 485f47e04ebSDmytro Linkin u32 divider; 4861ae258f8SDmytro Linkin int err; 4871ae258f8SDmytro Linkin 488f47e04ebSDmytro Linkin list_del(&group->list); 489f47e04ebSDmytro Linkin 490f47e04ebSDmytro Linkin divider = esw_qos_calculate_min_rate_divider(esw, NULL, true); 491f47e04ebSDmytro Linkin err = esw_qos_normalize_groups_min_rate(esw, divider, extack); 492f47e04ebSDmytro Linkin if (err) 493f47e04ebSDmytro Linkin NL_SET_ERR_MSG_MOD(extack, "E-Switch groups' normalization failed"); 494f47e04ebSDmytro Linkin 4951ae258f8SDmytro Linkin err = mlx5_destroy_scheduling_element_cmd(esw->dev, 4961ae258f8SDmytro Linkin SCHEDULING_HIERARCHY_E_SWITCH, 4971ae258f8SDmytro Linkin group->tsar_ix); 4981ae258f8SDmytro Linkin if (err) 4991ae258f8SDmytro Linkin NL_SET_ERR_MSG_MOD(extack, "E-Switch destroy TSAR_ID failed"); 5001ae258f8SDmytro Linkin 5013202ea65SDmytro Linkin trace_mlx5_esw_group_qos_destroy(esw->dev, group, group->tsar_ix); 5021ae258f8SDmytro Linkin kfree(group); 5031ae258f8SDmytro Linkin return err; 5041ae258f8SDmytro Linkin } 5051ae258f8SDmytro Linkin 5062d116e3eSDmytro Linkin static bool esw_qos_element_type_supported(struct mlx5_core_dev *dev, int type) 5072d116e3eSDmytro Linkin { 5082d116e3eSDmytro Linkin switch (type) { 5092d116e3eSDmytro Linkin case SCHEDULING_CONTEXT_ELEMENT_TYPE_TSAR: 5102d116e3eSDmytro Linkin return MLX5_CAP_QOS(dev, esw_element_type) & 5112d116e3eSDmytro Linkin ELEMENT_TYPE_CAP_MASK_TASR; 5122d116e3eSDmytro Linkin case SCHEDULING_CONTEXT_ELEMENT_TYPE_VPORT: 5132d116e3eSDmytro Linkin return MLX5_CAP_QOS(dev, esw_element_type) & 5142d116e3eSDmytro Linkin ELEMENT_TYPE_CAP_MASK_VPORT; 5152d116e3eSDmytro Linkin case SCHEDULING_CONTEXT_ELEMENT_TYPE_VPORT_TC: 5162d116e3eSDmytro Linkin return MLX5_CAP_QOS(dev, esw_element_type) & 5172d116e3eSDmytro Linkin ELEMENT_TYPE_CAP_MASK_VPORT_TC; 5182d116e3eSDmytro Linkin case SCHEDULING_CONTEXT_ELEMENT_TYPE_PARA_VPORT_TC: 5192d116e3eSDmytro Linkin return MLX5_CAP_QOS(dev, esw_element_type) & 5202d116e3eSDmytro Linkin ELEMENT_TYPE_CAP_MASK_PARA_VPORT_TC; 5212d116e3eSDmytro Linkin } 5222d116e3eSDmytro Linkin return false; 5232d116e3eSDmytro Linkin } 5242d116e3eSDmytro Linkin 5252d116e3eSDmytro Linkin void mlx5_esw_qos_create(struct mlx5_eswitch *esw) 5262d116e3eSDmytro Linkin { 5272d116e3eSDmytro Linkin u32 tsar_ctx[MLX5_ST_SZ_DW(scheduling_context)] = {}; 5282d116e3eSDmytro Linkin struct mlx5_core_dev *dev = esw->dev; 5292d116e3eSDmytro Linkin __be32 *attr; 5302d116e3eSDmytro Linkin int err; 5312d116e3eSDmytro Linkin 5322d116e3eSDmytro Linkin if (!MLX5_CAP_GEN(dev, qos) || !MLX5_CAP_QOS(dev, esw_scheduling)) 5332d116e3eSDmytro Linkin return; 5342d116e3eSDmytro Linkin 5352d116e3eSDmytro Linkin if (!esw_qos_element_type_supported(dev, SCHEDULING_CONTEXT_ELEMENT_TYPE_TSAR)) 5362d116e3eSDmytro Linkin return; 5372d116e3eSDmytro Linkin 5381ae258f8SDmytro Linkin mutex_lock(&esw->state_lock); 5392d116e3eSDmytro Linkin if (esw->qos.enabled) 5401ae258f8SDmytro Linkin goto unlock; 5412d116e3eSDmytro Linkin 5422d116e3eSDmytro Linkin MLX5_SET(scheduling_context, tsar_ctx, element_type, 5432d116e3eSDmytro Linkin SCHEDULING_CONTEXT_ELEMENT_TYPE_TSAR); 5442d116e3eSDmytro Linkin 5452d116e3eSDmytro Linkin attr = MLX5_ADDR_OF(scheduling_context, tsar_ctx, element_attributes); 5462d116e3eSDmytro Linkin *attr = cpu_to_be32(TSAR_ELEMENT_TSAR_TYPE_DWRR << 16); 5472d116e3eSDmytro Linkin 5482d116e3eSDmytro Linkin err = mlx5_create_scheduling_element_cmd(dev, 5492d116e3eSDmytro Linkin SCHEDULING_HIERARCHY_E_SWITCH, 5502d116e3eSDmytro Linkin tsar_ctx, 5512d116e3eSDmytro Linkin &esw->qos.root_tsar_ix); 5522d116e3eSDmytro Linkin if (err) { 5531ae258f8SDmytro Linkin esw_warn(dev, "E-Switch create root TSAR failed (%d)\n", err); 5541ae258f8SDmytro Linkin goto unlock; 5552d116e3eSDmytro Linkin } 5562d116e3eSDmytro Linkin 557f47e04ebSDmytro Linkin INIT_LIST_HEAD(&esw->qos.groups); 5581ae258f8SDmytro Linkin if (MLX5_CAP_QOS(dev, log_esw_max_sched_depth)) { 5591ae258f8SDmytro Linkin esw->qos.group0 = esw_qos_create_rate_group(esw, NULL); 5601ae258f8SDmytro Linkin if (IS_ERR(esw->qos.group0)) { 5611ae258f8SDmytro Linkin esw_warn(dev, "E-Switch create rate group 0 failed (%ld)\n", 5621ae258f8SDmytro Linkin PTR_ERR(esw->qos.group0)); 5631ae258f8SDmytro Linkin goto err_group0; 5641ae258f8SDmytro Linkin } 5651ae258f8SDmytro Linkin } 5662d116e3eSDmytro Linkin esw->qos.enabled = true; 5671ae258f8SDmytro Linkin unlock: 5681ae258f8SDmytro Linkin mutex_unlock(&esw->state_lock); 5691ae258f8SDmytro Linkin return; 5701ae258f8SDmytro Linkin 5711ae258f8SDmytro Linkin err_group0: 5721ae258f8SDmytro Linkin err = mlx5_destroy_scheduling_element_cmd(esw->dev, 5731ae258f8SDmytro Linkin SCHEDULING_HIERARCHY_E_SWITCH, 5741ae258f8SDmytro Linkin esw->qos.root_tsar_ix); 5751ae258f8SDmytro Linkin if (err) 5761ae258f8SDmytro Linkin esw_warn(esw->dev, "E-Switch destroy root TSAR failed (%d)\n", err); 5771ae258f8SDmytro Linkin mutex_unlock(&esw->state_lock); 5782d116e3eSDmytro Linkin } 5792d116e3eSDmytro Linkin 5802d116e3eSDmytro Linkin void mlx5_esw_qos_destroy(struct mlx5_eswitch *esw) 5812d116e3eSDmytro Linkin { 5821ae258f8SDmytro Linkin struct devlink *devlink = priv_to_devlink(esw->dev); 5832d116e3eSDmytro Linkin int err; 5842d116e3eSDmytro Linkin 5851ae258f8SDmytro Linkin devlink_rate_nodes_destroy(devlink); 5861ae258f8SDmytro Linkin mutex_lock(&esw->state_lock); 5872d116e3eSDmytro Linkin if (!esw->qos.enabled) 5881ae258f8SDmytro Linkin goto unlock; 5891ae258f8SDmytro Linkin 5901ae258f8SDmytro Linkin if (esw->qos.group0) 5911ae258f8SDmytro Linkin esw_qos_destroy_rate_group(esw, esw->qos.group0, NULL); 5922d116e3eSDmytro Linkin 5932d116e3eSDmytro Linkin err = mlx5_destroy_scheduling_element_cmd(esw->dev, 5942d116e3eSDmytro Linkin SCHEDULING_HIERARCHY_E_SWITCH, 5952d116e3eSDmytro Linkin esw->qos.root_tsar_ix); 5962d116e3eSDmytro Linkin if (err) 5971ae258f8SDmytro Linkin esw_warn(esw->dev, "E-Switch destroy root TSAR failed (%d)\n", err); 5982d116e3eSDmytro Linkin 5992d116e3eSDmytro Linkin esw->qos.enabled = false; 6001ae258f8SDmytro Linkin unlock: 6011ae258f8SDmytro Linkin mutex_unlock(&esw->state_lock); 6022d116e3eSDmytro Linkin } 6032d116e3eSDmytro Linkin 604*d7df09f5SDmytro Linkin static int esw_qos_vport_enable(struct mlx5_eswitch *esw, struct mlx5_vport *vport, 6052d116e3eSDmytro Linkin u32 max_rate, u32 bw_share) 6062d116e3eSDmytro Linkin { 6072d116e3eSDmytro Linkin int err; 6082d116e3eSDmytro Linkin 6092d116e3eSDmytro Linkin lockdep_assert_held(&esw->state_lock); 6102d116e3eSDmytro Linkin if (!esw->qos.enabled) 6112d116e3eSDmytro Linkin return 0; 6122d116e3eSDmytro Linkin 6132d116e3eSDmytro Linkin if (vport->qos.enabled) 614*d7df09f5SDmytro Linkin return 0; 6152d116e3eSDmytro Linkin 6160fe132eaSDmytro Linkin vport->qos.group = esw->qos.group0; 6172d116e3eSDmytro Linkin 6180fe132eaSDmytro Linkin err = esw_qos_vport_create_sched_element(esw, vport, max_rate, bw_share); 6193202ea65SDmytro Linkin if (!err) { 6202d116e3eSDmytro Linkin vport->qos.enabled = true; 6213202ea65SDmytro Linkin trace_mlx5_esw_vport_qos_create(vport, bw_share, max_rate); 6223202ea65SDmytro Linkin } 6232d116e3eSDmytro Linkin 6242d116e3eSDmytro Linkin return err; 6252d116e3eSDmytro Linkin } 6262d116e3eSDmytro Linkin 6272d116e3eSDmytro Linkin void mlx5_esw_qos_vport_disable(struct mlx5_eswitch *esw, struct mlx5_vport *vport) 6282d116e3eSDmytro Linkin { 6292d116e3eSDmytro Linkin int err; 6302d116e3eSDmytro Linkin 6312d116e3eSDmytro Linkin lockdep_assert_held(&esw->state_lock); 6322d116e3eSDmytro Linkin if (!esw->qos.enabled || !vport->qos.enabled) 6332d116e3eSDmytro Linkin return; 6340fe132eaSDmytro Linkin WARN(vport->qos.group && vport->qos.group != esw->qos.group0, 6350fe132eaSDmytro Linkin "Disabling QoS on port before detaching it from group"); 6362d116e3eSDmytro Linkin 6372d116e3eSDmytro Linkin err = mlx5_destroy_scheduling_element_cmd(esw->dev, 6382d116e3eSDmytro Linkin SCHEDULING_HIERARCHY_E_SWITCH, 6392d116e3eSDmytro Linkin vport->qos.esw_tsar_ix); 6402d116e3eSDmytro Linkin if (err) 6412d116e3eSDmytro Linkin esw_warn(esw->dev, "E-Switch destroy TSAR vport element failed (vport=%d,err=%d)\n", 6422d116e3eSDmytro Linkin vport->vport, err); 6432d116e3eSDmytro Linkin 644*d7df09f5SDmytro Linkin memset(&vport->qos, 0, sizeof(vport->qos)); 6453202ea65SDmytro Linkin trace_mlx5_esw_vport_qos_destroy(vport); 6462d116e3eSDmytro Linkin } 6472d116e3eSDmytro Linkin 648*d7df09f5SDmytro Linkin int mlx5_esw_qos_set_vport_rate(struct mlx5_eswitch *esw, struct mlx5_vport *vport, 649*d7df09f5SDmytro Linkin u32 min_rate, u32 max_rate) 650*d7df09f5SDmytro Linkin { 651*d7df09f5SDmytro Linkin int err; 652*d7df09f5SDmytro Linkin 653*d7df09f5SDmytro Linkin lockdep_assert_held(&esw->state_lock); 654*d7df09f5SDmytro Linkin err = esw_qos_vport_enable(esw, vport, 0, 0); 655*d7df09f5SDmytro Linkin if (err) 656*d7df09f5SDmytro Linkin return err; 657*d7df09f5SDmytro Linkin 658*d7df09f5SDmytro Linkin err = esw_qos_set_vport_min_rate(esw, vport, min_rate, NULL); 659*d7df09f5SDmytro Linkin if (!err) 660*d7df09f5SDmytro Linkin err = esw_qos_set_vport_max_rate(esw, vport, max_rate, NULL); 661*d7df09f5SDmytro Linkin 662*d7df09f5SDmytro Linkin return err; 663*d7df09f5SDmytro Linkin } 664*d7df09f5SDmytro Linkin 6652d116e3eSDmytro Linkin int mlx5_esw_qos_modify_vport_rate(struct mlx5_eswitch *esw, u16 vport_num, u32 rate_mbps) 6662d116e3eSDmytro Linkin { 6672d116e3eSDmytro Linkin u32 ctx[MLX5_ST_SZ_DW(scheduling_context)] = {}; 6682d116e3eSDmytro Linkin struct mlx5_vport *vport; 6692d116e3eSDmytro Linkin u32 bitmask; 670*d7df09f5SDmytro Linkin int err; 6712d116e3eSDmytro Linkin 6722d116e3eSDmytro Linkin vport = mlx5_eswitch_get_vport(esw, vport_num); 6732d116e3eSDmytro Linkin if (IS_ERR(vport)) 6742d116e3eSDmytro Linkin return PTR_ERR(vport); 6752d116e3eSDmytro Linkin 676*d7df09f5SDmytro Linkin mutex_lock(&esw->state_lock); 677*d7df09f5SDmytro Linkin if (!vport->qos.enabled) { 678*d7df09f5SDmytro Linkin /* Eswitch QoS wasn't enabled yet. Enable it and vport QoS. */ 679*d7df09f5SDmytro Linkin err = esw_qos_vport_enable(esw, vport, rate_mbps, vport->qos.bw_share); 680*d7df09f5SDmytro Linkin } else { 6812d116e3eSDmytro Linkin MLX5_SET(scheduling_context, ctx, max_average_bw, rate_mbps); 6822d116e3eSDmytro Linkin 683*d7df09f5SDmytro Linkin bitmask = MODIFY_SCHEDULING_ELEMENT_IN_MODIFY_BITMASK_MAX_AVERAGE_BW; 684*d7df09f5SDmytro Linkin err = mlx5_modify_scheduling_element_cmd(esw->dev, 6852d116e3eSDmytro Linkin SCHEDULING_HIERARCHY_E_SWITCH, 6862d116e3eSDmytro Linkin ctx, 6872d116e3eSDmytro Linkin vport->qos.esw_tsar_ix, 6882d116e3eSDmytro Linkin bitmask); 6892d116e3eSDmytro Linkin } 690*d7df09f5SDmytro Linkin mutex_unlock(&esw->state_lock); 691*d7df09f5SDmytro Linkin 692*d7df09f5SDmytro Linkin return err; 693*d7df09f5SDmytro Linkin } 694ad34f02fSDmytro Linkin 695ad34f02fSDmytro Linkin #define MLX5_LINKSPEED_UNIT 125000 /* 1Mbps in Bps */ 696ad34f02fSDmytro Linkin 697ad34f02fSDmytro Linkin /* Converts bytes per second value passed in a pointer into megabits per 698ad34f02fSDmytro Linkin * second, rewriting last. If converted rate exceed link speed or is not a 699ad34f02fSDmytro Linkin * fraction of Mbps - returns error. 700ad34f02fSDmytro Linkin */ 701ad34f02fSDmytro Linkin static int esw_qos_devlink_rate_to_mbps(struct mlx5_core_dev *mdev, const char *name, 702ad34f02fSDmytro Linkin u64 *rate, struct netlink_ext_ack *extack) 703ad34f02fSDmytro Linkin { 704ad34f02fSDmytro Linkin u32 link_speed_max, reminder; 705ad34f02fSDmytro Linkin u64 value; 706ad34f02fSDmytro Linkin int err; 707ad34f02fSDmytro Linkin 708ad34f02fSDmytro Linkin err = mlx5e_port_max_linkspeed(mdev, &link_speed_max); 709ad34f02fSDmytro Linkin if (err) { 710ad34f02fSDmytro Linkin NL_SET_ERR_MSG_MOD(extack, "Failed to get link maximum speed"); 711ad34f02fSDmytro Linkin return err; 712ad34f02fSDmytro Linkin } 713ad34f02fSDmytro Linkin 714ad34f02fSDmytro Linkin value = div_u64_rem(*rate, MLX5_LINKSPEED_UNIT, &reminder); 715ad34f02fSDmytro Linkin if (reminder) { 716ad34f02fSDmytro Linkin pr_err("%s rate value %lluBps not in link speed units of 1Mbps.\n", 717ad34f02fSDmytro Linkin name, *rate); 718ad34f02fSDmytro Linkin NL_SET_ERR_MSG_MOD(extack, "TX rate value not in link speed units of 1Mbps"); 719ad34f02fSDmytro Linkin return -EINVAL; 720ad34f02fSDmytro Linkin } 721ad34f02fSDmytro Linkin 722ad34f02fSDmytro Linkin if (value > link_speed_max) { 723ad34f02fSDmytro Linkin pr_err("%s rate value %lluMbps exceed link maximum speed %u.\n", 724ad34f02fSDmytro Linkin name, value, link_speed_max); 725ad34f02fSDmytro Linkin NL_SET_ERR_MSG_MOD(extack, "TX rate value exceed link maximum speed"); 726ad34f02fSDmytro Linkin return -EINVAL; 727ad34f02fSDmytro Linkin } 728ad34f02fSDmytro Linkin 729ad34f02fSDmytro Linkin *rate = value; 730ad34f02fSDmytro Linkin return 0; 731ad34f02fSDmytro Linkin } 732ad34f02fSDmytro Linkin 733ad34f02fSDmytro Linkin /* Eswitch devlink rate API */ 734ad34f02fSDmytro Linkin 735ad34f02fSDmytro Linkin int mlx5_esw_devlink_rate_leaf_tx_share_set(struct devlink_rate *rate_leaf, void *priv, 736ad34f02fSDmytro Linkin u64 tx_share, struct netlink_ext_ack *extack) 737ad34f02fSDmytro Linkin { 738ad34f02fSDmytro Linkin struct mlx5_vport *vport = priv; 739ad34f02fSDmytro Linkin struct mlx5_eswitch *esw; 740ad34f02fSDmytro Linkin int err; 741ad34f02fSDmytro Linkin 742ad34f02fSDmytro Linkin esw = vport->dev->priv.eswitch; 743ad34f02fSDmytro Linkin if (!mlx5_esw_allowed(esw)) 744ad34f02fSDmytro Linkin return -EPERM; 745ad34f02fSDmytro Linkin 746ad34f02fSDmytro Linkin err = esw_qos_devlink_rate_to_mbps(vport->dev, "tx_share", &tx_share, extack); 747ad34f02fSDmytro Linkin if (err) 748ad34f02fSDmytro Linkin return err; 749ad34f02fSDmytro Linkin 750ad34f02fSDmytro Linkin mutex_lock(&esw->state_lock); 751*d7df09f5SDmytro Linkin err = esw_qos_vport_enable(esw, vport, 0, 0); 752*d7df09f5SDmytro Linkin if (err) 753*d7df09f5SDmytro Linkin goto unlock; 754*d7df09f5SDmytro Linkin 755*d7df09f5SDmytro Linkin err = esw_qos_set_vport_min_rate(esw, vport, tx_share, extack); 756*d7df09f5SDmytro Linkin unlock: 757ad34f02fSDmytro Linkin mutex_unlock(&esw->state_lock); 758ad34f02fSDmytro Linkin return err; 759ad34f02fSDmytro Linkin } 760ad34f02fSDmytro Linkin 761ad34f02fSDmytro Linkin int mlx5_esw_devlink_rate_leaf_tx_max_set(struct devlink_rate *rate_leaf, void *priv, 762ad34f02fSDmytro Linkin u64 tx_max, struct netlink_ext_ack *extack) 763ad34f02fSDmytro Linkin { 764ad34f02fSDmytro Linkin struct mlx5_vport *vport = priv; 765ad34f02fSDmytro Linkin struct mlx5_eswitch *esw; 766ad34f02fSDmytro Linkin int err; 767ad34f02fSDmytro Linkin 768ad34f02fSDmytro Linkin esw = vport->dev->priv.eswitch; 769ad34f02fSDmytro Linkin if (!mlx5_esw_allowed(esw)) 770ad34f02fSDmytro Linkin return -EPERM; 771ad34f02fSDmytro Linkin 772ad34f02fSDmytro Linkin err = esw_qos_devlink_rate_to_mbps(vport->dev, "tx_max", &tx_max, extack); 773ad34f02fSDmytro Linkin if (err) 774ad34f02fSDmytro Linkin return err; 775ad34f02fSDmytro Linkin 776ad34f02fSDmytro Linkin mutex_lock(&esw->state_lock); 777*d7df09f5SDmytro Linkin err = esw_qos_vport_enable(esw, vport, 0, 0); 778*d7df09f5SDmytro Linkin if (err) 779*d7df09f5SDmytro Linkin goto unlock; 780*d7df09f5SDmytro Linkin 781*d7df09f5SDmytro Linkin err = esw_qos_set_vport_max_rate(esw, vport, tx_max, extack); 782*d7df09f5SDmytro Linkin unlock: 783ad34f02fSDmytro Linkin mutex_unlock(&esw->state_lock); 784ad34f02fSDmytro Linkin return err; 785ad34f02fSDmytro Linkin } 7861ae258f8SDmytro Linkin 787f47e04ebSDmytro Linkin int mlx5_esw_devlink_rate_node_tx_share_set(struct devlink_rate *rate_node, void *priv, 788f47e04ebSDmytro Linkin u64 tx_share, struct netlink_ext_ack *extack) 789f47e04ebSDmytro Linkin { 790f47e04ebSDmytro Linkin struct mlx5_core_dev *dev = devlink_priv(rate_node->devlink); 791f47e04ebSDmytro Linkin struct mlx5_eswitch *esw = dev->priv.eswitch; 792f47e04ebSDmytro Linkin struct mlx5_esw_rate_group *group = priv; 793f47e04ebSDmytro Linkin int err; 794f47e04ebSDmytro Linkin 795f47e04ebSDmytro Linkin err = esw_qos_devlink_rate_to_mbps(dev, "tx_share", &tx_share, extack); 796f47e04ebSDmytro Linkin if (err) 797f47e04ebSDmytro Linkin return err; 798f47e04ebSDmytro Linkin 799f47e04ebSDmytro Linkin mutex_lock(&esw->state_lock); 800f47e04ebSDmytro Linkin err = esw_qos_set_group_min_rate(esw, group, tx_share, extack); 801f47e04ebSDmytro Linkin mutex_unlock(&esw->state_lock); 802f47e04ebSDmytro Linkin return err; 803f47e04ebSDmytro Linkin } 804f47e04ebSDmytro Linkin 805f47e04ebSDmytro Linkin int mlx5_esw_devlink_rate_node_tx_max_set(struct devlink_rate *rate_node, void *priv, 806f47e04ebSDmytro Linkin u64 tx_max, struct netlink_ext_ack *extack) 807f47e04ebSDmytro Linkin { 808f47e04ebSDmytro Linkin struct mlx5_core_dev *dev = devlink_priv(rate_node->devlink); 809f47e04ebSDmytro Linkin struct mlx5_eswitch *esw = dev->priv.eswitch; 810f47e04ebSDmytro Linkin struct mlx5_esw_rate_group *group = priv; 811f47e04ebSDmytro Linkin int err; 812f47e04ebSDmytro Linkin 813f47e04ebSDmytro Linkin err = esw_qos_devlink_rate_to_mbps(dev, "tx_max", &tx_max, extack); 814f47e04ebSDmytro Linkin if (err) 815f47e04ebSDmytro Linkin return err; 816f47e04ebSDmytro Linkin 817f47e04ebSDmytro Linkin mutex_lock(&esw->state_lock); 818f47e04ebSDmytro Linkin err = esw_qos_set_group_max_rate(esw, group, tx_max, extack); 819f47e04ebSDmytro Linkin mutex_unlock(&esw->state_lock); 820f47e04ebSDmytro Linkin return err; 821f47e04ebSDmytro Linkin } 822f47e04ebSDmytro Linkin 8231ae258f8SDmytro Linkin int mlx5_esw_devlink_rate_node_new(struct devlink_rate *rate_node, void **priv, 8241ae258f8SDmytro Linkin struct netlink_ext_ack *extack) 8251ae258f8SDmytro Linkin { 8261ae258f8SDmytro Linkin struct mlx5_esw_rate_group *group; 8271ae258f8SDmytro Linkin struct mlx5_eswitch *esw; 8281ae258f8SDmytro Linkin int err = 0; 8291ae258f8SDmytro Linkin 8301ae258f8SDmytro Linkin esw = mlx5_devlink_eswitch_get(rate_node->devlink); 8311ae258f8SDmytro Linkin if (IS_ERR(esw)) 8321ae258f8SDmytro Linkin return PTR_ERR(esw); 8331ae258f8SDmytro Linkin 8341ae258f8SDmytro Linkin mutex_lock(&esw->state_lock); 8351ae258f8SDmytro Linkin if (esw->mode != MLX5_ESWITCH_OFFLOADS) { 8361ae258f8SDmytro Linkin NL_SET_ERR_MSG_MOD(extack, 8371ae258f8SDmytro Linkin "Rate node creation supported only in switchdev mode"); 8381ae258f8SDmytro Linkin err = -EOPNOTSUPP; 8391ae258f8SDmytro Linkin goto unlock; 8401ae258f8SDmytro Linkin } 8411ae258f8SDmytro Linkin 8421ae258f8SDmytro Linkin group = esw_qos_create_rate_group(esw, extack); 8431ae258f8SDmytro Linkin if (IS_ERR(group)) { 8441ae258f8SDmytro Linkin err = PTR_ERR(group); 8451ae258f8SDmytro Linkin goto unlock; 8461ae258f8SDmytro Linkin } 8471ae258f8SDmytro Linkin 8481ae258f8SDmytro Linkin *priv = group; 8491ae258f8SDmytro Linkin unlock: 8501ae258f8SDmytro Linkin mutex_unlock(&esw->state_lock); 8511ae258f8SDmytro Linkin return err; 8521ae258f8SDmytro Linkin } 8531ae258f8SDmytro Linkin 8541ae258f8SDmytro Linkin int mlx5_esw_devlink_rate_node_del(struct devlink_rate *rate_node, void *priv, 8551ae258f8SDmytro Linkin struct netlink_ext_ack *extack) 8561ae258f8SDmytro Linkin { 8571ae258f8SDmytro Linkin struct mlx5_esw_rate_group *group = priv; 8581ae258f8SDmytro Linkin struct mlx5_eswitch *esw; 8591ae258f8SDmytro Linkin int err; 8601ae258f8SDmytro Linkin 8611ae258f8SDmytro Linkin esw = mlx5_devlink_eswitch_get(rate_node->devlink); 8621ae258f8SDmytro Linkin if (IS_ERR(esw)) 8631ae258f8SDmytro Linkin return PTR_ERR(esw); 8641ae258f8SDmytro Linkin 8651ae258f8SDmytro Linkin mutex_lock(&esw->state_lock); 8661ae258f8SDmytro Linkin err = esw_qos_destroy_rate_group(esw, group, extack); 8671ae258f8SDmytro Linkin mutex_unlock(&esw->state_lock); 8681ae258f8SDmytro Linkin return err; 8691ae258f8SDmytro Linkin } 8700fe132eaSDmytro Linkin 8710fe132eaSDmytro Linkin int mlx5_esw_qos_vport_update_group(struct mlx5_eswitch *esw, 8720fe132eaSDmytro Linkin struct mlx5_vport *vport, 8730fe132eaSDmytro Linkin struct mlx5_esw_rate_group *group, 8740fe132eaSDmytro Linkin struct netlink_ext_ack *extack) 8750fe132eaSDmytro Linkin { 8760fe132eaSDmytro Linkin int err; 8770fe132eaSDmytro Linkin 8780fe132eaSDmytro Linkin mutex_lock(&esw->state_lock); 879*d7df09f5SDmytro Linkin err = esw_qos_vport_enable(esw, vport, 0, 0); 880*d7df09f5SDmytro Linkin if (!err) 8810fe132eaSDmytro Linkin err = esw_qos_vport_update_group(esw, vport, group, extack); 8820fe132eaSDmytro Linkin mutex_unlock(&esw->state_lock); 8830fe132eaSDmytro Linkin return err; 8840fe132eaSDmytro Linkin } 8850fe132eaSDmytro Linkin 8860fe132eaSDmytro Linkin int mlx5_esw_devlink_rate_parent_set(struct devlink_rate *devlink_rate, 8870fe132eaSDmytro Linkin struct devlink_rate *parent, 8880fe132eaSDmytro Linkin void *priv, void *parent_priv, 8890fe132eaSDmytro Linkin struct netlink_ext_ack *extack) 8900fe132eaSDmytro Linkin { 8910fe132eaSDmytro Linkin struct mlx5_esw_rate_group *group; 8920fe132eaSDmytro Linkin struct mlx5_vport *vport = priv; 8930fe132eaSDmytro Linkin 8940fe132eaSDmytro Linkin if (!parent) 8950fe132eaSDmytro Linkin return mlx5_esw_qos_vport_update_group(vport->dev->priv.eswitch, 8960fe132eaSDmytro Linkin vport, NULL, extack); 8970fe132eaSDmytro Linkin 8980fe132eaSDmytro Linkin group = parent_priv; 8990fe132eaSDmytro Linkin return mlx5_esw_qos_vport_update_group(vport->dev->priv.eswitch, vport, group, extack); 9000fe132eaSDmytro Linkin } 901