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