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