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