19948a064SJiri Pirko // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
29948a064SJiri Pirko /* Copyright (c) 2017-2018 Mellanox Technologies. All rights reserved */
396f17e07SNogah Frankel 
496f17e07SNogah Frankel #include <linux/kernel.h>
596f17e07SNogah Frankel #include <linux/errno.h>
696f17e07SNogah Frankel #include <linux/netdevice.h>
796f17e07SNogah Frankel #include <net/pkt_cls.h>
8861fb829SNogah Frankel #include <net/red.h>
996f17e07SNogah Frankel 
1096f17e07SNogah Frankel #include "spectrum.h"
1196f17e07SNogah Frankel #include "reg.h"
1296f17e07SNogah Frankel 
1346a3615bSNogah Frankel #define MLXSW_SP_PRIO_BAND_TO_TCLASS(band) (IEEE_8021QAZ_MAX_TCS - band - 1)
14eed4baebSNogah Frankel #define MLXSW_SP_PRIO_CHILD_TO_TCLASS(child) \
15eed4baebSNogah Frankel 	MLXSW_SP_PRIO_BAND_TO_TCLASS((child - 1))
1646a3615bSNogah Frankel 
17371b437aSNogah Frankel enum mlxsw_sp_qdisc_type {
18371b437aSNogah Frankel 	MLXSW_SP_QDISC_NO_QDISC,
19371b437aSNogah Frankel 	MLXSW_SP_QDISC_RED,
2046a3615bSNogah Frankel 	MLXSW_SP_QDISC_PRIO,
21371b437aSNogah Frankel };
22371b437aSNogah Frankel 
23562ffbc4SNogah Frankel struct mlxsw_sp_qdisc_ops {
249cf6c9c7SNogah Frankel 	enum mlxsw_sp_qdisc_type type;
259cf6c9c7SNogah Frankel 	int (*check_params)(struct mlxsw_sp_port *mlxsw_sp_port,
269cf6c9c7SNogah Frankel 			    struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
279cf6c9c7SNogah Frankel 			    void *params);
289cf6c9c7SNogah Frankel 	int (*replace)(struct mlxsw_sp_port *mlxsw_sp_port,
299cf6c9c7SNogah Frankel 		       struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, void *params);
309a37a59fSNogah Frankel 	int (*destroy)(struct mlxsw_sp_port *mlxsw_sp_port,
319a37a59fSNogah Frankel 		       struct mlxsw_sp_qdisc *mlxsw_sp_qdisc);
32562ffbc4SNogah Frankel 	int (*get_stats)(struct mlxsw_sp_port *mlxsw_sp_port,
33562ffbc4SNogah Frankel 			 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
34562ffbc4SNogah Frankel 			 struct tc_qopt_offload_stats *stats_ptr);
35562ffbc4SNogah Frankel 	int (*get_xstats)(struct mlxsw_sp_port *mlxsw_sp_port,
36562ffbc4SNogah Frankel 			  struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
37562ffbc4SNogah Frankel 			  void *xstats_ptr);
389cf6c9c7SNogah Frankel 	void (*clean_stats)(struct mlxsw_sp_port *mlxsw_sp_port,
399cf6c9c7SNogah Frankel 			    struct mlxsw_sp_qdisc *mlxsw_sp_qdisc);
4093d8a4c1SNogah Frankel 	/* unoffload - to be used for a qdisc that stops being offloaded without
4193d8a4c1SNogah Frankel 	 * being destroyed.
4293d8a4c1SNogah Frankel 	 */
4393d8a4c1SNogah Frankel 	void (*unoffload)(struct mlxsw_sp_port *mlxsw_sp_port,
4493d8a4c1SNogah Frankel 			  struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, void *params);
45562ffbc4SNogah Frankel };
46562ffbc4SNogah Frankel 
47371b437aSNogah Frankel struct mlxsw_sp_qdisc {
48371b437aSNogah Frankel 	u32 handle;
49d56c8955SNogah Frankel 	u8 tclass_num;
501631ab2eSNogah Frankel 	u8 prio_bitmap;
51371b437aSNogah Frankel 	union {
524d1a4b84SNogah Frankel 		struct red_stats red;
534d1a4b84SNogah Frankel 	} xstats_base;
544d1a4b84SNogah Frankel 	struct mlxsw_sp_qdisc_stats {
55371b437aSNogah Frankel 		u64 tx_bytes;
56371b437aSNogah Frankel 		u64 tx_packets;
57371b437aSNogah Frankel 		u64 drops;
58371b437aSNogah Frankel 		u64 overlimits;
5993d8a4c1SNogah Frankel 		u64 backlog;
604d1a4b84SNogah Frankel 	} stats_base;
61562ffbc4SNogah Frankel 
62562ffbc4SNogah Frankel 	struct mlxsw_sp_qdisc_ops *ops;
63371b437aSNogah Frankel };
64371b437aSNogah Frankel 
65cba7158fSNogah Frankel static bool
66cba7158fSNogah Frankel mlxsw_sp_qdisc_compare(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, u32 handle,
67cba7158fSNogah Frankel 		       enum mlxsw_sp_qdisc_type type)
68cba7158fSNogah Frankel {
699cf6c9c7SNogah Frankel 	return mlxsw_sp_qdisc && mlxsw_sp_qdisc->ops &&
709cf6c9c7SNogah Frankel 	       mlxsw_sp_qdisc->ops->type == type &&
719cf6c9c7SNogah Frankel 	       mlxsw_sp_qdisc->handle == handle;
72cba7158fSNogah Frankel }
73cba7158fSNogah Frankel 
74eed4baebSNogah Frankel static struct mlxsw_sp_qdisc *
75eed4baebSNogah Frankel mlxsw_sp_qdisc_find(struct mlxsw_sp_port *mlxsw_sp_port, u32 parent,
76eed4baebSNogah Frankel 		    bool root_only)
77eed4baebSNogah Frankel {
78eed4baebSNogah Frankel 	int tclass, child_index;
79eed4baebSNogah Frankel 
80eed4baebSNogah Frankel 	if (parent == TC_H_ROOT)
81eed4baebSNogah Frankel 		return mlxsw_sp_port->root_qdisc;
82eed4baebSNogah Frankel 
83eed4baebSNogah Frankel 	if (root_only || !mlxsw_sp_port->root_qdisc ||
84eed4baebSNogah Frankel 	    !mlxsw_sp_port->root_qdisc->ops ||
85eed4baebSNogah Frankel 	    TC_H_MAJ(parent) != mlxsw_sp_port->root_qdisc->handle ||
86eed4baebSNogah Frankel 	    TC_H_MIN(parent) > IEEE_8021QAZ_MAX_TCS)
87eed4baebSNogah Frankel 		return NULL;
88eed4baebSNogah Frankel 
89eed4baebSNogah Frankel 	child_index = TC_H_MIN(parent);
90eed4baebSNogah Frankel 	tclass = MLXSW_SP_PRIO_CHILD_TO_TCLASS(child_index);
91eed4baebSNogah Frankel 	return &mlxsw_sp_port->tclass_qdiscs[tclass];
92eed4baebSNogah Frankel }
93eed4baebSNogah Frankel 
9432dc5efcSNogah Frankel static struct mlxsw_sp_qdisc *
9532dc5efcSNogah Frankel mlxsw_sp_qdisc_find_by_handle(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle)
9632dc5efcSNogah Frankel {
9732dc5efcSNogah Frankel 	int i;
9832dc5efcSNogah Frankel 
9932dc5efcSNogah Frankel 	if (mlxsw_sp_port->root_qdisc->handle == handle)
10032dc5efcSNogah Frankel 		return mlxsw_sp_port->root_qdisc;
10132dc5efcSNogah Frankel 
10232dc5efcSNogah Frankel 	if (mlxsw_sp_port->root_qdisc->handle == TC_H_UNSPEC)
10332dc5efcSNogah Frankel 		return NULL;
10432dc5efcSNogah Frankel 
10532dc5efcSNogah Frankel 	for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++)
10632dc5efcSNogah Frankel 		if (mlxsw_sp_port->tclass_qdiscs[i].handle == handle)
10732dc5efcSNogah Frankel 			return &mlxsw_sp_port->tclass_qdiscs[i];
10832dc5efcSNogah Frankel 
10932dc5efcSNogah Frankel 	return NULL;
11032dc5efcSNogah Frankel }
11132dc5efcSNogah Frankel 
11296f17e07SNogah Frankel static int
1139a37a59fSNogah Frankel mlxsw_sp_qdisc_destroy(struct mlxsw_sp_port *mlxsw_sp_port,
1149a37a59fSNogah Frankel 		       struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
1159a37a59fSNogah Frankel {
1169a37a59fSNogah Frankel 	int err = 0;
1179a37a59fSNogah Frankel 
1189a37a59fSNogah Frankel 	if (!mlxsw_sp_qdisc)
1199a37a59fSNogah Frankel 		return 0;
1209a37a59fSNogah Frankel 
1219a37a59fSNogah Frankel 	if (mlxsw_sp_qdisc->ops && mlxsw_sp_qdisc->ops->destroy)
1229a37a59fSNogah Frankel 		err = mlxsw_sp_qdisc->ops->destroy(mlxsw_sp_port,
1239a37a59fSNogah Frankel 						   mlxsw_sp_qdisc);
1249a37a59fSNogah Frankel 
1259a37a59fSNogah Frankel 	mlxsw_sp_qdisc->handle = TC_H_UNSPEC;
1269a37a59fSNogah Frankel 	mlxsw_sp_qdisc->ops = NULL;
1279a37a59fSNogah Frankel 	return err;
1289a37a59fSNogah Frankel }
1299a37a59fSNogah Frankel 
1309a37a59fSNogah Frankel static int
1319cf6c9c7SNogah Frankel mlxsw_sp_qdisc_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
1329cf6c9c7SNogah Frankel 		       struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
1339cf6c9c7SNogah Frankel 		       struct mlxsw_sp_qdisc_ops *ops, void *params)
1349cf6c9c7SNogah Frankel {
1359cf6c9c7SNogah Frankel 	int err;
1369cf6c9c7SNogah Frankel 
13756202ca4SNogah Frankel 	if (mlxsw_sp_qdisc->ops && mlxsw_sp_qdisc->ops->type != ops->type)
13856202ca4SNogah Frankel 		/* In case this location contained a different qdisc of the
13956202ca4SNogah Frankel 		 * same type we can override the old qdisc configuration.
14056202ca4SNogah Frankel 		 * Otherwise, we need to remove the old qdisc before setting the
14156202ca4SNogah Frankel 		 * new one.
14256202ca4SNogah Frankel 		 */
14356202ca4SNogah Frankel 		mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
1449cf6c9c7SNogah Frankel 	err = ops->check_params(mlxsw_sp_port, mlxsw_sp_qdisc, params);
1459cf6c9c7SNogah Frankel 	if (err)
1469cf6c9c7SNogah Frankel 		goto err_bad_param;
1479cf6c9c7SNogah Frankel 
1489cf6c9c7SNogah Frankel 	err = ops->replace(mlxsw_sp_port, mlxsw_sp_qdisc, params);
1499cf6c9c7SNogah Frankel 	if (err)
1509cf6c9c7SNogah Frankel 		goto err_config;
1519cf6c9c7SNogah Frankel 
1529cf6c9c7SNogah Frankel 	if (mlxsw_sp_qdisc->handle != handle) {
1539cf6c9c7SNogah Frankel 		mlxsw_sp_qdisc->ops = ops;
1549cf6c9c7SNogah Frankel 		if (ops->clean_stats)
1559cf6c9c7SNogah Frankel 			ops->clean_stats(mlxsw_sp_port, mlxsw_sp_qdisc);
1569cf6c9c7SNogah Frankel 	}
1579cf6c9c7SNogah Frankel 
1589cf6c9c7SNogah Frankel 	mlxsw_sp_qdisc->handle = handle;
1599cf6c9c7SNogah Frankel 	return 0;
1609cf6c9c7SNogah Frankel 
1619cf6c9c7SNogah Frankel err_bad_param:
1629cf6c9c7SNogah Frankel err_config:
16393d8a4c1SNogah Frankel 	if (mlxsw_sp_qdisc->handle == handle && ops->unoffload)
16493d8a4c1SNogah Frankel 		ops->unoffload(mlxsw_sp_port, mlxsw_sp_qdisc, params);
16593d8a4c1SNogah Frankel 
1669cf6c9c7SNogah Frankel 	mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
1679cf6c9c7SNogah Frankel 	return err;
1689cf6c9c7SNogah Frankel }
1699cf6c9c7SNogah Frankel 
1709cf6c9c7SNogah Frankel static int
171562ffbc4SNogah Frankel mlxsw_sp_qdisc_get_stats(struct mlxsw_sp_port *mlxsw_sp_port,
172562ffbc4SNogah Frankel 			 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
173562ffbc4SNogah Frankel 			 struct tc_qopt_offload_stats *stats_ptr)
174562ffbc4SNogah Frankel {
175562ffbc4SNogah Frankel 	if (mlxsw_sp_qdisc && mlxsw_sp_qdisc->ops &&
176562ffbc4SNogah Frankel 	    mlxsw_sp_qdisc->ops->get_stats)
177562ffbc4SNogah Frankel 		return mlxsw_sp_qdisc->ops->get_stats(mlxsw_sp_port,
178562ffbc4SNogah Frankel 						      mlxsw_sp_qdisc,
179562ffbc4SNogah Frankel 						      stats_ptr);
180562ffbc4SNogah Frankel 
181562ffbc4SNogah Frankel 	return -EOPNOTSUPP;
182562ffbc4SNogah Frankel }
183562ffbc4SNogah Frankel 
184562ffbc4SNogah Frankel static int
185562ffbc4SNogah Frankel mlxsw_sp_qdisc_get_xstats(struct mlxsw_sp_port *mlxsw_sp_port,
186562ffbc4SNogah Frankel 			  struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
187562ffbc4SNogah Frankel 			  void *xstats_ptr)
188562ffbc4SNogah Frankel {
189562ffbc4SNogah Frankel 	if (mlxsw_sp_qdisc && mlxsw_sp_qdisc->ops &&
190562ffbc4SNogah Frankel 	    mlxsw_sp_qdisc->ops->get_xstats)
191562ffbc4SNogah Frankel 		return mlxsw_sp_qdisc->ops->get_xstats(mlxsw_sp_port,
192562ffbc4SNogah Frankel 						      mlxsw_sp_qdisc,
193562ffbc4SNogah Frankel 						      xstats_ptr);
194562ffbc4SNogah Frankel 
195562ffbc4SNogah Frankel 	return -EOPNOTSUPP;
196562ffbc4SNogah Frankel }
197562ffbc4SNogah Frankel 
19804cc0bf5SNogah Frankel static void
19904cc0bf5SNogah Frankel mlxsw_sp_qdisc_bstats_per_priority_get(struct mlxsw_sp_port_xstats *xstats,
20004cc0bf5SNogah Frankel 				       u8 prio_bitmap, u64 *tx_packets,
20104cc0bf5SNogah Frankel 				       u64 *tx_bytes)
20204cc0bf5SNogah Frankel {
20304cc0bf5SNogah Frankel 	int i;
20404cc0bf5SNogah Frankel 
20504cc0bf5SNogah Frankel 	*tx_packets = 0;
20604cc0bf5SNogah Frankel 	*tx_bytes = 0;
20704cc0bf5SNogah Frankel 	for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
20804cc0bf5SNogah Frankel 		if (prio_bitmap & BIT(i)) {
20904cc0bf5SNogah Frankel 			*tx_packets += xstats->tx_packets[i];
21004cc0bf5SNogah Frankel 			*tx_bytes += xstats->tx_bytes[i];
21104cc0bf5SNogah Frankel 		}
21204cc0bf5SNogah Frankel 	}
21304cc0bf5SNogah Frankel }
21404cc0bf5SNogah Frankel 
215562ffbc4SNogah Frankel static int
21696f17e07SNogah Frankel mlxsw_sp_tclass_congestion_enable(struct mlxsw_sp_port *mlxsw_sp_port,
21796f17e07SNogah Frankel 				  int tclass_num, u32 min, u32 max,
21896f17e07SNogah Frankel 				  u32 probability, bool is_ecn)
21996f17e07SNogah Frankel {
220db84924cSJiri Pirko 	char cwtpm_cmd[MLXSW_REG_CWTPM_LEN];
221db84924cSJiri Pirko 	char cwtp_cmd[MLXSW_REG_CWTP_LEN];
22296f17e07SNogah Frankel 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
22396f17e07SNogah Frankel 	int err;
22496f17e07SNogah Frankel 
22596f17e07SNogah Frankel 	mlxsw_reg_cwtp_pack(cwtp_cmd, mlxsw_sp_port->local_port, tclass_num);
22696f17e07SNogah Frankel 	mlxsw_reg_cwtp_profile_pack(cwtp_cmd, MLXSW_REG_CWTP_DEFAULT_PROFILE,
22796f17e07SNogah Frankel 				    roundup(min, MLXSW_REG_CWTP_MIN_VALUE),
22896f17e07SNogah Frankel 				    roundup(max, MLXSW_REG_CWTP_MIN_VALUE),
22996f17e07SNogah Frankel 				    probability);
23096f17e07SNogah Frankel 
23196f17e07SNogah Frankel 	err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(cwtp), cwtp_cmd);
23296f17e07SNogah Frankel 	if (err)
23396f17e07SNogah Frankel 		return err;
23496f17e07SNogah Frankel 
235db84924cSJiri Pirko 	mlxsw_reg_cwtpm_pack(cwtpm_cmd, mlxsw_sp_port->local_port, tclass_num,
23696f17e07SNogah Frankel 			     MLXSW_REG_CWTP_DEFAULT_PROFILE, true, is_ecn);
23796f17e07SNogah Frankel 
238db84924cSJiri Pirko 	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(cwtpm), cwtpm_cmd);
23996f17e07SNogah Frankel }
24096f17e07SNogah Frankel 
24196f17e07SNogah Frankel static int
24296f17e07SNogah Frankel mlxsw_sp_tclass_congestion_disable(struct mlxsw_sp_port *mlxsw_sp_port,
24396f17e07SNogah Frankel 				   int tclass_num)
24496f17e07SNogah Frankel {
24596f17e07SNogah Frankel 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
24696f17e07SNogah Frankel 	char cwtpm_cmd[MLXSW_REG_CWTPM_LEN];
24796f17e07SNogah Frankel 
24896f17e07SNogah Frankel 	mlxsw_reg_cwtpm_pack(cwtpm_cmd, mlxsw_sp_port->local_port, tclass_num,
24996f17e07SNogah Frankel 			     MLXSW_REG_CWTPM_RESET_PROFILE, false, false);
25096f17e07SNogah Frankel 	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(cwtpm), cwtpm_cmd);
25196f17e07SNogah Frankel }
25296f17e07SNogah Frankel 
253861fb829SNogah Frankel static void
254c2ed6db7SNogah Frankel mlxsw_sp_setup_tc_qdisc_red_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port,
255d56c8955SNogah Frankel 					struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
256861fb829SNogah Frankel {
257d56c8955SNogah Frankel 	u8 tclass_num = mlxsw_sp_qdisc->tclass_num;
2584d1a4b84SNogah Frankel 	struct mlxsw_sp_qdisc_stats *stats_base;
259861fb829SNogah Frankel 	struct mlxsw_sp_port_xstats *xstats;
2604d1a4b84SNogah Frankel 	struct red_stats *red_base;
261861fb829SNogah Frankel 
262861fb829SNogah Frankel 	xstats = &mlxsw_sp_port->periodic_hw_stats.xstats;
2634d1a4b84SNogah Frankel 	stats_base = &mlxsw_sp_qdisc->stats_base;
264c2ed6db7SNogah Frankel 	red_base = &mlxsw_sp_qdisc->xstats_base.red;
2653670756fSNogah Frankel 
26604cc0bf5SNogah Frankel 	mlxsw_sp_qdisc_bstats_per_priority_get(xstats,
26704cc0bf5SNogah Frankel 					       mlxsw_sp_qdisc->prio_bitmap,
26804cc0bf5SNogah Frankel 					       &stats_base->tx_packets,
26904cc0bf5SNogah Frankel 					       &stats_base->tx_bytes);
2704d1a4b84SNogah Frankel 	red_base->prob_mark = xstats->ecn;
2714d1a4b84SNogah Frankel 	red_base->prob_drop = xstats->wred_drop[tclass_num];
2724d1a4b84SNogah Frankel 	red_base->pdrop = xstats->tail_drop[tclass_num];
2733670756fSNogah Frankel 
274c2ed6db7SNogah Frankel 	stats_base->overlimits = red_base->prob_drop + red_base->prob_mark;
2754d1a4b84SNogah Frankel 	stats_base->drops = red_base->prob_drop + red_base->pdrop;
276416ef9b1SJakub Kicinski 
277416ef9b1SJakub Kicinski 	stats_base->backlog = 0;
278861fb829SNogah Frankel }
279861fb829SNogah Frankel 
28096f17e07SNogah Frankel static int
281cba7158fSNogah Frankel mlxsw_sp_qdisc_red_destroy(struct mlxsw_sp_port *mlxsw_sp_port,
282d56c8955SNogah Frankel 			   struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
28396f17e07SNogah Frankel {
284cc6e5c13SNogah Frankel 	struct mlxsw_sp_qdisc *root_qdisc = mlxsw_sp_port->root_qdisc;
285cc6e5c13SNogah Frankel 
286cc6e5c13SNogah Frankel 	if (root_qdisc != mlxsw_sp_qdisc)
287cc6e5c13SNogah Frankel 		root_qdisc->stats_base.backlog -=
288cc6e5c13SNogah Frankel 					mlxsw_sp_qdisc->stats_base.backlog;
289cc6e5c13SNogah Frankel 
2909a37a59fSNogah Frankel 	return mlxsw_sp_tclass_congestion_disable(mlxsw_sp_port,
291d56c8955SNogah Frankel 						  mlxsw_sp_qdisc->tclass_num);
29296f17e07SNogah Frankel }
29396f17e07SNogah Frankel 
29496f17e07SNogah Frankel static int
2959cf6c9c7SNogah Frankel mlxsw_sp_qdisc_red_check_params(struct mlxsw_sp_port *mlxsw_sp_port,
29696f17e07SNogah Frankel 				struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
2979cf6c9c7SNogah Frankel 				void *params)
29896f17e07SNogah Frankel {
29996f17e07SNogah Frankel 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
3009cf6c9c7SNogah Frankel 	struct tc_red_qopt_offload_params *p = params;
30196f17e07SNogah Frankel 
30296f17e07SNogah Frankel 	if (p->min > p->max) {
30396f17e07SNogah Frankel 		dev_err(mlxsw_sp->bus_info->dev,
30496f17e07SNogah Frankel 			"spectrum: RED: min %u is bigger then max %u\n", p->min,
30596f17e07SNogah Frankel 			p->max);
3069cf6c9c7SNogah Frankel 		return -EINVAL;
30796f17e07SNogah Frankel 	}
308914c4fc1SPetr Machata 	if (p->max > MLXSW_CORE_RES_GET(mlxsw_sp->core,
309914c4fc1SPetr Machata 					GUARANTEED_SHARED_BUFFER)) {
31096f17e07SNogah Frankel 		dev_err(mlxsw_sp->bus_info->dev,
31196f17e07SNogah Frankel 			"spectrum: RED: max value %u is too big\n", p->max);
3129cf6c9c7SNogah Frankel 		return -EINVAL;
31396f17e07SNogah Frankel 	}
31496f17e07SNogah Frankel 	if (p->min == 0 || p->max == 0) {
31596f17e07SNogah Frankel 		dev_err(mlxsw_sp->bus_info->dev,
31696f17e07SNogah Frankel 			"spectrum: RED: 0 value is illegal for min and max\n");
3179cf6c9c7SNogah Frankel 		return -EINVAL;
31896f17e07SNogah Frankel 	}
3199cf6c9c7SNogah Frankel 	return 0;
3209cf6c9c7SNogah Frankel }
3219cf6c9c7SNogah Frankel 
3229cf6c9c7SNogah Frankel static int
3239cf6c9c7SNogah Frankel mlxsw_sp_qdisc_red_replace(struct mlxsw_sp_port *mlxsw_sp_port,
3249cf6c9c7SNogah Frankel 			   struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
3259cf6c9c7SNogah Frankel 			   void *params)
3269cf6c9c7SNogah Frankel {
3279cf6c9c7SNogah Frankel 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
3289cf6c9c7SNogah Frankel 	struct tc_red_qopt_offload_params *p = params;
3299cf6c9c7SNogah Frankel 	u8 tclass_num = mlxsw_sp_qdisc->tclass_num;
3309cf6c9c7SNogah Frankel 	u32 min, max;
3319cf6c9c7SNogah Frankel 	u64 prob;
33296f17e07SNogah Frankel 
33396f17e07SNogah Frankel 	/* calculate probability in percentage */
33496f17e07SNogah Frankel 	prob = p->probability;
33596f17e07SNogah Frankel 	prob *= 100;
33696f17e07SNogah Frankel 	prob = DIV_ROUND_UP(prob, 1 << 16);
33796f17e07SNogah Frankel 	prob = DIV_ROUND_UP(prob, 1 << 16);
33896f17e07SNogah Frankel 	min = mlxsw_sp_bytes_cells(mlxsw_sp, p->min);
33996f17e07SNogah Frankel 	max = mlxsw_sp_bytes_cells(mlxsw_sp, p->max);
3409cf6c9c7SNogah Frankel 	return mlxsw_sp_tclass_congestion_enable(mlxsw_sp_port, tclass_num, min,
34196f17e07SNogah Frankel 						 max, prob, p->is_ecn);
34296f17e07SNogah Frankel }
34396f17e07SNogah Frankel 
344416ef9b1SJakub Kicinski static void
345416ef9b1SJakub Kicinski mlxsw_sp_qdisc_red_unoffload(struct mlxsw_sp_port *mlxsw_sp_port,
346416ef9b1SJakub Kicinski 			     struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
347416ef9b1SJakub Kicinski 			     void *params)
348416ef9b1SJakub Kicinski {
349416ef9b1SJakub Kicinski 	struct tc_red_qopt_offload_params *p = params;
350416ef9b1SJakub Kicinski 	u64 backlog;
351416ef9b1SJakub Kicinski 
352416ef9b1SJakub Kicinski 	backlog = mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp,
353416ef9b1SJakub Kicinski 				       mlxsw_sp_qdisc->stats_base.backlog);
354416ef9b1SJakub Kicinski 	p->qstats->backlog -= backlog;
355cc6e5c13SNogah Frankel 	mlxsw_sp_qdisc->stats_base.backlog = 0;
356416ef9b1SJakub Kicinski }
357416ef9b1SJakub Kicinski 
358861fb829SNogah Frankel static int
359cba7158fSNogah Frankel mlxsw_sp_qdisc_get_red_xstats(struct mlxsw_sp_port *mlxsw_sp_port,
360861fb829SNogah Frankel 			      struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
361562ffbc4SNogah Frankel 			      void *xstats_ptr)
362861fb829SNogah Frankel {
3634d1a4b84SNogah Frankel 	struct red_stats *xstats_base = &mlxsw_sp_qdisc->xstats_base.red;
364d56c8955SNogah Frankel 	u8 tclass_num = mlxsw_sp_qdisc->tclass_num;
365861fb829SNogah Frankel 	struct mlxsw_sp_port_xstats *xstats;
366562ffbc4SNogah Frankel 	struct red_stats *res = xstats_ptr;
367f8253df5SNogah Frankel 	int early_drops, marks, pdrops;
368861fb829SNogah Frankel 
369861fb829SNogah Frankel 	xstats = &mlxsw_sp_port->periodic_hw_stats.xstats;
370861fb829SNogah Frankel 
371f8253df5SNogah Frankel 	early_drops = xstats->wred_drop[tclass_num] - xstats_base->prob_drop;
372f8253df5SNogah Frankel 	marks = xstats->ecn - xstats_base->prob_mark;
373f8253df5SNogah Frankel 	pdrops = xstats->tail_drop[tclass_num] - xstats_base->pdrop;
374f8253df5SNogah Frankel 
375f8253df5SNogah Frankel 	res->pdrop += pdrops;
376f8253df5SNogah Frankel 	res->prob_drop += early_drops;
377f8253df5SNogah Frankel 	res->prob_mark += marks;
378f8253df5SNogah Frankel 
379f8253df5SNogah Frankel 	xstats_base->pdrop += pdrops;
380f8253df5SNogah Frankel 	xstats_base->prob_drop += early_drops;
381f8253df5SNogah Frankel 	xstats_base->prob_mark += marks;
382861fb829SNogah Frankel 	return 0;
383861fb829SNogah Frankel }
384861fb829SNogah Frankel 
3853670756fSNogah Frankel static int
386cba7158fSNogah Frankel mlxsw_sp_qdisc_get_red_stats(struct mlxsw_sp_port *mlxsw_sp_port,
3873670756fSNogah Frankel 			     struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
388562ffbc4SNogah Frankel 			     struct tc_qopt_offload_stats *stats_ptr)
3893670756fSNogah Frankel {
390416ef9b1SJakub Kicinski 	u64 tx_bytes, tx_packets, overlimits, drops, backlog;
391d56c8955SNogah Frankel 	u8 tclass_num = mlxsw_sp_qdisc->tclass_num;
3924d1a4b84SNogah Frankel 	struct mlxsw_sp_qdisc_stats *stats_base;
3933670756fSNogah Frankel 	struct mlxsw_sp_port_xstats *xstats;
3943670756fSNogah Frankel 
3953670756fSNogah Frankel 	xstats = &mlxsw_sp_port->periodic_hw_stats.xstats;
3964d1a4b84SNogah Frankel 	stats_base = &mlxsw_sp_qdisc->stats_base;
3973670756fSNogah Frankel 
39804cc0bf5SNogah Frankel 	mlxsw_sp_qdisc_bstats_per_priority_get(xstats,
39904cc0bf5SNogah Frankel 					       mlxsw_sp_qdisc->prio_bitmap,
40004cc0bf5SNogah Frankel 					       &tx_packets, &tx_bytes);
40104cc0bf5SNogah Frankel 	tx_bytes = tx_bytes - stats_base->tx_bytes;
40204cc0bf5SNogah Frankel 	tx_packets = tx_packets - stats_base->tx_packets;
40304cc0bf5SNogah Frankel 
4043670756fSNogah Frankel 	overlimits = xstats->wred_drop[tclass_num] + xstats->ecn -
4054d1a4b84SNogah Frankel 		     stats_base->overlimits;
4063670756fSNogah Frankel 	drops = xstats->wred_drop[tclass_num] + xstats->tail_drop[tclass_num] -
4074d1a4b84SNogah Frankel 		stats_base->drops;
408416ef9b1SJakub Kicinski 	backlog = xstats->backlog[tclass_num];
4093670756fSNogah Frankel 
410562ffbc4SNogah Frankel 	_bstats_update(stats_ptr->bstats, tx_bytes, tx_packets);
411562ffbc4SNogah Frankel 	stats_ptr->qstats->overlimits += overlimits;
412562ffbc4SNogah Frankel 	stats_ptr->qstats->drops += drops;
413562ffbc4SNogah Frankel 	stats_ptr->qstats->backlog +=
414562ffbc4SNogah Frankel 				mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp,
415416ef9b1SJakub Kicinski 						     backlog) -
416416ef9b1SJakub Kicinski 				mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp,
417416ef9b1SJakub Kicinski 						     stats_base->backlog);
4183670756fSNogah Frankel 
419416ef9b1SJakub Kicinski 	stats_base->backlog = backlog;
4204d1a4b84SNogah Frankel 	stats_base->drops +=  drops;
4214d1a4b84SNogah Frankel 	stats_base->overlimits += overlimits;
4224d1a4b84SNogah Frankel 	stats_base->tx_bytes += tx_bytes;
4234d1a4b84SNogah Frankel 	stats_base->tx_packets += tx_packets;
4243670756fSNogah Frankel 	return 0;
4253670756fSNogah Frankel }
4263670756fSNogah Frankel 
42796f17e07SNogah Frankel #define MLXSW_SP_PORT_DEFAULT_TCLASS 0
42896f17e07SNogah Frankel 
429562ffbc4SNogah Frankel static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_red = {
4309cf6c9c7SNogah Frankel 	.type = MLXSW_SP_QDISC_RED,
4319cf6c9c7SNogah Frankel 	.check_params = mlxsw_sp_qdisc_red_check_params,
4329cf6c9c7SNogah Frankel 	.replace = mlxsw_sp_qdisc_red_replace,
433416ef9b1SJakub Kicinski 	.unoffload = mlxsw_sp_qdisc_red_unoffload,
4349a37a59fSNogah Frankel 	.destroy = mlxsw_sp_qdisc_red_destroy,
435562ffbc4SNogah Frankel 	.get_stats = mlxsw_sp_qdisc_get_red_stats,
436562ffbc4SNogah Frankel 	.get_xstats = mlxsw_sp_qdisc_get_red_xstats,
4379cf6c9c7SNogah Frankel 	.clean_stats = mlxsw_sp_setup_tc_qdisc_red_clean_stats,
438562ffbc4SNogah Frankel };
439562ffbc4SNogah Frankel 
44096f17e07SNogah Frankel int mlxsw_sp_setup_tc_red(struct mlxsw_sp_port *mlxsw_sp_port,
44196f17e07SNogah Frankel 			  struct tc_red_qopt_offload *p)
44296f17e07SNogah Frankel {
44396f17e07SNogah Frankel 	struct mlxsw_sp_qdisc *mlxsw_sp_qdisc;
44496f17e07SNogah Frankel 
445eed4baebSNogah Frankel 	mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent, false);
446eed4baebSNogah Frankel 	if (!mlxsw_sp_qdisc)
44796f17e07SNogah Frankel 		return -EOPNOTSUPP;
44896f17e07SNogah Frankel 
449cba7158fSNogah Frankel 	if (p->command == TC_RED_REPLACE)
4509cf6c9c7SNogah Frankel 		return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle,
451562ffbc4SNogah Frankel 					      mlxsw_sp_qdisc,
452562ffbc4SNogah Frankel 					      &mlxsw_sp_qdisc_ops_red,
453562ffbc4SNogah Frankel 					      &p->set);
454cba7158fSNogah Frankel 
455cba7158fSNogah Frankel 	if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle,
456cba7158fSNogah Frankel 				    MLXSW_SP_QDISC_RED))
457cba7158fSNogah Frankel 		return -EOPNOTSUPP;
458cba7158fSNogah Frankel 
459cba7158fSNogah Frankel 	switch (p->command) {
46096f17e07SNogah Frankel 	case TC_RED_DESTROY:
4619a37a59fSNogah Frankel 		return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
462861fb829SNogah Frankel 	case TC_RED_XSTATS:
463562ffbc4SNogah Frankel 		return mlxsw_sp_qdisc_get_xstats(mlxsw_sp_port, mlxsw_sp_qdisc,
464861fb829SNogah Frankel 						 p->xstats);
4653670756fSNogah Frankel 	case TC_RED_STATS:
466562ffbc4SNogah Frankel 		return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc,
4673670756fSNogah Frankel 						&p->stats);
46896f17e07SNogah Frankel 	default:
46996f17e07SNogah Frankel 		return -EOPNOTSUPP;
47096f17e07SNogah Frankel 	}
47196f17e07SNogah Frankel }
472371b437aSNogah Frankel 
47346a3615bSNogah Frankel static int
47446a3615bSNogah Frankel mlxsw_sp_qdisc_prio_destroy(struct mlxsw_sp_port *mlxsw_sp_port,
47546a3615bSNogah Frankel 			    struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
47646a3615bSNogah Frankel {
47746a3615bSNogah Frankel 	int i;
47846a3615bSNogah Frankel 
479eed4baebSNogah Frankel 	for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
48046a3615bSNogah Frankel 		mlxsw_sp_port_prio_tc_set(mlxsw_sp_port, i,
48146a3615bSNogah Frankel 					  MLXSW_SP_PORT_DEFAULT_TCLASS);
482eed4baebSNogah Frankel 		mlxsw_sp_qdisc_destroy(mlxsw_sp_port,
483eed4baebSNogah Frankel 				       &mlxsw_sp_port->tclass_qdiscs[i]);
4841631ab2eSNogah Frankel 		mlxsw_sp_port->tclass_qdiscs[i].prio_bitmap = 0;
485eed4baebSNogah Frankel 	}
48646a3615bSNogah Frankel 
48746a3615bSNogah Frankel 	return 0;
48846a3615bSNogah Frankel }
48946a3615bSNogah Frankel 
49046a3615bSNogah Frankel static int
49146a3615bSNogah Frankel mlxsw_sp_qdisc_prio_check_params(struct mlxsw_sp_port *mlxsw_sp_port,
49246a3615bSNogah Frankel 				 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
49346a3615bSNogah Frankel 				 void *params)
49446a3615bSNogah Frankel {
49546a3615bSNogah Frankel 	struct tc_prio_qopt_offload_params *p = params;
49646a3615bSNogah Frankel 
49746a3615bSNogah Frankel 	if (p->bands > IEEE_8021QAZ_MAX_TCS)
49846a3615bSNogah Frankel 		return -EOPNOTSUPP;
49946a3615bSNogah Frankel 
50046a3615bSNogah Frankel 	return 0;
50146a3615bSNogah Frankel }
50246a3615bSNogah Frankel 
50346a3615bSNogah Frankel static int
50446a3615bSNogah Frankel mlxsw_sp_qdisc_prio_replace(struct mlxsw_sp_port *mlxsw_sp_port,
50546a3615bSNogah Frankel 			    struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
50646a3615bSNogah Frankel 			    void *params)
50746a3615bSNogah Frankel {
50846a3615bSNogah Frankel 	struct tc_prio_qopt_offload_params *p = params;
50904cc0bf5SNogah Frankel 	struct mlxsw_sp_qdisc *child_qdisc;
510cc6e5c13SNogah Frankel 	int tclass, i, band, backlog;
51104cc0bf5SNogah Frankel 	u8 old_priomap;
51246a3615bSNogah Frankel 	int err;
51346a3615bSNogah Frankel 
51404cc0bf5SNogah Frankel 	for (band = 0; band < p->bands; band++) {
51504cc0bf5SNogah Frankel 		tclass = MLXSW_SP_PRIO_BAND_TO_TCLASS(band);
51604cc0bf5SNogah Frankel 		child_qdisc = &mlxsw_sp_port->tclass_qdiscs[tclass];
51704cc0bf5SNogah Frankel 		old_priomap = child_qdisc->prio_bitmap;
51804cc0bf5SNogah Frankel 		child_qdisc->prio_bitmap = 0;
51946a3615bSNogah Frankel 		for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
52004cc0bf5SNogah Frankel 			if (p->priomap[i] == band) {
52104cc0bf5SNogah Frankel 				child_qdisc->prio_bitmap |= BIT(i);
52204cc0bf5SNogah Frankel 				if (BIT(i) & old_priomap)
52304cc0bf5SNogah Frankel 					continue;
52404cc0bf5SNogah Frankel 				err = mlxsw_sp_port_prio_tc_set(mlxsw_sp_port,
52504cc0bf5SNogah Frankel 								i, tclass);
52646a3615bSNogah Frankel 				if (err)
52746a3615bSNogah Frankel 					return err;
52804cc0bf5SNogah Frankel 			}
52904cc0bf5SNogah Frankel 		}
53004cc0bf5SNogah Frankel 		if (old_priomap != child_qdisc->prio_bitmap &&
531cc6e5c13SNogah Frankel 		    child_qdisc->ops && child_qdisc->ops->clean_stats) {
532cc6e5c13SNogah Frankel 			backlog = child_qdisc->stats_base.backlog;
53304cc0bf5SNogah Frankel 			child_qdisc->ops->clean_stats(mlxsw_sp_port,
53404cc0bf5SNogah Frankel 						      child_qdisc);
535cc6e5c13SNogah Frankel 			child_qdisc->stats_base.backlog = backlog;
536cc6e5c13SNogah Frankel 		}
53746a3615bSNogah Frankel 	}
53898ceb7b6SNogah Frankel 	for (; band < IEEE_8021QAZ_MAX_TCS; band++) {
53998ceb7b6SNogah Frankel 		tclass = MLXSW_SP_PRIO_BAND_TO_TCLASS(band);
54098ceb7b6SNogah Frankel 		child_qdisc = &mlxsw_sp_port->tclass_qdiscs[tclass];
54198ceb7b6SNogah Frankel 		child_qdisc->prio_bitmap = 0;
54298ceb7b6SNogah Frankel 		mlxsw_sp_qdisc_destroy(mlxsw_sp_port, child_qdisc);
54398ceb7b6SNogah Frankel 	}
54446a3615bSNogah Frankel 	return 0;
54546a3615bSNogah Frankel }
54646a3615bSNogah Frankel 
547e02f08a0SWei Yongjun static void
54893d8a4c1SNogah Frankel mlxsw_sp_qdisc_prio_unoffload(struct mlxsw_sp_port *mlxsw_sp_port,
54993d8a4c1SNogah Frankel 			      struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
55093d8a4c1SNogah Frankel 			      void *params)
55193d8a4c1SNogah Frankel {
55293d8a4c1SNogah Frankel 	struct tc_prio_qopt_offload_params *p = params;
55393d8a4c1SNogah Frankel 	u64 backlog;
55493d8a4c1SNogah Frankel 
55593d8a4c1SNogah Frankel 	backlog = mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp,
55693d8a4c1SNogah Frankel 				       mlxsw_sp_qdisc->stats_base.backlog);
55793d8a4c1SNogah Frankel 	p->qstats->backlog -= backlog;
55893d8a4c1SNogah Frankel }
55993d8a4c1SNogah Frankel 
56093d8a4c1SNogah Frankel static int
56193d8a4c1SNogah Frankel mlxsw_sp_qdisc_get_prio_stats(struct mlxsw_sp_port *mlxsw_sp_port,
56293d8a4c1SNogah Frankel 			      struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
56393d8a4c1SNogah Frankel 			      struct tc_qopt_offload_stats *stats_ptr)
56493d8a4c1SNogah Frankel {
56593d8a4c1SNogah Frankel 	u64 tx_bytes, tx_packets, drops = 0, backlog = 0;
56693d8a4c1SNogah Frankel 	struct mlxsw_sp_qdisc_stats *stats_base;
56793d8a4c1SNogah Frankel 	struct mlxsw_sp_port_xstats *xstats;
56893d8a4c1SNogah Frankel 	struct rtnl_link_stats64 *stats;
56993d8a4c1SNogah Frankel 	int i;
57093d8a4c1SNogah Frankel 
57193d8a4c1SNogah Frankel 	xstats = &mlxsw_sp_port->periodic_hw_stats.xstats;
57293d8a4c1SNogah Frankel 	stats = &mlxsw_sp_port->periodic_hw_stats.stats;
57393d8a4c1SNogah Frankel 	stats_base = &mlxsw_sp_qdisc->stats_base;
57493d8a4c1SNogah Frankel 
57593d8a4c1SNogah Frankel 	tx_bytes = stats->tx_bytes - stats_base->tx_bytes;
57693d8a4c1SNogah Frankel 	tx_packets = stats->tx_packets - stats_base->tx_packets;
57793d8a4c1SNogah Frankel 
57893d8a4c1SNogah Frankel 	for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
57993d8a4c1SNogah Frankel 		drops += xstats->tail_drop[i];
58023f2b404SNogah Frankel 		drops += xstats->wred_drop[i];
58193d8a4c1SNogah Frankel 		backlog += xstats->backlog[i];
58293d8a4c1SNogah Frankel 	}
58393d8a4c1SNogah Frankel 	drops = drops - stats_base->drops;
58493d8a4c1SNogah Frankel 
58593d8a4c1SNogah Frankel 	_bstats_update(stats_ptr->bstats, tx_bytes, tx_packets);
58693d8a4c1SNogah Frankel 	stats_ptr->qstats->drops += drops;
58793d8a4c1SNogah Frankel 	stats_ptr->qstats->backlog +=
58893d8a4c1SNogah Frankel 				mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp,
58993d8a4c1SNogah Frankel 						     backlog) -
59093d8a4c1SNogah Frankel 				mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp,
59193d8a4c1SNogah Frankel 						     stats_base->backlog);
59293d8a4c1SNogah Frankel 	stats_base->backlog = backlog;
59393d8a4c1SNogah Frankel 	stats_base->drops += drops;
59493d8a4c1SNogah Frankel 	stats_base->tx_bytes += tx_bytes;
59593d8a4c1SNogah Frankel 	stats_base->tx_packets += tx_packets;
59693d8a4c1SNogah Frankel 	return 0;
59793d8a4c1SNogah Frankel }
59893d8a4c1SNogah Frankel 
59993d8a4c1SNogah Frankel static void
60093d8a4c1SNogah Frankel mlxsw_sp_setup_tc_qdisc_prio_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port,
60193d8a4c1SNogah Frankel 					 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
60293d8a4c1SNogah Frankel {
60393d8a4c1SNogah Frankel 	struct mlxsw_sp_qdisc_stats *stats_base;
60493d8a4c1SNogah Frankel 	struct mlxsw_sp_port_xstats *xstats;
60593d8a4c1SNogah Frankel 	struct rtnl_link_stats64 *stats;
60693d8a4c1SNogah Frankel 	int i;
60793d8a4c1SNogah Frankel 
60893d8a4c1SNogah Frankel 	xstats = &mlxsw_sp_port->periodic_hw_stats.xstats;
60993d8a4c1SNogah Frankel 	stats = &mlxsw_sp_port->periodic_hw_stats.stats;
61093d8a4c1SNogah Frankel 	stats_base = &mlxsw_sp_qdisc->stats_base;
61193d8a4c1SNogah Frankel 
61293d8a4c1SNogah Frankel 	stats_base->tx_packets = stats->tx_packets;
61393d8a4c1SNogah Frankel 	stats_base->tx_bytes = stats->tx_bytes;
61493d8a4c1SNogah Frankel 
61593d8a4c1SNogah Frankel 	stats_base->drops = 0;
61623f2b404SNogah Frankel 	for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
61793d8a4c1SNogah Frankel 		stats_base->drops += xstats->tail_drop[i];
61823f2b404SNogah Frankel 		stats_base->drops += xstats->wred_drop[i];
61923f2b404SNogah Frankel 	}
62093d8a4c1SNogah Frankel 
62193d8a4c1SNogah Frankel 	mlxsw_sp_qdisc->stats_base.backlog = 0;
62293d8a4c1SNogah Frankel }
62393d8a4c1SNogah Frankel 
62446a3615bSNogah Frankel static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_prio = {
62546a3615bSNogah Frankel 	.type = MLXSW_SP_QDISC_PRIO,
62646a3615bSNogah Frankel 	.check_params = mlxsw_sp_qdisc_prio_check_params,
62746a3615bSNogah Frankel 	.replace = mlxsw_sp_qdisc_prio_replace,
62893d8a4c1SNogah Frankel 	.unoffload = mlxsw_sp_qdisc_prio_unoffload,
62946a3615bSNogah Frankel 	.destroy = mlxsw_sp_qdisc_prio_destroy,
63093d8a4c1SNogah Frankel 	.get_stats = mlxsw_sp_qdisc_get_prio_stats,
63193d8a4c1SNogah Frankel 	.clean_stats = mlxsw_sp_setup_tc_qdisc_prio_clean_stats,
63246a3615bSNogah Frankel };
63346a3615bSNogah Frankel 
63432dc5efcSNogah Frankel /* Grafting is not supported in mlxsw. It will result in un-offloading of the
63532dc5efcSNogah Frankel  * grafted qdisc as well as the qdisc in the qdisc new location.
63632dc5efcSNogah Frankel  * (However, if the graft is to the location where the qdisc is already at, it
63732dc5efcSNogah Frankel  * will be ignored completely and won't cause un-offloading).
63832dc5efcSNogah Frankel  */
63932dc5efcSNogah Frankel static int
64032dc5efcSNogah Frankel mlxsw_sp_qdisc_prio_graft(struct mlxsw_sp_port *mlxsw_sp_port,
64132dc5efcSNogah Frankel 			  struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
64232dc5efcSNogah Frankel 			  struct tc_prio_qopt_offload_graft_params *p)
64332dc5efcSNogah Frankel {
64432dc5efcSNogah Frankel 	int tclass_num = MLXSW_SP_PRIO_BAND_TO_TCLASS(p->band);
64532dc5efcSNogah Frankel 	struct mlxsw_sp_qdisc *old_qdisc;
64632dc5efcSNogah Frankel 
64732dc5efcSNogah Frankel 	/* Check if the grafted qdisc is already in its "new" location. If so -
64832dc5efcSNogah Frankel 	 * nothing needs to be done.
64932dc5efcSNogah Frankel 	 */
65032dc5efcSNogah Frankel 	if (p->band < IEEE_8021QAZ_MAX_TCS &&
65132dc5efcSNogah Frankel 	    mlxsw_sp_port->tclass_qdiscs[tclass_num].handle == p->child_handle)
65232dc5efcSNogah Frankel 		return 0;
65332dc5efcSNogah Frankel 
6543971a535SPetr Machata 	if (!p->child_handle) {
6553971a535SPetr Machata 		/* This is an invisible FIFO replacing the original Qdisc.
6563971a535SPetr Machata 		 * Ignore it--the original Qdisc's destroy will follow.
6573971a535SPetr Machata 		 */
6583971a535SPetr Machata 		return 0;
6593971a535SPetr Machata 	}
6603971a535SPetr Machata 
66132dc5efcSNogah Frankel 	/* See if the grafted qdisc is already offloaded on any tclass. If so,
66232dc5efcSNogah Frankel 	 * unoffload it.
66332dc5efcSNogah Frankel 	 */
66432dc5efcSNogah Frankel 	old_qdisc = mlxsw_sp_qdisc_find_by_handle(mlxsw_sp_port,
66532dc5efcSNogah Frankel 						  p->child_handle);
66632dc5efcSNogah Frankel 	if (old_qdisc)
66732dc5efcSNogah Frankel 		mlxsw_sp_qdisc_destroy(mlxsw_sp_port, old_qdisc);
66832dc5efcSNogah Frankel 
66932dc5efcSNogah Frankel 	mlxsw_sp_qdisc_destroy(mlxsw_sp_port,
67032dc5efcSNogah Frankel 			       &mlxsw_sp_port->tclass_qdiscs[tclass_num]);
67132dc5efcSNogah Frankel 	return -EOPNOTSUPP;
67232dc5efcSNogah Frankel }
67332dc5efcSNogah Frankel 
67446a3615bSNogah Frankel int mlxsw_sp_setup_tc_prio(struct mlxsw_sp_port *mlxsw_sp_port,
67546a3615bSNogah Frankel 			   struct tc_prio_qopt_offload *p)
67646a3615bSNogah Frankel {
67746a3615bSNogah Frankel 	struct mlxsw_sp_qdisc *mlxsw_sp_qdisc;
67846a3615bSNogah Frankel 
679eed4baebSNogah Frankel 	mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent, true);
680eed4baebSNogah Frankel 	if (!mlxsw_sp_qdisc)
68146a3615bSNogah Frankel 		return -EOPNOTSUPP;
68246a3615bSNogah Frankel 
68346a3615bSNogah Frankel 	if (p->command == TC_PRIO_REPLACE)
68446a3615bSNogah Frankel 		return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle,
68546a3615bSNogah Frankel 					      mlxsw_sp_qdisc,
68646a3615bSNogah Frankel 					      &mlxsw_sp_qdisc_ops_prio,
68746a3615bSNogah Frankel 					      &p->replace_params);
68846a3615bSNogah Frankel 
68946a3615bSNogah Frankel 	if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle,
69046a3615bSNogah Frankel 				    MLXSW_SP_QDISC_PRIO))
69146a3615bSNogah Frankel 		return -EOPNOTSUPP;
69246a3615bSNogah Frankel 
69346a3615bSNogah Frankel 	switch (p->command) {
69446a3615bSNogah Frankel 	case TC_PRIO_DESTROY:
69546a3615bSNogah Frankel 		return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
69693d8a4c1SNogah Frankel 	case TC_PRIO_STATS:
69793d8a4c1SNogah Frankel 		return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc,
69893d8a4c1SNogah Frankel 						&p->stats);
69932dc5efcSNogah Frankel 	case TC_PRIO_GRAFT:
70032dc5efcSNogah Frankel 		return mlxsw_sp_qdisc_prio_graft(mlxsw_sp_port, mlxsw_sp_qdisc,
70132dc5efcSNogah Frankel 						 &p->graft_params);
70246a3615bSNogah Frankel 	default:
70346a3615bSNogah Frankel 		return -EOPNOTSUPP;
70446a3615bSNogah Frankel 	}
70546a3615bSNogah Frankel }
70646a3615bSNogah Frankel 
707371b437aSNogah Frankel int mlxsw_sp_tc_qdisc_init(struct mlxsw_sp_port *mlxsw_sp_port)
708371b437aSNogah Frankel {
709eed4baebSNogah Frankel 	struct mlxsw_sp_qdisc *mlxsw_sp_qdisc;
710eed4baebSNogah Frankel 	int i;
711371b437aSNogah Frankel 
712eed4baebSNogah Frankel 	mlxsw_sp_qdisc = kzalloc(sizeof(*mlxsw_sp_qdisc), GFP_KERNEL);
713eed4baebSNogah Frankel 	if (!mlxsw_sp_qdisc)
714eed4baebSNogah Frankel 		goto err_root_qdisc_init;
715eed4baebSNogah Frankel 
716eed4baebSNogah Frankel 	mlxsw_sp_port->root_qdisc = mlxsw_sp_qdisc;
7171631ab2eSNogah Frankel 	mlxsw_sp_port->root_qdisc->prio_bitmap = 0xff;
718d56c8955SNogah Frankel 	mlxsw_sp_port->root_qdisc->tclass_num = MLXSW_SP_PORT_DEFAULT_TCLASS;
719d56c8955SNogah Frankel 
7206396bb22SKees Cook 	mlxsw_sp_qdisc = kcalloc(IEEE_8021QAZ_MAX_TCS,
7216396bb22SKees Cook 				 sizeof(*mlxsw_sp_qdisc),
722eed4baebSNogah Frankel 				 GFP_KERNEL);
723eed4baebSNogah Frankel 	if (!mlxsw_sp_qdisc)
724eed4baebSNogah Frankel 		goto err_tclass_qdiscs_init;
725eed4baebSNogah Frankel 
726eed4baebSNogah Frankel 	mlxsw_sp_port->tclass_qdiscs = mlxsw_sp_qdisc;
727eed4baebSNogah Frankel 	for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++)
728eed4baebSNogah Frankel 		mlxsw_sp_port->tclass_qdiscs[i].tclass_num = i;
729eed4baebSNogah Frankel 
730371b437aSNogah Frankel 	return 0;
731eed4baebSNogah Frankel 
732eed4baebSNogah Frankel err_tclass_qdiscs_init:
733eed4baebSNogah Frankel 	kfree(mlxsw_sp_port->root_qdisc);
734eed4baebSNogah Frankel err_root_qdisc_init:
735eed4baebSNogah Frankel 	return -ENOMEM;
736371b437aSNogah Frankel }
737371b437aSNogah Frankel 
738371b437aSNogah Frankel void mlxsw_sp_tc_qdisc_fini(struct mlxsw_sp_port *mlxsw_sp_port)
739371b437aSNogah Frankel {
740eed4baebSNogah Frankel 	kfree(mlxsw_sp_port->tclass_qdiscs);
741371b437aSNogah Frankel 	kfree(mlxsw_sp_port->root_qdisc);
742371b437aSNogah Frankel }
743