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"
11f6668eacSPetr Machata #include "spectrum_span.h"
1296f17e07SNogah Frankel #include "reg.h"
1396f17e07SNogah Frankel 
1446a3615bSNogah Frankel #define MLXSW_SP_PRIO_BAND_TO_TCLASS(band) (IEEE_8021QAZ_MAX_TCS - band - 1)
15eed4baebSNogah Frankel #define MLXSW_SP_PRIO_CHILD_TO_TCLASS(child) \
16eed4baebSNogah Frankel 	MLXSW_SP_PRIO_BAND_TO_TCLASS((child - 1))
1746a3615bSNogah Frankel 
18371b437aSNogah Frankel enum mlxsw_sp_qdisc_type {
19371b437aSNogah Frankel 	MLXSW_SP_QDISC_NO_QDISC,
20371b437aSNogah Frankel 	MLXSW_SP_QDISC_RED,
2146a3615bSNogah Frankel 	MLXSW_SP_QDISC_PRIO,
2219f405b9SPetr Machata 	MLXSW_SP_QDISC_ETS,
23a44f58c4SPetr Machata 	MLXSW_SP_QDISC_TBF,
247bec1a45SPetr Machata 	MLXSW_SP_QDISC_FIFO,
25371b437aSNogah Frankel };
26371b437aSNogah Frankel 
27ee88450dSPetr Machata struct mlxsw_sp_qdisc;
28ee88450dSPetr Machata 
29562ffbc4SNogah Frankel struct mlxsw_sp_qdisc_ops {
309cf6c9c7SNogah Frankel 	enum mlxsw_sp_qdisc_type type;
319cf6c9c7SNogah Frankel 	int (*check_params)(struct mlxsw_sp_port *mlxsw_sp_port,
329cf6c9c7SNogah Frankel 			    void *params);
33c4e372e2SPetr Machata 	int (*replace)(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
349cf6c9c7SNogah Frankel 		       struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, void *params);
359a37a59fSNogah Frankel 	int (*destroy)(struct mlxsw_sp_port *mlxsw_sp_port,
369a37a59fSNogah Frankel 		       struct mlxsw_sp_qdisc *mlxsw_sp_qdisc);
37562ffbc4SNogah Frankel 	int (*get_stats)(struct mlxsw_sp_port *mlxsw_sp_port,
38562ffbc4SNogah Frankel 			 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
39562ffbc4SNogah Frankel 			 struct tc_qopt_offload_stats *stats_ptr);
40562ffbc4SNogah Frankel 	int (*get_xstats)(struct mlxsw_sp_port *mlxsw_sp_port,
41562ffbc4SNogah Frankel 			  struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
42562ffbc4SNogah Frankel 			  void *xstats_ptr);
439cf6c9c7SNogah Frankel 	void (*clean_stats)(struct mlxsw_sp_port *mlxsw_sp_port,
449cf6c9c7SNogah Frankel 			    struct mlxsw_sp_qdisc *mlxsw_sp_qdisc);
4593d8a4c1SNogah Frankel 	/* unoffload - to be used for a qdisc that stops being offloaded without
4693d8a4c1SNogah Frankel 	 * being destroyed.
4793d8a4c1SNogah Frankel 	 */
4893d8a4c1SNogah Frankel 	void (*unoffload)(struct mlxsw_sp_port *mlxsw_sp_port,
4993d8a4c1SNogah Frankel 			  struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, void *params);
5051d52ed9SPetr Machata 	struct mlxsw_sp_qdisc *(*find_class)(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
5151d52ed9SPetr Machata 					     u32 parent);
525cbd9602SPetr Machata 	unsigned int num_classes;
5376ff72a7SPetr Machata 
5476ff72a7SPetr Machata 	u8 (*get_prio_bitmap)(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
5576ff72a7SPetr Machata 			      struct mlxsw_sp_qdisc *child);
5676ff72a7SPetr Machata 	int (*get_tclass_num)(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
5776ff72a7SPetr Machata 			      struct mlxsw_sp_qdisc *child);
5876ff72a7SPetr Machata };
5976ff72a7SPetr Machata 
6076ff72a7SPetr Machata struct mlxsw_sp_qdisc_ets_band {
6176ff72a7SPetr Machata 	u8 prio_bitmap;
6276ff72a7SPetr Machata 	int tclass_num;
6376ff72a7SPetr Machata };
6476ff72a7SPetr Machata 
6576ff72a7SPetr Machata struct mlxsw_sp_qdisc_ets_data {
6676ff72a7SPetr Machata 	struct mlxsw_sp_qdisc_ets_band bands[IEEE_8021QAZ_MAX_TCS];
67562ffbc4SNogah Frankel };
68562ffbc4SNogah Frankel 
69371b437aSNogah Frankel struct mlxsw_sp_qdisc {
70371b437aSNogah Frankel 	u32 handle;
71371b437aSNogah Frankel 	union {
724d1a4b84SNogah Frankel 		struct red_stats red;
734d1a4b84SNogah Frankel 	} xstats_base;
744d1a4b84SNogah Frankel 	struct mlxsw_sp_qdisc_stats {
75371b437aSNogah Frankel 		u64 tx_bytes;
76371b437aSNogah Frankel 		u64 tx_packets;
77371b437aSNogah Frankel 		u64 drops;
78371b437aSNogah Frankel 		u64 overlimits;
7993d8a4c1SNogah Frankel 		u64 backlog;
804d1a4b84SNogah Frankel 	} stats_base;
81562ffbc4SNogah Frankel 
8276ff72a7SPetr Machata 	union {
8376ff72a7SPetr Machata 		struct mlxsw_sp_qdisc_ets_data *ets_data;
8476ff72a7SPetr Machata 	};
8576ff72a7SPetr Machata 
86562ffbc4SNogah Frankel 	struct mlxsw_sp_qdisc_ops *ops;
87b21832b5SPetr Machata 	struct mlxsw_sp_qdisc *parent;
8851d52ed9SPetr Machata 	struct mlxsw_sp_qdisc *qdiscs;
8951d52ed9SPetr Machata 	unsigned int num_classes;
90371b437aSNogah Frankel };
91371b437aSNogah Frankel 
92ee88450dSPetr Machata struct mlxsw_sp_qdisc_state {
93ee88450dSPetr Machata 	struct mlxsw_sp_qdisc root_qdisc;
947bec1a45SPetr Machata 
957bec1a45SPetr Machata 	/* When a PRIO or ETS are added, the invisible FIFOs in their bands are
967bec1a45SPetr Machata 	 * created first. When notifications for these FIFOs arrive, it is not
977bec1a45SPetr Machata 	 * known what qdisc their parent handle refers to. It could be a
987bec1a45SPetr Machata 	 * newly-created PRIO that will replace the currently-offloaded one, or
997bec1a45SPetr Machata 	 * it could be e.g. a RED that will be attached below it.
1007bec1a45SPetr Machata 	 *
1017bec1a45SPetr Machata 	 * As the notifications start to arrive, use them to note what the
1027bec1a45SPetr Machata 	 * future parent handle is, and keep track of which child FIFOs were
1037bec1a45SPetr Machata 	 * seen. Then when the parent is known, retroactively offload those
1047bec1a45SPetr Machata 	 * FIFOs.
1057bec1a45SPetr Machata 	 */
1067bec1a45SPetr Machata 	u32 future_handle;
1077bec1a45SPetr Machata 	bool future_fifos[IEEE_8021QAZ_MAX_TCS];
108cff99e20SPetr Machata 	struct mutex lock; /* Protects qdisc state. */
109ee88450dSPetr Machata };
110ee88450dSPetr Machata 
111cba7158fSNogah Frankel static bool
112290fe2c5SPetr Machata mlxsw_sp_qdisc_compare(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, u32 handle)
113cba7158fSNogah Frankel {
114290fe2c5SPetr Machata 	return mlxsw_sp_qdisc->ops && mlxsw_sp_qdisc->handle == handle;
115cba7158fSNogah Frankel }
116cba7158fSNogah Frankel 
117eed4baebSNogah Frankel static struct mlxsw_sp_qdisc *
11851d52ed9SPetr Machata mlxsw_sp_qdisc_walk(struct mlxsw_sp_qdisc *qdisc,
11951d52ed9SPetr Machata 		    struct mlxsw_sp_qdisc *(*pre)(struct mlxsw_sp_qdisc *,
12051d52ed9SPetr Machata 						  void *),
12151d52ed9SPetr Machata 		    void *data)
12251d52ed9SPetr Machata {
12351d52ed9SPetr Machata 	struct mlxsw_sp_qdisc *tmp;
12451d52ed9SPetr Machata 	unsigned int i;
12551d52ed9SPetr Machata 
12651d52ed9SPetr Machata 	if (pre) {
12751d52ed9SPetr Machata 		tmp = pre(qdisc, data);
12851d52ed9SPetr Machata 		if (tmp)
12951d52ed9SPetr Machata 			return tmp;
13051d52ed9SPetr Machata 	}
13151d52ed9SPetr Machata 
13251d52ed9SPetr Machata 	if (qdisc->ops) {
13351d52ed9SPetr Machata 		for (i = 0; i < qdisc->num_classes; i++) {
13451d52ed9SPetr Machata 			tmp = &qdisc->qdiscs[i];
13551d52ed9SPetr Machata 			if (qdisc->ops) {
13651d52ed9SPetr Machata 				tmp = mlxsw_sp_qdisc_walk(tmp, pre, data);
13751d52ed9SPetr Machata 				if (tmp)
13851d52ed9SPetr Machata 					return tmp;
13951d52ed9SPetr Machata 			}
14051d52ed9SPetr Machata 		}
14151d52ed9SPetr Machata 	}
14251d52ed9SPetr Machata 
14351d52ed9SPetr Machata 	return NULL;
14451d52ed9SPetr Machata }
14551d52ed9SPetr Machata 
14651d52ed9SPetr Machata static struct mlxsw_sp_qdisc *
14751d52ed9SPetr Machata mlxsw_sp_qdisc_walk_cb_find(struct mlxsw_sp_qdisc *qdisc, void *data)
14851d52ed9SPetr Machata {
14951d52ed9SPetr Machata 	u32 parent = *(u32 *)data;
15051d52ed9SPetr Machata 
15151d52ed9SPetr Machata 	if (qdisc->ops && TC_H_MAJ(qdisc->handle) == TC_H_MAJ(parent)) {
15251d52ed9SPetr Machata 		if (qdisc->ops->find_class)
15351d52ed9SPetr Machata 			return qdisc->ops->find_class(qdisc, parent);
15451d52ed9SPetr Machata 	}
15551d52ed9SPetr Machata 
15651d52ed9SPetr Machata 	return NULL;
15751d52ed9SPetr Machata }
15851d52ed9SPetr Machata 
15951d52ed9SPetr Machata static struct mlxsw_sp_qdisc *
160*c2792f38SPetr Machata mlxsw_sp_qdisc_find(struct mlxsw_sp_port *mlxsw_sp_port, u32 parent)
161eed4baebSNogah Frankel {
162ee88450dSPetr Machata 	struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc;
163eed4baebSNogah Frankel 
16451d52ed9SPetr Machata 	if (!qdisc_state)
16551d52ed9SPetr Machata 		return NULL;
166eed4baebSNogah Frankel 	if (parent == TC_H_ROOT)
167ee88450dSPetr Machata 		return &qdisc_state->root_qdisc;
16851d52ed9SPetr Machata 	return mlxsw_sp_qdisc_walk(&qdisc_state->root_qdisc,
16951d52ed9SPetr Machata 				   mlxsw_sp_qdisc_walk_cb_find, &parent);
17051d52ed9SPetr Machata }
171eed4baebSNogah Frankel 
17251d52ed9SPetr Machata static struct mlxsw_sp_qdisc *
17351d52ed9SPetr Machata mlxsw_sp_qdisc_walk_cb_find_by_handle(struct mlxsw_sp_qdisc *qdisc, void *data)
17451d52ed9SPetr Machata {
17551d52ed9SPetr Machata 	u32 handle = *(u32 *)data;
17651d52ed9SPetr Machata 
17751d52ed9SPetr Machata 	if (qdisc->ops && qdisc->handle == handle)
17851d52ed9SPetr Machata 		return qdisc;
17951d52ed9SPetr Machata 	return NULL;
180eed4baebSNogah Frankel }
181eed4baebSNogah Frankel 
18232dc5efcSNogah Frankel static struct mlxsw_sp_qdisc *
18332dc5efcSNogah Frankel mlxsw_sp_qdisc_find_by_handle(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle)
18432dc5efcSNogah Frankel {
185ee88450dSPetr Machata 	struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc;
18632dc5efcSNogah Frankel 
18751d52ed9SPetr Machata 	if (!qdisc_state)
18832dc5efcSNogah Frankel 		return NULL;
18951d52ed9SPetr Machata 	return mlxsw_sp_qdisc_walk(&qdisc_state->root_qdisc,
19051d52ed9SPetr Machata 				   mlxsw_sp_qdisc_walk_cb_find_by_handle,
19151d52ed9SPetr Machata 				   &handle);
19232dc5efcSNogah Frankel }
19332dc5efcSNogah Frankel 
194b21832b5SPetr Machata static void
195b21832b5SPetr Machata mlxsw_sp_qdisc_reduce_parent_backlog(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
196b21832b5SPetr Machata {
197b21832b5SPetr Machata 	struct mlxsw_sp_qdisc *tmp;
198b21832b5SPetr Machata 
199b21832b5SPetr Machata 	for (tmp = mlxsw_sp_qdisc->parent; tmp; tmp = tmp->parent)
200b21832b5SPetr Machata 		tmp->stats_base.backlog -= mlxsw_sp_qdisc->stats_base.backlog;
201b21832b5SPetr Machata }
202b21832b5SPetr Machata 
20376ff72a7SPetr Machata static u8 mlxsw_sp_qdisc_get_prio_bitmap(struct mlxsw_sp_port *mlxsw_sp_port,
20476ff72a7SPetr Machata 					 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
20576ff72a7SPetr Machata {
20676ff72a7SPetr Machata 	struct mlxsw_sp_qdisc *parent = mlxsw_sp_qdisc->parent;
20776ff72a7SPetr Machata 
20876ff72a7SPetr Machata 	if (!parent)
20976ff72a7SPetr Machata 		return 0xff;
21076ff72a7SPetr Machata 	return parent->ops->get_prio_bitmap(parent, mlxsw_sp_qdisc);
21176ff72a7SPetr Machata }
21276ff72a7SPetr Machata 
21376ff72a7SPetr Machata #define MLXSW_SP_PORT_DEFAULT_TCLASS 0
21476ff72a7SPetr Machata 
21576ff72a7SPetr Machata static int mlxsw_sp_qdisc_get_tclass_num(struct mlxsw_sp_port *mlxsw_sp_port,
21676ff72a7SPetr Machata 					 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
21776ff72a7SPetr Machata {
21876ff72a7SPetr Machata 	struct mlxsw_sp_qdisc *parent = mlxsw_sp_qdisc->parent;
21976ff72a7SPetr Machata 
22076ff72a7SPetr Machata 	if (!parent)
22176ff72a7SPetr Machata 		return MLXSW_SP_PORT_DEFAULT_TCLASS;
22276ff72a7SPetr Machata 	return parent->ops->get_tclass_num(parent, mlxsw_sp_qdisc);
22376ff72a7SPetr Machata }
22476ff72a7SPetr Machata 
22596f17e07SNogah Frankel static int
2269a37a59fSNogah Frankel mlxsw_sp_qdisc_destroy(struct mlxsw_sp_port *mlxsw_sp_port,
2279a37a59fSNogah Frankel 		       struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
2289a37a59fSNogah Frankel {
229509f04caSPetr Machata 	struct mlxsw_sp_qdisc *root_qdisc = &mlxsw_sp_port->qdisc->root_qdisc;
230509f04caSPetr Machata 	int err_hdroom = 0;
2319a37a59fSNogah Frankel 	int err = 0;
23265626e07SPetr Machata 	int i;
2339a37a59fSNogah Frankel 
2349a37a59fSNogah Frankel 	if (!mlxsw_sp_qdisc)
2359a37a59fSNogah Frankel 		return 0;
2369a37a59fSNogah Frankel 
237509f04caSPetr Machata 	if (root_qdisc == mlxsw_sp_qdisc) {
238509f04caSPetr Machata 		struct mlxsw_sp_hdroom hdroom = *mlxsw_sp_port->hdroom;
239509f04caSPetr Machata 
240509f04caSPetr Machata 		hdroom.mode = MLXSW_SP_HDROOM_MODE_DCB;
241509f04caSPetr Machata 		mlxsw_sp_hdroom_prios_reset_buf_idx(&hdroom);
242509f04caSPetr Machata 		mlxsw_sp_hdroom_bufs_reset_lossiness(&hdroom);
243509f04caSPetr Machata 		mlxsw_sp_hdroom_bufs_reset_sizes(mlxsw_sp_port, &hdroom);
244509f04caSPetr Machata 		err_hdroom = mlxsw_sp_hdroom_configure(mlxsw_sp_port, &hdroom);
245509f04caSPetr Machata 	}
246509f04caSPetr Machata 
247b21832b5SPetr Machata 	if (!mlxsw_sp_qdisc->ops)
248b21832b5SPetr Machata 		return 0;
249b21832b5SPetr Machata 
25065626e07SPetr Machata 	for (i = 0; i < mlxsw_sp_qdisc->num_classes; i++)
25165626e07SPetr Machata 		mlxsw_sp_qdisc_destroy(mlxsw_sp_port,
25265626e07SPetr Machata 				       &mlxsw_sp_qdisc->qdiscs[i]);
253b21832b5SPetr Machata 	mlxsw_sp_qdisc_reduce_parent_backlog(mlxsw_sp_qdisc);
254b21832b5SPetr Machata 	if (mlxsw_sp_qdisc->ops->destroy)
2559a37a59fSNogah Frankel 		err = mlxsw_sp_qdisc->ops->destroy(mlxsw_sp_port,
2569a37a59fSNogah Frankel 						   mlxsw_sp_qdisc);
2575cbd9602SPetr Machata 	if (mlxsw_sp_qdisc->ops->clean_stats)
2585cbd9602SPetr Machata 		mlxsw_sp_qdisc->ops->clean_stats(mlxsw_sp_port, mlxsw_sp_qdisc);
2599a37a59fSNogah Frankel 
2609a37a59fSNogah Frankel 	mlxsw_sp_qdisc->handle = TC_H_UNSPEC;
2619a37a59fSNogah Frankel 	mlxsw_sp_qdisc->ops = NULL;
2625cbd9602SPetr Machata 	mlxsw_sp_qdisc->num_classes = 0;
2635cbd9602SPetr Machata 	kfree(mlxsw_sp_qdisc->qdiscs);
2645cbd9602SPetr Machata 	mlxsw_sp_qdisc->qdiscs = NULL;
265509f04caSPetr Machata 	return err_hdroom ?: err;
2669a37a59fSNogah Frankel }
2679a37a59fSNogah Frankel 
268*c2792f38SPetr Machata struct mlxsw_sp_qdisc_tree_validate {
269*c2792f38SPetr Machata 	bool forbid_ets;
270*c2792f38SPetr Machata 	bool forbid_tbf;
271*c2792f38SPetr Machata 	bool forbid_red;
272*c2792f38SPetr Machata };
273*c2792f38SPetr Machata 
274*c2792f38SPetr Machata static int
275*c2792f38SPetr Machata __mlxsw_sp_qdisc_tree_validate(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
276*c2792f38SPetr Machata 			       struct mlxsw_sp_qdisc_tree_validate validate);
277*c2792f38SPetr Machata 
278*c2792f38SPetr Machata static int
279*c2792f38SPetr Machata mlxsw_sp_qdisc_tree_validate_children(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
280*c2792f38SPetr Machata 				      struct mlxsw_sp_qdisc_tree_validate validate)
281*c2792f38SPetr Machata {
282*c2792f38SPetr Machata 	unsigned int i;
283*c2792f38SPetr Machata 	int err;
284*c2792f38SPetr Machata 
285*c2792f38SPetr Machata 	for (i = 0; i < mlxsw_sp_qdisc->num_classes; i++) {
286*c2792f38SPetr Machata 		err = __mlxsw_sp_qdisc_tree_validate(&mlxsw_sp_qdisc->qdiscs[i],
287*c2792f38SPetr Machata 						     validate);
288*c2792f38SPetr Machata 		if (err)
289*c2792f38SPetr Machata 			return err;
290*c2792f38SPetr Machata 	}
291*c2792f38SPetr Machata 
292*c2792f38SPetr Machata 	return 0;
293*c2792f38SPetr Machata }
294*c2792f38SPetr Machata 
295*c2792f38SPetr Machata static int
296*c2792f38SPetr Machata __mlxsw_sp_qdisc_tree_validate(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
297*c2792f38SPetr Machata 			       struct mlxsw_sp_qdisc_tree_validate validate)
298*c2792f38SPetr Machata {
299*c2792f38SPetr Machata 	if (!mlxsw_sp_qdisc->ops)
300*c2792f38SPetr Machata 		return 0;
301*c2792f38SPetr Machata 
302*c2792f38SPetr Machata 	switch (mlxsw_sp_qdisc->ops->type) {
303*c2792f38SPetr Machata 	case MLXSW_SP_QDISC_FIFO:
304*c2792f38SPetr Machata 		break;
305*c2792f38SPetr Machata 	case MLXSW_SP_QDISC_RED:
306*c2792f38SPetr Machata 		if (validate.forbid_red)
307*c2792f38SPetr Machata 			return -EINVAL;
308*c2792f38SPetr Machata 		validate.forbid_red = true;
309*c2792f38SPetr Machata 		validate.forbid_ets = true;
310*c2792f38SPetr Machata 		break;
311*c2792f38SPetr Machata 	case MLXSW_SP_QDISC_TBF:
312*c2792f38SPetr Machata 		if (validate.forbid_tbf)
313*c2792f38SPetr Machata 			return -EINVAL;
314*c2792f38SPetr Machata 		validate.forbid_tbf = true;
315*c2792f38SPetr Machata 		validate.forbid_ets = true;
316*c2792f38SPetr Machata 		break;
317*c2792f38SPetr Machata 	case MLXSW_SP_QDISC_PRIO:
318*c2792f38SPetr Machata 	case MLXSW_SP_QDISC_ETS:
319*c2792f38SPetr Machata 		if (validate.forbid_ets)
320*c2792f38SPetr Machata 			return -EINVAL;
321*c2792f38SPetr Machata 		validate.forbid_ets = true;
322*c2792f38SPetr Machata 		break;
323*c2792f38SPetr Machata 	default:
324*c2792f38SPetr Machata 		WARN_ON(1);
325*c2792f38SPetr Machata 		return -EINVAL;
326*c2792f38SPetr Machata 	}
327*c2792f38SPetr Machata 
328*c2792f38SPetr Machata 	return mlxsw_sp_qdisc_tree_validate_children(mlxsw_sp_qdisc, validate);
329*c2792f38SPetr Machata }
330*c2792f38SPetr Machata 
331*c2792f38SPetr Machata static int mlxsw_sp_qdisc_tree_validate(struct mlxsw_sp_port *mlxsw_sp_port)
332*c2792f38SPetr Machata {
333*c2792f38SPetr Machata 	struct mlxsw_sp_qdisc_tree_validate validate = {};
334*c2792f38SPetr Machata 	struct mlxsw_sp_qdisc *mlxsw_sp_qdisc;
335*c2792f38SPetr Machata 
336*c2792f38SPetr Machata 	mlxsw_sp_qdisc = &mlxsw_sp_port->qdisc->root_qdisc;
337*c2792f38SPetr Machata 	return __mlxsw_sp_qdisc_tree_validate(mlxsw_sp_qdisc, validate);
338*c2792f38SPetr Machata }
339*c2792f38SPetr Machata 
3405cbd9602SPetr Machata static int mlxsw_sp_qdisc_create(struct mlxsw_sp_port *mlxsw_sp_port,
3415cbd9602SPetr Machata 				 u32 handle,
3429cf6c9c7SNogah Frankel 				 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
3439cf6c9c7SNogah Frankel 				 struct mlxsw_sp_qdisc_ops *ops, void *params)
3449cf6c9c7SNogah Frankel {
345509f04caSPetr Machata 	struct mlxsw_sp_qdisc *root_qdisc = &mlxsw_sp_port->qdisc->root_qdisc;
346509f04caSPetr Machata 	struct mlxsw_sp_hdroom orig_hdroom;
3475cbd9602SPetr Machata 	unsigned int i;
3489cf6c9c7SNogah Frankel 	int err;
3499cf6c9c7SNogah Frankel 
3505cbd9602SPetr Machata 	err = ops->check_params(mlxsw_sp_port, params);
3515cbd9602SPetr Machata 	if (err)
3525cbd9602SPetr Machata 		return err;
3535cbd9602SPetr Machata 
3545cbd9602SPetr Machata 	if (ops->num_classes) {
3555cbd9602SPetr Machata 		mlxsw_sp_qdisc->qdiscs = kcalloc(ops->num_classes,
3565cbd9602SPetr Machata 						 sizeof(*mlxsw_sp_qdisc->qdiscs),
3575cbd9602SPetr Machata 						 GFP_KERNEL);
3585cbd9602SPetr Machata 		if (!mlxsw_sp_qdisc->qdiscs)
3595cbd9602SPetr Machata 			return -ENOMEM;
3605cbd9602SPetr Machata 
3615cbd9602SPetr Machata 		for (i = 0; i < ops->num_classes; i++)
3625cbd9602SPetr Machata 			mlxsw_sp_qdisc->qdiscs[i].parent = mlxsw_sp_qdisc;
3635cbd9602SPetr Machata 	}
364509f04caSPetr Machata 
365509f04caSPetr Machata 	orig_hdroom = *mlxsw_sp_port->hdroom;
366509f04caSPetr Machata 	if (root_qdisc == mlxsw_sp_qdisc) {
367509f04caSPetr Machata 		struct mlxsw_sp_hdroom hdroom = orig_hdroom;
368509f04caSPetr Machata 
369509f04caSPetr Machata 		hdroom.mode = MLXSW_SP_HDROOM_MODE_TC;
370509f04caSPetr Machata 		mlxsw_sp_hdroom_prios_reset_buf_idx(&hdroom);
371509f04caSPetr Machata 		mlxsw_sp_hdroom_bufs_reset_lossiness(&hdroom);
372509f04caSPetr Machata 		mlxsw_sp_hdroom_bufs_reset_sizes(mlxsw_sp_port, &hdroom);
373509f04caSPetr Machata 
374509f04caSPetr Machata 		err = mlxsw_sp_hdroom_configure(mlxsw_sp_port, &hdroom);
375509f04caSPetr Machata 		if (err)
376509f04caSPetr Machata 			goto err_hdroom_configure;
377509f04caSPetr Machata 	}
378509f04caSPetr Machata 
3795cbd9602SPetr Machata 	mlxsw_sp_qdisc->num_classes = ops->num_classes;
3805cbd9602SPetr Machata 	mlxsw_sp_qdisc->ops = ops;
3815cbd9602SPetr Machata 	mlxsw_sp_qdisc->handle = handle;
382*c2792f38SPetr Machata 	err = mlxsw_sp_qdisc_tree_validate(mlxsw_sp_port);
383*c2792f38SPetr Machata 	if (err)
384*c2792f38SPetr Machata 		goto err_replace;
385*c2792f38SPetr Machata 
3865cbd9602SPetr Machata 	err = ops->replace(mlxsw_sp_port, handle, mlxsw_sp_qdisc, params);
3875cbd9602SPetr Machata 	if (err)
3885cbd9602SPetr Machata 		goto err_replace;
3895cbd9602SPetr Machata 
3905cbd9602SPetr Machata 	return 0;
3915cbd9602SPetr Machata 
3925cbd9602SPetr Machata err_replace:
3935cbd9602SPetr Machata 	mlxsw_sp_qdisc->handle = TC_H_UNSPEC;
3945cbd9602SPetr Machata 	mlxsw_sp_qdisc->ops = NULL;
3955cbd9602SPetr Machata 	mlxsw_sp_qdisc->num_classes = 0;
3965cbd9602SPetr Machata 	mlxsw_sp_hdroom_configure(mlxsw_sp_port, &orig_hdroom);
3975cbd9602SPetr Machata err_hdroom_configure:
3985cbd9602SPetr Machata 	kfree(mlxsw_sp_qdisc->qdiscs);
3995cbd9602SPetr Machata 	mlxsw_sp_qdisc->qdiscs = NULL;
4005cbd9602SPetr Machata 	return err;
4015cbd9602SPetr Machata }
4025cbd9602SPetr Machata 
4035cbd9602SPetr Machata static int
4045cbd9602SPetr Machata mlxsw_sp_qdisc_change(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
4055cbd9602SPetr Machata 		      struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, void *params)
4065cbd9602SPetr Machata {
4075cbd9602SPetr Machata 	struct mlxsw_sp_qdisc_ops *ops = mlxsw_sp_qdisc->ops;
4085cbd9602SPetr Machata 	int err;
4095cbd9602SPetr Machata 
41017c0e6d1SPetr Machata 	err = ops->check_params(mlxsw_sp_port, params);
4119cf6c9c7SNogah Frankel 	if (err)
4125cbd9602SPetr Machata 		goto unoffload;
4139cf6c9c7SNogah Frankel 
414c4e372e2SPetr Machata 	err = ops->replace(mlxsw_sp_port, handle, mlxsw_sp_qdisc, params);
4159cf6c9c7SNogah Frankel 	if (err)
4165cbd9602SPetr Machata 		goto unoffload;
4179cf6c9c7SNogah Frankel 
4187bec1a45SPetr Machata 	/* Check if the Qdisc changed. That includes a situation where an
4197bec1a45SPetr Machata 	 * invisible Qdisc replaces another one, or is being added for the
4207bec1a45SPetr Machata 	 * first time.
4217bec1a45SPetr Machata 	 */
4225cbd9602SPetr Machata 	if (mlxsw_sp_qdisc->handle != handle) {
4239cf6c9c7SNogah Frankel 		if (ops->clean_stats)
4249cf6c9c7SNogah Frankel 			ops->clean_stats(mlxsw_sp_port, mlxsw_sp_qdisc);
4259cf6c9c7SNogah Frankel 	}
4269cf6c9c7SNogah Frankel 
4279cf6c9c7SNogah Frankel 	mlxsw_sp_qdisc->handle = handle;
4289cf6c9c7SNogah Frankel 	return 0;
4299cf6c9c7SNogah Frankel 
4305cbd9602SPetr Machata unoffload:
4315cbd9602SPetr Machata 	if (ops->unoffload)
43293d8a4c1SNogah Frankel 		ops->unoffload(mlxsw_sp_port, mlxsw_sp_qdisc, params);
43393d8a4c1SNogah Frankel 
4349cf6c9c7SNogah Frankel 	mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
4359cf6c9c7SNogah Frankel 	return err;
4369cf6c9c7SNogah Frankel }
4379cf6c9c7SNogah Frankel 
4389cf6c9c7SNogah Frankel static int
4395cbd9602SPetr Machata mlxsw_sp_qdisc_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
4405cbd9602SPetr Machata 		       struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
4415cbd9602SPetr Machata 		       struct mlxsw_sp_qdisc_ops *ops, void *params)
4425cbd9602SPetr Machata {
4435cbd9602SPetr Machata 	if (mlxsw_sp_qdisc->ops && mlxsw_sp_qdisc->ops->type != ops->type)
4445cbd9602SPetr Machata 		/* In case this location contained a different qdisc of the
4455cbd9602SPetr Machata 		 * same type we can override the old qdisc configuration.
4465cbd9602SPetr Machata 		 * Otherwise, we need to remove the old qdisc before setting the
4475cbd9602SPetr Machata 		 * new one.
4485cbd9602SPetr Machata 		 */
4495cbd9602SPetr Machata 		mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
4505cbd9602SPetr Machata 
4515cbd9602SPetr Machata 	if (!mlxsw_sp_qdisc->ops)
4525cbd9602SPetr Machata 		return mlxsw_sp_qdisc_create(mlxsw_sp_port, handle,
4535cbd9602SPetr Machata 					     mlxsw_sp_qdisc, ops, params);
4545cbd9602SPetr Machata 	else
4555cbd9602SPetr Machata 		return mlxsw_sp_qdisc_change(mlxsw_sp_port, handle,
4565cbd9602SPetr Machata 					     mlxsw_sp_qdisc, params);
4575cbd9602SPetr Machata }
4585cbd9602SPetr Machata 
4595cbd9602SPetr Machata static int
460562ffbc4SNogah Frankel mlxsw_sp_qdisc_get_stats(struct mlxsw_sp_port *mlxsw_sp_port,
461562ffbc4SNogah Frankel 			 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
462562ffbc4SNogah Frankel 			 struct tc_qopt_offload_stats *stats_ptr)
463562ffbc4SNogah Frankel {
464562ffbc4SNogah Frankel 	if (mlxsw_sp_qdisc && mlxsw_sp_qdisc->ops &&
465562ffbc4SNogah Frankel 	    mlxsw_sp_qdisc->ops->get_stats)
466562ffbc4SNogah Frankel 		return mlxsw_sp_qdisc->ops->get_stats(mlxsw_sp_port,
467562ffbc4SNogah Frankel 						      mlxsw_sp_qdisc,
468562ffbc4SNogah Frankel 						      stats_ptr);
469562ffbc4SNogah Frankel 
470562ffbc4SNogah Frankel 	return -EOPNOTSUPP;
471562ffbc4SNogah Frankel }
472562ffbc4SNogah Frankel 
473562ffbc4SNogah Frankel static int
474562ffbc4SNogah Frankel mlxsw_sp_qdisc_get_xstats(struct mlxsw_sp_port *mlxsw_sp_port,
475562ffbc4SNogah Frankel 			  struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
476562ffbc4SNogah Frankel 			  void *xstats_ptr)
477562ffbc4SNogah Frankel {
478562ffbc4SNogah Frankel 	if (mlxsw_sp_qdisc && mlxsw_sp_qdisc->ops &&
479562ffbc4SNogah Frankel 	    mlxsw_sp_qdisc->ops->get_xstats)
480562ffbc4SNogah Frankel 		return mlxsw_sp_qdisc->ops->get_xstats(mlxsw_sp_port,
481562ffbc4SNogah Frankel 						      mlxsw_sp_qdisc,
482562ffbc4SNogah Frankel 						      xstats_ptr);
483562ffbc4SNogah Frankel 
484562ffbc4SNogah Frankel 	return -EOPNOTSUPP;
485562ffbc4SNogah Frankel }
486562ffbc4SNogah Frankel 
48785005b82SPetr Machata static u64
48885005b82SPetr Machata mlxsw_sp_xstats_backlog(struct mlxsw_sp_port_xstats *xstats, int tclass_num)
48985005b82SPetr Machata {
49085005b82SPetr Machata 	return xstats->backlog[tclass_num] +
49185005b82SPetr Machata 	       xstats->backlog[tclass_num + 8];
49285005b82SPetr Machata }
49385005b82SPetr Machata 
49485005b82SPetr Machata static u64
49585005b82SPetr Machata mlxsw_sp_xstats_tail_drop(struct mlxsw_sp_port_xstats *xstats, int tclass_num)
49685005b82SPetr Machata {
49785005b82SPetr Machata 	return xstats->tail_drop[tclass_num] +
49885005b82SPetr Machata 	       xstats->tail_drop[tclass_num + 8];
49985005b82SPetr Machata }
50085005b82SPetr Machata 
50104cc0bf5SNogah Frankel static void
50204cc0bf5SNogah Frankel mlxsw_sp_qdisc_bstats_per_priority_get(struct mlxsw_sp_port_xstats *xstats,
50304cc0bf5SNogah Frankel 				       u8 prio_bitmap, u64 *tx_packets,
50404cc0bf5SNogah Frankel 				       u64 *tx_bytes)
50504cc0bf5SNogah Frankel {
50604cc0bf5SNogah Frankel 	int i;
50704cc0bf5SNogah Frankel 
50804cc0bf5SNogah Frankel 	*tx_packets = 0;
50904cc0bf5SNogah Frankel 	*tx_bytes = 0;
51004cc0bf5SNogah Frankel 	for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
51104cc0bf5SNogah Frankel 		if (prio_bitmap & BIT(i)) {
51204cc0bf5SNogah Frankel 			*tx_packets += xstats->tx_packets[i];
51304cc0bf5SNogah Frankel 			*tx_bytes += xstats->tx_bytes[i];
51404cc0bf5SNogah Frankel 		}
51504cc0bf5SNogah Frankel 	}
51604cc0bf5SNogah Frankel }
51704cc0bf5SNogah Frankel 
518cf9af379SPetr Machata static void
519cf9af379SPetr Machata mlxsw_sp_qdisc_collect_tc_stats(struct mlxsw_sp_port *mlxsw_sp_port,
520cf9af379SPetr Machata 				struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
521cf9af379SPetr Machata 				u64 *p_tx_bytes, u64 *p_tx_packets,
522cf9af379SPetr Machata 				u64 *p_drops, u64 *p_backlog)
523cf9af379SPetr Machata {
524cf9af379SPetr Machata 	struct mlxsw_sp_port_xstats *xstats;
525cf9af379SPetr Machata 	u64 tx_bytes, tx_packets;
52676ff72a7SPetr Machata 	u8 prio_bitmap;
52776ff72a7SPetr Machata 	int tclass_num;
528cf9af379SPetr Machata 
52976ff72a7SPetr Machata 	prio_bitmap = mlxsw_sp_qdisc_get_prio_bitmap(mlxsw_sp_port,
53076ff72a7SPetr Machata 						     mlxsw_sp_qdisc);
53176ff72a7SPetr Machata 	tclass_num = mlxsw_sp_qdisc_get_tclass_num(mlxsw_sp_port,
53276ff72a7SPetr Machata 						   mlxsw_sp_qdisc);
533cf9af379SPetr Machata 	xstats = &mlxsw_sp_port->periodic_hw_stats.xstats;
53476ff72a7SPetr Machata 	mlxsw_sp_qdisc_bstats_per_priority_get(xstats, prio_bitmap,
535cf9af379SPetr Machata 					       &tx_packets, &tx_bytes);
536cf9af379SPetr Machata 
537cf9af379SPetr Machata 	*p_tx_packets += tx_packets;
538cf9af379SPetr Machata 	*p_tx_bytes += tx_bytes;
539cf9af379SPetr Machata 	*p_drops += xstats->wred_drop[tclass_num] +
540cf9af379SPetr Machata 		    mlxsw_sp_xstats_tail_drop(xstats, tclass_num);
541cf9af379SPetr Machata 	*p_backlog += mlxsw_sp_xstats_backlog(xstats, tclass_num);
542cf9af379SPetr Machata }
543cf9af379SPetr Machata 
544cf9af379SPetr Machata static void
545cf9af379SPetr Machata mlxsw_sp_qdisc_update_stats(struct mlxsw_sp *mlxsw_sp,
546cf9af379SPetr Machata 			    struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
547cf9af379SPetr Machata 			    u64 tx_bytes, u64 tx_packets,
548cf9af379SPetr Machata 			    u64 drops, u64 backlog,
549cf9af379SPetr Machata 			    struct tc_qopt_offload_stats *stats_ptr)
550cf9af379SPetr Machata {
551cf9af379SPetr Machata 	struct mlxsw_sp_qdisc_stats *stats_base = &mlxsw_sp_qdisc->stats_base;
552cf9af379SPetr Machata 
553cf9af379SPetr Machata 	tx_bytes -= stats_base->tx_bytes;
554cf9af379SPetr Machata 	tx_packets -= stats_base->tx_packets;
555cf9af379SPetr Machata 	drops -= stats_base->drops;
556cf9af379SPetr Machata 	backlog -= stats_base->backlog;
557cf9af379SPetr Machata 
558cf9af379SPetr Machata 	_bstats_update(stats_ptr->bstats, tx_bytes, tx_packets);
559cf9af379SPetr Machata 	stats_ptr->qstats->drops += drops;
560cf9af379SPetr Machata 	stats_ptr->qstats->backlog += mlxsw_sp_cells_bytes(mlxsw_sp, backlog);
561cf9af379SPetr Machata 
562cf9af379SPetr Machata 	stats_base->backlog += backlog;
563cf9af379SPetr Machata 	stats_base->drops += drops;
564cf9af379SPetr Machata 	stats_base->tx_bytes += tx_bytes;
565cf9af379SPetr Machata 	stats_base->tx_packets += tx_packets;
566cf9af379SPetr Machata }
567cf9af379SPetr Machata 
5683d0d5921SPetr Machata static void
5693d0d5921SPetr Machata mlxsw_sp_qdisc_get_tc_stats(struct mlxsw_sp_port *mlxsw_sp_port,
5703d0d5921SPetr Machata 			    struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
5713d0d5921SPetr Machata 			    struct tc_qopt_offload_stats *stats_ptr)
5723d0d5921SPetr Machata {
5733d0d5921SPetr Machata 	u64 tx_packets = 0;
5743d0d5921SPetr Machata 	u64 tx_bytes = 0;
5753d0d5921SPetr Machata 	u64 backlog = 0;
5763d0d5921SPetr Machata 	u64 drops = 0;
5773d0d5921SPetr Machata 
5783d0d5921SPetr Machata 	mlxsw_sp_qdisc_collect_tc_stats(mlxsw_sp_port, mlxsw_sp_qdisc,
5793d0d5921SPetr Machata 					&tx_bytes, &tx_packets,
5803d0d5921SPetr Machata 					&drops, &backlog);
5813d0d5921SPetr Machata 	mlxsw_sp_qdisc_update_stats(mlxsw_sp_port->mlxsw_sp, mlxsw_sp_qdisc,
5823d0d5921SPetr Machata 				    tx_bytes, tx_packets, drops, backlog,
5833d0d5921SPetr Machata 				    stats_ptr);
5843d0d5921SPetr Machata }
5853d0d5921SPetr Machata 
586562ffbc4SNogah Frankel static int
58796f17e07SNogah Frankel mlxsw_sp_tclass_congestion_enable(struct mlxsw_sp_port *mlxsw_sp_port,
58896f17e07SNogah Frankel 				  int tclass_num, u32 min, u32 max,
5898040c96bSPetr Machata 				  u32 probability, bool is_wred, bool is_ecn)
59096f17e07SNogah Frankel {
591db84924cSJiri Pirko 	char cwtpm_cmd[MLXSW_REG_CWTPM_LEN];
592db84924cSJiri Pirko 	char cwtp_cmd[MLXSW_REG_CWTP_LEN];
59396f17e07SNogah Frankel 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
59496f17e07SNogah Frankel 	int err;
59596f17e07SNogah Frankel 
59696f17e07SNogah Frankel 	mlxsw_reg_cwtp_pack(cwtp_cmd, mlxsw_sp_port->local_port, tclass_num);
59796f17e07SNogah Frankel 	mlxsw_reg_cwtp_profile_pack(cwtp_cmd, MLXSW_REG_CWTP_DEFAULT_PROFILE,
59896f17e07SNogah Frankel 				    roundup(min, MLXSW_REG_CWTP_MIN_VALUE),
59996f17e07SNogah Frankel 				    roundup(max, MLXSW_REG_CWTP_MIN_VALUE),
60096f17e07SNogah Frankel 				    probability);
60196f17e07SNogah Frankel 
60296f17e07SNogah Frankel 	err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(cwtp), cwtp_cmd);
60396f17e07SNogah Frankel 	if (err)
60496f17e07SNogah Frankel 		return err;
60596f17e07SNogah Frankel 
606db84924cSJiri Pirko 	mlxsw_reg_cwtpm_pack(cwtpm_cmd, mlxsw_sp_port->local_port, tclass_num,
6078040c96bSPetr Machata 			     MLXSW_REG_CWTP_DEFAULT_PROFILE, is_wred, is_ecn);
60896f17e07SNogah Frankel 
609db84924cSJiri Pirko 	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(cwtpm), cwtpm_cmd);
61096f17e07SNogah Frankel }
61196f17e07SNogah Frankel 
61296f17e07SNogah Frankel static int
61396f17e07SNogah Frankel mlxsw_sp_tclass_congestion_disable(struct mlxsw_sp_port *mlxsw_sp_port,
61496f17e07SNogah Frankel 				   int tclass_num)
61596f17e07SNogah Frankel {
61696f17e07SNogah Frankel 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
61796f17e07SNogah Frankel 	char cwtpm_cmd[MLXSW_REG_CWTPM_LEN];
61896f17e07SNogah Frankel 
61996f17e07SNogah Frankel 	mlxsw_reg_cwtpm_pack(cwtpm_cmd, mlxsw_sp_port->local_port, tclass_num,
62096f17e07SNogah Frankel 			     MLXSW_REG_CWTPM_RESET_PROFILE, false, false);
62196f17e07SNogah Frankel 	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(cwtpm), cwtpm_cmd);
62296f17e07SNogah Frankel }
62396f17e07SNogah Frankel 
624861fb829SNogah Frankel static void
625c2ed6db7SNogah Frankel mlxsw_sp_setup_tc_qdisc_red_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port,
626d56c8955SNogah Frankel 					struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
627861fb829SNogah Frankel {
6284d1a4b84SNogah Frankel 	struct mlxsw_sp_qdisc_stats *stats_base;
629861fb829SNogah Frankel 	struct mlxsw_sp_port_xstats *xstats;
6304d1a4b84SNogah Frankel 	struct red_stats *red_base;
63176ff72a7SPetr Machata 	u8 prio_bitmap;
63276ff72a7SPetr Machata 	int tclass_num;
633861fb829SNogah Frankel 
63476ff72a7SPetr Machata 	prio_bitmap = mlxsw_sp_qdisc_get_prio_bitmap(mlxsw_sp_port,
63576ff72a7SPetr Machata 						     mlxsw_sp_qdisc);
63676ff72a7SPetr Machata 	tclass_num = mlxsw_sp_qdisc_get_tclass_num(mlxsw_sp_port,
63776ff72a7SPetr Machata 						   mlxsw_sp_qdisc);
638861fb829SNogah Frankel 	xstats = &mlxsw_sp_port->periodic_hw_stats.xstats;
6394d1a4b84SNogah Frankel 	stats_base = &mlxsw_sp_qdisc->stats_base;
640c2ed6db7SNogah Frankel 	red_base = &mlxsw_sp_qdisc->xstats_base.red;
6413670756fSNogah Frankel 
64276ff72a7SPetr Machata 	mlxsw_sp_qdisc_bstats_per_priority_get(xstats, prio_bitmap,
64304cc0bf5SNogah Frankel 					       &stats_base->tx_packets,
64404cc0bf5SNogah Frankel 					       &stats_base->tx_bytes);
64515be36b8SPetr Machata 	red_base->prob_mark = xstats->tc_ecn[tclass_num];
6464d1a4b84SNogah Frankel 	red_base->prob_drop = xstats->wred_drop[tclass_num];
64785005b82SPetr Machata 	red_base->pdrop = mlxsw_sp_xstats_tail_drop(xstats, tclass_num);
6483670756fSNogah Frankel 
649c2ed6db7SNogah Frankel 	stats_base->overlimits = red_base->prob_drop + red_base->prob_mark;
6504d1a4b84SNogah Frankel 	stats_base->drops = red_base->prob_drop + red_base->pdrop;
651416ef9b1SJakub Kicinski 
652416ef9b1SJakub Kicinski 	stats_base->backlog = 0;
653861fb829SNogah Frankel }
654861fb829SNogah Frankel 
65596f17e07SNogah Frankel static int
656cba7158fSNogah Frankel mlxsw_sp_qdisc_red_destroy(struct mlxsw_sp_port *mlxsw_sp_port,
657d56c8955SNogah Frankel 			   struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
65896f17e07SNogah Frankel {
65976ff72a7SPetr Machata 	int tclass_num = mlxsw_sp_qdisc_get_tclass_num(mlxsw_sp_port,
66076ff72a7SPetr Machata 						       mlxsw_sp_qdisc);
66176ff72a7SPetr Machata 
66276ff72a7SPetr Machata 	return mlxsw_sp_tclass_congestion_disable(mlxsw_sp_port, tclass_num);
66396f17e07SNogah Frankel }
66496f17e07SNogah Frankel 
66596f17e07SNogah Frankel static int
6669cf6c9c7SNogah Frankel mlxsw_sp_qdisc_red_check_params(struct mlxsw_sp_port *mlxsw_sp_port,
6679cf6c9c7SNogah Frankel 				void *params)
66896f17e07SNogah Frankel {
66996f17e07SNogah Frankel 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
6709cf6c9c7SNogah Frankel 	struct tc_red_qopt_offload_params *p = params;
67196f17e07SNogah Frankel 
67296f17e07SNogah Frankel 	if (p->min > p->max) {
67396f17e07SNogah Frankel 		dev_err(mlxsw_sp->bus_info->dev,
67496f17e07SNogah Frankel 			"spectrum: RED: min %u is bigger then max %u\n", p->min,
67596f17e07SNogah Frankel 			p->max);
6769cf6c9c7SNogah Frankel 		return -EINVAL;
67796f17e07SNogah Frankel 	}
678914c4fc1SPetr Machata 	if (p->max > MLXSW_CORE_RES_GET(mlxsw_sp->core,
679914c4fc1SPetr Machata 					GUARANTEED_SHARED_BUFFER)) {
68096f17e07SNogah Frankel 		dev_err(mlxsw_sp->bus_info->dev,
68196f17e07SNogah Frankel 			"spectrum: RED: max value %u is too big\n", p->max);
6829cf6c9c7SNogah Frankel 		return -EINVAL;
68396f17e07SNogah Frankel 	}
68496f17e07SNogah Frankel 	if (p->min == 0 || p->max == 0) {
68596f17e07SNogah Frankel 		dev_err(mlxsw_sp->bus_info->dev,
68696f17e07SNogah Frankel 			"spectrum: RED: 0 value is illegal for min and max\n");
6879cf6c9c7SNogah Frankel 		return -EINVAL;
68896f17e07SNogah Frankel 	}
6899cf6c9c7SNogah Frankel 	return 0;
6909cf6c9c7SNogah Frankel }
6919cf6c9c7SNogah Frankel 
6929cf6c9c7SNogah Frankel static int
693c4e372e2SPetr Machata mlxsw_sp_qdisc_red_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
6949cf6c9c7SNogah Frankel 			   struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
6959cf6c9c7SNogah Frankel 			   void *params)
6969cf6c9c7SNogah Frankel {
6979cf6c9c7SNogah Frankel 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
6989cf6c9c7SNogah Frankel 	struct tc_red_qopt_offload_params *p = params;
69976ff72a7SPetr Machata 	int tclass_num;
7009cf6c9c7SNogah Frankel 	u32 min, max;
7019cf6c9c7SNogah Frankel 	u64 prob;
70296f17e07SNogah Frankel 
70376ff72a7SPetr Machata 	tclass_num = mlxsw_sp_qdisc_get_tclass_num(mlxsw_sp_port,
70476ff72a7SPetr Machata 						   mlxsw_sp_qdisc);
70576ff72a7SPetr Machata 
70696f17e07SNogah Frankel 	/* calculate probability in percentage */
70796f17e07SNogah Frankel 	prob = p->probability;
70896f17e07SNogah Frankel 	prob *= 100;
70996f17e07SNogah Frankel 	prob = DIV_ROUND_UP(prob, 1 << 16);
71096f17e07SNogah Frankel 	prob = DIV_ROUND_UP(prob, 1 << 16);
71196f17e07SNogah Frankel 	min = mlxsw_sp_bytes_cells(mlxsw_sp, p->min);
71296f17e07SNogah Frankel 	max = mlxsw_sp_bytes_cells(mlxsw_sp, p->max);
7138040c96bSPetr Machata 	return mlxsw_sp_tclass_congestion_enable(mlxsw_sp_port, tclass_num,
7148040c96bSPetr Machata 						 min, max, prob,
7158040c96bSPetr Machata 						 !p->is_nodrop, p->is_ecn);
71696f17e07SNogah Frankel }
71796f17e07SNogah Frankel 
718416ef9b1SJakub Kicinski static void
719be1d5a8aSPetr Machata mlxsw_sp_qdisc_leaf_unoffload(struct mlxsw_sp_port *mlxsw_sp_port,
720be1d5a8aSPetr Machata 			      struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
721be1d5a8aSPetr Machata 			      struct gnet_stats_queue *qstats)
722be1d5a8aSPetr Machata {
723be1d5a8aSPetr Machata 	u64 backlog;
724be1d5a8aSPetr Machata 
725be1d5a8aSPetr Machata 	backlog = mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp,
726be1d5a8aSPetr Machata 				       mlxsw_sp_qdisc->stats_base.backlog);
727be1d5a8aSPetr Machata 	qstats->backlog -= backlog;
728be1d5a8aSPetr Machata 	mlxsw_sp_qdisc->stats_base.backlog = 0;
729be1d5a8aSPetr Machata }
730be1d5a8aSPetr Machata 
731be1d5a8aSPetr Machata static void
732416ef9b1SJakub Kicinski mlxsw_sp_qdisc_red_unoffload(struct mlxsw_sp_port *mlxsw_sp_port,
733416ef9b1SJakub Kicinski 			     struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
734416ef9b1SJakub Kicinski 			     void *params)
735416ef9b1SJakub Kicinski {
736416ef9b1SJakub Kicinski 	struct tc_red_qopt_offload_params *p = params;
737416ef9b1SJakub Kicinski 
738be1d5a8aSPetr Machata 	mlxsw_sp_qdisc_leaf_unoffload(mlxsw_sp_port, mlxsw_sp_qdisc, p->qstats);
739416ef9b1SJakub Kicinski }
740416ef9b1SJakub Kicinski 
741861fb829SNogah Frankel static int
742cba7158fSNogah Frankel mlxsw_sp_qdisc_get_red_xstats(struct mlxsw_sp_port *mlxsw_sp_port,
743861fb829SNogah Frankel 			      struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
744562ffbc4SNogah Frankel 			      void *xstats_ptr)
745861fb829SNogah Frankel {
7464d1a4b84SNogah Frankel 	struct red_stats *xstats_base = &mlxsw_sp_qdisc->xstats_base.red;
747861fb829SNogah Frankel 	struct mlxsw_sp_port_xstats *xstats;
748562ffbc4SNogah Frankel 	struct red_stats *res = xstats_ptr;
74915be36b8SPetr Machata 	int early_drops, marks, pdrops;
75076ff72a7SPetr Machata 	int tclass_num;
751861fb829SNogah Frankel 
75276ff72a7SPetr Machata 	tclass_num = mlxsw_sp_qdisc_get_tclass_num(mlxsw_sp_port,
75376ff72a7SPetr Machata 						   mlxsw_sp_qdisc);
754861fb829SNogah Frankel 	xstats = &mlxsw_sp_port->periodic_hw_stats.xstats;
755861fb829SNogah Frankel 
756f8253df5SNogah Frankel 	early_drops = xstats->wred_drop[tclass_num] - xstats_base->prob_drop;
75715be36b8SPetr Machata 	marks = xstats->tc_ecn[tclass_num] - xstats_base->prob_mark;
75885005b82SPetr Machata 	pdrops = mlxsw_sp_xstats_tail_drop(xstats, tclass_num) -
75985005b82SPetr Machata 		 xstats_base->pdrop;
760f8253df5SNogah Frankel 
761f8253df5SNogah Frankel 	res->pdrop += pdrops;
762f8253df5SNogah Frankel 	res->prob_drop += early_drops;
76315be36b8SPetr Machata 	res->prob_mark += marks;
764f8253df5SNogah Frankel 
765f8253df5SNogah Frankel 	xstats_base->pdrop += pdrops;
766f8253df5SNogah Frankel 	xstats_base->prob_drop += early_drops;
76715be36b8SPetr Machata 	xstats_base->prob_mark += marks;
768861fb829SNogah Frankel 	return 0;
769861fb829SNogah Frankel }
770861fb829SNogah Frankel 
7713670756fSNogah Frankel static int
772cba7158fSNogah Frankel mlxsw_sp_qdisc_get_red_stats(struct mlxsw_sp_port *mlxsw_sp_port,
7733670756fSNogah Frankel 			     struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
774562ffbc4SNogah Frankel 			     struct tc_qopt_offload_stats *stats_ptr)
7753670756fSNogah Frankel {
7764d1a4b84SNogah Frankel 	struct mlxsw_sp_qdisc_stats *stats_base;
7773670756fSNogah Frankel 	struct mlxsw_sp_port_xstats *xstats;
778cf9af379SPetr Machata 	u64 overlimits;
77976ff72a7SPetr Machata 	int tclass_num;
7803670756fSNogah Frankel 
78176ff72a7SPetr Machata 	tclass_num = mlxsw_sp_qdisc_get_tclass_num(mlxsw_sp_port,
78276ff72a7SPetr Machata 						   mlxsw_sp_qdisc);
7833670756fSNogah Frankel 	xstats = &mlxsw_sp_port->periodic_hw_stats.xstats;
7844d1a4b84SNogah Frankel 	stats_base = &mlxsw_sp_qdisc->stats_base;
7853670756fSNogah Frankel 
7863d0d5921SPetr Machata 	mlxsw_sp_qdisc_get_tc_stats(mlxsw_sp_port, mlxsw_sp_qdisc, stats_ptr);
78715be36b8SPetr Machata 	overlimits = xstats->wred_drop[tclass_num] +
78815be36b8SPetr Machata 		     xstats->tc_ecn[tclass_num] - stats_base->overlimits;
7893670756fSNogah Frankel 
790562ffbc4SNogah Frankel 	stats_ptr->qstats->overlimits += overlimits;
7914d1a4b84SNogah Frankel 	stats_base->overlimits += overlimits;
792cf9af379SPetr Machata 
7933670756fSNogah Frankel 	return 0;
7943670756fSNogah Frankel }
7953670756fSNogah Frankel 
79651d52ed9SPetr Machata static struct mlxsw_sp_qdisc *
79751d52ed9SPetr Machata mlxsw_sp_qdisc_leaf_find_class(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
79851d52ed9SPetr Machata 			       u32 parent)
79951d52ed9SPetr Machata {
80051d52ed9SPetr Machata 	return NULL;
80151d52ed9SPetr Machata }
80251d52ed9SPetr Machata 
803562ffbc4SNogah Frankel static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_red = {
8049cf6c9c7SNogah Frankel 	.type = MLXSW_SP_QDISC_RED,
8059cf6c9c7SNogah Frankel 	.check_params = mlxsw_sp_qdisc_red_check_params,
8069cf6c9c7SNogah Frankel 	.replace = mlxsw_sp_qdisc_red_replace,
807416ef9b1SJakub Kicinski 	.unoffload = mlxsw_sp_qdisc_red_unoffload,
8089a37a59fSNogah Frankel 	.destroy = mlxsw_sp_qdisc_red_destroy,
809562ffbc4SNogah Frankel 	.get_stats = mlxsw_sp_qdisc_get_red_stats,
810562ffbc4SNogah Frankel 	.get_xstats = mlxsw_sp_qdisc_get_red_xstats,
8119cf6c9c7SNogah Frankel 	.clean_stats = mlxsw_sp_setup_tc_qdisc_red_clean_stats,
81251d52ed9SPetr Machata 	.find_class = mlxsw_sp_qdisc_leaf_find_class,
813562ffbc4SNogah Frankel };
814562ffbc4SNogah Frankel 
815be7e2a5aSPetr Machata static int mlxsw_sp_qdisc_graft(struct mlxsw_sp_port *mlxsw_sp_port,
816be7e2a5aSPetr Machata 				struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
817be7e2a5aSPetr Machata 				u8 band, u32 child_handle);
818be7e2a5aSPetr Machata 
819cff99e20SPetr Machata static int __mlxsw_sp_setup_tc_red(struct mlxsw_sp_port *mlxsw_sp_port,
82096f17e07SNogah Frankel 				   struct tc_red_qopt_offload *p)
82196f17e07SNogah Frankel {
82296f17e07SNogah Frankel 	struct mlxsw_sp_qdisc *mlxsw_sp_qdisc;
82396f17e07SNogah Frankel 
824*c2792f38SPetr Machata 	mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent);
825eed4baebSNogah Frankel 	if (!mlxsw_sp_qdisc)
82696f17e07SNogah Frankel 		return -EOPNOTSUPP;
82796f17e07SNogah Frankel 
828cba7158fSNogah Frankel 	if (p->command == TC_RED_REPLACE)
8299cf6c9c7SNogah Frankel 		return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle,
830562ffbc4SNogah Frankel 					      mlxsw_sp_qdisc,
831562ffbc4SNogah Frankel 					      &mlxsw_sp_qdisc_ops_red,
832562ffbc4SNogah Frankel 					      &p->set);
833cba7158fSNogah Frankel 
834290fe2c5SPetr Machata 	if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle))
835cba7158fSNogah Frankel 		return -EOPNOTSUPP;
836cba7158fSNogah Frankel 
837cba7158fSNogah Frankel 	switch (p->command) {
83896f17e07SNogah Frankel 	case TC_RED_DESTROY:
8399a37a59fSNogah Frankel 		return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
840861fb829SNogah Frankel 	case TC_RED_XSTATS:
841562ffbc4SNogah Frankel 		return mlxsw_sp_qdisc_get_xstats(mlxsw_sp_port, mlxsw_sp_qdisc,
842861fb829SNogah Frankel 						 p->xstats);
8433670756fSNogah Frankel 	case TC_RED_STATS:
844562ffbc4SNogah Frankel 		return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc,
8453670756fSNogah Frankel 						&p->stats);
846be7e2a5aSPetr Machata 	case TC_RED_GRAFT:
847be7e2a5aSPetr Machata 		return mlxsw_sp_qdisc_graft(mlxsw_sp_port, mlxsw_sp_qdisc, 0,
848be7e2a5aSPetr Machata 					    p->child_handle);
84996f17e07SNogah Frankel 	default:
85096f17e07SNogah Frankel 		return -EOPNOTSUPP;
85196f17e07SNogah Frankel 	}
85296f17e07SNogah Frankel }
853371b437aSNogah Frankel 
854cff99e20SPetr Machata int mlxsw_sp_setup_tc_red(struct mlxsw_sp_port *mlxsw_sp_port,
855cff99e20SPetr Machata 			  struct tc_red_qopt_offload *p)
856cff99e20SPetr Machata {
857cff99e20SPetr Machata 	int err;
858cff99e20SPetr Machata 
859cff99e20SPetr Machata 	mutex_lock(&mlxsw_sp_port->qdisc->lock);
860cff99e20SPetr Machata 	err = __mlxsw_sp_setup_tc_red(mlxsw_sp_port, p);
861cff99e20SPetr Machata 	mutex_unlock(&mlxsw_sp_port->qdisc->lock);
862cff99e20SPetr Machata 
863cff99e20SPetr Machata 	return err;
864cff99e20SPetr Machata }
865cff99e20SPetr Machata 
866a44f58c4SPetr Machata static void
867a44f58c4SPetr Machata mlxsw_sp_setup_tc_qdisc_leaf_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port,
868a44f58c4SPetr Machata 					 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
869a44f58c4SPetr Machata {
870a44f58c4SPetr Machata 	u64 backlog_cells = 0;
871a44f58c4SPetr Machata 	u64 tx_packets = 0;
872a44f58c4SPetr Machata 	u64 tx_bytes = 0;
873a44f58c4SPetr Machata 	u64 drops = 0;
874a44f58c4SPetr Machata 
875a44f58c4SPetr Machata 	mlxsw_sp_qdisc_collect_tc_stats(mlxsw_sp_port, mlxsw_sp_qdisc,
876a44f58c4SPetr Machata 					&tx_bytes, &tx_packets,
877a44f58c4SPetr Machata 					&drops, &backlog_cells);
878a44f58c4SPetr Machata 
879a44f58c4SPetr Machata 	mlxsw_sp_qdisc->stats_base.tx_packets = tx_packets;
880a44f58c4SPetr Machata 	mlxsw_sp_qdisc->stats_base.tx_bytes = tx_bytes;
881a44f58c4SPetr Machata 	mlxsw_sp_qdisc->stats_base.drops = drops;
882a44f58c4SPetr Machata 	mlxsw_sp_qdisc->stats_base.backlog = 0;
883a44f58c4SPetr Machata }
884a44f58c4SPetr Machata 
885a44f58c4SPetr Machata static int
886a44f58c4SPetr Machata mlxsw_sp_qdisc_tbf_destroy(struct mlxsw_sp_port *mlxsw_sp_port,
887a44f58c4SPetr Machata 			   struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
888a44f58c4SPetr Machata {
88976ff72a7SPetr Machata 	int tclass_num = mlxsw_sp_qdisc_get_tclass_num(mlxsw_sp_port,
89076ff72a7SPetr Machata 						       mlxsw_sp_qdisc);
89176ff72a7SPetr Machata 
892a44f58c4SPetr Machata 	return mlxsw_sp_port_ets_maxrate_set(mlxsw_sp_port,
893a44f58c4SPetr Machata 					     MLXSW_REG_QEEC_HR_SUBGROUP,
89476ff72a7SPetr Machata 					     tclass_num, 0,
895a44f58c4SPetr Machata 					     MLXSW_REG_QEEC_MAS_DIS, 0);
896a44f58c4SPetr Machata }
897a44f58c4SPetr Machata 
898a44f58c4SPetr Machata static int
899a44f58c4SPetr Machata mlxsw_sp_qdisc_tbf_bs(struct mlxsw_sp_port *mlxsw_sp_port,
900a44f58c4SPetr Machata 		      u32 max_size, u8 *p_burst_size)
901a44f58c4SPetr Machata {
902a44f58c4SPetr Machata 	/* TBF burst size is configured in bytes. The ASIC burst size value is
903a44f58c4SPetr Machata 	 * ((2 ^ bs) * 512 bits. Convert the TBF bytes to 512-bit units.
904a44f58c4SPetr Machata 	 */
905a44f58c4SPetr Machata 	u32 bs512 = max_size / 64;
906a44f58c4SPetr Machata 	u8 bs = fls(bs512);
907a44f58c4SPetr Machata 
908a44f58c4SPetr Machata 	if (!bs)
909a44f58c4SPetr Machata 		return -EINVAL;
910a44f58c4SPetr Machata 	--bs;
911a44f58c4SPetr Machata 
912a44f58c4SPetr Machata 	/* Demand a power of two. */
913a44f58c4SPetr Machata 	if ((1 << bs) != bs512)
914a44f58c4SPetr Machata 		return -EINVAL;
915a44f58c4SPetr Machata 
916a44f58c4SPetr Machata 	if (bs < mlxsw_sp_port->mlxsw_sp->lowest_shaper_bs ||
917a44f58c4SPetr Machata 	    bs > MLXSW_REG_QEEC_HIGHEST_SHAPER_BS)
918a44f58c4SPetr Machata 		return -EINVAL;
919a44f58c4SPetr Machata 
920a44f58c4SPetr Machata 	*p_burst_size = bs;
921a44f58c4SPetr Machata 	return 0;
922a44f58c4SPetr Machata }
923a44f58c4SPetr Machata 
924a44f58c4SPetr Machata static u32
925a44f58c4SPetr Machata mlxsw_sp_qdisc_tbf_max_size(u8 bs)
926a44f58c4SPetr Machata {
927a44f58c4SPetr Machata 	return (1U << bs) * 64;
928a44f58c4SPetr Machata }
929a44f58c4SPetr Machata 
930a44f58c4SPetr Machata static u64
931a44f58c4SPetr Machata mlxsw_sp_qdisc_tbf_rate_kbps(struct tc_tbf_qopt_offload_replace_params *p)
932a44f58c4SPetr Machata {
933a44f58c4SPetr Machata 	/* TBF interface is in bytes/s, whereas Spectrum ASIC is configured in
934a44f58c4SPetr Machata 	 * Kbits/s.
935a44f58c4SPetr Machata 	 */
93691a7d4bfSNathan Chancellor 	return div_u64(p->rate.rate_bytes_ps, 1000) * 8;
937a44f58c4SPetr Machata }
938a44f58c4SPetr Machata 
939a44f58c4SPetr Machata static int
940a44f58c4SPetr Machata mlxsw_sp_qdisc_tbf_check_params(struct mlxsw_sp_port *mlxsw_sp_port,
941a44f58c4SPetr Machata 				void *params)
942a44f58c4SPetr Machata {
943a44f58c4SPetr Machata 	struct tc_tbf_qopt_offload_replace_params *p = params;
944a44f58c4SPetr Machata 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
945a44f58c4SPetr Machata 	u64 rate_kbps = mlxsw_sp_qdisc_tbf_rate_kbps(p);
946a44f58c4SPetr Machata 	u8 burst_size;
947a44f58c4SPetr Machata 	int err;
948a44f58c4SPetr Machata 
949a44f58c4SPetr Machata 	if (rate_kbps >= MLXSW_REG_QEEC_MAS_DIS) {
950a44f58c4SPetr Machata 		dev_err(mlxsw_sp_port->mlxsw_sp->bus_info->dev,
951a44f58c4SPetr Machata 			"spectrum: TBF: rate of %lluKbps must be below %u\n",
952a44f58c4SPetr Machata 			rate_kbps, MLXSW_REG_QEEC_MAS_DIS);
953a44f58c4SPetr Machata 		return -EINVAL;
954a44f58c4SPetr Machata 	}
955a44f58c4SPetr Machata 
956a44f58c4SPetr Machata 	err = mlxsw_sp_qdisc_tbf_bs(mlxsw_sp_port, p->max_size, &burst_size);
957a44f58c4SPetr Machata 	if (err) {
958a44f58c4SPetr Machata 		u8 highest_shaper_bs = MLXSW_REG_QEEC_HIGHEST_SHAPER_BS;
959a44f58c4SPetr Machata 
960a44f58c4SPetr Machata 		dev_err(mlxsw_sp->bus_info->dev,
961a44f58c4SPetr Machata 			"spectrum: TBF: invalid burst size of %u, must be a power of two between %u and %u",
962a44f58c4SPetr Machata 			p->max_size,
963a44f58c4SPetr Machata 			mlxsw_sp_qdisc_tbf_max_size(mlxsw_sp->lowest_shaper_bs),
964a44f58c4SPetr Machata 			mlxsw_sp_qdisc_tbf_max_size(highest_shaper_bs));
965a44f58c4SPetr Machata 		return -EINVAL;
966a44f58c4SPetr Machata 	}
967a44f58c4SPetr Machata 
968a44f58c4SPetr Machata 	return 0;
969a44f58c4SPetr Machata }
970a44f58c4SPetr Machata 
971a44f58c4SPetr Machata static int
972c4e372e2SPetr Machata mlxsw_sp_qdisc_tbf_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
973a44f58c4SPetr Machata 			   struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
974a44f58c4SPetr Machata 			   void *params)
975a44f58c4SPetr Machata {
976a44f58c4SPetr Machata 	struct tc_tbf_qopt_offload_replace_params *p = params;
977a44f58c4SPetr Machata 	u64 rate_kbps = mlxsw_sp_qdisc_tbf_rate_kbps(p);
97876ff72a7SPetr Machata 	int tclass_num;
979a44f58c4SPetr Machata 	u8 burst_size;
980a44f58c4SPetr Machata 	int err;
981a44f58c4SPetr Machata 
98276ff72a7SPetr Machata 	tclass_num = mlxsw_sp_qdisc_get_tclass_num(mlxsw_sp_port,
98376ff72a7SPetr Machata 						   mlxsw_sp_qdisc);
98476ff72a7SPetr Machata 
985a44f58c4SPetr Machata 	err = mlxsw_sp_qdisc_tbf_bs(mlxsw_sp_port, p->max_size, &burst_size);
986a44f58c4SPetr Machata 	if (WARN_ON_ONCE(err))
987a44f58c4SPetr Machata 		/* check_params above was supposed to reject this value. */
988a44f58c4SPetr Machata 		return -EINVAL;
989a44f58c4SPetr Machata 
990a44f58c4SPetr Machata 	/* Configure subgroup shaper, so that both UC and MC traffic is subject
991a44f58c4SPetr Machata 	 * to shaping. That is unlike RED, however UC queue lengths are going to
992a44f58c4SPetr Machata 	 * be different than MC ones due to different pool and quota
993a44f58c4SPetr Machata 	 * configurations, so the configuration is not applicable. For shaper on
994a44f58c4SPetr Machata 	 * the other hand, subjecting the overall stream to the configured
995a44f58c4SPetr Machata 	 * shaper makes sense. Also note that that is what we do for
996a44f58c4SPetr Machata 	 * ieee_setmaxrate().
997a44f58c4SPetr Machata 	 */
998a44f58c4SPetr Machata 	return mlxsw_sp_port_ets_maxrate_set(mlxsw_sp_port,
999a44f58c4SPetr Machata 					     MLXSW_REG_QEEC_HR_SUBGROUP,
100076ff72a7SPetr Machata 					     tclass_num, 0,
1001a44f58c4SPetr Machata 					     rate_kbps, burst_size);
1002a44f58c4SPetr Machata }
1003a44f58c4SPetr Machata 
1004a44f58c4SPetr Machata static void
1005a44f58c4SPetr Machata mlxsw_sp_qdisc_tbf_unoffload(struct mlxsw_sp_port *mlxsw_sp_port,
1006a44f58c4SPetr Machata 			     struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
1007a44f58c4SPetr Machata 			     void *params)
1008a44f58c4SPetr Machata {
1009a44f58c4SPetr Machata 	struct tc_tbf_qopt_offload_replace_params *p = params;
1010a44f58c4SPetr Machata 
1011a44f58c4SPetr Machata 	mlxsw_sp_qdisc_leaf_unoffload(mlxsw_sp_port, mlxsw_sp_qdisc, p->qstats);
1012a44f58c4SPetr Machata }
1013a44f58c4SPetr Machata 
1014a44f58c4SPetr Machata static int
1015a44f58c4SPetr Machata mlxsw_sp_qdisc_get_tbf_stats(struct mlxsw_sp_port *mlxsw_sp_port,
1016a44f58c4SPetr Machata 			     struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
1017a44f58c4SPetr Machata 			     struct tc_qopt_offload_stats *stats_ptr)
1018a44f58c4SPetr Machata {
1019a44f58c4SPetr Machata 	mlxsw_sp_qdisc_get_tc_stats(mlxsw_sp_port, mlxsw_sp_qdisc,
1020a44f58c4SPetr Machata 				    stats_ptr);
1021a44f58c4SPetr Machata 	return 0;
1022a44f58c4SPetr Machata }
1023a44f58c4SPetr Machata 
1024a44f58c4SPetr Machata static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_tbf = {
1025a44f58c4SPetr Machata 	.type = MLXSW_SP_QDISC_TBF,
1026a44f58c4SPetr Machata 	.check_params = mlxsw_sp_qdisc_tbf_check_params,
1027a44f58c4SPetr Machata 	.replace = mlxsw_sp_qdisc_tbf_replace,
1028a44f58c4SPetr Machata 	.unoffload = mlxsw_sp_qdisc_tbf_unoffload,
1029a44f58c4SPetr Machata 	.destroy = mlxsw_sp_qdisc_tbf_destroy,
1030a44f58c4SPetr Machata 	.get_stats = mlxsw_sp_qdisc_get_tbf_stats,
1031a44f58c4SPetr Machata 	.clean_stats = mlxsw_sp_setup_tc_qdisc_leaf_clean_stats,
103251d52ed9SPetr Machata 	.find_class = mlxsw_sp_qdisc_leaf_find_class,
1033a44f58c4SPetr Machata };
1034a44f58c4SPetr Machata 
1035cff99e20SPetr Machata static int __mlxsw_sp_setup_tc_tbf(struct mlxsw_sp_port *mlxsw_sp_port,
1036a44f58c4SPetr Machata 				   struct tc_tbf_qopt_offload *p)
1037a44f58c4SPetr Machata {
1038a44f58c4SPetr Machata 	struct mlxsw_sp_qdisc *mlxsw_sp_qdisc;
1039a44f58c4SPetr Machata 
1040*c2792f38SPetr Machata 	mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent);
1041a44f58c4SPetr Machata 	if (!mlxsw_sp_qdisc)
1042a44f58c4SPetr Machata 		return -EOPNOTSUPP;
1043a44f58c4SPetr Machata 
1044a44f58c4SPetr Machata 	if (p->command == TC_TBF_REPLACE)
1045a44f58c4SPetr Machata 		return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle,
1046a44f58c4SPetr Machata 					      mlxsw_sp_qdisc,
1047a44f58c4SPetr Machata 					      &mlxsw_sp_qdisc_ops_tbf,
1048a44f58c4SPetr Machata 					      &p->replace_params);
1049a44f58c4SPetr Machata 
1050290fe2c5SPetr Machata 	if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle))
1051a44f58c4SPetr Machata 		return -EOPNOTSUPP;
1052a44f58c4SPetr Machata 
1053a44f58c4SPetr Machata 	switch (p->command) {
1054a44f58c4SPetr Machata 	case TC_TBF_DESTROY:
1055a44f58c4SPetr Machata 		return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
1056a44f58c4SPetr Machata 	case TC_TBF_STATS:
1057a44f58c4SPetr Machata 		return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc,
1058a44f58c4SPetr Machata 						&p->stats);
1059be7e2a5aSPetr Machata 	case TC_TBF_GRAFT:
1060be7e2a5aSPetr Machata 		return mlxsw_sp_qdisc_graft(mlxsw_sp_port, mlxsw_sp_qdisc, 0,
1061be7e2a5aSPetr Machata 					    p->child_handle);
1062a44f58c4SPetr Machata 	default:
1063a44f58c4SPetr Machata 		return -EOPNOTSUPP;
1064a44f58c4SPetr Machata 	}
1065a44f58c4SPetr Machata }
1066a44f58c4SPetr Machata 
1067cff99e20SPetr Machata int mlxsw_sp_setup_tc_tbf(struct mlxsw_sp_port *mlxsw_sp_port,
1068cff99e20SPetr Machata 			  struct tc_tbf_qopt_offload *p)
1069cff99e20SPetr Machata {
1070cff99e20SPetr Machata 	int err;
1071cff99e20SPetr Machata 
1072cff99e20SPetr Machata 	mutex_lock(&mlxsw_sp_port->qdisc->lock);
1073cff99e20SPetr Machata 	err = __mlxsw_sp_setup_tc_tbf(mlxsw_sp_port, p);
1074cff99e20SPetr Machata 	mutex_unlock(&mlxsw_sp_port->qdisc->lock);
1075cff99e20SPetr Machata 
1076cff99e20SPetr Machata 	return err;
1077cff99e20SPetr Machata }
1078cff99e20SPetr Machata 
107946a3615bSNogah Frankel static int
10807bec1a45SPetr Machata mlxsw_sp_qdisc_fifo_check_params(struct mlxsw_sp_port *mlxsw_sp_port,
10817bec1a45SPetr Machata 				 void *params)
10827bec1a45SPetr Machata {
10837bec1a45SPetr Machata 	return 0;
10847bec1a45SPetr Machata }
10857bec1a45SPetr Machata 
10867bec1a45SPetr Machata static int
10877bec1a45SPetr Machata mlxsw_sp_qdisc_fifo_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
10887bec1a45SPetr Machata 			    struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
10897bec1a45SPetr Machata 			    void *params)
10907bec1a45SPetr Machata {
10917bec1a45SPetr Machata 	return 0;
10927bec1a45SPetr Machata }
10937bec1a45SPetr Machata 
10947bec1a45SPetr Machata static int
10957bec1a45SPetr Machata mlxsw_sp_qdisc_get_fifo_stats(struct mlxsw_sp_port *mlxsw_sp_port,
10967bec1a45SPetr Machata 			      struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
10977bec1a45SPetr Machata 			      struct tc_qopt_offload_stats *stats_ptr)
10987bec1a45SPetr Machata {
10997bec1a45SPetr Machata 	mlxsw_sp_qdisc_get_tc_stats(mlxsw_sp_port, mlxsw_sp_qdisc,
11007bec1a45SPetr Machata 				    stats_ptr);
11017bec1a45SPetr Machata 	return 0;
11027bec1a45SPetr Machata }
11037bec1a45SPetr Machata 
11047bec1a45SPetr Machata static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_fifo = {
11057bec1a45SPetr Machata 	.type = MLXSW_SP_QDISC_FIFO,
11067bec1a45SPetr Machata 	.check_params = mlxsw_sp_qdisc_fifo_check_params,
11077bec1a45SPetr Machata 	.replace = mlxsw_sp_qdisc_fifo_replace,
11087bec1a45SPetr Machata 	.get_stats = mlxsw_sp_qdisc_get_fifo_stats,
11097bec1a45SPetr Machata 	.clean_stats = mlxsw_sp_setup_tc_qdisc_leaf_clean_stats,
11107bec1a45SPetr Machata };
11117bec1a45SPetr Machata 
111291796f50SPetr Machata static int
111391796f50SPetr Machata mlxsw_sp_qdisc_future_fifo_replace(struct mlxsw_sp_port *mlxsw_sp_port,
111491796f50SPetr Machata 				   u32 handle, unsigned int band,
111591796f50SPetr Machata 				   struct mlxsw_sp_qdisc *child_qdisc)
111691796f50SPetr Machata {
111791796f50SPetr Machata 	struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc;
111891796f50SPetr Machata 
111991796f50SPetr Machata 	if (handle == qdisc_state->future_handle &&
112091796f50SPetr Machata 	    qdisc_state->future_fifos[band])
112191796f50SPetr Machata 		return mlxsw_sp_qdisc_replace(mlxsw_sp_port, TC_H_UNSPEC,
112291796f50SPetr Machata 					      child_qdisc,
112391796f50SPetr Machata 					      &mlxsw_sp_qdisc_ops_fifo,
112491796f50SPetr Machata 					      NULL);
112591796f50SPetr Machata 	return 0;
112691796f50SPetr Machata }
112791796f50SPetr Machata 
112891796f50SPetr Machata static void
112991796f50SPetr Machata mlxsw_sp_qdisc_future_fifos_init(struct mlxsw_sp_port *mlxsw_sp_port,
113091796f50SPetr Machata 				 u32 handle)
113191796f50SPetr Machata {
113291796f50SPetr Machata 	struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc;
113391796f50SPetr Machata 
113491796f50SPetr Machata 	qdisc_state->future_handle = handle;
113591796f50SPetr Machata 	memset(qdisc_state->future_fifos, 0, sizeof(qdisc_state->future_fifos));
113691796f50SPetr Machata }
113791796f50SPetr Machata 
1138cff99e20SPetr Machata static int __mlxsw_sp_setup_tc_fifo(struct mlxsw_sp_port *mlxsw_sp_port,
11397bec1a45SPetr Machata 				    struct tc_fifo_qopt_offload *p)
11407bec1a45SPetr Machata {
11417bec1a45SPetr Machata 	struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc;
11427bec1a45SPetr Machata 	struct mlxsw_sp_qdisc *mlxsw_sp_qdisc;
11437de85b04SPetr Machata 	unsigned int band;
11447bec1a45SPetr Machata 	u32 parent_handle;
11457bec1a45SPetr Machata 
1146*c2792f38SPetr Machata 	mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent);
11477bec1a45SPetr Machata 	if (!mlxsw_sp_qdisc && p->handle == TC_H_UNSPEC) {
11487bec1a45SPetr Machata 		parent_handle = TC_H_MAJ(p->parent);
11497bec1a45SPetr Machata 		if (parent_handle != qdisc_state->future_handle) {
11507bec1a45SPetr Machata 			/* This notifications is for a different Qdisc than
11517bec1a45SPetr Machata 			 * previously. Wipe the future cache.
11527bec1a45SPetr Machata 			 */
115391796f50SPetr Machata 			mlxsw_sp_qdisc_future_fifos_init(mlxsw_sp_port,
115491796f50SPetr Machata 							 parent_handle);
11557bec1a45SPetr Machata 		}
11567bec1a45SPetr Machata 
11577de85b04SPetr Machata 		band = TC_H_MIN(p->parent) - 1;
11587de85b04SPetr Machata 		if (band < IEEE_8021QAZ_MAX_TCS) {
11597bec1a45SPetr Machata 			if (p->command == TC_FIFO_REPLACE)
11607de85b04SPetr Machata 				qdisc_state->future_fifos[band] = true;
11617bec1a45SPetr Machata 			else if (p->command == TC_FIFO_DESTROY)
11627de85b04SPetr Machata 				qdisc_state->future_fifos[band] = false;
11637bec1a45SPetr Machata 		}
11647bec1a45SPetr Machata 	}
11657bec1a45SPetr Machata 	if (!mlxsw_sp_qdisc)
11667bec1a45SPetr Machata 		return -EOPNOTSUPP;
11677bec1a45SPetr Machata 
11687bec1a45SPetr Machata 	if (p->command == TC_FIFO_REPLACE) {
11697bec1a45SPetr Machata 		return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle,
11707bec1a45SPetr Machata 					      mlxsw_sp_qdisc,
11717bec1a45SPetr Machata 					      &mlxsw_sp_qdisc_ops_fifo, NULL);
11727bec1a45SPetr Machata 	}
11737bec1a45SPetr Machata 
1174290fe2c5SPetr Machata 	if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle))
11757bec1a45SPetr Machata 		return -EOPNOTSUPP;
11767bec1a45SPetr Machata 
11777bec1a45SPetr Machata 	switch (p->command) {
11787bec1a45SPetr Machata 	case TC_FIFO_DESTROY:
1179549f2aaeSPetr Machata 		return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
11807bec1a45SPetr Machata 	case TC_FIFO_STATS:
11817bec1a45SPetr Machata 		return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc,
11827bec1a45SPetr Machata 						&p->stats);
11837bec1a45SPetr Machata 	case TC_FIFO_REPLACE: /* Handled above. */
11847bec1a45SPetr Machata 		break;
11857bec1a45SPetr Machata 	}
11867bec1a45SPetr Machata 
11877bec1a45SPetr Machata 	return -EOPNOTSUPP;
11887bec1a45SPetr Machata }
11897bec1a45SPetr Machata 
1190cff99e20SPetr Machata int mlxsw_sp_setup_tc_fifo(struct mlxsw_sp_port *mlxsw_sp_port,
1191cff99e20SPetr Machata 			   struct tc_fifo_qopt_offload *p)
1192cff99e20SPetr Machata {
1193cff99e20SPetr Machata 	int err;
1194cff99e20SPetr Machata 
1195cff99e20SPetr Machata 	mutex_lock(&mlxsw_sp_port->qdisc->lock);
1196cff99e20SPetr Machata 	err = __mlxsw_sp_setup_tc_fifo(mlxsw_sp_port, p);
1197cff99e20SPetr Machata 	mutex_unlock(&mlxsw_sp_port->qdisc->lock);
1198cff99e20SPetr Machata 
1199cff99e20SPetr Machata 	return err;
1200cff99e20SPetr Machata }
1201cff99e20SPetr Machata 
120251d52ed9SPetr Machata static int __mlxsw_sp_qdisc_ets_destroy(struct mlxsw_sp_port *mlxsw_sp_port,
120351d52ed9SPetr Machata 					struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
120446a3615bSNogah Frankel {
120546a3615bSNogah Frankel 	int i;
120646a3615bSNogah Frankel 
120751d52ed9SPetr Machata 	for (i = 0; i < mlxsw_sp_qdisc->num_classes; i++) {
120846a3615bSNogah Frankel 		mlxsw_sp_port_prio_tc_set(mlxsw_sp_port, i,
120946a3615bSNogah Frankel 					  MLXSW_SP_PORT_DEFAULT_TCLASS);
12107917f52aSPetr Machata 		mlxsw_sp_port_ets_set(mlxsw_sp_port,
12117917f52aSPetr Machata 				      MLXSW_REG_QEEC_HR_SUBGROUP,
12127917f52aSPetr Machata 				      i, 0, false, 0);
1213eed4baebSNogah Frankel 	}
121446a3615bSNogah Frankel 
121576ff72a7SPetr Machata 	kfree(mlxsw_sp_qdisc->ets_data);
121676ff72a7SPetr Machata 	mlxsw_sp_qdisc->ets_data = NULL;
121746a3615bSNogah Frankel 	return 0;
121846a3615bSNogah Frankel }
121946a3615bSNogah Frankel 
122046a3615bSNogah Frankel static int
12217917f52aSPetr Machata mlxsw_sp_qdisc_prio_destroy(struct mlxsw_sp_port *mlxsw_sp_port,
12227917f52aSPetr Machata 			    struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
12237917f52aSPetr Machata {
122451d52ed9SPetr Machata 	return __mlxsw_sp_qdisc_ets_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
12257917f52aSPetr Machata }
12267917f52aSPetr Machata 
12277917f52aSPetr Machata static int
12287917f52aSPetr Machata __mlxsw_sp_qdisc_ets_check_params(unsigned int nbands)
12297917f52aSPetr Machata {
12307917f52aSPetr Machata 	if (nbands > IEEE_8021QAZ_MAX_TCS)
12317917f52aSPetr Machata 		return -EOPNOTSUPP;
12327917f52aSPetr Machata 
12337917f52aSPetr Machata 	return 0;
12347917f52aSPetr Machata }
12357917f52aSPetr Machata 
12367917f52aSPetr Machata static int
123746a3615bSNogah Frankel mlxsw_sp_qdisc_prio_check_params(struct mlxsw_sp_port *mlxsw_sp_port,
123846a3615bSNogah Frankel 				 void *params)
123946a3615bSNogah Frankel {
124046a3615bSNogah Frankel 	struct tc_prio_qopt_offload_params *p = params;
124146a3615bSNogah Frankel 
12427917f52aSPetr Machata 	return __mlxsw_sp_qdisc_ets_check_params(p->bands);
124346a3615bSNogah Frankel }
124446a3615bSNogah Frankel 
124501164ddaSPetr Machata static struct mlxsw_sp_qdisc *
124601164ddaSPetr Machata mlxsw_sp_qdisc_walk_cb_clean_stats(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
124701164ddaSPetr Machata 				   void *mlxsw_sp_port)
124801164ddaSPetr Machata {
124901164ddaSPetr Machata 	u64 backlog;
125001164ddaSPetr Machata 
125101164ddaSPetr Machata 	if (mlxsw_sp_qdisc->ops) {
125201164ddaSPetr Machata 		backlog = mlxsw_sp_qdisc->stats_base.backlog;
125301164ddaSPetr Machata 		if (mlxsw_sp_qdisc->ops->clean_stats)
125401164ddaSPetr Machata 			mlxsw_sp_qdisc->ops->clean_stats(mlxsw_sp_port,
125501164ddaSPetr Machata 							 mlxsw_sp_qdisc);
125601164ddaSPetr Machata 		mlxsw_sp_qdisc->stats_base.backlog = backlog;
125701164ddaSPetr Machata 	}
125801164ddaSPetr Machata 
125901164ddaSPetr Machata 	return NULL;
126001164ddaSPetr Machata }
126101164ddaSPetr Machata 
126201164ddaSPetr Machata static void
126301164ddaSPetr Machata mlxsw_sp_qdisc_tree_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port,
126401164ddaSPetr Machata 				struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
126501164ddaSPetr Machata {
126601164ddaSPetr Machata 	mlxsw_sp_qdisc_walk(mlxsw_sp_qdisc, mlxsw_sp_qdisc_walk_cb_clean_stats,
126701164ddaSPetr Machata 			    mlxsw_sp_port);
126801164ddaSPetr Machata }
126901164ddaSPetr Machata 
127046a3615bSNogah Frankel static int
127151d52ed9SPetr Machata __mlxsw_sp_qdisc_ets_replace(struct mlxsw_sp_port *mlxsw_sp_port,
127251d52ed9SPetr Machata 			     struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
127351d52ed9SPetr Machata 			     u32 handle, unsigned int nbands,
12747917f52aSPetr Machata 			     const unsigned int *quanta,
12757917f52aSPetr Machata 			     const unsigned int *weights,
12767917f52aSPetr Machata 			     const u8 *priomap)
127746a3615bSNogah Frankel {
127876ff72a7SPetr Machata 	struct mlxsw_sp_qdisc_ets_data *ets_data = mlxsw_sp_qdisc->ets_data;
127976ff72a7SPetr Machata 	struct mlxsw_sp_qdisc_ets_band *ets_band;
128004cc0bf5SNogah Frankel 	struct mlxsw_sp_qdisc *child_qdisc;
128176ff72a7SPetr Machata 	u8 old_priomap, new_priomap;
128201164ddaSPetr Machata 	int i, band;
128346a3615bSNogah Frankel 	int err;
128446a3615bSNogah Frankel 
128576ff72a7SPetr Machata 	if (!ets_data) {
128676ff72a7SPetr Machata 		ets_data = kzalloc(sizeof(*ets_data), GFP_KERNEL);
128776ff72a7SPetr Machata 		if (!ets_data)
128876ff72a7SPetr Machata 			return -ENOMEM;
128976ff72a7SPetr Machata 		mlxsw_sp_qdisc->ets_data = ets_data;
129076ff72a7SPetr Machata 
129176ff72a7SPetr Machata 		for (band = 0; band < mlxsw_sp_qdisc->num_classes; band++) {
129276ff72a7SPetr Machata 			int tclass_num = MLXSW_SP_PRIO_BAND_TO_TCLASS(band);
129376ff72a7SPetr Machata 
129476ff72a7SPetr Machata 			ets_band = &ets_data->bands[band];
129576ff72a7SPetr Machata 			ets_band->tclass_num = tclass_num;
129676ff72a7SPetr Machata 		}
129776ff72a7SPetr Machata 	}
129876ff72a7SPetr Machata 
12997917f52aSPetr Machata 	for (band = 0; band < nbands; band++) {
130076ff72a7SPetr Machata 		int tclass_num;
130176ff72a7SPetr Machata 
130251d52ed9SPetr Machata 		child_qdisc = &mlxsw_sp_qdisc->qdiscs[band];
130376ff72a7SPetr Machata 		ets_band = &ets_data->bands[band];
130476ff72a7SPetr Machata 
130576ff72a7SPetr Machata 		tclass_num = ets_band->tclass_num;
130676ff72a7SPetr Machata 		old_priomap = ets_band->prio_bitmap;
130776ff72a7SPetr Machata 		new_priomap = 0;
13087917f52aSPetr Machata 
13097917f52aSPetr Machata 		err = mlxsw_sp_port_ets_set(mlxsw_sp_port,
13107917f52aSPetr Machata 					    MLXSW_REG_QEEC_HR_SUBGROUP,
131176ff72a7SPetr Machata 					    tclass_num, 0, !!quanta[band],
13127917f52aSPetr Machata 					    weights[band]);
13137917f52aSPetr Machata 		if (err)
13147917f52aSPetr Machata 			return err;
13157917f52aSPetr Machata 
131646a3615bSNogah Frankel 		for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
13177917f52aSPetr Machata 			if (priomap[i] == band) {
131876ff72a7SPetr Machata 				new_priomap |= BIT(i);
131904cc0bf5SNogah Frankel 				if (BIT(i) & old_priomap)
132004cc0bf5SNogah Frankel 					continue;
132104cc0bf5SNogah Frankel 				err = mlxsw_sp_port_prio_tc_set(mlxsw_sp_port,
132276ff72a7SPetr Machata 								i, tclass_num);
132346a3615bSNogah Frankel 				if (err)
132446a3615bSNogah Frankel 					return err;
132504cc0bf5SNogah Frankel 			}
132604cc0bf5SNogah Frankel 		}
13275cbd9602SPetr Machata 
132876ff72a7SPetr Machata 		ets_band->prio_bitmap = new_priomap;
13295cbd9602SPetr Machata 
133001164ddaSPetr Machata 		if (old_priomap != new_priomap)
133101164ddaSPetr Machata 			mlxsw_sp_qdisc_tree_clean_stats(mlxsw_sp_port,
133204cc0bf5SNogah Frankel 							child_qdisc);
13337bec1a45SPetr Machata 
133491796f50SPetr Machata 		err = mlxsw_sp_qdisc_future_fifo_replace(mlxsw_sp_port, handle,
133591796f50SPetr Machata 							 band, child_qdisc);
13367bec1a45SPetr Machata 		if (err)
13377bec1a45SPetr Machata 			return err;
13387bec1a45SPetr Machata 	}
133998ceb7b6SNogah Frankel 	for (; band < IEEE_8021QAZ_MAX_TCS; band++) {
134076ff72a7SPetr Machata 		ets_band = &ets_data->bands[band];
134176ff72a7SPetr Machata 		ets_band->prio_bitmap = 0;
134276ff72a7SPetr Machata 
134351d52ed9SPetr Machata 		child_qdisc = &mlxsw_sp_qdisc->qdiscs[band];
134498ceb7b6SNogah Frankel 		mlxsw_sp_qdisc_destroy(mlxsw_sp_port, child_qdisc);
134576ff72a7SPetr Machata 
13467917f52aSPetr Machata 		mlxsw_sp_port_ets_set(mlxsw_sp_port,
13477917f52aSPetr Machata 				      MLXSW_REG_QEEC_HR_SUBGROUP,
134876ff72a7SPetr Machata 				      ets_band->tclass_num, 0, false, 0);
134998ceb7b6SNogah Frankel 	}
13507bec1a45SPetr Machata 
135191796f50SPetr Machata 	mlxsw_sp_qdisc_future_fifos_init(mlxsw_sp_port, TC_H_UNSPEC);
135246a3615bSNogah Frankel 	return 0;
135346a3615bSNogah Frankel }
135446a3615bSNogah Frankel 
13557917f52aSPetr Machata static int
1356c4e372e2SPetr Machata mlxsw_sp_qdisc_prio_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
13577917f52aSPetr Machata 			    struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
13587917f52aSPetr Machata 			    void *params)
13597917f52aSPetr Machata {
13607917f52aSPetr Machata 	struct tc_prio_qopt_offload_params *p = params;
13617917f52aSPetr Machata 	unsigned int zeroes[TCQ_ETS_MAX_BANDS] = {0};
13627917f52aSPetr Machata 
136351d52ed9SPetr Machata 	return __mlxsw_sp_qdisc_ets_replace(mlxsw_sp_port, mlxsw_sp_qdisc,
136451d52ed9SPetr Machata 					    handle, p->bands, zeroes,
136551d52ed9SPetr Machata 					    zeroes, p->priomap);
13667917f52aSPetr Machata }
13677917f52aSPetr Machata 
13687917f52aSPetr Machata static void
13697917f52aSPetr Machata __mlxsw_sp_qdisc_ets_unoffload(struct mlxsw_sp_port *mlxsw_sp_port,
13707917f52aSPetr Machata 			       struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
13717917f52aSPetr Machata 			       struct gnet_stats_queue *qstats)
13727917f52aSPetr Machata {
13737917f52aSPetr Machata 	u64 backlog;
13747917f52aSPetr Machata 
13757917f52aSPetr Machata 	backlog = mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp,
13767917f52aSPetr Machata 				       mlxsw_sp_qdisc->stats_base.backlog);
13777917f52aSPetr Machata 	qstats->backlog -= backlog;
13787917f52aSPetr Machata }
13797917f52aSPetr Machata 
1380e02f08a0SWei Yongjun static void
138193d8a4c1SNogah Frankel mlxsw_sp_qdisc_prio_unoffload(struct mlxsw_sp_port *mlxsw_sp_port,
138293d8a4c1SNogah Frankel 			      struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
138393d8a4c1SNogah Frankel 			      void *params)
138493d8a4c1SNogah Frankel {
138593d8a4c1SNogah Frankel 	struct tc_prio_qopt_offload_params *p = params;
138693d8a4c1SNogah Frankel 
13877917f52aSPetr Machata 	__mlxsw_sp_qdisc_ets_unoffload(mlxsw_sp_port, mlxsw_sp_qdisc,
13887917f52aSPetr Machata 				       p->qstats);
138993d8a4c1SNogah Frankel }
139093d8a4c1SNogah Frankel 
139193d8a4c1SNogah Frankel static int
139293d8a4c1SNogah Frankel mlxsw_sp_qdisc_get_prio_stats(struct mlxsw_sp_port *mlxsw_sp_port,
139393d8a4c1SNogah Frankel 			      struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
139493d8a4c1SNogah Frankel 			      struct tc_qopt_offload_stats *stats_ptr)
139593d8a4c1SNogah Frankel {
1396cf9af379SPetr Machata 	struct mlxsw_sp_qdisc *tc_qdisc;
1397cf9af379SPetr Machata 	u64 tx_packets = 0;
1398cf9af379SPetr Machata 	u64 tx_bytes = 0;
1399cf9af379SPetr Machata 	u64 backlog = 0;
1400cf9af379SPetr Machata 	u64 drops = 0;
140193d8a4c1SNogah Frankel 	int i;
140293d8a4c1SNogah Frankel 
140351d52ed9SPetr Machata 	for (i = 0; i < mlxsw_sp_qdisc->num_classes; i++) {
140451d52ed9SPetr Machata 		tc_qdisc = &mlxsw_sp_qdisc->qdiscs[i];
1405cf9af379SPetr Machata 		mlxsw_sp_qdisc_collect_tc_stats(mlxsw_sp_port, tc_qdisc,
1406cf9af379SPetr Machata 						&tx_bytes, &tx_packets,
1407cf9af379SPetr Machata 						&drops, &backlog);
140893d8a4c1SNogah Frankel 	}
140993d8a4c1SNogah Frankel 
1410cf9af379SPetr Machata 	mlxsw_sp_qdisc_update_stats(mlxsw_sp_port->mlxsw_sp, mlxsw_sp_qdisc,
1411cf9af379SPetr Machata 				    tx_bytes, tx_packets, drops, backlog,
1412cf9af379SPetr Machata 				    stats_ptr);
141393d8a4c1SNogah Frankel 	return 0;
141493d8a4c1SNogah Frankel }
141593d8a4c1SNogah Frankel 
141693d8a4c1SNogah Frankel static void
141793d8a4c1SNogah Frankel mlxsw_sp_setup_tc_qdisc_prio_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port,
141893d8a4c1SNogah Frankel 					 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
141993d8a4c1SNogah Frankel {
142093d8a4c1SNogah Frankel 	struct mlxsw_sp_qdisc_stats *stats_base;
142193d8a4c1SNogah Frankel 	struct mlxsw_sp_port_xstats *xstats;
142293d8a4c1SNogah Frankel 	struct rtnl_link_stats64 *stats;
142393d8a4c1SNogah Frankel 	int i;
142493d8a4c1SNogah Frankel 
142593d8a4c1SNogah Frankel 	xstats = &mlxsw_sp_port->periodic_hw_stats.xstats;
142693d8a4c1SNogah Frankel 	stats = &mlxsw_sp_port->periodic_hw_stats.stats;
142793d8a4c1SNogah Frankel 	stats_base = &mlxsw_sp_qdisc->stats_base;
142893d8a4c1SNogah Frankel 
142993d8a4c1SNogah Frankel 	stats_base->tx_packets = stats->tx_packets;
143093d8a4c1SNogah Frankel 	stats_base->tx_bytes = stats->tx_bytes;
143193d8a4c1SNogah Frankel 
143293d8a4c1SNogah Frankel 	stats_base->drops = 0;
143323f2b404SNogah Frankel 	for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
143485005b82SPetr Machata 		stats_base->drops += mlxsw_sp_xstats_tail_drop(xstats, i);
143523f2b404SNogah Frankel 		stats_base->drops += xstats->wred_drop[i];
143623f2b404SNogah Frankel 	}
143793d8a4c1SNogah Frankel 
143893d8a4c1SNogah Frankel 	mlxsw_sp_qdisc->stats_base.backlog = 0;
143993d8a4c1SNogah Frankel }
144093d8a4c1SNogah Frankel 
144151d52ed9SPetr Machata static struct mlxsw_sp_qdisc *
144251d52ed9SPetr Machata mlxsw_sp_qdisc_prio_find_class(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
144351d52ed9SPetr Machata 			       u32 parent)
144451d52ed9SPetr Machata {
144551d52ed9SPetr Machata 	int child_index = TC_H_MIN(parent);
144651d52ed9SPetr Machata 	int band = child_index - 1;
144751d52ed9SPetr Machata 
144851d52ed9SPetr Machata 	if (band < 0 || band >= mlxsw_sp_qdisc->num_classes)
144951d52ed9SPetr Machata 		return NULL;
145051d52ed9SPetr Machata 	return &mlxsw_sp_qdisc->qdiscs[band];
145151d52ed9SPetr Machata }
145251d52ed9SPetr Machata 
145376ff72a7SPetr Machata static struct mlxsw_sp_qdisc_ets_band *
145476ff72a7SPetr Machata mlxsw_sp_qdisc_ets_get_band(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
145576ff72a7SPetr Machata 			    struct mlxsw_sp_qdisc *child)
145676ff72a7SPetr Machata {
145776ff72a7SPetr Machata 	unsigned int band = child - mlxsw_sp_qdisc->qdiscs;
145876ff72a7SPetr Machata 
145976ff72a7SPetr Machata 	if (WARN_ON(band >= IEEE_8021QAZ_MAX_TCS))
146076ff72a7SPetr Machata 		band = 0;
146176ff72a7SPetr Machata 	return &mlxsw_sp_qdisc->ets_data->bands[band];
146276ff72a7SPetr Machata }
146376ff72a7SPetr Machata 
146476ff72a7SPetr Machata static u8
146576ff72a7SPetr Machata mlxsw_sp_qdisc_ets_get_prio_bitmap(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
146676ff72a7SPetr Machata 				   struct mlxsw_sp_qdisc *child)
146776ff72a7SPetr Machata {
146876ff72a7SPetr Machata 	return mlxsw_sp_qdisc_ets_get_band(mlxsw_sp_qdisc, child)->prio_bitmap;
146976ff72a7SPetr Machata }
147076ff72a7SPetr Machata 
147176ff72a7SPetr Machata static int
147276ff72a7SPetr Machata mlxsw_sp_qdisc_ets_get_tclass_num(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
147376ff72a7SPetr Machata 				  struct mlxsw_sp_qdisc *child)
147476ff72a7SPetr Machata {
147576ff72a7SPetr Machata 	return mlxsw_sp_qdisc_ets_get_band(mlxsw_sp_qdisc, child)->tclass_num;
147676ff72a7SPetr Machata }
147776ff72a7SPetr Machata 
147846a3615bSNogah Frankel static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_prio = {
147946a3615bSNogah Frankel 	.type = MLXSW_SP_QDISC_PRIO,
148046a3615bSNogah Frankel 	.check_params = mlxsw_sp_qdisc_prio_check_params,
148146a3615bSNogah Frankel 	.replace = mlxsw_sp_qdisc_prio_replace,
148293d8a4c1SNogah Frankel 	.unoffload = mlxsw_sp_qdisc_prio_unoffload,
148346a3615bSNogah Frankel 	.destroy = mlxsw_sp_qdisc_prio_destroy,
148493d8a4c1SNogah Frankel 	.get_stats = mlxsw_sp_qdisc_get_prio_stats,
148593d8a4c1SNogah Frankel 	.clean_stats = mlxsw_sp_setup_tc_qdisc_prio_clean_stats,
148651d52ed9SPetr Machata 	.find_class = mlxsw_sp_qdisc_prio_find_class,
14875cbd9602SPetr Machata 	.num_classes = IEEE_8021QAZ_MAX_TCS,
148876ff72a7SPetr Machata 	.get_prio_bitmap = mlxsw_sp_qdisc_ets_get_prio_bitmap,
148976ff72a7SPetr Machata 	.get_tclass_num = mlxsw_sp_qdisc_ets_get_tclass_num,
149046a3615bSNogah Frankel };
149146a3615bSNogah Frankel 
149219f405b9SPetr Machata static int
149319f405b9SPetr Machata mlxsw_sp_qdisc_ets_check_params(struct mlxsw_sp_port *mlxsw_sp_port,
149419f405b9SPetr Machata 				void *params)
149519f405b9SPetr Machata {
149619f405b9SPetr Machata 	struct tc_ets_qopt_offload_replace_params *p = params;
149719f405b9SPetr Machata 
149819f405b9SPetr Machata 	return __mlxsw_sp_qdisc_ets_check_params(p->bands);
149919f405b9SPetr Machata }
150019f405b9SPetr Machata 
150119f405b9SPetr Machata static int
1502c4e372e2SPetr Machata mlxsw_sp_qdisc_ets_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
150319f405b9SPetr Machata 			   struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
150419f405b9SPetr Machata 			   void *params)
150519f405b9SPetr Machata {
150619f405b9SPetr Machata 	struct tc_ets_qopt_offload_replace_params *p = params;
150719f405b9SPetr Machata 
150851d52ed9SPetr Machata 	return __mlxsw_sp_qdisc_ets_replace(mlxsw_sp_port, mlxsw_sp_qdisc,
150951d52ed9SPetr Machata 					    handle, p->bands, p->quanta,
151051d52ed9SPetr Machata 					    p->weights, p->priomap);
151119f405b9SPetr Machata }
151219f405b9SPetr Machata 
151319f405b9SPetr Machata static void
151419f405b9SPetr Machata mlxsw_sp_qdisc_ets_unoffload(struct mlxsw_sp_port *mlxsw_sp_port,
151519f405b9SPetr Machata 			     struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
151619f405b9SPetr Machata 			     void *params)
151719f405b9SPetr Machata {
151819f405b9SPetr Machata 	struct tc_ets_qopt_offload_replace_params *p = params;
151919f405b9SPetr Machata 
152019f405b9SPetr Machata 	__mlxsw_sp_qdisc_ets_unoffload(mlxsw_sp_port, mlxsw_sp_qdisc,
152119f405b9SPetr Machata 				       p->qstats);
152219f405b9SPetr Machata }
152319f405b9SPetr Machata 
152419f405b9SPetr Machata static int
152519f405b9SPetr Machata mlxsw_sp_qdisc_ets_destroy(struct mlxsw_sp_port *mlxsw_sp_port,
152619f405b9SPetr Machata 			   struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
152719f405b9SPetr Machata {
152851d52ed9SPetr Machata 	return __mlxsw_sp_qdisc_ets_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
152919f405b9SPetr Machata }
153019f405b9SPetr Machata 
153119f405b9SPetr Machata static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_ets = {
153219f405b9SPetr Machata 	.type = MLXSW_SP_QDISC_ETS,
153319f405b9SPetr Machata 	.check_params = mlxsw_sp_qdisc_ets_check_params,
153419f405b9SPetr Machata 	.replace = mlxsw_sp_qdisc_ets_replace,
153519f405b9SPetr Machata 	.unoffload = mlxsw_sp_qdisc_ets_unoffload,
153619f405b9SPetr Machata 	.destroy = mlxsw_sp_qdisc_ets_destroy,
153719f405b9SPetr Machata 	.get_stats = mlxsw_sp_qdisc_get_prio_stats,
153819f405b9SPetr Machata 	.clean_stats = mlxsw_sp_setup_tc_qdisc_prio_clean_stats,
153951d52ed9SPetr Machata 	.find_class = mlxsw_sp_qdisc_prio_find_class,
15405cbd9602SPetr Machata 	.num_classes = IEEE_8021QAZ_MAX_TCS,
154176ff72a7SPetr Machata 	.get_prio_bitmap = mlxsw_sp_qdisc_ets_get_prio_bitmap,
154276ff72a7SPetr Machata 	.get_tclass_num = mlxsw_sp_qdisc_ets_get_tclass_num,
154319f405b9SPetr Machata };
154419f405b9SPetr Machata 
15455bc146c9SPetr Machata /* Linux allows linking of Qdiscs to arbitrary classes (so long as the resulting
15465bc146c9SPetr Machata  * graph is free of cycles). These operations do not change the parent handle
15475bc146c9SPetr Machata  * though, which means it can be incomplete (if there is more than one class
15485bc146c9SPetr Machata  * where the Qdisc in question is grafted) or outright wrong (if the Qdisc was
15495bc146c9SPetr Machata  * linked to a different class and then removed from the original class).
15505bc146c9SPetr Machata  *
15515bc146c9SPetr Machata  * E.g. consider this sequence of operations:
15525bc146c9SPetr Machata  *
15535bc146c9SPetr Machata  *  # tc qdisc add dev swp1 root handle 1: prio
15545bc146c9SPetr Machata  *  # tc qdisc add dev swp1 parent 1:3 handle 13: red limit 1000000 avpkt 10000
15555bc146c9SPetr Machata  *  RED: set bandwidth to 10Mbit
15565bc146c9SPetr Machata  *  # tc qdisc link dev swp1 handle 13: parent 1:2
15575bc146c9SPetr Machata  *
15585bc146c9SPetr Machata  * At this point, both 1:2 and 1:3 have the same RED Qdisc instance as their
15595bc146c9SPetr Machata  * child. But RED will still only claim that 1:3 is its parent. If it's removed
15605bc146c9SPetr Machata  * from that band, its only parent will be 1:2, but it will continue to claim
15615bc146c9SPetr Machata  * that it is in fact 1:3.
15625bc146c9SPetr Machata  *
15635bc146c9SPetr Machata  * The notification for child Qdisc replace (e.g. TC_RED_REPLACE) comes before
15645bc146c9SPetr Machata  * the notification for parent graft (e.g. TC_PRIO_GRAFT). We take the replace
15655bc146c9SPetr Machata  * notification to offload the child Qdisc, based on its parent handle, and use
15665bc146c9SPetr Machata  * the graft operation to validate that the class where the child is actually
15675bc146c9SPetr Machata  * grafted corresponds to the parent handle. If the two don't match, we
15685bc146c9SPetr Machata  * unoffload the child.
156932dc5efcSNogah Frankel  */
1570be7e2a5aSPetr Machata static int mlxsw_sp_qdisc_graft(struct mlxsw_sp_port *mlxsw_sp_port,
157132dc5efcSNogah Frankel 				struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
15727917f52aSPetr Machata 				u8 band, u32 child_handle)
157332dc5efcSNogah Frankel {
157432dc5efcSNogah Frankel 	struct mlxsw_sp_qdisc *old_qdisc;
1575d566ed04SPetr Machata 	u32 parent;
157632dc5efcSNogah Frankel 
157751d52ed9SPetr Machata 	if (band < mlxsw_sp_qdisc->num_classes &&
157851d52ed9SPetr Machata 	    mlxsw_sp_qdisc->qdiscs[band].handle == child_handle)
157932dc5efcSNogah Frankel 		return 0;
158032dc5efcSNogah Frankel 
1581a2d6d7aeSDavid S. Miller 	if (!child_handle) {
15823971a535SPetr Machata 		/* This is an invisible FIFO replacing the original Qdisc.
15833971a535SPetr Machata 		 * Ignore it--the original Qdisc's destroy will follow.
15843971a535SPetr Machata 		 */
15853971a535SPetr Machata 		return 0;
15863971a535SPetr Machata 	}
15873971a535SPetr Machata 
158832dc5efcSNogah Frankel 	/* See if the grafted qdisc is already offloaded on any tclass. If so,
158932dc5efcSNogah Frankel 	 * unoffload it.
159032dc5efcSNogah Frankel 	 */
159132dc5efcSNogah Frankel 	old_qdisc = mlxsw_sp_qdisc_find_by_handle(mlxsw_sp_port,
15927917f52aSPetr Machata 						  child_handle);
159332dc5efcSNogah Frankel 	if (old_qdisc)
159432dc5efcSNogah Frankel 		mlxsw_sp_qdisc_destroy(mlxsw_sp_port, old_qdisc);
159532dc5efcSNogah Frankel 
1596d566ed04SPetr Machata 	parent = TC_H_MAKE(mlxsw_sp_qdisc->handle, band + 1);
1597d566ed04SPetr Machata 	mlxsw_sp_qdisc = mlxsw_sp_qdisc->ops->find_class(mlxsw_sp_qdisc,
1598d566ed04SPetr Machata 							 parent);
159951d52ed9SPetr Machata 	if (!WARN_ON(!mlxsw_sp_qdisc))
160051d52ed9SPetr Machata 		mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
160151d52ed9SPetr Machata 
160232dc5efcSNogah Frankel 	return -EOPNOTSUPP;
160332dc5efcSNogah Frankel }
160432dc5efcSNogah Frankel 
1605cff99e20SPetr Machata static int __mlxsw_sp_setup_tc_prio(struct mlxsw_sp_port *mlxsw_sp_port,
160646a3615bSNogah Frankel 				    struct tc_prio_qopt_offload *p)
160746a3615bSNogah Frankel {
160846a3615bSNogah Frankel 	struct mlxsw_sp_qdisc *mlxsw_sp_qdisc;
160946a3615bSNogah Frankel 
1610*c2792f38SPetr Machata 	mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent);
1611eed4baebSNogah Frankel 	if (!mlxsw_sp_qdisc)
161246a3615bSNogah Frankel 		return -EOPNOTSUPP;
161346a3615bSNogah Frankel 
161446a3615bSNogah Frankel 	if (p->command == TC_PRIO_REPLACE)
161546a3615bSNogah Frankel 		return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle,
161646a3615bSNogah Frankel 					      mlxsw_sp_qdisc,
161746a3615bSNogah Frankel 					      &mlxsw_sp_qdisc_ops_prio,
161846a3615bSNogah Frankel 					      &p->replace_params);
161946a3615bSNogah Frankel 
1620290fe2c5SPetr Machata 	if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle))
162146a3615bSNogah Frankel 		return -EOPNOTSUPP;
162246a3615bSNogah Frankel 
162346a3615bSNogah Frankel 	switch (p->command) {
162446a3615bSNogah Frankel 	case TC_PRIO_DESTROY:
162546a3615bSNogah Frankel 		return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
162693d8a4c1SNogah Frankel 	case TC_PRIO_STATS:
162793d8a4c1SNogah Frankel 		return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc,
162893d8a4c1SNogah Frankel 						&p->stats);
162932dc5efcSNogah Frankel 	case TC_PRIO_GRAFT:
1630be7e2a5aSPetr Machata 		return mlxsw_sp_qdisc_graft(mlxsw_sp_port, mlxsw_sp_qdisc,
1631be7e2a5aSPetr Machata 					    p->graft_params.band,
1632be7e2a5aSPetr Machata 					    p->graft_params.child_handle);
163346a3615bSNogah Frankel 	default:
163446a3615bSNogah Frankel 		return -EOPNOTSUPP;
163546a3615bSNogah Frankel 	}
163646a3615bSNogah Frankel }
163746a3615bSNogah Frankel 
1638cff99e20SPetr Machata int mlxsw_sp_setup_tc_prio(struct mlxsw_sp_port *mlxsw_sp_port,
1639cff99e20SPetr Machata 			   struct tc_prio_qopt_offload *p)
1640cff99e20SPetr Machata {
1641cff99e20SPetr Machata 	int err;
1642cff99e20SPetr Machata 
1643cff99e20SPetr Machata 	mutex_lock(&mlxsw_sp_port->qdisc->lock);
1644cff99e20SPetr Machata 	err = __mlxsw_sp_setup_tc_prio(mlxsw_sp_port, p);
1645cff99e20SPetr Machata 	mutex_unlock(&mlxsw_sp_port->qdisc->lock);
1646cff99e20SPetr Machata 
1647cff99e20SPetr Machata 	return err;
1648cff99e20SPetr Machata }
1649cff99e20SPetr Machata 
1650cff99e20SPetr Machata static int __mlxsw_sp_setup_tc_ets(struct mlxsw_sp_port *mlxsw_sp_port,
165119f405b9SPetr Machata 				   struct tc_ets_qopt_offload *p)
165219f405b9SPetr Machata {
165319f405b9SPetr Machata 	struct mlxsw_sp_qdisc *mlxsw_sp_qdisc;
165419f405b9SPetr Machata 
1655*c2792f38SPetr Machata 	mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent);
165619f405b9SPetr Machata 	if (!mlxsw_sp_qdisc)
165719f405b9SPetr Machata 		return -EOPNOTSUPP;
165819f405b9SPetr Machata 
165919f405b9SPetr Machata 	if (p->command == TC_ETS_REPLACE)
166019f405b9SPetr Machata 		return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle,
166119f405b9SPetr Machata 					      mlxsw_sp_qdisc,
166219f405b9SPetr Machata 					      &mlxsw_sp_qdisc_ops_ets,
166319f405b9SPetr Machata 					      &p->replace_params);
166419f405b9SPetr Machata 
1665290fe2c5SPetr Machata 	if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle))
166619f405b9SPetr Machata 		return -EOPNOTSUPP;
166719f405b9SPetr Machata 
166819f405b9SPetr Machata 	switch (p->command) {
166919f405b9SPetr Machata 	case TC_ETS_DESTROY:
167019f405b9SPetr Machata 		return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
167119f405b9SPetr Machata 	case TC_ETS_STATS:
167219f405b9SPetr Machata 		return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc,
167319f405b9SPetr Machata 						&p->stats);
167419f405b9SPetr Machata 	case TC_ETS_GRAFT:
1675be7e2a5aSPetr Machata 		return mlxsw_sp_qdisc_graft(mlxsw_sp_port, mlxsw_sp_qdisc,
167619f405b9SPetr Machata 					    p->graft_params.band,
167719f405b9SPetr Machata 					    p->graft_params.child_handle);
167819f405b9SPetr Machata 	default:
167919f405b9SPetr Machata 		return -EOPNOTSUPP;
168019f405b9SPetr Machata 	}
168119f405b9SPetr Machata }
168219f405b9SPetr Machata 
1683cff99e20SPetr Machata int mlxsw_sp_setup_tc_ets(struct mlxsw_sp_port *mlxsw_sp_port,
1684cff99e20SPetr Machata 			  struct tc_ets_qopt_offload *p)
1685cff99e20SPetr Machata {
1686cff99e20SPetr Machata 	int err;
1687cff99e20SPetr Machata 
1688cff99e20SPetr Machata 	mutex_lock(&mlxsw_sp_port->qdisc->lock);
1689cff99e20SPetr Machata 	err = __mlxsw_sp_setup_tc_ets(mlxsw_sp_port, p);
1690cff99e20SPetr Machata 	mutex_unlock(&mlxsw_sp_port->qdisc->lock);
1691cff99e20SPetr Machata 
1692cff99e20SPetr Machata 	return err;
1693cff99e20SPetr Machata }
1694cff99e20SPetr Machata 
1695f6668eacSPetr Machata struct mlxsw_sp_qevent_block {
1696f6668eacSPetr Machata 	struct list_head binding_list;
1697f6668eacSPetr Machata 	struct list_head mall_entry_list;
1698f6668eacSPetr Machata 	struct mlxsw_sp *mlxsw_sp;
1699f6668eacSPetr Machata };
1700f6668eacSPetr Machata 
1701f6668eacSPetr Machata struct mlxsw_sp_qevent_binding {
1702f6668eacSPetr Machata 	struct list_head list;
1703f6668eacSPetr Machata 	struct mlxsw_sp_port *mlxsw_sp_port;
1704f6668eacSPetr Machata 	u32 handle;
1705f6668eacSPetr Machata 	int tclass_num;
1706f6668eacSPetr Machata 	enum mlxsw_sp_span_trigger span_trigger;
1707099bf89dSPetr Machata 	unsigned int action_mask;
1708f6668eacSPetr Machata };
1709f6668eacSPetr Machata 
1710f6668eacSPetr Machata static LIST_HEAD(mlxsw_sp_qevent_block_cb_list);
1711f6668eacSPetr Machata 
171254a92385SPetr Machata static int mlxsw_sp_qevent_span_configure(struct mlxsw_sp *mlxsw_sp,
1713f6668eacSPetr Machata 					  struct mlxsw_sp_mall_entry *mall_entry,
171454a92385SPetr Machata 					  struct mlxsw_sp_qevent_binding *qevent_binding,
171554a92385SPetr Machata 					  const struct mlxsw_sp_span_agent_parms *agent_parms,
171654a92385SPetr Machata 					  int *p_span_id)
1717f6668eacSPetr Machata {
17180908e42aSPetr Machata 	enum mlxsw_sp_span_trigger span_trigger = qevent_binding->span_trigger;
1719f6668eacSPetr Machata 	struct mlxsw_sp_port *mlxsw_sp_port = qevent_binding->mlxsw_sp_port;
1720f6668eacSPetr Machata 	struct mlxsw_sp_span_trigger_parms trigger_parms = {};
17210908e42aSPetr Machata 	bool ingress;
1722f6668eacSPetr Machata 	int span_id;
1723f6668eacSPetr Machata 	int err;
1724f6668eacSPetr Machata 
172554a92385SPetr Machata 	err = mlxsw_sp_span_agent_get(mlxsw_sp, &span_id, agent_parms);
1726f6668eacSPetr Machata 	if (err)
1727f6668eacSPetr Machata 		return err;
1728f6668eacSPetr Machata 
17290908e42aSPetr Machata 	ingress = mlxsw_sp_span_trigger_is_ingress(span_trigger);
17300908e42aSPetr Machata 	err = mlxsw_sp_span_analyzed_port_get(mlxsw_sp_port, ingress);
1731f6668eacSPetr Machata 	if (err)
1732f6668eacSPetr Machata 		goto err_analyzed_port_get;
1733f6668eacSPetr Machata 
1734f6668eacSPetr Machata 	trigger_parms.span_id = span_id;
17352dcbd920SIdo Schimmel 	trigger_parms.probability_rate = 1;
17360908e42aSPetr Machata 	err = mlxsw_sp_span_agent_bind(mlxsw_sp, span_trigger, mlxsw_sp_port,
1737f6668eacSPetr Machata 				       &trigger_parms);
1738f6668eacSPetr Machata 	if (err)
1739f6668eacSPetr Machata 		goto err_agent_bind;
1740f6668eacSPetr Machata 
17410908e42aSPetr Machata 	err = mlxsw_sp_span_trigger_enable(mlxsw_sp_port, span_trigger,
1742f6668eacSPetr Machata 					   qevent_binding->tclass_num);
1743f6668eacSPetr Machata 	if (err)
1744f6668eacSPetr Machata 		goto err_trigger_enable;
1745f6668eacSPetr Machata 
174654a92385SPetr Machata 	*p_span_id = span_id;
1747f6668eacSPetr Machata 	return 0;
1748f6668eacSPetr Machata 
1749f6668eacSPetr Machata err_trigger_enable:
17500908e42aSPetr Machata 	mlxsw_sp_span_agent_unbind(mlxsw_sp, span_trigger, mlxsw_sp_port,
1751f6668eacSPetr Machata 				   &trigger_parms);
1752f6668eacSPetr Machata err_agent_bind:
17530908e42aSPetr Machata 	mlxsw_sp_span_analyzed_port_put(mlxsw_sp_port, ingress);
1754f6668eacSPetr Machata err_analyzed_port_get:
1755f6668eacSPetr Machata 	mlxsw_sp_span_agent_put(mlxsw_sp, span_id);
1756f6668eacSPetr Machata 	return err;
1757f6668eacSPetr Machata }
1758f6668eacSPetr Machata 
175954a92385SPetr Machata static void mlxsw_sp_qevent_span_deconfigure(struct mlxsw_sp *mlxsw_sp,
176054a92385SPetr Machata 					     struct mlxsw_sp_qevent_binding *qevent_binding,
176154a92385SPetr Machata 					     int span_id)
1762f6668eacSPetr Machata {
17630908e42aSPetr Machata 	enum mlxsw_sp_span_trigger span_trigger = qevent_binding->span_trigger;
1764f6668eacSPetr Machata 	struct mlxsw_sp_port *mlxsw_sp_port = qevent_binding->mlxsw_sp_port;
1765f6668eacSPetr Machata 	struct mlxsw_sp_span_trigger_parms trigger_parms = {
176654a92385SPetr Machata 		.span_id = span_id,
1767f6668eacSPetr Machata 	};
17680908e42aSPetr Machata 	bool ingress;
1769f6668eacSPetr Machata 
17700908e42aSPetr Machata 	ingress = mlxsw_sp_span_trigger_is_ingress(span_trigger);
17710908e42aSPetr Machata 
17720908e42aSPetr Machata 	mlxsw_sp_span_trigger_disable(mlxsw_sp_port, span_trigger,
1773f6668eacSPetr Machata 				      qevent_binding->tclass_num);
17740908e42aSPetr Machata 	mlxsw_sp_span_agent_unbind(mlxsw_sp, span_trigger, mlxsw_sp_port,
1775f6668eacSPetr Machata 				   &trigger_parms);
17760908e42aSPetr Machata 	mlxsw_sp_span_analyzed_port_put(mlxsw_sp_port, ingress);
177754a92385SPetr Machata 	mlxsw_sp_span_agent_put(mlxsw_sp, span_id);
177854a92385SPetr Machata }
177954a92385SPetr Machata 
178054a92385SPetr Machata static int mlxsw_sp_qevent_mirror_configure(struct mlxsw_sp *mlxsw_sp,
178154a92385SPetr Machata 					    struct mlxsw_sp_mall_entry *mall_entry,
178254a92385SPetr Machata 					    struct mlxsw_sp_qevent_binding *qevent_binding)
178354a92385SPetr Machata {
178454a92385SPetr Machata 	struct mlxsw_sp_span_agent_parms agent_parms = {
178554a92385SPetr Machata 		.to_dev = mall_entry->mirror.to_dev,
178654a92385SPetr Machata 	};
178754a92385SPetr Machata 
178854a92385SPetr Machata 	return mlxsw_sp_qevent_span_configure(mlxsw_sp, mall_entry, qevent_binding,
178954a92385SPetr Machata 					      &agent_parms, &mall_entry->mirror.span_id);
179054a92385SPetr Machata }
179154a92385SPetr Machata 
179254a92385SPetr Machata static void mlxsw_sp_qevent_mirror_deconfigure(struct mlxsw_sp *mlxsw_sp,
179354a92385SPetr Machata 					       struct mlxsw_sp_mall_entry *mall_entry,
179454a92385SPetr Machata 					       struct mlxsw_sp_qevent_binding *qevent_binding)
179554a92385SPetr Machata {
179654a92385SPetr Machata 	mlxsw_sp_qevent_span_deconfigure(mlxsw_sp, qevent_binding, mall_entry->mirror.span_id);
179754a92385SPetr Machata }
179854a92385SPetr Machata 
179954a92385SPetr Machata static int mlxsw_sp_qevent_trap_configure(struct mlxsw_sp *mlxsw_sp,
180054a92385SPetr Machata 					  struct mlxsw_sp_mall_entry *mall_entry,
180154a92385SPetr Machata 					  struct mlxsw_sp_qevent_binding *qevent_binding)
180254a92385SPetr Machata {
18035c7659ebSIdo Schimmel 	struct mlxsw_sp_span_agent_parms agent_parms = {
18045c7659ebSIdo Schimmel 		.session_id = MLXSW_SP_SPAN_SESSION_ID_BUFFER,
18055c7659ebSIdo Schimmel 	};
180654a92385SPetr Machata 	int err;
180754a92385SPetr Machata 
180854a92385SPetr Machata 	err = mlxsw_sp_trap_group_policer_hw_id_get(mlxsw_sp,
180954a92385SPetr Machata 						    DEVLINK_TRAP_GROUP_GENERIC_ID_BUFFER_DROPS,
181054a92385SPetr Machata 						    &agent_parms.policer_enable,
181154a92385SPetr Machata 						    &agent_parms.policer_id);
181254a92385SPetr Machata 	if (err)
181354a92385SPetr Machata 		return err;
181454a92385SPetr Machata 
181554a92385SPetr Machata 	return mlxsw_sp_qevent_span_configure(mlxsw_sp, mall_entry, qevent_binding,
181654a92385SPetr Machata 					      &agent_parms, &mall_entry->trap.span_id);
181754a92385SPetr Machata }
181854a92385SPetr Machata 
181954a92385SPetr Machata static void mlxsw_sp_qevent_trap_deconfigure(struct mlxsw_sp *mlxsw_sp,
182054a92385SPetr Machata 					     struct mlxsw_sp_mall_entry *mall_entry,
182154a92385SPetr Machata 					     struct mlxsw_sp_qevent_binding *qevent_binding)
182254a92385SPetr Machata {
182354a92385SPetr Machata 	mlxsw_sp_qevent_span_deconfigure(mlxsw_sp, qevent_binding, mall_entry->trap.span_id);
1824f6668eacSPetr Machata }
1825f6668eacSPetr Machata 
1826a34dda72SPetr Machata static int
1827a34dda72SPetr Machata mlxsw_sp_qevent_entry_configure(struct mlxsw_sp *mlxsw_sp,
1828f6668eacSPetr Machata 				struct mlxsw_sp_mall_entry *mall_entry,
1829a34dda72SPetr Machata 				struct mlxsw_sp_qevent_binding *qevent_binding,
1830a34dda72SPetr Machata 				struct netlink_ext_ack *extack)
1831f6668eacSPetr Machata {
1832099bf89dSPetr Machata 	if (!(BIT(mall_entry->type) & qevent_binding->action_mask)) {
1833099bf89dSPetr Machata 		NL_SET_ERR_MSG(extack, "Action not supported at this qevent");
1834099bf89dSPetr Machata 		return -EOPNOTSUPP;
1835099bf89dSPetr Machata 	}
1836099bf89dSPetr Machata 
1837f6668eacSPetr Machata 	switch (mall_entry->type) {
1838f6668eacSPetr Machata 	case MLXSW_SP_MALL_ACTION_TYPE_MIRROR:
1839f6668eacSPetr Machata 		return mlxsw_sp_qevent_mirror_configure(mlxsw_sp, mall_entry, qevent_binding);
184054a92385SPetr Machata 	case MLXSW_SP_MALL_ACTION_TYPE_TRAP:
184154a92385SPetr Machata 		return mlxsw_sp_qevent_trap_configure(mlxsw_sp, mall_entry, qevent_binding);
1842f6668eacSPetr Machata 	default:
1843f6668eacSPetr Machata 		/* This should have been validated away. */
1844f6668eacSPetr Machata 		WARN_ON(1);
1845f6668eacSPetr Machata 		return -EOPNOTSUPP;
1846f6668eacSPetr Machata 	}
1847f6668eacSPetr Machata }
1848f6668eacSPetr Machata 
1849f6668eacSPetr Machata static void mlxsw_sp_qevent_entry_deconfigure(struct mlxsw_sp *mlxsw_sp,
1850f6668eacSPetr Machata 					      struct mlxsw_sp_mall_entry *mall_entry,
1851f6668eacSPetr Machata 					      struct mlxsw_sp_qevent_binding *qevent_binding)
1852f6668eacSPetr Machata {
1853f6668eacSPetr Machata 	switch (mall_entry->type) {
1854f6668eacSPetr Machata 	case MLXSW_SP_MALL_ACTION_TYPE_MIRROR:
1855f6668eacSPetr Machata 		return mlxsw_sp_qevent_mirror_deconfigure(mlxsw_sp, mall_entry, qevent_binding);
185654a92385SPetr Machata 	case MLXSW_SP_MALL_ACTION_TYPE_TRAP:
185754a92385SPetr Machata 		return mlxsw_sp_qevent_trap_deconfigure(mlxsw_sp, mall_entry, qevent_binding);
1858f6668eacSPetr Machata 	default:
1859f6668eacSPetr Machata 		WARN_ON(1);
1860f6668eacSPetr Machata 		return;
1861f6668eacSPetr Machata 	}
1862f6668eacSPetr Machata }
1863f6668eacSPetr Machata 
1864a34dda72SPetr Machata static int
1865a34dda72SPetr Machata mlxsw_sp_qevent_binding_configure(struct mlxsw_sp_qevent_block *qevent_block,
1866a34dda72SPetr Machata 				  struct mlxsw_sp_qevent_binding *qevent_binding,
1867a34dda72SPetr Machata 				  struct netlink_ext_ack *extack)
1868f6668eacSPetr Machata {
1869f6668eacSPetr Machata 	struct mlxsw_sp_mall_entry *mall_entry;
1870f6668eacSPetr Machata 	int err;
1871f6668eacSPetr Machata 
1872f6668eacSPetr Machata 	list_for_each_entry(mall_entry, &qevent_block->mall_entry_list, list) {
1873f6668eacSPetr Machata 		err = mlxsw_sp_qevent_entry_configure(qevent_block->mlxsw_sp, mall_entry,
1874a34dda72SPetr Machata 						      qevent_binding, extack);
1875f6668eacSPetr Machata 		if (err)
1876f6668eacSPetr Machata 			goto err_entry_configure;
1877f6668eacSPetr Machata 	}
1878f6668eacSPetr Machata 
1879f6668eacSPetr Machata 	return 0;
1880f6668eacSPetr Machata 
1881f6668eacSPetr Machata err_entry_configure:
1882f6668eacSPetr Machata 	list_for_each_entry_continue_reverse(mall_entry, &qevent_block->mall_entry_list, list)
1883f6668eacSPetr Machata 		mlxsw_sp_qevent_entry_deconfigure(qevent_block->mlxsw_sp, mall_entry,
1884f6668eacSPetr Machata 						  qevent_binding);
1885f6668eacSPetr Machata 	return err;
1886f6668eacSPetr Machata }
1887f6668eacSPetr Machata 
1888f6668eacSPetr Machata static void mlxsw_sp_qevent_binding_deconfigure(struct mlxsw_sp_qevent_block *qevent_block,
1889f6668eacSPetr Machata 						struct mlxsw_sp_qevent_binding *qevent_binding)
1890f6668eacSPetr Machata {
1891f6668eacSPetr Machata 	struct mlxsw_sp_mall_entry *mall_entry;
1892f6668eacSPetr Machata 
1893f6668eacSPetr Machata 	list_for_each_entry(mall_entry, &qevent_block->mall_entry_list, list)
1894f6668eacSPetr Machata 		mlxsw_sp_qevent_entry_deconfigure(qevent_block->mlxsw_sp, mall_entry,
1895f6668eacSPetr Machata 						  qevent_binding);
1896f6668eacSPetr Machata }
1897f6668eacSPetr Machata 
1898a34dda72SPetr Machata static int
1899a34dda72SPetr Machata mlxsw_sp_qevent_block_configure(struct mlxsw_sp_qevent_block *qevent_block,
1900a34dda72SPetr Machata 				struct netlink_ext_ack *extack)
1901f6668eacSPetr Machata {
1902f6668eacSPetr Machata 	struct mlxsw_sp_qevent_binding *qevent_binding;
1903f6668eacSPetr Machata 	int err;
1904f6668eacSPetr Machata 
1905f6668eacSPetr Machata 	list_for_each_entry(qevent_binding, &qevent_block->binding_list, list) {
1906a34dda72SPetr Machata 		err = mlxsw_sp_qevent_binding_configure(qevent_block,
1907a34dda72SPetr Machata 							qevent_binding,
1908a34dda72SPetr Machata 							extack);
1909f6668eacSPetr Machata 		if (err)
1910f6668eacSPetr Machata 			goto err_binding_configure;
1911f6668eacSPetr Machata 	}
1912f6668eacSPetr Machata 
1913f6668eacSPetr Machata 	return 0;
1914f6668eacSPetr Machata 
1915f6668eacSPetr Machata err_binding_configure:
1916f6668eacSPetr Machata 	list_for_each_entry_continue_reverse(qevent_binding, &qevent_block->binding_list, list)
1917f6668eacSPetr Machata 		mlxsw_sp_qevent_binding_deconfigure(qevent_block, qevent_binding);
1918f6668eacSPetr Machata 	return err;
1919f6668eacSPetr Machata }
1920f6668eacSPetr Machata 
1921f6668eacSPetr Machata static void mlxsw_sp_qevent_block_deconfigure(struct mlxsw_sp_qevent_block *qevent_block)
1922f6668eacSPetr Machata {
1923f6668eacSPetr Machata 	struct mlxsw_sp_qevent_binding *qevent_binding;
1924f6668eacSPetr Machata 
1925f6668eacSPetr Machata 	list_for_each_entry(qevent_binding, &qevent_block->binding_list, list)
1926f6668eacSPetr Machata 		mlxsw_sp_qevent_binding_deconfigure(qevent_block, qevent_binding);
1927f6668eacSPetr Machata }
1928f6668eacSPetr Machata 
1929f6668eacSPetr Machata static struct mlxsw_sp_mall_entry *
1930f6668eacSPetr Machata mlxsw_sp_qevent_mall_entry_find(struct mlxsw_sp_qevent_block *block, unsigned long cookie)
1931f6668eacSPetr Machata {
1932f6668eacSPetr Machata 	struct mlxsw_sp_mall_entry *mall_entry;
1933f6668eacSPetr Machata 
1934f6668eacSPetr Machata 	list_for_each_entry(mall_entry, &block->mall_entry_list, list)
1935f6668eacSPetr Machata 		if (mall_entry->cookie == cookie)
1936f6668eacSPetr Machata 			return mall_entry;
1937f6668eacSPetr Machata 
1938f6668eacSPetr Machata 	return NULL;
1939f6668eacSPetr Machata }
1940f6668eacSPetr Machata 
1941f6668eacSPetr Machata static int mlxsw_sp_qevent_mall_replace(struct mlxsw_sp *mlxsw_sp,
1942f6668eacSPetr Machata 					struct mlxsw_sp_qevent_block *qevent_block,
1943f6668eacSPetr Machata 					struct tc_cls_matchall_offload *f)
1944f6668eacSPetr Machata {
1945f6668eacSPetr Machata 	struct mlxsw_sp_mall_entry *mall_entry;
1946f6668eacSPetr Machata 	struct flow_action_entry *act;
1947f6668eacSPetr Machata 	int err;
1948f6668eacSPetr Machata 
1949f6668eacSPetr Machata 	/* It should not currently be possible to replace a matchall rule. So
1950f6668eacSPetr Machata 	 * this must be a new rule.
1951f6668eacSPetr Machata 	 */
1952f6668eacSPetr Machata 	if (!list_empty(&qevent_block->mall_entry_list)) {
1953f6668eacSPetr Machata 		NL_SET_ERR_MSG(f->common.extack, "At most one filter supported");
1954f6668eacSPetr Machata 		return -EOPNOTSUPP;
1955f6668eacSPetr Machata 	}
1956f6668eacSPetr Machata 	if (f->rule->action.num_entries != 1) {
1957f6668eacSPetr Machata 		NL_SET_ERR_MSG(f->common.extack, "Only singular actions supported");
1958f6668eacSPetr Machata 		return -EOPNOTSUPP;
1959f6668eacSPetr Machata 	}
1960f6668eacSPetr Machata 	if (f->common.chain_index) {
1961f6668eacSPetr Machata 		NL_SET_ERR_MSG(f->common.extack, "Only chain 0 is supported");
1962f6668eacSPetr Machata 		return -EOPNOTSUPP;
1963f6668eacSPetr Machata 	}
1964f6668eacSPetr Machata 	if (f->common.protocol != htons(ETH_P_ALL)) {
1965f6668eacSPetr Machata 		NL_SET_ERR_MSG(f->common.extack, "Protocol matching not supported");
1966f6668eacSPetr Machata 		return -EOPNOTSUPP;
1967f6668eacSPetr Machata 	}
1968f6668eacSPetr Machata 
1969f6668eacSPetr Machata 	act = &f->rule->action.entries[0];
1970f6668eacSPetr Machata 	if (!(act->hw_stats & FLOW_ACTION_HW_STATS_DISABLED)) {
1971f6668eacSPetr Machata 		NL_SET_ERR_MSG(f->common.extack, "HW counters not supported on qevents");
1972f6668eacSPetr Machata 		return -EOPNOTSUPP;
1973f6668eacSPetr Machata 	}
1974f6668eacSPetr Machata 
1975f6668eacSPetr Machata 	mall_entry = kzalloc(sizeof(*mall_entry), GFP_KERNEL);
1976f6668eacSPetr Machata 	if (!mall_entry)
1977f6668eacSPetr Machata 		return -ENOMEM;
1978f6668eacSPetr Machata 	mall_entry->cookie = f->cookie;
1979f6668eacSPetr Machata 
1980f6668eacSPetr Machata 	if (act->id == FLOW_ACTION_MIRRED) {
1981f6668eacSPetr Machata 		mall_entry->type = MLXSW_SP_MALL_ACTION_TYPE_MIRROR;
1982f6668eacSPetr Machata 		mall_entry->mirror.to_dev = act->dev;
198354a92385SPetr Machata 	} else if (act->id == FLOW_ACTION_TRAP) {
198454a92385SPetr Machata 		mall_entry->type = MLXSW_SP_MALL_ACTION_TYPE_TRAP;
1985f6668eacSPetr Machata 	} else {
1986f6668eacSPetr Machata 		NL_SET_ERR_MSG(f->common.extack, "Unsupported action");
1987f6668eacSPetr Machata 		err = -EOPNOTSUPP;
1988f6668eacSPetr Machata 		goto err_unsupported_action;
1989f6668eacSPetr Machata 	}
1990f6668eacSPetr Machata 
1991f6668eacSPetr Machata 	list_add_tail(&mall_entry->list, &qevent_block->mall_entry_list);
1992f6668eacSPetr Machata 
1993a34dda72SPetr Machata 	err = mlxsw_sp_qevent_block_configure(qevent_block, f->common.extack);
1994f6668eacSPetr Machata 	if (err)
1995f6668eacSPetr Machata 		goto err_block_configure;
1996f6668eacSPetr Machata 
1997f6668eacSPetr Machata 	return 0;
1998f6668eacSPetr Machata 
1999f6668eacSPetr Machata err_block_configure:
2000f6668eacSPetr Machata 	list_del(&mall_entry->list);
2001f6668eacSPetr Machata err_unsupported_action:
2002f6668eacSPetr Machata 	kfree(mall_entry);
2003f6668eacSPetr Machata 	return err;
2004f6668eacSPetr Machata }
2005f6668eacSPetr Machata 
2006f6668eacSPetr Machata static void mlxsw_sp_qevent_mall_destroy(struct mlxsw_sp_qevent_block *qevent_block,
2007f6668eacSPetr Machata 					 struct tc_cls_matchall_offload *f)
2008f6668eacSPetr Machata {
2009f6668eacSPetr Machata 	struct mlxsw_sp_mall_entry *mall_entry;
2010f6668eacSPetr Machata 
2011f6668eacSPetr Machata 	mall_entry = mlxsw_sp_qevent_mall_entry_find(qevent_block, f->cookie);
2012f6668eacSPetr Machata 	if (!mall_entry)
2013f6668eacSPetr Machata 		return;
2014f6668eacSPetr Machata 
2015f6668eacSPetr Machata 	mlxsw_sp_qevent_block_deconfigure(qevent_block);
2016f6668eacSPetr Machata 
2017f6668eacSPetr Machata 	list_del(&mall_entry->list);
2018f6668eacSPetr Machata 	kfree(mall_entry);
2019f6668eacSPetr Machata }
2020f6668eacSPetr Machata 
2021f6668eacSPetr Machata static int mlxsw_sp_qevent_block_mall_cb(struct mlxsw_sp_qevent_block *qevent_block,
2022f6668eacSPetr Machata 					 struct tc_cls_matchall_offload *f)
2023f6668eacSPetr Machata {
2024f6668eacSPetr Machata 	struct mlxsw_sp *mlxsw_sp = qevent_block->mlxsw_sp;
2025f6668eacSPetr Machata 
2026f6668eacSPetr Machata 	switch (f->command) {
2027f6668eacSPetr Machata 	case TC_CLSMATCHALL_REPLACE:
2028f6668eacSPetr Machata 		return mlxsw_sp_qevent_mall_replace(mlxsw_sp, qevent_block, f);
2029f6668eacSPetr Machata 	case TC_CLSMATCHALL_DESTROY:
2030f6668eacSPetr Machata 		mlxsw_sp_qevent_mall_destroy(qevent_block, f);
2031f6668eacSPetr Machata 		return 0;
2032f6668eacSPetr Machata 	default:
2033f6668eacSPetr Machata 		return -EOPNOTSUPP;
2034f6668eacSPetr Machata 	}
2035f6668eacSPetr Machata }
2036f6668eacSPetr Machata 
2037f6668eacSPetr Machata static int mlxsw_sp_qevent_block_cb(enum tc_setup_type type, void *type_data, void *cb_priv)
2038f6668eacSPetr Machata {
2039f6668eacSPetr Machata 	struct mlxsw_sp_qevent_block *qevent_block = cb_priv;
2040f6668eacSPetr Machata 
2041f6668eacSPetr Machata 	switch (type) {
2042f6668eacSPetr Machata 	case TC_SETUP_CLSMATCHALL:
2043f6668eacSPetr Machata 		return mlxsw_sp_qevent_block_mall_cb(qevent_block, type_data);
2044f6668eacSPetr Machata 	default:
2045f6668eacSPetr Machata 		return -EOPNOTSUPP;
2046f6668eacSPetr Machata 	}
2047f6668eacSPetr Machata }
2048f6668eacSPetr Machata 
2049f6668eacSPetr Machata static struct mlxsw_sp_qevent_block *mlxsw_sp_qevent_block_create(struct mlxsw_sp *mlxsw_sp,
2050f6668eacSPetr Machata 								  struct net *net)
2051f6668eacSPetr Machata {
2052f6668eacSPetr Machata 	struct mlxsw_sp_qevent_block *qevent_block;
2053f6668eacSPetr Machata 
2054f6668eacSPetr Machata 	qevent_block = kzalloc(sizeof(*qevent_block), GFP_KERNEL);
2055f6668eacSPetr Machata 	if (!qevent_block)
2056f6668eacSPetr Machata 		return NULL;
2057f6668eacSPetr Machata 
2058f6668eacSPetr Machata 	INIT_LIST_HEAD(&qevent_block->binding_list);
2059f6668eacSPetr Machata 	INIT_LIST_HEAD(&qevent_block->mall_entry_list);
2060f6668eacSPetr Machata 	qevent_block->mlxsw_sp = mlxsw_sp;
2061f6668eacSPetr Machata 	return qevent_block;
2062f6668eacSPetr Machata }
2063f6668eacSPetr Machata 
2064f6668eacSPetr Machata static void
2065f6668eacSPetr Machata mlxsw_sp_qevent_block_destroy(struct mlxsw_sp_qevent_block *qevent_block)
2066f6668eacSPetr Machata {
2067f6668eacSPetr Machata 	WARN_ON(!list_empty(&qevent_block->binding_list));
2068f6668eacSPetr Machata 	WARN_ON(!list_empty(&qevent_block->mall_entry_list));
2069f6668eacSPetr Machata 	kfree(qevent_block);
2070f6668eacSPetr Machata }
2071f6668eacSPetr Machata 
2072f6668eacSPetr Machata static void mlxsw_sp_qevent_block_release(void *cb_priv)
2073f6668eacSPetr Machata {
2074f6668eacSPetr Machata 	struct mlxsw_sp_qevent_block *qevent_block = cb_priv;
2075f6668eacSPetr Machata 
2076f6668eacSPetr Machata 	mlxsw_sp_qevent_block_destroy(qevent_block);
2077f6668eacSPetr Machata }
2078f6668eacSPetr Machata 
2079f6668eacSPetr Machata static struct mlxsw_sp_qevent_binding *
2080f6668eacSPetr Machata mlxsw_sp_qevent_binding_create(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, int tclass_num,
2081099bf89dSPetr Machata 			       enum mlxsw_sp_span_trigger span_trigger,
2082099bf89dSPetr Machata 			       unsigned int action_mask)
2083f6668eacSPetr Machata {
2084f6668eacSPetr Machata 	struct mlxsw_sp_qevent_binding *binding;
2085f6668eacSPetr Machata 
2086f6668eacSPetr Machata 	binding = kzalloc(sizeof(*binding), GFP_KERNEL);
2087f6668eacSPetr Machata 	if (!binding)
2088f6668eacSPetr Machata 		return ERR_PTR(-ENOMEM);
2089f6668eacSPetr Machata 
2090f6668eacSPetr Machata 	binding->mlxsw_sp_port = mlxsw_sp_port;
2091f6668eacSPetr Machata 	binding->handle = handle;
2092f6668eacSPetr Machata 	binding->tclass_num = tclass_num;
2093f6668eacSPetr Machata 	binding->span_trigger = span_trigger;
2094099bf89dSPetr Machata 	binding->action_mask = action_mask;
2095f6668eacSPetr Machata 	return binding;
2096f6668eacSPetr Machata }
2097f6668eacSPetr Machata 
2098f6668eacSPetr Machata static void
2099f6668eacSPetr Machata mlxsw_sp_qevent_binding_destroy(struct mlxsw_sp_qevent_binding *binding)
2100f6668eacSPetr Machata {
2101f6668eacSPetr Machata 	kfree(binding);
2102f6668eacSPetr Machata }
2103f6668eacSPetr Machata 
2104f6668eacSPetr Machata static struct mlxsw_sp_qevent_binding *
2105f6668eacSPetr Machata mlxsw_sp_qevent_binding_lookup(struct mlxsw_sp_qevent_block *block,
2106f6668eacSPetr Machata 			       struct mlxsw_sp_port *mlxsw_sp_port,
2107f6668eacSPetr Machata 			       u32 handle,
2108f6668eacSPetr Machata 			       enum mlxsw_sp_span_trigger span_trigger)
2109f6668eacSPetr Machata {
2110f6668eacSPetr Machata 	struct mlxsw_sp_qevent_binding *qevent_binding;
2111f6668eacSPetr Machata 
2112f6668eacSPetr Machata 	list_for_each_entry(qevent_binding, &block->binding_list, list)
2113f6668eacSPetr Machata 		if (qevent_binding->mlxsw_sp_port == mlxsw_sp_port &&
2114f6668eacSPetr Machata 		    qevent_binding->handle == handle &&
2115f6668eacSPetr Machata 		    qevent_binding->span_trigger == span_trigger)
2116f6668eacSPetr Machata 			return qevent_binding;
2117f6668eacSPetr Machata 	return NULL;
2118f6668eacSPetr Machata }
2119f6668eacSPetr Machata 
2120099bf89dSPetr Machata static int
2121099bf89dSPetr Machata mlxsw_sp_setup_tc_block_qevent_bind(struct mlxsw_sp_port *mlxsw_sp_port,
2122f6668eacSPetr Machata 				    struct flow_block_offload *f,
2123099bf89dSPetr Machata 				    enum mlxsw_sp_span_trigger span_trigger,
2124099bf89dSPetr Machata 				    unsigned int action_mask)
2125f6668eacSPetr Machata {
2126f6668eacSPetr Machata 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
2127f6668eacSPetr Machata 	struct mlxsw_sp_qevent_binding *qevent_binding;
2128f6668eacSPetr Machata 	struct mlxsw_sp_qevent_block *qevent_block;
2129f6668eacSPetr Machata 	struct flow_block_cb *block_cb;
2130f6668eacSPetr Machata 	struct mlxsw_sp_qdisc *qdisc;
2131f6668eacSPetr Machata 	bool register_block = false;
213276ff72a7SPetr Machata 	int tclass_num;
2133f6668eacSPetr Machata 	int err;
2134f6668eacSPetr Machata 
2135f6668eacSPetr Machata 	block_cb = flow_block_cb_lookup(f->block, mlxsw_sp_qevent_block_cb, mlxsw_sp);
2136f6668eacSPetr Machata 	if (!block_cb) {
2137f6668eacSPetr Machata 		qevent_block = mlxsw_sp_qevent_block_create(mlxsw_sp, f->net);
2138f6668eacSPetr Machata 		if (!qevent_block)
2139f6668eacSPetr Machata 			return -ENOMEM;
2140f6668eacSPetr Machata 		block_cb = flow_block_cb_alloc(mlxsw_sp_qevent_block_cb, mlxsw_sp, qevent_block,
2141f6668eacSPetr Machata 					       mlxsw_sp_qevent_block_release);
2142f6668eacSPetr Machata 		if (IS_ERR(block_cb)) {
2143f6668eacSPetr Machata 			mlxsw_sp_qevent_block_destroy(qevent_block);
2144f6668eacSPetr Machata 			return PTR_ERR(block_cb);
2145f6668eacSPetr Machata 		}
2146f6668eacSPetr Machata 		register_block = true;
2147f6668eacSPetr Machata 	} else {
2148f6668eacSPetr Machata 		qevent_block = flow_block_cb_priv(block_cb);
2149f6668eacSPetr Machata 	}
2150f6668eacSPetr Machata 	flow_block_cb_incref(block_cb);
2151f6668eacSPetr Machata 
2152f6668eacSPetr Machata 	qdisc = mlxsw_sp_qdisc_find_by_handle(mlxsw_sp_port, f->sch->handle);
2153f6668eacSPetr Machata 	if (!qdisc) {
2154f6668eacSPetr Machata 		NL_SET_ERR_MSG(f->extack, "Qdisc not offloaded");
2155f6668eacSPetr Machata 		err = -ENOENT;
2156f6668eacSPetr Machata 		goto err_find_qdisc;
2157f6668eacSPetr Machata 	}
2158f6668eacSPetr Machata 
2159f6668eacSPetr Machata 	if (WARN_ON(mlxsw_sp_qevent_binding_lookup(qevent_block, mlxsw_sp_port, f->sch->handle,
2160f6668eacSPetr Machata 						   span_trigger))) {
2161f6668eacSPetr Machata 		err = -EEXIST;
2162f6668eacSPetr Machata 		goto err_binding_exists;
2163f6668eacSPetr Machata 	}
2164f6668eacSPetr Machata 
216576ff72a7SPetr Machata 	tclass_num = mlxsw_sp_qdisc_get_tclass_num(mlxsw_sp_port, qdisc);
2166099bf89dSPetr Machata 	qevent_binding = mlxsw_sp_qevent_binding_create(mlxsw_sp_port,
2167099bf89dSPetr Machata 							f->sch->handle,
216876ff72a7SPetr Machata 							tclass_num,
2169099bf89dSPetr Machata 							span_trigger,
2170099bf89dSPetr Machata 							action_mask);
2171f6668eacSPetr Machata 	if (IS_ERR(qevent_binding)) {
2172f6668eacSPetr Machata 		err = PTR_ERR(qevent_binding);
2173f6668eacSPetr Machata 		goto err_binding_create;
2174f6668eacSPetr Machata 	}
2175f6668eacSPetr Machata 
2176a34dda72SPetr Machata 	err = mlxsw_sp_qevent_binding_configure(qevent_block, qevent_binding,
2177a34dda72SPetr Machata 						f->extack);
2178f6668eacSPetr Machata 	if (err)
2179f6668eacSPetr Machata 		goto err_binding_configure;
2180f6668eacSPetr Machata 
2181f6668eacSPetr Machata 	list_add(&qevent_binding->list, &qevent_block->binding_list);
2182f6668eacSPetr Machata 
2183f6668eacSPetr Machata 	if (register_block) {
2184f6668eacSPetr Machata 		flow_block_cb_add(block_cb, f);
2185f6668eacSPetr Machata 		list_add_tail(&block_cb->driver_list, &mlxsw_sp_qevent_block_cb_list);
2186f6668eacSPetr Machata 	}
2187f6668eacSPetr Machata 
2188f6668eacSPetr Machata 	return 0;
2189f6668eacSPetr Machata 
2190f6668eacSPetr Machata err_binding_configure:
2191f6668eacSPetr Machata 	mlxsw_sp_qevent_binding_destroy(qevent_binding);
2192f6668eacSPetr Machata err_binding_create:
2193f6668eacSPetr Machata err_binding_exists:
2194f6668eacSPetr Machata err_find_qdisc:
2195f6668eacSPetr Machata 	if (!flow_block_cb_decref(block_cb))
2196f6668eacSPetr Machata 		flow_block_cb_free(block_cb);
2197f6668eacSPetr Machata 	return err;
2198f6668eacSPetr Machata }
2199f6668eacSPetr Machata 
2200f6668eacSPetr Machata static void mlxsw_sp_setup_tc_block_qevent_unbind(struct mlxsw_sp_port *mlxsw_sp_port,
2201f6668eacSPetr Machata 						  struct flow_block_offload *f,
2202f6668eacSPetr Machata 						  enum mlxsw_sp_span_trigger span_trigger)
2203f6668eacSPetr Machata {
2204f6668eacSPetr Machata 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
2205f6668eacSPetr Machata 	struct mlxsw_sp_qevent_binding *qevent_binding;
2206f6668eacSPetr Machata 	struct mlxsw_sp_qevent_block *qevent_block;
2207f6668eacSPetr Machata 	struct flow_block_cb *block_cb;
2208f6668eacSPetr Machata 
2209f6668eacSPetr Machata 	block_cb = flow_block_cb_lookup(f->block, mlxsw_sp_qevent_block_cb, mlxsw_sp);
2210f6668eacSPetr Machata 	if (!block_cb)
2211f6668eacSPetr Machata 		return;
2212f6668eacSPetr Machata 	qevent_block = flow_block_cb_priv(block_cb);
2213f6668eacSPetr Machata 
2214f6668eacSPetr Machata 	qevent_binding = mlxsw_sp_qevent_binding_lookup(qevent_block, mlxsw_sp_port, f->sch->handle,
2215f6668eacSPetr Machata 							span_trigger);
2216f6668eacSPetr Machata 	if (!qevent_binding)
2217f6668eacSPetr Machata 		return;
2218f6668eacSPetr Machata 
2219f6668eacSPetr Machata 	list_del(&qevent_binding->list);
2220f6668eacSPetr Machata 	mlxsw_sp_qevent_binding_deconfigure(qevent_block, qevent_binding);
2221f6668eacSPetr Machata 	mlxsw_sp_qevent_binding_destroy(qevent_binding);
2222f6668eacSPetr Machata 
2223f6668eacSPetr Machata 	if (!flow_block_cb_decref(block_cb)) {
2224f6668eacSPetr Machata 		flow_block_cb_remove(block_cb, f);
2225f6668eacSPetr Machata 		list_del(&block_cb->driver_list);
2226f6668eacSPetr Machata 	}
2227f6668eacSPetr Machata }
2228f6668eacSPetr Machata 
2229099bf89dSPetr Machata static int
2230099bf89dSPetr Machata mlxsw_sp_setup_tc_block_qevent(struct mlxsw_sp_port *mlxsw_sp_port,
2231f6668eacSPetr Machata 			       struct flow_block_offload *f,
2232099bf89dSPetr Machata 			       enum mlxsw_sp_span_trigger span_trigger,
2233099bf89dSPetr Machata 			       unsigned int action_mask)
2234f6668eacSPetr Machata {
2235f6668eacSPetr Machata 	f->driver_block_list = &mlxsw_sp_qevent_block_cb_list;
2236f6668eacSPetr Machata 
2237f6668eacSPetr Machata 	switch (f->command) {
2238f6668eacSPetr Machata 	case FLOW_BLOCK_BIND:
2239099bf89dSPetr Machata 		return mlxsw_sp_setup_tc_block_qevent_bind(mlxsw_sp_port, f,
2240099bf89dSPetr Machata 							   span_trigger,
2241099bf89dSPetr Machata 							   action_mask);
2242f6668eacSPetr Machata 	case FLOW_BLOCK_UNBIND:
2243f6668eacSPetr Machata 		mlxsw_sp_setup_tc_block_qevent_unbind(mlxsw_sp_port, f, span_trigger);
2244f6668eacSPetr Machata 		return 0;
2245f6668eacSPetr Machata 	default:
2246f6668eacSPetr Machata 		return -EOPNOTSUPP;
2247f6668eacSPetr Machata 	}
2248f6668eacSPetr Machata }
2249f6668eacSPetr Machata 
2250f6668eacSPetr Machata int mlxsw_sp_setup_tc_block_qevent_early_drop(struct mlxsw_sp_port *mlxsw_sp_port,
2251f6668eacSPetr Machata 					      struct flow_block_offload *f)
2252f6668eacSPetr Machata {
2253099bf89dSPetr Machata 	unsigned int action_mask = BIT(MLXSW_SP_MALL_ACTION_TYPE_MIRROR) |
2254099bf89dSPetr Machata 				   BIT(MLXSW_SP_MALL_ACTION_TYPE_TRAP);
2255099bf89dSPetr Machata 
2256099bf89dSPetr Machata 	return mlxsw_sp_setup_tc_block_qevent(mlxsw_sp_port, f,
2257099bf89dSPetr Machata 					      MLXSW_SP_SPAN_TRIGGER_EARLY_DROP,
2258099bf89dSPetr Machata 					      action_mask);
2259f6668eacSPetr Machata }
2260f6668eacSPetr Machata 
22619c18eaf2SPetr Machata int mlxsw_sp_setup_tc_block_qevent_mark(struct mlxsw_sp_port *mlxsw_sp_port,
22629c18eaf2SPetr Machata 					struct flow_block_offload *f)
22639c18eaf2SPetr Machata {
22649c18eaf2SPetr Machata 	unsigned int action_mask = BIT(MLXSW_SP_MALL_ACTION_TYPE_MIRROR);
22659c18eaf2SPetr Machata 
22669c18eaf2SPetr Machata 	return mlxsw_sp_setup_tc_block_qevent(mlxsw_sp_port, f,
22679c18eaf2SPetr Machata 					      MLXSW_SP_SPAN_TRIGGER_ECN,
22689c18eaf2SPetr Machata 					      action_mask);
22699c18eaf2SPetr Machata }
22709c18eaf2SPetr Machata 
2271371b437aSNogah Frankel int mlxsw_sp_tc_qdisc_init(struct mlxsw_sp_port *mlxsw_sp_port)
2272371b437aSNogah Frankel {
2273ee88450dSPetr Machata 	struct mlxsw_sp_qdisc_state *qdisc_state;
2274371b437aSNogah Frankel 
2275ee88450dSPetr Machata 	qdisc_state = kzalloc(sizeof(*qdisc_state), GFP_KERNEL);
2276ee88450dSPetr Machata 	if (!qdisc_state)
2277eed4baebSNogah Frankel 		return -ENOMEM;
2278ee88450dSPetr Machata 
2279cff99e20SPetr Machata 	mutex_init(&qdisc_state->lock);
2280ee88450dSPetr Machata 	mlxsw_sp_port->qdisc = qdisc_state;
2281ee88450dSPetr Machata 	return 0;
2282371b437aSNogah Frankel }
2283371b437aSNogah Frankel 
2284371b437aSNogah Frankel void mlxsw_sp_tc_qdisc_fini(struct mlxsw_sp_port *mlxsw_sp_port)
2285371b437aSNogah Frankel {
2286cff99e20SPetr Machata 	mutex_destroy(&mlxsw_sp_port->qdisc->lock);
2287ee88450dSPetr Machata 	kfree(mlxsw_sp_port->qdisc);
2288371b437aSNogah Frankel }
2289