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
mlxsw_sp_qdisc_compare(struct mlxsw_sp_qdisc * mlxsw_sp_qdisc,u32 handle)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 *
mlxsw_sp_qdisc_walk(struct mlxsw_sp_qdisc * qdisc,struct mlxsw_sp_qdisc * (* pre)(struct mlxsw_sp_qdisc *,void *),void * data)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 *
mlxsw_sp_qdisc_walk_cb_find(struct mlxsw_sp_qdisc * qdisc,void * data)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 *
mlxsw_sp_qdisc_find(struct mlxsw_sp_port * mlxsw_sp_port,u32 parent)160c2792f38SPetr 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 *
mlxsw_sp_qdisc_walk_cb_find_by_handle(struct mlxsw_sp_qdisc * qdisc,void * data)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 *
mlxsw_sp_qdisc_find_by_handle(struct mlxsw_sp_port * mlxsw_sp_port,u32 handle)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
mlxsw_sp_qdisc_reduce_parent_backlog(struct mlxsw_sp_qdisc * mlxsw_sp_qdisc)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 
mlxsw_sp_qdisc_get_prio_bitmap(struct mlxsw_sp_port * mlxsw_sp_port,struct mlxsw_sp_qdisc * mlxsw_sp_qdisc)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;
2102a18c08dSPetr Machata 	if (!parent->ops->get_prio_bitmap)
2112a18c08dSPetr Machata 		return mlxsw_sp_qdisc_get_prio_bitmap(mlxsw_sp_port, parent);
21276ff72a7SPetr Machata 	return parent->ops->get_prio_bitmap(parent, mlxsw_sp_qdisc);
21376ff72a7SPetr Machata }
21476ff72a7SPetr Machata 
21576ff72a7SPetr Machata #define MLXSW_SP_PORT_DEFAULT_TCLASS 0
21676ff72a7SPetr Machata 
mlxsw_sp_qdisc_get_tclass_num(struct mlxsw_sp_port * mlxsw_sp_port,struct mlxsw_sp_qdisc * mlxsw_sp_qdisc)21776ff72a7SPetr Machata static int mlxsw_sp_qdisc_get_tclass_num(struct mlxsw_sp_port *mlxsw_sp_port,
21876ff72a7SPetr Machata 					 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
21976ff72a7SPetr Machata {
22076ff72a7SPetr Machata 	struct mlxsw_sp_qdisc *parent = mlxsw_sp_qdisc->parent;
22176ff72a7SPetr Machata 
22276ff72a7SPetr Machata 	if (!parent)
22376ff72a7SPetr Machata 		return MLXSW_SP_PORT_DEFAULT_TCLASS;
2242a18c08dSPetr Machata 	if (!parent->ops->get_tclass_num)
2252a18c08dSPetr Machata 		return mlxsw_sp_qdisc_get_tclass_num(mlxsw_sp_port, parent);
22676ff72a7SPetr Machata 	return parent->ops->get_tclass_num(parent, mlxsw_sp_qdisc);
22776ff72a7SPetr Machata }
22876ff72a7SPetr Machata 
22996f17e07SNogah Frankel static int
mlxsw_sp_qdisc_destroy(struct mlxsw_sp_port * mlxsw_sp_port,struct mlxsw_sp_qdisc * mlxsw_sp_qdisc)2309a37a59fSNogah Frankel mlxsw_sp_qdisc_destroy(struct mlxsw_sp_port *mlxsw_sp_port,
2319a37a59fSNogah Frankel 		       struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
2329a37a59fSNogah Frankel {
233509f04caSPetr Machata 	struct mlxsw_sp_qdisc *root_qdisc = &mlxsw_sp_port->qdisc->root_qdisc;
234509f04caSPetr Machata 	int err_hdroom = 0;
2359a37a59fSNogah Frankel 	int err = 0;
23665626e07SPetr Machata 	int i;
2379a37a59fSNogah Frankel 
2389a37a59fSNogah Frankel 	if (!mlxsw_sp_qdisc)
2399a37a59fSNogah Frankel 		return 0;
2409a37a59fSNogah Frankel 
241509f04caSPetr Machata 	if (root_qdisc == mlxsw_sp_qdisc) {
242509f04caSPetr Machata 		struct mlxsw_sp_hdroom hdroom = *mlxsw_sp_port->hdroom;
243509f04caSPetr Machata 
244509f04caSPetr Machata 		hdroom.mode = MLXSW_SP_HDROOM_MODE_DCB;
245509f04caSPetr Machata 		mlxsw_sp_hdroom_prios_reset_buf_idx(&hdroom);
246509f04caSPetr Machata 		mlxsw_sp_hdroom_bufs_reset_lossiness(&hdroom);
247509f04caSPetr Machata 		mlxsw_sp_hdroom_bufs_reset_sizes(mlxsw_sp_port, &hdroom);
248509f04caSPetr Machata 		err_hdroom = mlxsw_sp_hdroom_configure(mlxsw_sp_port, &hdroom);
249509f04caSPetr Machata 	}
250509f04caSPetr Machata 
251b21832b5SPetr Machata 	if (!mlxsw_sp_qdisc->ops)
252b21832b5SPetr Machata 		return 0;
253b21832b5SPetr Machata 
25465626e07SPetr Machata 	for (i = 0; i < mlxsw_sp_qdisc->num_classes; i++)
25565626e07SPetr Machata 		mlxsw_sp_qdisc_destroy(mlxsw_sp_port,
25665626e07SPetr Machata 				       &mlxsw_sp_qdisc->qdiscs[i]);
257b21832b5SPetr Machata 	mlxsw_sp_qdisc_reduce_parent_backlog(mlxsw_sp_qdisc);
258b21832b5SPetr Machata 	if (mlxsw_sp_qdisc->ops->destroy)
2599a37a59fSNogah Frankel 		err = mlxsw_sp_qdisc->ops->destroy(mlxsw_sp_port,
2609a37a59fSNogah Frankel 						   mlxsw_sp_qdisc);
2615cbd9602SPetr Machata 	if (mlxsw_sp_qdisc->ops->clean_stats)
2625cbd9602SPetr Machata 		mlxsw_sp_qdisc->ops->clean_stats(mlxsw_sp_port, mlxsw_sp_qdisc);
2639a37a59fSNogah Frankel 
2649a37a59fSNogah Frankel 	mlxsw_sp_qdisc->handle = TC_H_UNSPEC;
2659a37a59fSNogah Frankel 	mlxsw_sp_qdisc->ops = NULL;
2665cbd9602SPetr Machata 	mlxsw_sp_qdisc->num_classes = 0;
2675cbd9602SPetr Machata 	kfree(mlxsw_sp_qdisc->qdiscs);
2685cbd9602SPetr Machata 	mlxsw_sp_qdisc->qdiscs = NULL;
269509f04caSPetr Machata 	return err_hdroom ?: err;
2709a37a59fSNogah Frankel }
2719a37a59fSNogah Frankel 
272c2792f38SPetr Machata struct mlxsw_sp_qdisc_tree_validate {
273c2792f38SPetr Machata 	bool forbid_ets;
274*48e4d00bSPetr Machata 	bool forbid_root_tbf;
275c2792f38SPetr Machata 	bool forbid_tbf;
276c2792f38SPetr Machata 	bool forbid_red;
277c2792f38SPetr Machata };
278c2792f38SPetr Machata 
279c2792f38SPetr Machata static int
280c2792f38SPetr Machata __mlxsw_sp_qdisc_tree_validate(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
281c2792f38SPetr Machata 			       struct mlxsw_sp_qdisc_tree_validate validate);
282c2792f38SPetr Machata 
283c2792f38SPetr Machata static int
mlxsw_sp_qdisc_tree_validate_children(struct mlxsw_sp_qdisc * mlxsw_sp_qdisc,struct mlxsw_sp_qdisc_tree_validate validate)284c2792f38SPetr Machata mlxsw_sp_qdisc_tree_validate_children(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
285c2792f38SPetr Machata 				      struct mlxsw_sp_qdisc_tree_validate validate)
286c2792f38SPetr Machata {
287c2792f38SPetr Machata 	unsigned int i;
288c2792f38SPetr Machata 	int err;
289c2792f38SPetr Machata 
290c2792f38SPetr Machata 	for (i = 0; i < mlxsw_sp_qdisc->num_classes; i++) {
291c2792f38SPetr Machata 		err = __mlxsw_sp_qdisc_tree_validate(&mlxsw_sp_qdisc->qdiscs[i],
292c2792f38SPetr Machata 						     validate);
293c2792f38SPetr Machata 		if (err)
294c2792f38SPetr Machata 			return err;
295c2792f38SPetr Machata 	}
296c2792f38SPetr Machata 
297c2792f38SPetr Machata 	return 0;
298c2792f38SPetr Machata }
299c2792f38SPetr Machata 
300c2792f38SPetr Machata static int
__mlxsw_sp_qdisc_tree_validate(struct mlxsw_sp_qdisc * mlxsw_sp_qdisc,struct mlxsw_sp_qdisc_tree_validate validate)301c2792f38SPetr Machata __mlxsw_sp_qdisc_tree_validate(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
302c2792f38SPetr Machata 			       struct mlxsw_sp_qdisc_tree_validate validate)
303c2792f38SPetr Machata {
304c2792f38SPetr Machata 	if (!mlxsw_sp_qdisc->ops)
305c2792f38SPetr Machata 		return 0;
306c2792f38SPetr Machata 
307c2792f38SPetr Machata 	switch (mlxsw_sp_qdisc->ops->type) {
308c2792f38SPetr Machata 	case MLXSW_SP_QDISC_FIFO:
309c2792f38SPetr Machata 		break;
310c2792f38SPetr Machata 	case MLXSW_SP_QDISC_RED:
311c2792f38SPetr Machata 		if (validate.forbid_red)
312c2792f38SPetr Machata 			return -EINVAL;
313c2792f38SPetr Machata 		validate.forbid_red = true;
314*48e4d00bSPetr Machata 		validate.forbid_root_tbf = true;
315c2792f38SPetr Machata 		validate.forbid_ets = true;
316c2792f38SPetr Machata 		break;
317c2792f38SPetr Machata 	case MLXSW_SP_QDISC_TBF:
318*48e4d00bSPetr Machata 		if (validate.forbid_root_tbf) {
319c2792f38SPetr Machata 			if (validate.forbid_tbf)
320c2792f38SPetr Machata 				return -EINVAL;
321*48e4d00bSPetr Machata 			/* This is a TC TBF. */
322c2792f38SPetr Machata 			validate.forbid_tbf = true;
323c2792f38SPetr Machata 			validate.forbid_ets = true;
324*48e4d00bSPetr Machata 		} else {
325*48e4d00bSPetr Machata 			/* This is root TBF. */
326*48e4d00bSPetr Machata 			validate.forbid_root_tbf = true;
327*48e4d00bSPetr Machata 		}
328c2792f38SPetr Machata 		break;
329c2792f38SPetr Machata 	case MLXSW_SP_QDISC_PRIO:
330c2792f38SPetr Machata 	case MLXSW_SP_QDISC_ETS:
331c2792f38SPetr Machata 		if (validate.forbid_ets)
332c2792f38SPetr Machata 			return -EINVAL;
333*48e4d00bSPetr Machata 		validate.forbid_root_tbf = true;
334c2792f38SPetr Machata 		validate.forbid_ets = true;
335c2792f38SPetr Machata 		break;
336c2792f38SPetr Machata 	default:
337c2792f38SPetr Machata 		WARN_ON(1);
338c2792f38SPetr Machata 		return -EINVAL;
339c2792f38SPetr Machata 	}
340c2792f38SPetr Machata 
341c2792f38SPetr Machata 	return mlxsw_sp_qdisc_tree_validate_children(mlxsw_sp_qdisc, validate);
342c2792f38SPetr Machata }
343c2792f38SPetr Machata 
mlxsw_sp_qdisc_tree_validate(struct mlxsw_sp_port * mlxsw_sp_port)344c2792f38SPetr Machata static int mlxsw_sp_qdisc_tree_validate(struct mlxsw_sp_port *mlxsw_sp_port)
345c2792f38SPetr Machata {
346c2792f38SPetr Machata 	struct mlxsw_sp_qdisc_tree_validate validate = {};
347c2792f38SPetr Machata 	struct mlxsw_sp_qdisc *mlxsw_sp_qdisc;
348c2792f38SPetr Machata 
349c2792f38SPetr Machata 	mlxsw_sp_qdisc = &mlxsw_sp_port->qdisc->root_qdisc;
350c2792f38SPetr Machata 	return __mlxsw_sp_qdisc_tree_validate(mlxsw_sp_qdisc, validate);
351c2792f38SPetr Machata }
352c2792f38SPetr Machata 
mlxsw_sp_qdisc_create(struct mlxsw_sp_port * mlxsw_sp_port,u32 handle,struct mlxsw_sp_qdisc * mlxsw_sp_qdisc,struct mlxsw_sp_qdisc_ops * ops,void * params)3535cbd9602SPetr Machata static int mlxsw_sp_qdisc_create(struct mlxsw_sp_port *mlxsw_sp_port,
3545cbd9602SPetr Machata 				 u32 handle,
3559cf6c9c7SNogah Frankel 				 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
3569cf6c9c7SNogah Frankel 				 struct mlxsw_sp_qdisc_ops *ops, void *params)
3579cf6c9c7SNogah Frankel {
358509f04caSPetr Machata 	struct mlxsw_sp_qdisc *root_qdisc = &mlxsw_sp_port->qdisc->root_qdisc;
359509f04caSPetr Machata 	struct mlxsw_sp_hdroom orig_hdroom;
3605cbd9602SPetr Machata 	unsigned int i;
3619cf6c9c7SNogah Frankel 	int err;
3629cf6c9c7SNogah Frankel 
3635cbd9602SPetr Machata 	err = ops->check_params(mlxsw_sp_port, params);
3645cbd9602SPetr Machata 	if (err)
3655cbd9602SPetr Machata 		return err;
3665cbd9602SPetr Machata 
3675cbd9602SPetr Machata 	if (ops->num_classes) {
3685cbd9602SPetr Machata 		mlxsw_sp_qdisc->qdiscs = kcalloc(ops->num_classes,
3695cbd9602SPetr Machata 						 sizeof(*mlxsw_sp_qdisc->qdiscs),
3705cbd9602SPetr Machata 						 GFP_KERNEL);
3715cbd9602SPetr Machata 		if (!mlxsw_sp_qdisc->qdiscs)
3725cbd9602SPetr Machata 			return -ENOMEM;
3735cbd9602SPetr Machata 
3745cbd9602SPetr Machata 		for (i = 0; i < ops->num_classes; i++)
3755cbd9602SPetr Machata 			mlxsw_sp_qdisc->qdiscs[i].parent = mlxsw_sp_qdisc;
3765cbd9602SPetr Machata 	}
377509f04caSPetr Machata 
378509f04caSPetr Machata 	orig_hdroom = *mlxsw_sp_port->hdroom;
379509f04caSPetr Machata 	if (root_qdisc == mlxsw_sp_qdisc) {
380509f04caSPetr Machata 		struct mlxsw_sp_hdroom hdroom = orig_hdroom;
381509f04caSPetr Machata 
382509f04caSPetr Machata 		hdroom.mode = MLXSW_SP_HDROOM_MODE_TC;
383509f04caSPetr Machata 		mlxsw_sp_hdroom_prios_reset_buf_idx(&hdroom);
384509f04caSPetr Machata 		mlxsw_sp_hdroom_bufs_reset_lossiness(&hdroom);
385509f04caSPetr Machata 		mlxsw_sp_hdroom_bufs_reset_sizes(mlxsw_sp_port, &hdroom);
386509f04caSPetr Machata 
387509f04caSPetr Machata 		err = mlxsw_sp_hdroom_configure(mlxsw_sp_port, &hdroom);
388509f04caSPetr Machata 		if (err)
389509f04caSPetr Machata 			goto err_hdroom_configure;
390509f04caSPetr Machata 	}
391509f04caSPetr Machata 
3925cbd9602SPetr Machata 	mlxsw_sp_qdisc->num_classes = ops->num_classes;
3935cbd9602SPetr Machata 	mlxsw_sp_qdisc->ops = ops;
3945cbd9602SPetr Machata 	mlxsw_sp_qdisc->handle = handle;
395c2792f38SPetr Machata 	err = mlxsw_sp_qdisc_tree_validate(mlxsw_sp_port);
396c2792f38SPetr Machata 	if (err)
397c2792f38SPetr Machata 		goto err_replace;
398c2792f38SPetr Machata 
3995cbd9602SPetr Machata 	err = ops->replace(mlxsw_sp_port, handle, mlxsw_sp_qdisc, params);
4005cbd9602SPetr Machata 	if (err)
4015cbd9602SPetr Machata 		goto err_replace;
4025cbd9602SPetr Machata 
4035cbd9602SPetr Machata 	return 0;
4045cbd9602SPetr Machata 
4055cbd9602SPetr Machata err_replace:
4065cbd9602SPetr Machata 	mlxsw_sp_qdisc->handle = TC_H_UNSPEC;
4075cbd9602SPetr Machata 	mlxsw_sp_qdisc->ops = NULL;
4085cbd9602SPetr Machata 	mlxsw_sp_qdisc->num_classes = 0;
4095cbd9602SPetr Machata 	mlxsw_sp_hdroom_configure(mlxsw_sp_port, &orig_hdroom);
4105cbd9602SPetr Machata err_hdroom_configure:
4115cbd9602SPetr Machata 	kfree(mlxsw_sp_qdisc->qdiscs);
4125cbd9602SPetr Machata 	mlxsw_sp_qdisc->qdiscs = NULL;
4135cbd9602SPetr Machata 	return err;
4145cbd9602SPetr Machata }
4155cbd9602SPetr Machata 
4165cbd9602SPetr Machata static int
mlxsw_sp_qdisc_change(struct mlxsw_sp_port * mlxsw_sp_port,u32 handle,struct mlxsw_sp_qdisc * mlxsw_sp_qdisc,void * params)4175cbd9602SPetr Machata mlxsw_sp_qdisc_change(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
4185cbd9602SPetr Machata 		      struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, void *params)
4195cbd9602SPetr Machata {
4205cbd9602SPetr Machata 	struct mlxsw_sp_qdisc_ops *ops = mlxsw_sp_qdisc->ops;
4215cbd9602SPetr Machata 	int err;
4225cbd9602SPetr Machata 
42317c0e6d1SPetr Machata 	err = ops->check_params(mlxsw_sp_port, params);
4249cf6c9c7SNogah Frankel 	if (err)
4255cbd9602SPetr Machata 		goto unoffload;
4269cf6c9c7SNogah Frankel 
427c4e372e2SPetr Machata 	err = ops->replace(mlxsw_sp_port, handle, mlxsw_sp_qdisc, params);
4289cf6c9c7SNogah Frankel 	if (err)
4295cbd9602SPetr Machata 		goto unoffload;
4309cf6c9c7SNogah Frankel 
4317bec1a45SPetr Machata 	/* Check if the Qdisc changed. That includes a situation where an
4327bec1a45SPetr Machata 	 * invisible Qdisc replaces another one, or is being added for the
4337bec1a45SPetr Machata 	 * first time.
4347bec1a45SPetr Machata 	 */
4355cbd9602SPetr Machata 	if (mlxsw_sp_qdisc->handle != handle) {
4369cf6c9c7SNogah Frankel 		if (ops->clean_stats)
4379cf6c9c7SNogah Frankel 			ops->clean_stats(mlxsw_sp_port, mlxsw_sp_qdisc);
4389cf6c9c7SNogah Frankel 	}
4399cf6c9c7SNogah Frankel 
4409cf6c9c7SNogah Frankel 	mlxsw_sp_qdisc->handle = handle;
4419cf6c9c7SNogah Frankel 	return 0;
4429cf6c9c7SNogah Frankel 
4435cbd9602SPetr Machata unoffload:
4445cbd9602SPetr Machata 	if (ops->unoffload)
44593d8a4c1SNogah Frankel 		ops->unoffload(mlxsw_sp_port, mlxsw_sp_qdisc, params);
44693d8a4c1SNogah Frankel 
4479cf6c9c7SNogah Frankel 	mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
4489cf6c9c7SNogah Frankel 	return err;
4499cf6c9c7SNogah Frankel }
4509cf6c9c7SNogah Frankel 
4519cf6c9c7SNogah Frankel static int
mlxsw_sp_qdisc_replace(struct mlxsw_sp_port * mlxsw_sp_port,u32 handle,struct mlxsw_sp_qdisc * mlxsw_sp_qdisc,struct mlxsw_sp_qdisc_ops * ops,void * params)4525cbd9602SPetr Machata mlxsw_sp_qdisc_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
4535cbd9602SPetr Machata 		       struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
4545cbd9602SPetr Machata 		       struct mlxsw_sp_qdisc_ops *ops, void *params)
4555cbd9602SPetr Machata {
4565cbd9602SPetr Machata 	if (mlxsw_sp_qdisc->ops && mlxsw_sp_qdisc->ops->type != ops->type)
4575cbd9602SPetr Machata 		/* In case this location contained a different qdisc of the
4585cbd9602SPetr Machata 		 * same type we can override the old qdisc configuration.
4595cbd9602SPetr Machata 		 * Otherwise, we need to remove the old qdisc before setting the
4605cbd9602SPetr Machata 		 * new one.
4615cbd9602SPetr Machata 		 */
4625cbd9602SPetr Machata 		mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
4635cbd9602SPetr Machata 
4645cbd9602SPetr Machata 	if (!mlxsw_sp_qdisc->ops)
4655cbd9602SPetr Machata 		return mlxsw_sp_qdisc_create(mlxsw_sp_port, handle,
4665cbd9602SPetr Machata 					     mlxsw_sp_qdisc, ops, params);
4675cbd9602SPetr Machata 	else
4685cbd9602SPetr Machata 		return mlxsw_sp_qdisc_change(mlxsw_sp_port, handle,
4695cbd9602SPetr Machata 					     mlxsw_sp_qdisc, params);
4705cbd9602SPetr Machata }
4715cbd9602SPetr Machata 
4725cbd9602SPetr Machata static int
mlxsw_sp_qdisc_get_stats(struct mlxsw_sp_port * mlxsw_sp_port,struct mlxsw_sp_qdisc * mlxsw_sp_qdisc,struct tc_qopt_offload_stats * stats_ptr)473562ffbc4SNogah Frankel mlxsw_sp_qdisc_get_stats(struct mlxsw_sp_port *mlxsw_sp_port,
474562ffbc4SNogah Frankel 			 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
475562ffbc4SNogah Frankel 			 struct tc_qopt_offload_stats *stats_ptr)
476562ffbc4SNogah Frankel {
477562ffbc4SNogah Frankel 	if (mlxsw_sp_qdisc && mlxsw_sp_qdisc->ops &&
478562ffbc4SNogah Frankel 	    mlxsw_sp_qdisc->ops->get_stats)
479562ffbc4SNogah Frankel 		return mlxsw_sp_qdisc->ops->get_stats(mlxsw_sp_port,
480562ffbc4SNogah Frankel 						      mlxsw_sp_qdisc,
481562ffbc4SNogah Frankel 						      stats_ptr);
482562ffbc4SNogah Frankel 
483562ffbc4SNogah Frankel 	return -EOPNOTSUPP;
484562ffbc4SNogah Frankel }
485562ffbc4SNogah Frankel 
486562ffbc4SNogah Frankel static int
mlxsw_sp_qdisc_get_xstats(struct mlxsw_sp_port * mlxsw_sp_port,struct mlxsw_sp_qdisc * mlxsw_sp_qdisc,void * xstats_ptr)487562ffbc4SNogah Frankel mlxsw_sp_qdisc_get_xstats(struct mlxsw_sp_port *mlxsw_sp_port,
488562ffbc4SNogah Frankel 			  struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
489562ffbc4SNogah Frankel 			  void *xstats_ptr)
490562ffbc4SNogah Frankel {
491562ffbc4SNogah Frankel 	if (mlxsw_sp_qdisc && mlxsw_sp_qdisc->ops &&
492562ffbc4SNogah Frankel 	    mlxsw_sp_qdisc->ops->get_xstats)
493562ffbc4SNogah Frankel 		return mlxsw_sp_qdisc->ops->get_xstats(mlxsw_sp_port,
494562ffbc4SNogah Frankel 						      mlxsw_sp_qdisc,
495562ffbc4SNogah Frankel 						      xstats_ptr);
496562ffbc4SNogah Frankel 
497562ffbc4SNogah Frankel 	return -EOPNOTSUPP;
498562ffbc4SNogah Frankel }
499562ffbc4SNogah Frankel 
50085005b82SPetr Machata static u64
mlxsw_sp_xstats_backlog(struct mlxsw_sp_port_xstats * xstats,int tclass_num)50185005b82SPetr Machata mlxsw_sp_xstats_backlog(struct mlxsw_sp_port_xstats *xstats, int tclass_num)
50285005b82SPetr Machata {
50385005b82SPetr Machata 	return xstats->backlog[tclass_num] +
50485005b82SPetr Machata 	       xstats->backlog[tclass_num + 8];
50585005b82SPetr Machata }
50685005b82SPetr Machata 
50785005b82SPetr Machata static u64
mlxsw_sp_xstats_tail_drop(struct mlxsw_sp_port_xstats * xstats,int tclass_num)50885005b82SPetr Machata mlxsw_sp_xstats_tail_drop(struct mlxsw_sp_port_xstats *xstats, int tclass_num)
50985005b82SPetr Machata {
51085005b82SPetr Machata 	return xstats->tail_drop[tclass_num] +
51185005b82SPetr Machata 	       xstats->tail_drop[tclass_num + 8];
51285005b82SPetr Machata }
51385005b82SPetr Machata 
51404cc0bf5SNogah Frankel static void
mlxsw_sp_qdisc_bstats_per_priority_get(struct mlxsw_sp_port_xstats * xstats,u8 prio_bitmap,u64 * tx_packets,u64 * tx_bytes)51504cc0bf5SNogah Frankel mlxsw_sp_qdisc_bstats_per_priority_get(struct mlxsw_sp_port_xstats *xstats,
51604cc0bf5SNogah Frankel 				       u8 prio_bitmap, u64 *tx_packets,
51704cc0bf5SNogah Frankel 				       u64 *tx_bytes)
51804cc0bf5SNogah Frankel {
51904cc0bf5SNogah Frankel 	int i;
52004cc0bf5SNogah Frankel 
52104cc0bf5SNogah Frankel 	*tx_packets = 0;
52204cc0bf5SNogah Frankel 	*tx_bytes = 0;
52304cc0bf5SNogah Frankel 	for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
52404cc0bf5SNogah Frankel 		if (prio_bitmap & BIT(i)) {
52504cc0bf5SNogah Frankel 			*tx_packets += xstats->tx_packets[i];
52604cc0bf5SNogah Frankel 			*tx_bytes += xstats->tx_bytes[i];
52704cc0bf5SNogah Frankel 		}
52804cc0bf5SNogah Frankel 	}
52904cc0bf5SNogah Frankel }
53004cc0bf5SNogah Frankel 
531cf9af379SPetr Machata static void
mlxsw_sp_qdisc_collect_tc_stats(struct mlxsw_sp_port * mlxsw_sp_port,struct mlxsw_sp_qdisc * mlxsw_sp_qdisc,u64 * p_tx_bytes,u64 * p_tx_packets,u64 * p_drops,u64 * p_backlog)532cf9af379SPetr Machata mlxsw_sp_qdisc_collect_tc_stats(struct mlxsw_sp_port *mlxsw_sp_port,
533cf9af379SPetr Machata 				struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
534cf9af379SPetr Machata 				u64 *p_tx_bytes, u64 *p_tx_packets,
535cf9af379SPetr Machata 				u64 *p_drops, u64 *p_backlog)
536cf9af379SPetr Machata {
537cf9af379SPetr Machata 	struct mlxsw_sp_port_xstats *xstats;
538cf9af379SPetr Machata 	u64 tx_bytes, tx_packets;
53976ff72a7SPetr Machata 	u8 prio_bitmap;
54076ff72a7SPetr Machata 	int tclass_num;
541cf9af379SPetr Machata 
54276ff72a7SPetr Machata 	prio_bitmap = mlxsw_sp_qdisc_get_prio_bitmap(mlxsw_sp_port,
54376ff72a7SPetr Machata 						     mlxsw_sp_qdisc);
54476ff72a7SPetr Machata 	tclass_num = mlxsw_sp_qdisc_get_tclass_num(mlxsw_sp_port,
54576ff72a7SPetr Machata 						   mlxsw_sp_qdisc);
546cf9af379SPetr Machata 	xstats = &mlxsw_sp_port->periodic_hw_stats.xstats;
54776ff72a7SPetr Machata 	mlxsw_sp_qdisc_bstats_per_priority_get(xstats, prio_bitmap,
548cf9af379SPetr Machata 					       &tx_packets, &tx_bytes);
549cf9af379SPetr Machata 
550cf9af379SPetr Machata 	*p_tx_packets += tx_packets;
551cf9af379SPetr Machata 	*p_tx_bytes += tx_bytes;
552cf9af379SPetr Machata 	*p_drops += xstats->wred_drop[tclass_num] +
553cf9af379SPetr Machata 		    mlxsw_sp_xstats_tail_drop(xstats, tclass_num);
554cf9af379SPetr Machata 	*p_backlog += mlxsw_sp_xstats_backlog(xstats, tclass_num);
555cf9af379SPetr Machata }
556cf9af379SPetr Machata 
557cf9af379SPetr Machata static void
mlxsw_sp_qdisc_update_stats(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_qdisc * mlxsw_sp_qdisc,u64 tx_bytes,u64 tx_packets,u64 drops,u64 backlog,struct tc_qopt_offload_stats * stats_ptr)558cf9af379SPetr Machata mlxsw_sp_qdisc_update_stats(struct mlxsw_sp *mlxsw_sp,
559cf9af379SPetr Machata 			    struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
560cf9af379SPetr Machata 			    u64 tx_bytes, u64 tx_packets,
561cf9af379SPetr Machata 			    u64 drops, u64 backlog,
562cf9af379SPetr Machata 			    struct tc_qopt_offload_stats *stats_ptr)
563cf9af379SPetr Machata {
564cf9af379SPetr Machata 	struct mlxsw_sp_qdisc_stats *stats_base = &mlxsw_sp_qdisc->stats_base;
565cf9af379SPetr Machata 
566cf9af379SPetr Machata 	tx_bytes -= stats_base->tx_bytes;
567cf9af379SPetr Machata 	tx_packets -= stats_base->tx_packets;
568cf9af379SPetr Machata 	drops -= stats_base->drops;
569cf9af379SPetr Machata 	backlog -= stats_base->backlog;
570cf9af379SPetr Machata 
571cf9af379SPetr Machata 	_bstats_update(stats_ptr->bstats, tx_bytes, tx_packets);
572cf9af379SPetr Machata 	stats_ptr->qstats->drops += drops;
573cf9af379SPetr Machata 	stats_ptr->qstats->backlog += mlxsw_sp_cells_bytes(mlxsw_sp, backlog);
574cf9af379SPetr Machata 
575cf9af379SPetr Machata 	stats_base->backlog += backlog;
576cf9af379SPetr Machata 	stats_base->drops += drops;
577cf9af379SPetr Machata 	stats_base->tx_bytes += tx_bytes;
578cf9af379SPetr Machata 	stats_base->tx_packets += tx_packets;
579cf9af379SPetr Machata }
580cf9af379SPetr Machata 
5813d0d5921SPetr Machata static void
mlxsw_sp_qdisc_get_tc_stats(struct mlxsw_sp_port * mlxsw_sp_port,struct mlxsw_sp_qdisc * mlxsw_sp_qdisc,struct tc_qopt_offload_stats * stats_ptr)5823d0d5921SPetr Machata mlxsw_sp_qdisc_get_tc_stats(struct mlxsw_sp_port *mlxsw_sp_port,
5833d0d5921SPetr Machata 			    struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
5843d0d5921SPetr Machata 			    struct tc_qopt_offload_stats *stats_ptr)
5853d0d5921SPetr Machata {
5863d0d5921SPetr Machata 	u64 tx_packets = 0;
5873d0d5921SPetr Machata 	u64 tx_bytes = 0;
5883d0d5921SPetr Machata 	u64 backlog = 0;
5893d0d5921SPetr Machata 	u64 drops = 0;
5903d0d5921SPetr Machata 
5913d0d5921SPetr Machata 	mlxsw_sp_qdisc_collect_tc_stats(mlxsw_sp_port, mlxsw_sp_qdisc,
5923d0d5921SPetr Machata 					&tx_bytes, &tx_packets,
5933d0d5921SPetr Machata 					&drops, &backlog);
5943d0d5921SPetr Machata 	mlxsw_sp_qdisc_update_stats(mlxsw_sp_port->mlxsw_sp, mlxsw_sp_qdisc,
5953d0d5921SPetr Machata 				    tx_bytes, tx_packets, drops, backlog,
5963d0d5921SPetr Machata 				    stats_ptr);
5973d0d5921SPetr Machata }
5983d0d5921SPetr Machata 
599562ffbc4SNogah Frankel static int
mlxsw_sp_tclass_congestion_enable(struct mlxsw_sp_port * mlxsw_sp_port,int tclass_num,u32 min,u32 max,u32 probability,bool is_wred,bool is_ecn)60096f17e07SNogah Frankel mlxsw_sp_tclass_congestion_enable(struct mlxsw_sp_port *mlxsw_sp_port,
60196f17e07SNogah Frankel 				  int tclass_num, u32 min, u32 max,
6028040c96bSPetr Machata 				  u32 probability, bool is_wred, bool is_ecn)
60396f17e07SNogah Frankel {
604db84924cSJiri Pirko 	char cwtpm_cmd[MLXSW_REG_CWTPM_LEN];
605db84924cSJiri Pirko 	char cwtp_cmd[MLXSW_REG_CWTP_LEN];
60696f17e07SNogah Frankel 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
60796f17e07SNogah Frankel 	int err;
60896f17e07SNogah Frankel 
60996f17e07SNogah Frankel 	mlxsw_reg_cwtp_pack(cwtp_cmd, mlxsw_sp_port->local_port, tclass_num);
61096f17e07SNogah Frankel 	mlxsw_reg_cwtp_profile_pack(cwtp_cmd, MLXSW_REG_CWTP_DEFAULT_PROFILE,
61196f17e07SNogah Frankel 				    roundup(min, MLXSW_REG_CWTP_MIN_VALUE),
61296f17e07SNogah Frankel 				    roundup(max, MLXSW_REG_CWTP_MIN_VALUE),
61396f17e07SNogah Frankel 				    probability);
61496f17e07SNogah Frankel 
61596f17e07SNogah Frankel 	err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(cwtp), cwtp_cmd);
61696f17e07SNogah Frankel 	if (err)
61796f17e07SNogah Frankel 		return err;
61896f17e07SNogah Frankel 
619db84924cSJiri Pirko 	mlxsw_reg_cwtpm_pack(cwtpm_cmd, mlxsw_sp_port->local_port, tclass_num,
6208040c96bSPetr Machata 			     MLXSW_REG_CWTP_DEFAULT_PROFILE, is_wred, is_ecn);
62196f17e07SNogah Frankel 
622db84924cSJiri Pirko 	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(cwtpm), cwtpm_cmd);
62396f17e07SNogah Frankel }
62496f17e07SNogah Frankel 
62596f17e07SNogah Frankel static int
mlxsw_sp_tclass_congestion_disable(struct mlxsw_sp_port * mlxsw_sp_port,int tclass_num)62696f17e07SNogah Frankel mlxsw_sp_tclass_congestion_disable(struct mlxsw_sp_port *mlxsw_sp_port,
62796f17e07SNogah Frankel 				   int tclass_num)
62896f17e07SNogah Frankel {
62996f17e07SNogah Frankel 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
63096f17e07SNogah Frankel 	char cwtpm_cmd[MLXSW_REG_CWTPM_LEN];
63196f17e07SNogah Frankel 
63296f17e07SNogah Frankel 	mlxsw_reg_cwtpm_pack(cwtpm_cmd, mlxsw_sp_port->local_port, tclass_num,
63396f17e07SNogah Frankel 			     MLXSW_REG_CWTPM_RESET_PROFILE, false, false);
63496f17e07SNogah Frankel 	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(cwtpm), cwtpm_cmd);
63596f17e07SNogah Frankel }
63696f17e07SNogah Frankel 
637861fb829SNogah Frankel static void
mlxsw_sp_setup_tc_qdisc_red_clean_stats(struct mlxsw_sp_port * mlxsw_sp_port,struct mlxsw_sp_qdisc * mlxsw_sp_qdisc)638c2ed6db7SNogah Frankel mlxsw_sp_setup_tc_qdisc_red_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port,
639d56c8955SNogah Frankel 					struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
640861fb829SNogah Frankel {
6414d1a4b84SNogah Frankel 	struct mlxsw_sp_qdisc_stats *stats_base;
642861fb829SNogah Frankel 	struct mlxsw_sp_port_xstats *xstats;
6434d1a4b84SNogah Frankel 	struct red_stats *red_base;
64476ff72a7SPetr Machata 	u8 prio_bitmap;
64576ff72a7SPetr Machata 	int tclass_num;
646861fb829SNogah Frankel 
64776ff72a7SPetr Machata 	prio_bitmap = mlxsw_sp_qdisc_get_prio_bitmap(mlxsw_sp_port,
64876ff72a7SPetr Machata 						     mlxsw_sp_qdisc);
64976ff72a7SPetr Machata 	tclass_num = mlxsw_sp_qdisc_get_tclass_num(mlxsw_sp_port,
65076ff72a7SPetr Machata 						   mlxsw_sp_qdisc);
651861fb829SNogah Frankel 	xstats = &mlxsw_sp_port->periodic_hw_stats.xstats;
6524d1a4b84SNogah Frankel 	stats_base = &mlxsw_sp_qdisc->stats_base;
653c2ed6db7SNogah Frankel 	red_base = &mlxsw_sp_qdisc->xstats_base.red;
6543670756fSNogah Frankel 
65576ff72a7SPetr Machata 	mlxsw_sp_qdisc_bstats_per_priority_get(xstats, prio_bitmap,
65604cc0bf5SNogah Frankel 					       &stats_base->tx_packets,
65704cc0bf5SNogah Frankel 					       &stats_base->tx_bytes);
65815be36b8SPetr Machata 	red_base->prob_mark = xstats->tc_ecn[tclass_num];
6594d1a4b84SNogah Frankel 	red_base->prob_drop = xstats->wred_drop[tclass_num];
66085005b82SPetr Machata 	red_base->pdrop = mlxsw_sp_xstats_tail_drop(xstats, tclass_num);
6613670756fSNogah Frankel 
662c2ed6db7SNogah Frankel 	stats_base->overlimits = red_base->prob_drop + red_base->prob_mark;
6634d1a4b84SNogah Frankel 	stats_base->drops = red_base->prob_drop + red_base->pdrop;
664416ef9b1SJakub Kicinski 
665416ef9b1SJakub Kicinski 	stats_base->backlog = 0;
666861fb829SNogah Frankel }
667861fb829SNogah Frankel 
66896f17e07SNogah Frankel static int
mlxsw_sp_qdisc_red_destroy(struct mlxsw_sp_port * mlxsw_sp_port,struct mlxsw_sp_qdisc * mlxsw_sp_qdisc)669cba7158fSNogah Frankel mlxsw_sp_qdisc_red_destroy(struct mlxsw_sp_port *mlxsw_sp_port,
670d56c8955SNogah Frankel 			   struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
67196f17e07SNogah Frankel {
67276ff72a7SPetr Machata 	int tclass_num = mlxsw_sp_qdisc_get_tclass_num(mlxsw_sp_port,
67376ff72a7SPetr Machata 						       mlxsw_sp_qdisc);
67476ff72a7SPetr Machata 
67576ff72a7SPetr Machata 	return mlxsw_sp_tclass_congestion_disable(mlxsw_sp_port, tclass_num);
67696f17e07SNogah Frankel }
67796f17e07SNogah Frankel 
67896f17e07SNogah Frankel static int
mlxsw_sp_qdisc_red_check_params(struct mlxsw_sp_port * mlxsw_sp_port,void * params)6799cf6c9c7SNogah Frankel mlxsw_sp_qdisc_red_check_params(struct mlxsw_sp_port *mlxsw_sp_port,
6809cf6c9c7SNogah Frankel 				void *params)
68196f17e07SNogah Frankel {
68296f17e07SNogah Frankel 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
6839cf6c9c7SNogah Frankel 	struct tc_red_qopt_offload_params *p = params;
68496f17e07SNogah Frankel 
68596f17e07SNogah Frankel 	if (p->min > p->max) {
68696f17e07SNogah Frankel 		dev_err(mlxsw_sp->bus_info->dev,
68796f17e07SNogah Frankel 			"spectrum: RED: min %u is bigger then max %u\n", p->min,
68896f17e07SNogah Frankel 			p->max);
6899cf6c9c7SNogah Frankel 		return -EINVAL;
69096f17e07SNogah Frankel 	}
691914c4fc1SPetr Machata 	if (p->max > MLXSW_CORE_RES_GET(mlxsw_sp->core,
692914c4fc1SPetr Machata 					GUARANTEED_SHARED_BUFFER)) {
69396f17e07SNogah Frankel 		dev_err(mlxsw_sp->bus_info->dev,
69496f17e07SNogah Frankel 			"spectrum: RED: max value %u is too big\n", p->max);
6959cf6c9c7SNogah Frankel 		return -EINVAL;
69696f17e07SNogah Frankel 	}
69796f17e07SNogah Frankel 	if (p->min == 0 || p->max == 0) {
69896f17e07SNogah Frankel 		dev_err(mlxsw_sp->bus_info->dev,
69996f17e07SNogah Frankel 			"spectrum: RED: 0 value is illegal for min and max\n");
7009cf6c9c7SNogah Frankel 		return -EINVAL;
70196f17e07SNogah Frankel 	}
7029cf6c9c7SNogah Frankel 	return 0;
7039cf6c9c7SNogah Frankel }
7049cf6c9c7SNogah Frankel 
7059cf6c9c7SNogah Frankel static int
7062a18c08dSPetr Machata mlxsw_sp_qdisc_future_fifo_replace(struct mlxsw_sp_port *mlxsw_sp_port,
7072a18c08dSPetr Machata 				   u32 handle, unsigned int band,
7082a18c08dSPetr Machata 				   struct mlxsw_sp_qdisc *child_qdisc);
7092a18c08dSPetr Machata static void
7102a18c08dSPetr Machata mlxsw_sp_qdisc_future_fifos_init(struct mlxsw_sp_port *mlxsw_sp_port,
7112a18c08dSPetr Machata 				 u32 handle);
7122a18c08dSPetr Machata 
7132a18c08dSPetr Machata static int
mlxsw_sp_qdisc_red_replace(struct mlxsw_sp_port * mlxsw_sp_port,u32 handle,struct mlxsw_sp_qdisc * mlxsw_sp_qdisc,void * params)714c4e372e2SPetr Machata mlxsw_sp_qdisc_red_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
7159cf6c9c7SNogah Frankel 			   struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
7169cf6c9c7SNogah Frankel 			   void *params)
7179cf6c9c7SNogah Frankel {
7189cf6c9c7SNogah Frankel 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
7199cf6c9c7SNogah Frankel 	struct tc_red_qopt_offload_params *p = params;
72076ff72a7SPetr Machata 	int tclass_num;
7219cf6c9c7SNogah Frankel 	u32 min, max;
7229cf6c9c7SNogah Frankel 	u64 prob;
7232a18c08dSPetr Machata 	int err;
7242a18c08dSPetr Machata 
7252a18c08dSPetr Machata 	err = mlxsw_sp_qdisc_future_fifo_replace(mlxsw_sp_port, handle, 0,
7262a18c08dSPetr Machata 						 &mlxsw_sp_qdisc->qdiscs[0]);
7272a18c08dSPetr Machata 	if (err)
7282a18c08dSPetr Machata 		return err;
7292a18c08dSPetr Machata 	mlxsw_sp_qdisc_future_fifos_init(mlxsw_sp_port, TC_H_UNSPEC);
73096f17e07SNogah Frankel 
73176ff72a7SPetr Machata 	tclass_num = mlxsw_sp_qdisc_get_tclass_num(mlxsw_sp_port,
73276ff72a7SPetr Machata 						   mlxsw_sp_qdisc);
73376ff72a7SPetr Machata 
73496f17e07SNogah Frankel 	/* calculate probability in percentage */
73596f17e07SNogah Frankel 	prob = p->probability;
73696f17e07SNogah Frankel 	prob *= 100;
73796f17e07SNogah Frankel 	prob = DIV_ROUND_UP(prob, 1 << 16);
73896f17e07SNogah Frankel 	prob = DIV_ROUND_UP(prob, 1 << 16);
73996f17e07SNogah Frankel 	min = mlxsw_sp_bytes_cells(mlxsw_sp, p->min);
74096f17e07SNogah Frankel 	max = mlxsw_sp_bytes_cells(mlxsw_sp, p->max);
7418040c96bSPetr Machata 	return mlxsw_sp_tclass_congestion_enable(mlxsw_sp_port, tclass_num,
7428040c96bSPetr Machata 						 min, max, prob,
7438040c96bSPetr Machata 						 !p->is_nodrop, p->is_ecn);
74496f17e07SNogah Frankel }
74596f17e07SNogah Frankel 
746416ef9b1SJakub Kicinski static void
mlxsw_sp_qdisc_leaf_unoffload(struct mlxsw_sp_port * mlxsw_sp_port,struct mlxsw_sp_qdisc * mlxsw_sp_qdisc,struct gnet_stats_queue * qstats)747be1d5a8aSPetr Machata mlxsw_sp_qdisc_leaf_unoffload(struct mlxsw_sp_port *mlxsw_sp_port,
748be1d5a8aSPetr Machata 			      struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
749be1d5a8aSPetr Machata 			      struct gnet_stats_queue *qstats)
750be1d5a8aSPetr Machata {
751be1d5a8aSPetr Machata 	u64 backlog;
752be1d5a8aSPetr Machata 
753be1d5a8aSPetr Machata 	backlog = mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp,
754be1d5a8aSPetr Machata 				       mlxsw_sp_qdisc->stats_base.backlog);
755be1d5a8aSPetr Machata 	qstats->backlog -= backlog;
756be1d5a8aSPetr Machata 	mlxsw_sp_qdisc->stats_base.backlog = 0;
757be1d5a8aSPetr Machata }
758be1d5a8aSPetr Machata 
759be1d5a8aSPetr Machata static void
mlxsw_sp_qdisc_red_unoffload(struct mlxsw_sp_port * mlxsw_sp_port,struct mlxsw_sp_qdisc * mlxsw_sp_qdisc,void * params)760416ef9b1SJakub Kicinski mlxsw_sp_qdisc_red_unoffload(struct mlxsw_sp_port *mlxsw_sp_port,
761416ef9b1SJakub Kicinski 			     struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
762416ef9b1SJakub Kicinski 			     void *params)
763416ef9b1SJakub Kicinski {
764416ef9b1SJakub Kicinski 	struct tc_red_qopt_offload_params *p = params;
765416ef9b1SJakub Kicinski 
766be1d5a8aSPetr Machata 	mlxsw_sp_qdisc_leaf_unoffload(mlxsw_sp_port, mlxsw_sp_qdisc, p->qstats);
767416ef9b1SJakub Kicinski }
768416ef9b1SJakub Kicinski 
769861fb829SNogah Frankel static int
mlxsw_sp_qdisc_get_red_xstats(struct mlxsw_sp_port * mlxsw_sp_port,struct mlxsw_sp_qdisc * mlxsw_sp_qdisc,void * xstats_ptr)770cba7158fSNogah Frankel mlxsw_sp_qdisc_get_red_xstats(struct mlxsw_sp_port *mlxsw_sp_port,
771861fb829SNogah Frankel 			      struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
772562ffbc4SNogah Frankel 			      void *xstats_ptr)
773861fb829SNogah Frankel {
7744d1a4b84SNogah Frankel 	struct red_stats *xstats_base = &mlxsw_sp_qdisc->xstats_base.red;
775861fb829SNogah Frankel 	struct mlxsw_sp_port_xstats *xstats;
776562ffbc4SNogah Frankel 	struct red_stats *res = xstats_ptr;
77715be36b8SPetr Machata 	int early_drops, marks, pdrops;
77876ff72a7SPetr Machata 	int tclass_num;
779861fb829SNogah Frankel 
78076ff72a7SPetr Machata 	tclass_num = mlxsw_sp_qdisc_get_tclass_num(mlxsw_sp_port,
78176ff72a7SPetr Machata 						   mlxsw_sp_qdisc);
782861fb829SNogah Frankel 	xstats = &mlxsw_sp_port->periodic_hw_stats.xstats;
783861fb829SNogah Frankel 
784f8253df5SNogah Frankel 	early_drops = xstats->wred_drop[tclass_num] - xstats_base->prob_drop;
78515be36b8SPetr Machata 	marks = xstats->tc_ecn[tclass_num] - xstats_base->prob_mark;
78685005b82SPetr Machata 	pdrops = mlxsw_sp_xstats_tail_drop(xstats, tclass_num) -
78785005b82SPetr Machata 		 xstats_base->pdrop;
788f8253df5SNogah Frankel 
789f8253df5SNogah Frankel 	res->pdrop += pdrops;
790f8253df5SNogah Frankel 	res->prob_drop += early_drops;
79115be36b8SPetr Machata 	res->prob_mark += marks;
792f8253df5SNogah Frankel 
793f8253df5SNogah Frankel 	xstats_base->pdrop += pdrops;
794f8253df5SNogah Frankel 	xstats_base->prob_drop += early_drops;
79515be36b8SPetr Machata 	xstats_base->prob_mark += marks;
796861fb829SNogah Frankel 	return 0;
797861fb829SNogah Frankel }
798861fb829SNogah Frankel 
7993670756fSNogah Frankel static int
mlxsw_sp_qdisc_get_red_stats(struct mlxsw_sp_port * mlxsw_sp_port,struct mlxsw_sp_qdisc * mlxsw_sp_qdisc,struct tc_qopt_offload_stats * stats_ptr)800cba7158fSNogah Frankel mlxsw_sp_qdisc_get_red_stats(struct mlxsw_sp_port *mlxsw_sp_port,
8013670756fSNogah Frankel 			     struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
802562ffbc4SNogah Frankel 			     struct tc_qopt_offload_stats *stats_ptr)
8033670756fSNogah Frankel {
8044d1a4b84SNogah Frankel 	struct mlxsw_sp_qdisc_stats *stats_base;
8053670756fSNogah Frankel 	struct mlxsw_sp_port_xstats *xstats;
806cf9af379SPetr Machata 	u64 overlimits;
80776ff72a7SPetr Machata 	int tclass_num;
8083670756fSNogah Frankel 
80976ff72a7SPetr Machata 	tclass_num = mlxsw_sp_qdisc_get_tclass_num(mlxsw_sp_port,
81076ff72a7SPetr Machata 						   mlxsw_sp_qdisc);
8113670756fSNogah Frankel 	xstats = &mlxsw_sp_port->periodic_hw_stats.xstats;
8124d1a4b84SNogah Frankel 	stats_base = &mlxsw_sp_qdisc->stats_base;
8133670756fSNogah Frankel 
8143d0d5921SPetr Machata 	mlxsw_sp_qdisc_get_tc_stats(mlxsw_sp_port, mlxsw_sp_qdisc, stats_ptr);
81515be36b8SPetr Machata 	overlimits = xstats->wred_drop[tclass_num] +
81615be36b8SPetr Machata 		     xstats->tc_ecn[tclass_num] - stats_base->overlimits;
8173670756fSNogah Frankel 
818562ffbc4SNogah Frankel 	stats_ptr->qstats->overlimits += overlimits;
8194d1a4b84SNogah Frankel 	stats_base->overlimits += overlimits;
820cf9af379SPetr Machata 
8213670756fSNogah Frankel 	return 0;
8223670756fSNogah Frankel }
8233670756fSNogah Frankel 
82451d52ed9SPetr Machata static struct mlxsw_sp_qdisc *
mlxsw_sp_qdisc_leaf_find_class(struct mlxsw_sp_qdisc * mlxsw_sp_qdisc,u32 parent)82551d52ed9SPetr Machata mlxsw_sp_qdisc_leaf_find_class(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
82651d52ed9SPetr Machata 			       u32 parent)
82751d52ed9SPetr Machata {
8282a18c08dSPetr Machata 	/* RED and TBF are formally classful qdiscs, but all class references,
8292a18c08dSPetr Machata 	 * including X:0, just refer to the same one class.
8302a18c08dSPetr Machata 	 */
8312a18c08dSPetr Machata 	return &mlxsw_sp_qdisc->qdiscs[0];
83251d52ed9SPetr Machata }
83351d52ed9SPetr Machata 
834562ffbc4SNogah Frankel static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_red = {
8359cf6c9c7SNogah Frankel 	.type = MLXSW_SP_QDISC_RED,
8369cf6c9c7SNogah Frankel 	.check_params = mlxsw_sp_qdisc_red_check_params,
8379cf6c9c7SNogah Frankel 	.replace = mlxsw_sp_qdisc_red_replace,
838416ef9b1SJakub Kicinski 	.unoffload = mlxsw_sp_qdisc_red_unoffload,
8399a37a59fSNogah Frankel 	.destroy = mlxsw_sp_qdisc_red_destroy,
840562ffbc4SNogah Frankel 	.get_stats = mlxsw_sp_qdisc_get_red_stats,
841562ffbc4SNogah Frankel 	.get_xstats = mlxsw_sp_qdisc_get_red_xstats,
8429cf6c9c7SNogah Frankel 	.clean_stats = mlxsw_sp_setup_tc_qdisc_red_clean_stats,
84351d52ed9SPetr Machata 	.find_class = mlxsw_sp_qdisc_leaf_find_class,
8442a18c08dSPetr Machata 	.num_classes = 1,
845562ffbc4SNogah Frankel };
846562ffbc4SNogah Frankel 
847be7e2a5aSPetr Machata static int mlxsw_sp_qdisc_graft(struct mlxsw_sp_port *mlxsw_sp_port,
848be7e2a5aSPetr Machata 				struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
849be7e2a5aSPetr Machata 				u8 band, u32 child_handle);
850be7e2a5aSPetr Machata 
__mlxsw_sp_setup_tc_red(struct mlxsw_sp_port * mlxsw_sp_port,struct tc_red_qopt_offload * p)851cff99e20SPetr Machata static int __mlxsw_sp_setup_tc_red(struct mlxsw_sp_port *mlxsw_sp_port,
85296f17e07SNogah Frankel 				   struct tc_red_qopt_offload *p)
85396f17e07SNogah Frankel {
85496f17e07SNogah Frankel 	struct mlxsw_sp_qdisc *mlxsw_sp_qdisc;
85596f17e07SNogah Frankel 
856c2792f38SPetr Machata 	mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent);
857eed4baebSNogah Frankel 	if (!mlxsw_sp_qdisc)
85896f17e07SNogah Frankel 		return -EOPNOTSUPP;
85996f17e07SNogah Frankel 
860cba7158fSNogah Frankel 	if (p->command == TC_RED_REPLACE)
8619cf6c9c7SNogah Frankel 		return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle,
862562ffbc4SNogah Frankel 					      mlxsw_sp_qdisc,
863562ffbc4SNogah Frankel 					      &mlxsw_sp_qdisc_ops_red,
864562ffbc4SNogah Frankel 					      &p->set);
865cba7158fSNogah Frankel 
866290fe2c5SPetr Machata 	if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle))
867cba7158fSNogah Frankel 		return -EOPNOTSUPP;
868cba7158fSNogah Frankel 
869cba7158fSNogah Frankel 	switch (p->command) {
87096f17e07SNogah Frankel 	case TC_RED_DESTROY:
8719a37a59fSNogah Frankel 		return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
872861fb829SNogah Frankel 	case TC_RED_XSTATS:
873562ffbc4SNogah Frankel 		return mlxsw_sp_qdisc_get_xstats(mlxsw_sp_port, mlxsw_sp_qdisc,
874861fb829SNogah Frankel 						 p->xstats);
8753670756fSNogah Frankel 	case TC_RED_STATS:
876562ffbc4SNogah Frankel 		return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc,
8773670756fSNogah Frankel 						&p->stats);
878be7e2a5aSPetr Machata 	case TC_RED_GRAFT:
879be7e2a5aSPetr Machata 		return mlxsw_sp_qdisc_graft(mlxsw_sp_port, mlxsw_sp_qdisc, 0,
880be7e2a5aSPetr Machata 					    p->child_handle);
88196f17e07SNogah Frankel 	default:
88296f17e07SNogah Frankel 		return -EOPNOTSUPP;
88396f17e07SNogah Frankel 	}
88496f17e07SNogah Frankel }
885371b437aSNogah Frankel 
mlxsw_sp_setup_tc_red(struct mlxsw_sp_port * mlxsw_sp_port,struct tc_red_qopt_offload * p)886cff99e20SPetr Machata int mlxsw_sp_setup_tc_red(struct mlxsw_sp_port *mlxsw_sp_port,
887cff99e20SPetr Machata 			  struct tc_red_qopt_offload *p)
888cff99e20SPetr Machata {
889cff99e20SPetr Machata 	int err;
890cff99e20SPetr Machata 
891cff99e20SPetr Machata 	mutex_lock(&mlxsw_sp_port->qdisc->lock);
892cff99e20SPetr Machata 	err = __mlxsw_sp_setup_tc_red(mlxsw_sp_port, p);
893cff99e20SPetr Machata 	mutex_unlock(&mlxsw_sp_port->qdisc->lock);
894cff99e20SPetr Machata 
895cff99e20SPetr Machata 	return err;
896cff99e20SPetr Machata }
897cff99e20SPetr Machata 
898a44f58c4SPetr Machata static void
mlxsw_sp_setup_tc_qdisc_leaf_clean_stats(struct mlxsw_sp_port * mlxsw_sp_port,struct mlxsw_sp_qdisc * mlxsw_sp_qdisc)899a44f58c4SPetr Machata mlxsw_sp_setup_tc_qdisc_leaf_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port,
900a44f58c4SPetr Machata 					 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
901a44f58c4SPetr Machata {
902a44f58c4SPetr Machata 	u64 backlog_cells = 0;
903a44f58c4SPetr Machata 	u64 tx_packets = 0;
904a44f58c4SPetr Machata 	u64 tx_bytes = 0;
905a44f58c4SPetr Machata 	u64 drops = 0;
906a44f58c4SPetr Machata 
907a44f58c4SPetr Machata 	mlxsw_sp_qdisc_collect_tc_stats(mlxsw_sp_port, mlxsw_sp_qdisc,
908a44f58c4SPetr Machata 					&tx_bytes, &tx_packets,
909a44f58c4SPetr Machata 					&drops, &backlog_cells);
910a44f58c4SPetr Machata 
911a44f58c4SPetr Machata 	mlxsw_sp_qdisc->stats_base.tx_packets = tx_packets;
912a44f58c4SPetr Machata 	mlxsw_sp_qdisc->stats_base.tx_bytes = tx_bytes;
913a44f58c4SPetr Machata 	mlxsw_sp_qdisc->stats_base.drops = drops;
914a44f58c4SPetr Machata 	mlxsw_sp_qdisc->stats_base.backlog = 0;
915a44f58c4SPetr Machata }
916a44f58c4SPetr Machata 
917*48e4d00bSPetr Machata static enum mlxsw_reg_qeec_hr
mlxsw_sp_qdisc_tbf_hr(struct mlxsw_sp_port * mlxsw_sp_port,struct mlxsw_sp_qdisc * mlxsw_sp_qdisc)918*48e4d00bSPetr Machata mlxsw_sp_qdisc_tbf_hr(struct mlxsw_sp_port *mlxsw_sp_port,
919*48e4d00bSPetr Machata 		      struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
920*48e4d00bSPetr Machata {
921*48e4d00bSPetr Machata 	if (mlxsw_sp_qdisc == &mlxsw_sp_port->qdisc->root_qdisc)
922*48e4d00bSPetr Machata 		return MLXSW_REG_QEEC_HR_PORT;
923*48e4d00bSPetr Machata 
924*48e4d00bSPetr Machata 	/* Configure subgroup shaper, so that both UC and MC traffic is subject
925*48e4d00bSPetr Machata 	 * to shaping. That is unlike RED, however UC queue lengths are going to
926*48e4d00bSPetr Machata 	 * be different than MC ones due to different pool and quota
927*48e4d00bSPetr Machata 	 * configurations, so the configuration is not applicable. For shaper on
928*48e4d00bSPetr Machata 	 * the other hand, subjecting the overall stream to the configured
929*48e4d00bSPetr Machata 	 * shaper makes sense. Also note that that is what we do for
930*48e4d00bSPetr Machata 	 * ieee_setmaxrate().
931*48e4d00bSPetr Machata 	 */
932*48e4d00bSPetr Machata 	return MLXSW_REG_QEEC_HR_SUBGROUP;
933*48e4d00bSPetr Machata }
934*48e4d00bSPetr Machata 
935a44f58c4SPetr Machata static int
mlxsw_sp_qdisc_tbf_destroy(struct mlxsw_sp_port * mlxsw_sp_port,struct mlxsw_sp_qdisc * mlxsw_sp_qdisc)936a44f58c4SPetr Machata mlxsw_sp_qdisc_tbf_destroy(struct mlxsw_sp_port *mlxsw_sp_port,
937a44f58c4SPetr Machata 			   struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
938a44f58c4SPetr Machata {
939*48e4d00bSPetr Machata 	enum mlxsw_reg_qeec_hr hr = mlxsw_sp_qdisc_tbf_hr(mlxsw_sp_port,
940*48e4d00bSPetr Machata 							  mlxsw_sp_qdisc);
94176ff72a7SPetr Machata 	int tclass_num = mlxsw_sp_qdisc_get_tclass_num(mlxsw_sp_port,
94276ff72a7SPetr Machata 						       mlxsw_sp_qdisc);
94376ff72a7SPetr Machata 
944*48e4d00bSPetr Machata 	return mlxsw_sp_port_ets_maxrate_set(mlxsw_sp_port, hr, tclass_num, 0,
945a44f58c4SPetr Machata 					     MLXSW_REG_QEEC_MAS_DIS, 0);
946a44f58c4SPetr Machata }
947a44f58c4SPetr Machata 
948a44f58c4SPetr Machata static int
mlxsw_sp_qdisc_tbf_bs(struct mlxsw_sp_port * mlxsw_sp_port,u32 max_size,u8 * p_burst_size)949a44f58c4SPetr Machata mlxsw_sp_qdisc_tbf_bs(struct mlxsw_sp_port *mlxsw_sp_port,
950a44f58c4SPetr Machata 		      u32 max_size, u8 *p_burst_size)
951a44f58c4SPetr Machata {
952a44f58c4SPetr Machata 	/* TBF burst size is configured in bytes. The ASIC burst size value is
953a44f58c4SPetr Machata 	 * ((2 ^ bs) * 512 bits. Convert the TBF bytes to 512-bit units.
954a44f58c4SPetr Machata 	 */
955a44f58c4SPetr Machata 	u32 bs512 = max_size / 64;
956a44f58c4SPetr Machata 	u8 bs = fls(bs512);
957a44f58c4SPetr Machata 
958a44f58c4SPetr Machata 	if (!bs)
959a44f58c4SPetr Machata 		return -EINVAL;
960a44f58c4SPetr Machata 	--bs;
961a44f58c4SPetr Machata 
962a44f58c4SPetr Machata 	/* Demand a power of two. */
963a44f58c4SPetr Machata 	if ((1 << bs) != bs512)
964a44f58c4SPetr Machata 		return -EINVAL;
965a44f58c4SPetr Machata 
966a44f58c4SPetr Machata 	if (bs < mlxsw_sp_port->mlxsw_sp->lowest_shaper_bs ||
967a44f58c4SPetr Machata 	    bs > MLXSW_REG_QEEC_HIGHEST_SHAPER_BS)
968a44f58c4SPetr Machata 		return -EINVAL;
969a44f58c4SPetr Machata 
970a44f58c4SPetr Machata 	*p_burst_size = bs;
971a44f58c4SPetr Machata 	return 0;
972a44f58c4SPetr Machata }
973a44f58c4SPetr Machata 
974a44f58c4SPetr Machata static u32
mlxsw_sp_qdisc_tbf_max_size(u8 bs)975a44f58c4SPetr Machata mlxsw_sp_qdisc_tbf_max_size(u8 bs)
976a44f58c4SPetr Machata {
977a44f58c4SPetr Machata 	return (1U << bs) * 64;
978a44f58c4SPetr Machata }
979a44f58c4SPetr Machata 
980a44f58c4SPetr Machata static u64
mlxsw_sp_qdisc_tbf_rate_kbps(struct tc_tbf_qopt_offload_replace_params * p)981a44f58c4SPetr Machata mlxsw_sp_qdisc_tbf_rate_kbps(struct tc_tbf_qopt_offload_replace_params *p)
982a44f58c4SPetr Machata {
983a44f58c4SPetr Machata 	/* TBF interface is in bytes/s, whereas Spectrum ASIC is configured in
984a44f58c4SPetr Machata 	 * Kbits/s.
985a44f58c4SPetr Machata 	 */
98691a7d4bfSNathan Chancellor 	return div_u64(p->rate.rate_bytes_ps, 1000) * 8;
987a44f58c4SPetr Machata }
988a44f58c4SPetr Machata 
989a44f58c4SPetr Machata static int
mlxsw_sp_qdisc_tbf_check_params(struct mlxsw_sp_port * mlxsw_sp_port,void * params)990a44f58c4SPetr Machata mlxsw_sp_qdisc_tbf_check_params(struct mlxsw_sp_port *mlxsw_sp_port,
991a44f58c4SPetr Machata 				void *params)
992a44f58c4SPetr Machata {
993a44f58c4SPetr Machata 	struct tc_tbf_qopt_offload_replace_params *p = params;
994a44f58c4SPetr Machata 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
995a44f58c4SPetr Machata 	u64 rate_kbps = mlxsw_sp_qdisc_tbf_rate_kbps(p);
996a44f58c4SPetr Machata 	u8 burst_size;
997a44f58c4SPetr Machata 	int err;
998a44f58c4SPetr Machata 
999a44f58c4SPetr Machata 	if (rate_kbps >= MLXSW_REG_QEEC_MAS_DIS) {
1000a44f58c4SPetr Machata 		dev_err(mlxsw_sp_port->mlxsw_sp->bus_info->dev,
1001a44f58c4SPetr Machata 			"spectrum: TBF: rate of %lluKbps must be below %u\n",
1002a44f58c4SPetr Machata 			rate_kbps, MLXSW_REG_QEEC_MAS_DIS);
1003a44f58c4SPetr Machata 		return -EINVAL;
1004a44f58c4SPetr Machata 	}
1005a44f58c4SPetr Machata 
1006a44f58c4SPetr Machata 	err = mlxsw_sp_qdisc_tbf_bs(mlxsw_sp_port, p->max_size, &burst_size);
1007a44f58c4SPetr Machata 	if (err) {
1008a44f58c4SPetr Machata 		u8 highest_shaper_bs = MLXSW_REG_QEEC_HIGHEST_SHAPER_BS;
1009a44f58c4SPetr Machata 
1010a44f58c4SPetr Machata 		dev_err(mlxsw_sp->bus_info->dev,
1011a44f58c4SPetr Machata 			"spectrum: TBF: invalid burst size of %u, must be a power of two between %u and %u",
1012a44f58c4SPetr Machata 			p->max_size,
1013a44f58c4SPetr Machata 			mlxsw_sp_qdisc_tbf_max_size(mlxsw_sp->lowest_shaper_bs),
1014a44f58c4SPetr Machata 			mlxsw_sp_qdisc_tbf_max_size(highest_shaper_bs));
1015a44f58c4SPetr Machata 		return -EINVAL;
1016a44f58c4SPetr Machata 	}
1017a44f58c4SPetr Machata 
1018a44f58c4SPetr Machata 	return 0;
1019a44f58c4SPetr Machata }
1020a44f58c4SPetr Machata 
1021a44f58c4SPetr Machata static int
mlxsw_sp_qdisc_tbf_replace(struct mlxsw_sp_port * mlxsw_sp_port,u32 handle,struct mlxsw_sp_qdisc * mlxsw_sp_qdisc,void * params)1022c4e372e2SPetr Machata mlxsw_sp_qdisc_tbf_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
1023a44f58c4SPetr Machata 			   struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
1024a44f58c4SPetr Machata 			   void *params)
1025a44f58c4SPetr Machata {
1026*48e4d00bSPetr Machata 	enum mlxsw_reg_qeec_hr hr = mlxsw_sp_qdisc_tbf_hr(mlxsw_sp_port,
1027*48e4d00bSPetr Machata 							  mlxsw_sp_qdisc);
1028a44f58c4SPetr Machata 	struct tc_tbf_qopt_offload_replace_params *p = params;
1029a44f58c4SPetr Machata 	u64 rate_kbps = mlxsw_sp_qdisc_tbf_rate_kbps(p);
103076ff72a7SPetr Machata 	int tclass_num;
1031a44f58c4SPetr Machata 	u8 burst_size;
1032a44f58c4SPetr Machata 	int err;
1033a44f58c4SPetr Machata 
10342a18c08dSPetr Machata 	err = mlxsw_sp_qdisc_future_fifo_replace(mlxsw_sp_port, handle, 0,
10352a18c08dSPetr Machata 						 &mlxsw_sp_qdisc->qdiscs[0]);
10362a18c08dSPetr Machata 	if (err)
10372a18c08dSPetr Machata 		return err;
10382a18c08dSPetr Machata 	mlxsw_sp_qdisc_future_fifos_init(mlxsw_sp_port, TC_H_UNSPEC);
10392a18c08dSPetr Machata 
104076ff72a7SPetr Machata 	tclass_num = mlxsw_sp_qdisc_get_tclass_num(mlxsw_sp_port,
104176ff72a7SPetr Machata 						   mlxsw_sp_qdisc);
104276ff72a7SPetr Machata 
1043a44f58c4SPetr Machata 	err = mlxsw_sp_qdisc_tbf_bs(mlxsw_sp_port, p->max_size, &burst_size);
1044a44f58c4SPetr Machata 	if (WARN_ON_ONCE(err))
1045a44f58c4SPetr Machata 		/* check_params above was supposed to reject this value. */
1046a44f58c4SPetr Machata 		return -EINVAL;
1047a44f58c4SPetr Machata 
1048*48e4d00bSPetr Machata 	return mlxsw_sp_port_ets_maxrate_set(mlxsw_sp_port, hr, tclass_num, 0,
1049a44f58c4SPetr Machata 					     rate_kbps, burst_size);
1050a44f58c4SPetr Machata }
1051a44f58c4SPetr Machata 
1052a44f58c4SPetr Machata static void
mlxsw_sp_qdisc_tbf_unoffload(struct mlxsw_sp_port * mlxsw_sp_port,struct mlxsw_sp_qdisc * mlxsw_sp_qdisc,void * params)1053a44f58c4SPetr Machata mlxsw_sp_qdisc_tbf_unoffload(struct mlxsw_sp_port *mlxsw_sp_port,
1054a44f58c4SPetr Machata 			     struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
1055a44f58c4SPetr Machata 			     void *params)
1056a44f58c4SPetr Machata {
1057a44f58c4SPetr Machata 	struct tc_tbf_qopt_offload_replace_params *p = params;
1058a44f58c4SPetr Machata 
1059a44f58c4SPetr Machata 	mlxsw_sp_qdisc_leaf_unoffload(mlxsw_sp_port, mlxsw_sp_qdisc, p->qstats);
1060a44f58c4SPetr Machata }
1061a44f58c4SPetr Machata 
1062a44f58c4SPetr Machata static int
mlxsw_sp_qdisc_get_tbf_stats(struct mlxsw_sp_port * mlxsw_sp_port,struct mlxsw_sp_qdisc * mlxsw_sp_qdisc,struct tc_qopt_offload_stats * stats_ptr)1063a44f58c4SPetr Machata mlxsw_sp_qdisc_get_tbf_stats(struct mlxsw_sp_port *mlxsw_sp_port,
1064a44f58c4SPetr Machata 			     struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
1065a44f58c4SPetr Machata 			     struct tc_qopt_offload_stats *stats_ptr)
1066a44f58c4SPetr Machata {
1067a44f58c4SPetr Machata 	mlxsw_sp_qdisc_get_tc_stats(mlxsw_sp_port, mlxsw_sp_qdisc,
1068a44f58c4SPetr Machata 				    stats_ptr);
1069a44f58c4SPetr Machata 	return 0;
1070a44f58c4SPetr Machata }
1071a44f58c4SPetr Machata 
1072a44f58c4SPetr Machata static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_tbf = {
1073a44f58c4SPetr Machata 	.type = MLXSW_SP_QDISC_TBF,
1074a44f58c4SPetr Machata 	.check_params = mlxsw_sp_qdisc_tbf_check_params,
1075a44f58c4SPetr Machata 	.replace = mlxsw_sp_qdisc_tbf_replace,
1076a44f58c4SPetr Machata 	.unoffload = mlxsw_sp_qdisc_tbf_unoffload,
1077a44f58c4SPetr Machata 	.destroy = mlxsw_sp_qdisc_tbf_destroy,
1078a44f58c4SPetr Machata 	.get_stats = mlxsw_sp_qdisc_get_tbf_stats,
1079a44f58c4SPetr Machata 	.clean_stats = mlxsw_sp_setup_tc_qdisc_leaf_clean_stats,
108051d52ed9SPetr Machata 	.find_class = mlxsw_sp_qdisc_leaf_find_class,
10812a18c08dSPetr Machata 	.num_classes = 1,
1082a44f58c4SPetr Machata };
1083a44f58c4SPetr Machata 
__mlxsw_sp_setup_tc_tbf(struct mlxsw_sp_port * mlxsw_sp_port,struct tc_tbf_qopt_offload * p)1084cff99e20SPetr Machata static int __mlxsw_sp_setup_tc_tbf(struct mlxsw_sp_port *mlxsw_sp_port,
1085a44f58c4SPetr Machata 				   struct tc_tbf_qopt_offload *p)
1086a44f58c4SPetr Machata {
1087a44f58c4SPetr Machata 	struct mlxsw_sp_qdisc *mlxsw_sp_qdisc;
1088a44f58c4SPetr Machata 
1089c2792f38SPetr Machata 	mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent);
1090a44f58c4SPetr Machata 	if (!mlxsw_sp_qdisc)
1091a44f58c4SPetr Machata 		return -EOPNOTSUPP;
1092a44f58c4SPetr Machata 
1093a44f58c4SPetr Machata 	if (p->command == TC_TBF_REPLACE)
1094a44f58c4SPetr Machata 		return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle,
1095a44f58c4SPetr Machata 					      mlxsw_sp_qdisc,
1096a44f58c4SPetr Machata 					      &mlxsw_sp_qdisc_ops_tbf,
1097a44f58c4SPetr Machata 					      &p->replace_params);
1098a44f58c4SPetr Machata 
1099290fe2c5SPetr Machata 	if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle))
1100a44f58c4SPetr Machata 		return -EOPNOTSUPP;
1101a44f58c4SPetr Machata 
1102a44f58c4SPetr Machata 	switch (p->command) {
1103a44f58c4SPetr Machata 	case TC_TBF_DESTROY:
1104a44f58c4SPetr Machata 		return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
1105a44f58c4SPetr Machata 	case TC_TBF_STATS:
1106a44f58c4SPetr Machata 		return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc,
1107a44f58c4SPetr Machata 						&p->stats);
1108be7e2a5aSPetr Machata 	case TC_TBF_GRAFT:
1109be7e2a5aSPetr Machata 		return mlxsw_sp_qdisc_graft(mlxsw_sp_port, mlxsw_sp_qdisc, 0,
1110be7e2a5aSPetr Machata 					    p->child_handle);
1111a44f58c4SPetr Machata 	default:
1112a44f58c4SPetr Machata 		return -EOPNOTSUPP;
1113a44f58c4SPetr Machata 	}
1114a44f58c4SPetr Machata }
1115a44f58c4SPetr Machata 
mlxsw_sp_setup_tc_tbf(struct mlxsw_sp_port * mlxsw_sp_port,struct tc_tbf_qopt_offload * p)1116cff99e20SPetr Machata int mlxsw_sp_setup_tc_tbf(struct mlxsw_sp_port *mlxsw_sp_port,
1117cff99e20SPetr Machata 			  struct tc_tbf_qopt_offload *p)
1118cff99e20SPetr Machata {
1119cff99e20SPetr Machata 	int err;
1120cff99e20SPetr Machata 
1121cff99e20SPetr Machata 	mutex_lock(&mlxsw_sp_port->qdisc->lock);
1122cff99e20SPetr Machata 	err = __mlxsw_sp_setup_tc_tbf(mlxsw_sp_port, p);
1123cff99e20SPetr Machata 	mutex_unlock(&mlxsw_sp_port->qdisc->lock);
1124cff99e20SPetr Machata 
1125cff99e20SPetr Machata 	return err;
1126cff99e20SPetr Machata }
1127cff99e20SPetr Machata 
112846a3615bSNogah Frankel static int
mlxsw_sp_qdisc_fifo_check_params(struct mlxsw_sp_port * mlxsw_sp_port,void * params)11297bec1a45SPetr Machata mlxsw_sp_qdisc_fifo_check_params(struct mlxsw_sp_port *mlxsw_sp_port,
11307bec1a45SPetr Machata 				 void *params)
11317bec1a45SPetr Machata {
11327bec1a45SPetr Machata 	return 0;
11337bec1a45SPetr Machata }
11347bec1a45SPetr Machata 
11357bec1a45SPetr Machata static int
mlxsw_sp_qdisc_fifo_replace(struct mlxsw_sp_port * mlxsw_sp_port,u32 handle,struct mlxsw_sp_qdisc * mlxsw_sp_qdisc,void * params)11367bec1a45SPetr Machata mlxsw_sp_qdisc_fifo_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
11377bec1a45SPetr Machata 			    struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
11387bec1a45SPetr Machata 			    void *params)
11397bec1a45SPetr Machata {
11407bec1a45SPetr Machata 	return 0;
11417bec1a45SPetr Machata }
11427bec1a45SPetr Machata 
11437bec1a45SPetr Machata static int
mlxsw_sp_qdisc_get_fifo_stats(struct mlxsw_sp_port * mlxsw_sp_port,struct mlxsw_sp_qdisc * mlxsw_sp_qdisc,struct tc_qopt_offload_stats * stats_ptr)11447bec1a45SPetr Machata mlxsw_sp_qdisc_get_fifo_stats(struct mlxsw_sp_port *mlxsw_sp_port,
11457bec1a45SPetr Machata 			      struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
11467bec1a45SPetr Machata 			      struct tc_qopt_offload_stats *stats_ptr)
11477bec1a45SPetr Machata {
11487bec1a45SPetr Machata 	mlxsw_sp_qdisc_get_tc_stats(mlxsw_sp_port, mlxsw_sp_qdisc,
11497bec1a45SPetr Machata 				    stats_ptr);
11507bec1a45SPetr Machata 	return 0;
11517bec1a45SPetr Machata }
11527bec1a45SPetr Machata 
11537bec1a45SPetr Machata static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_fifo = {
11547bec1a45SPetr Machata 	.type = MLXSW_SP_QDISC_FIFO,
11557bec1a45SPetr Machata 	.check_params = mlxsw_sp_qdisc_fifo_check_params,
11567bec1a45SPetr Machata 	.replace = mlxsw_sp_qdisc_fifo_replace,
11577bec1a45SPetr Machata 	.get_stats = mlxsw_sp_qdisc_get_fifo_stats,
11587bec1a45SPetr Machata 	.clean_stats = mlxsw_sp_setup_tc_qdisc_leaf_clean_stats,
11597bec1a45SPetr Machata };
11607bec1a45SPetr Machata 
116191796f50SPetr Machata static int
mlxsw_sp_qdisc_future_fifo_replace(struct mlxsw_sp_port * mlxsw_sp_port,u32 handle,unsigned int band,struct mlxsw_sp_qdisc * child_qdisc)116291796f50SPetr Machata mlxsw_sp_qdisc_future_fifo_replace(struct mlxsw_sp_port *mlxsw_sp_port,
116391796f50SPetr Machata 				   u32 handle, unsigned int band,
116491796f50SPetr Machata 				   struct mlxsw_sp_qdisc *child_qdisc)
116591796f50SPetr Machata {
116691796f50SPetr Machata 	struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc;
116791796f50SPetr Machata 
116891796f50SPetr Machata 	if (handle == qdisc_state->future_handle &&
116991796f50SPetr Machata 	    qdisc_state->future_fifos[band])
117091796f50SPetr Machata 		return mlxsw_sp_qdisc_replace(mlxsw_sp_port, TC_H_UNSPEC,
117191796f50SPetr Machata 					      child_qdisc,
117291796f50SPetr Machata 					      &mlxsw_sp_qdisc_ops_fifo,
117391796f50SPetr Machata 					      NULL);
117491796f50SPetr Machata 	return 0;
117591796f50SPetr Machata }
117691796f50SPetr Machata 
117791796f50SPetr Machata static void
mlxsw_sp_qdisc_future_fifos_init(struct mlxsw_sp_port * mlxsw_sp_port,u32 handle)117891796f50SPetr Machata mlxsw_sp_qdisc_future_fifos_init(struct mlxsw_sp_port *mlxsw_sp_port,
117991796f50SPetr Machata 				 u32 handle)
118091796f50SPetr Machata {
118191796f50SPetr Machata 	struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc;
118291796f50SPetr Machata 
118391796f50SPetr Machata 	qdisc_state->future_handle = handle;
118491796f50SPetr Machata 	memset(qdisc_state->future_fifos, 0, sizeof(qdisc_state->future_fifos));
118591796f50SPetr Machata }
118691796f50SPetr Machata 
__mlxsw_sp_setup_tc_fifo(struct mlxsw_sp_port * mlxsw_sp_port,struct tc_fifo_qopt_offload * p)1187cff99e20SPetr Machata static int __mlxsw_sp_setup_tc_fifo(struct mlxsw_sp_port *mlxsw_sp_port,
11887bec1a45SPetr Machata 				    struct tc_fifo_qopt_offload *p)
11897bec1a45SPetr Machata {
11907bec1a45SPetr Machata 	struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc;
11917bec1a45SPetr Machata 	struct mlxsw_sp_qdisc *mlxsw_sp_qdisc;
11927de85b04SPetr Machata 	unsigned int band;
11937bec1a45SPetr Machata 	u32 parent_handle;
11947bec1a45SPetr Machata 
1195c2792f38SPetr Machata 	mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent);
11967bec1a45SPetr Machata 	if (!mlxsw_sp_qdisc && p->handle == TC_H_UNSPEC) {
11977bec1a45SPetr Machata 		parent_handle = TC_H_MAJ(p->parent);
11987bec1a45SPetr Machata 		if (parent_handle != qdisc_state->future_handle) {
11997bec1a45SPetr Machata 			/* This notifications is for a different Qdisc than
12007bec1a45SPetr Machata 			 * previously. Wipe the future cache.
12017bec1a45SPetr Machata 			 */
120291796f50SPetr Machata 			mlxsw_sp_qdisc_future_fifos_init(mlxsw_sp_port,
120391796f50SPetr Machata 							 parent_handle);
12047bec1a45SPetr Machata 		}
12057bec1a45SPetr Machata 
12067de85b04SPetr Machata 		band = TC_H_MIN(p->parent) - 1;
12077de85b04SPetr Machata 		if (band < IEEE_8021QAZ_MAX_TCS) {
12087bec1a45SPetr Machata 			if (p->command == TC_FIFO_REPLACE)
12097de85b04SPetr Machata 				qdisc_state->future_fifos[band] = true;
12107bec1a45SPetr Machata 			else if (p->command == TC_FIFO_DESTROY)
12117de85b04SPetr Machata 				qdisc_state->future_fifos[band] = false;
12127bec1a45SPetr Machata 		}
12137bec1a45SPetr Machata 	}
12147bec1a45SPetr Machata 	if (!mlxsw_sp_qdisc)
12157bec1a45SPetr Machata 		return -EOPNOTSUPP;
12167bec1a45SPetr Machata 
12177bec1a45SPetr Machata 	if (p->command == TC_FIFO_REPLACE) {
12187bec1a45SPetr Machata 		return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle,
12197bec1a45SPetr Machata 					      mlxsw_sp_qdisc,
12207bec1a45SPetr Machata 					      &mlxsw_sp_qdisc_ops_fifo, NULL);
12217bec1a45SPetr Machata 	}
12227bec1a45SPetr Machata 
1223290fe2c5SPetr Machata 	if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle))
12247bec1a45SPetr Machata 		return -EOPNOTSUPP;
12257bec1a45SPetr Machata 
12267bec1a45SPetr Machata 	switch (p->command) {
12277bec1a45SPetr Machata 	case TC_FIFO_DESTROY:
1228549f2aaeSPetr Machata 		return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
12297bec1a45SPetr Machata 	case TC_FIFO_STATS:
12307bec1a45SPetr Machata 		return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc,
12317bec1a45SPetr Machata 						&p->stats);
12327bec1a45SPetr Machata 	case TC_FIFO_REPLACE: /* Handled above. */
12337bec1a45SPetr Machata 		break;
12347bec1a45SPetr Machata 	}
12357bec1a45SPetr Machata 
12367bec1a45SPetr Machata 	return -EOPNOTSUPP;
12377bec1a45SPetr Machata }
12387bec1a45SPetr Machata 
mlxsw_sp_setup_tc_fifo(struct mlxsw_sp_port * mlxsw_sp_port,struct tc_fifo_qopt_offload * p)1239cff99e20SPetr Machata int mlxsw_sp_setup_tc_fifo(struct mlxsw_sp_port *mlxsw_sp_port,
1240cff99e20SPetr Machata 			   struct tc_fifo_qopt_offload *p)
1241cff99e20SPetr Machata {
1242cff99e20SPetr Machata 	int err;
1243cff99e20SPetr Machata 
1244cff99e20SPetr Machata 	mutex_lock(&mlxsw_sp_port->qdisc->lock);
1245cff99e20SPetr Machata 	err = __mlxsw_sp_setup_tc_fifo(mlxsw_sp_port, p);
1246cff99e20SPetr Machata 	mutex_unlock(&mlxsw_sp_port->qdisc->lock);
1247cff99e20SPetr Machata 
1248cff99e20SPetr Machata 	return err;
1249cff99e20SPetr Machata }
1250cff99e20SPetr Machata 
__mlxsw_sp_qdisc_ets_destroy(struct mlxsw_sp_port * mlxsw_sp_port,struct mlxsw_sp_qdisc * mlxsw_sp_qdisc)125151d52ed9SPetr Machata static int __mlxsw_sp_qdisc_ets_destroy(struct mlxsw_sp_port *mlxsw_sp_port,
125251d52ed9SPetr Machata 					struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
125346a3615bSNogah Frankel {
125446a3615bSNogah Frankel 	int i;
125546a3615bSNogah Frankel 
125651d52ed9SPetr Machata 	for (i = 0; i < mlxsw_sp_qdisc->num_classes; i++) {
125746a3615bSNogah Frankel 		mlxsw_sp_port_prio_tc_set(mlxsw_sp_port, i,
125846a3615bSNogah Frankel 					  MLXSW_SP_PORT_DEFAULT_TCLASS);
12597917f52aSPetr Machata 		mlxsw_sp_port_ets_set(mlxsw_sp_port,
12607917f52aSPetr Machata 				      MLXSW_REG_QEEC_HR_SUBGROUP,
12617917f52aSPetr Machata 				      i, 0, false, 0);
1262eed4baebSNogah Frankel 	}
126346a3615bSNogah Frankel 
126476ff72a7SPetr Machata 	kfree(mlxsw_sp_qdisc->ets_data);
126576ff72a7SPetr Machata 	mlxsw_sp_qdisc->ets_data = NULL;
126646a3615bSNogah Frankel 	return 0;
126746a3615bSNogah Frankel }
126846a3615bSNogah Frankel 
126946a3615bSNogah Frankel static int
mlxsw_sp_qdisc_prio_destroy(struct mlxsw_sp_port * mlxsw_sp_port,struct mlxsw_sp_qdisc * mlxsw_sp_qdisc)12707917f52aSPetr Machata mlxsw_sp_qdisc_prio_destroy(struct mlxsw_sp_port *mlxsw_sp_port,
12717917f52aSPetr Machata 			    struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
12727917f52aSPetr Machata {
127351d52ed9SPetr Machata 	return __mlxsw_sp_qdisc_ets_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
12747917f52aSPetr Machata }
12757917f52aSPetr Machata 
12767917f52aSPetr Machata static int
__mlxsw_sp_qdisc_ets_check_params(unsigned int nbands)12777917f52aSPetr Machata __mlxsw_sp_qdisc_ets_check_params(unsigned int nbands)
12787917f52aSPetr Machata {
12797917f52aSPetr Machata 	if (nbands > IEEE_8021QAZ_MAX_TCS)
12807917f52aSPetr Machata 		return -EOPNOTSUPP;
12817917f52aSPetr Machata 
12827917f52aSPetr Machata 	return 0;
12837917f52aSPetr Machata }
12847917f52aSPetr Machata 
12857917f52aSPetr Machata static int
mlxsw_sp_qdisc_prio_check_params(struct mlxsw_sp_port * mlxsw_sp_port,void * params)128646a3615bSNogah Frankel mlxsw_sp_qdisc_prio_check_params(struct mlxsw_sp_port *mlxsw_sp_port,
128746a3615bSNogah Frankel 				 void *params)
128846a3615bSNogah Frankel {
128946a3615bSNogah Frankel 	struct tc_prio_qopt_offload_params *p = params;
129046a3615bSNogah Frankel 
12917917f52aSPetr Machata 	return __mlxsw_sp_qdisc_ets_check_params(p->bands);
129246a3615bSNogah Frankel }
129346a3615bSNogah Frankel 
129401164ddaSPetr Machata static struct mlxsw_sp_qdisc *
mlxsw_sp_qdisc_walk_cb_clean_stats(struct mlxsw_sp_qdisc * mlxsw_sp_qdisc,void * mlxsw_sp_port)129501164ddaSPetr Machata mlxsw_sp_qdisc_walk_cb_clean_stats(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
129601164ddaSPetr Machata 				   void *mlxsw_sp_port)
129701164ddaSPetr Machata {
129801164ddaSPetr Machata 	u64 backlog;
129901164ddaSPetr Machata 
130001164ddaSPetr Machata 	if (mlxsw_sp_qdisc->ops) {
130101164ddaSPetr Machata 		backlog = mlxsw_sp_qdisc->stats_base.backlog;
130201164ddaSPetr Machata 		if (mlxsw_sp_qdisc->ops->clean_stats)
130301164ddaSPetr Machata 			mlxsw_sp_qdisc->ops->clean_stats(mlxsw_sp_port,
130401164ddaSPetr Machata 							 mlxsw_sp_qdisc);
130501164ddaSPetr Machata 		mlxsw_sp_qdisc->stats_base.backlog = backlog;
130601164ddaSPetr Machata 	}
130701164ddaSPetr Machata 
130801164ddaSPetr Machata 	return NULL;
130901164ddaSPetr Machata }
131001164ddaSPetr Machata 
131101164ddaSPetr Machata static void
mlxsw_sp_qdisc_tree_clean_stats(struct mlxsw_sp_port * mlxsw_sp_port,struct mlxsw_sp_qdisc * mlxsw_sp_qdisc)131201164ddaSPetr Machata mlxsw_sp_qdisc_tree_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port,
131301164ddaSPetr Machata 				struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
131401164ddaSPetr Machata {
131501164ddaSPetr Machata 	mlxsw_sp_qdisc_walk(mlxsw_sp_qdisc, mlxsw_sp_qdisc_walk_cb_clean_stats,
131601164ddaSPetr Machata 			    mlxsw_sp_port);
131701164ddaSPetr Machata }
131801164ddaSPetr Machata 
131946a3615bSNogah Frankel static int
__mlxsw_sp_qdisc_ets_replace(struct mlxsw_sp_port * mlxsw_sp_port,struct mlxsw_sp_qdisc * mlxsw_sp_qdisc,u32 handle,unsigned int nbands,const unsigned int * quanta,const unsigned int * weights,const u8 * priomap)132051d52ed9SPetr Machata __mlxsw_sp_qdisc_ets_replace(struct mlxsw_sp_port *mlxsw_sp_port,
132151d52ed9SPetr Machata 			     struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
132251d52ed9SPetr Machata 			     u32 handle, unsigned int nbands,
13237917f52aSPetr Machata 			     const unsigned int *quanta,
13247917f52aSPetr Machata 			     const unsigned int *weights,
13257917f52aSPetr Machata 			     const u8 *priomap)
132646a3615bSNogah Frankel {
132776ff72a7SPetr Machata 	struct mlxsw_sp_qdisc_ets_data *ets_data = mlxsw_sp_qdisc->ets_data;
132876ff72a7SPetr Machata 	struct mlxsw_sp_qdisc_ets_band *ets_band;
132904cc0bf5SNogah Frankel 	struct mlxsw_sp_qdisc *child_qdisc;
133076ff72a7SPetr Machata 	u8 old_priomap, new_priomap;
133101164ddaSPetr Machata 	int i, band;
133246a3615bSNogah Frankel 	int err;
133346a3615bSNogah Frankel 
133476ff72a7SPetr Machata 	if (!ets_data) {
133576ff72a7SPetr Machata 		ets_data = kzalloc(sizeof(*ets_data), GFP_KERNEL);
133676ff72a7SPetr Machata 		if (!ets_data)
133776ff72a7SPetr Machata 			return -ENOMEM;
133876ff72a7SPetr Machata 		mlxsw_sp_qdisc->ets_data = ets_data;
133976ff72a7SPetr Machata 
134076ff72a7SPetr Machata 		for (band = 0; band < mlxsw_sp_qdisc->num_classes; band++) {
134176ff72a7SPetr Machata 			int tclass_num = MLXSW_SP_PRIO_BAND_TO_TCLASS(band);
134276ff72a7SPetr Machata 
134376ff72a7SPetr Machata 			ets_band = &ets_data->bands[band];
134476ff72a7SPetr Machata 			ets_band->tclass_num = tclass_num;
134576ff72a7SPetr Machata 		}
134676ff72a7SPetr Machata 	}
134776ff72a7SPetr Machata 
13487917f52aSPetr Machata 	for (band = 0; band < nbands; band++) {
134976ff72a7SPetr Machata 		int tclass_num;
135076ff72a7SPetr Machata 
135151d52ed9SPetr Machata 		child_qdisc = &mlxsw_sp_qdisc->qdiscs[band];
135276ff72a7SPetr Machata 		ets_band = &ets_data->bands[band];
135376ff72a7SPetr Machata 
135476ff72a7SPetr Machata 		tclass_num = ets_band->tclass_num;
135576ff72a7SPetr Machata 		old_priomap = ets_band->prio_bitmap;
135676ff72a7SPetr Machata 		new_priomap = 0;
13577917f52aSPetr Machata 
13587917f52aSPetr Machata 		err = mlxsw_sp_port_ets_set(mlxsw_sp_port,
13597917f52aSPetr Machata 					    MLXSW_REG_QEEC_HR_SUBGROUP,
136076ff72a7SPetr Machata 					    tclass_num, 0, !!quanta[band],
13617917f52aSPetr Machata 					    weights[band]);
13627917f52aSPetr Machata 		if (err)
13637917f52aSPetr Machata 			return err;
13647917f52aSPetr Machata 
136546a3615bSNogah Frankel 		for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
13667917f52aSPetr Machata 			if (priomap[i] == band) {
136776ff72a7SPetr Machata 				new_priomap |= BIT(i);
136804cc0bf5SNogah Frankel 				if (BIT(i) & old_priomap)
136904cc0bf5SNogah Frankel 					continue;
137004cc0bf5SNogah Frankel 				err = mlxsw_sp_port_prio_tc_set(mlxsw_sp_port,
137176ff72a7SPetr Machata 								i, tclass_num);
137246a3615bSNogah Frankel 				if (err)
137346a3615bSNogah Frankel 					return err;
137404cc0bf5SNogah Frankel 			}
137504cc0bf5SNogah Frankel 		}
13765cbd9602SPetr Machata 
137776ff72a7SPetr Machata 		ets_band->prio_bitmap = new_priomap;
13785cbd9602SPetr Machata 
137901164ddaSPetr Machata 		if (old_priomap != new_priomap)
138001164ddaSPetr Machata 			mlxsw_sp_qdisc_tree_clean_stats(mlxsw_sp_port,
138104cc0bf5SNogah Frankel 							child_qdisc);
13827bec1a45SPetr Machata 
138391796f50SPetr Machata 		err = mlxsw_sp_qdisc_future_fifo_replace(mlxsw_sp_port, handle,
138491796f50SPetr Machata 							 band, child_qdisc);
13857bec1a45SPetr Machata 		if (err)
13867bec1a45SPetr Machata 			return err;
13877bec1a45SPetr Machata 	}
138898ceb7b6SNogah Frankel 	for (; band < IEEE_8021QAZ_MAX_TCS; band++) {
138976ff72a7SPetr Machata 		ets_band = &ets_data->bands[band];
139076ff72a7SPetr Machata 		ets_band->prio_bitmap = 0;
139176ff72a7SPetr Machata 
139251d52ed9SPetr Machata 		child_qdisc = &mlxsw_sp_qdisc->qdiscs[band];
139398ceb7b6SNogah Frankel 		mlxsw_sp_qdisc_destroy(mlxsw_sp_port, child_qdisc);
139476ff72a7SPetr Machata 
13957917f52aSPetr Machata 		mlxsw_sp_port_ets_set(mlxsw_sp_port,
13967917f52aSPetr Machata 				      MLXSW_REG_QEEC_HR_SUBGROUP,
139776ff72a7SPetr Machata 				      ets_band->tclass_num, 0, false, 0);
139898ceb7b6SNogah Frankel 	}
13997bec1a45SPetr Machata 
140091796f50SPetr Machata 	mlxsw_sp_qdisc_future_fifos_init(mlxsw_sp_port, TC_H_UNSPEC);
140146a3615bSNogah Frankel 	return 0;
140246a3615bSNogah Frankel }
140346a3615bSNogah Frankel 
14047917f52aSPetr Machata static int
mlxsw_sp_qdisc_prio_replace(struct mlxsw_sp_port * mlxsw_sp_port,u32 handle,struct mlxsw_sp_qdisc * mlxsw_sp_qdisc,void * params)1405c4e372e2SPetr Machata mlxsw_sp_qdisc_prio_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
14067917f52aSPetr Machata 			    struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
14077917f52aSPetr Machata 			    void *params)
14087917f52aSPetr Machata {
14097917f52aSPetr Machata 	struct tc_prio_qopt_offload_params *p = params;
14107917f52aSPetr Machata 	unsigned int zeroes[TCQ_ETS_MAX_BANDS] = {0};
14117917f52aSPetr Machata 
141251d52ed9SPetr Machata 	return __mlxsw_sp_qdisc_ets_replace(mlxsw_sp_port, mlxsw_sp_qdisc,
141351d52ed9SPetr Machata 					    handle, p->bands, zeroes,
141451d52ed9SPetr Machata 					    zeroes, p->priomap);
14157917f52aSPetr Machata }
14167917f52aSPetr Machata 
14177917f52aSPetr Machata static void
__mlxsw_sp_qdisc_ets_unoffload(struct mlxsw_sp_port * mlxsw_sp_port,struct mlxsw_sp_qdisc * mlxsw_sp_qdisc,struct gnet_stats_queue * qstats)14187917f52aSPetr Machata __mlxsw_sp_qdisc_ets_unoffload(struct mlxsw_sp_port *mlxsw_sp_port,
14197917f52aSPetr Machata 			       struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
14207917f52aSPetr Machata 			       struct gnet_stats_queue *qstats)
14217917f52aSPetr Machata {
14227917f52aSPetr Machata 	u64 backlog;
14237917f52aSPetr Machata 
14247917f52aSPetr Machata 	backlog = mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp,
14257917f52aSPetr Machata 				       mlxsw_sp_qdisc->stats_base.backlog);
14267917f52aSPetr Machata 	qstats->backlog -= backlog;
14277917f52aSPetr Machata }
14287917f52aSPetr Machata 
1429e02f08a0SWei Yongjun static void
mlxsw_sp_qdisc_prio_unoffload(struct mlxsw_sp_port * mlxsw_sp_port,struct mlxsw_sp_qdisc * mlxsw_sp_qdisc,void * params)143093d8a4c1SNogah Frankel mlxsw_sp_qdisc_prio_unoffload(struct mlxsw_sp_port *mlxsw_sp_port,
143193d8a4c1SNogah Frankel 			      struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
143293d8a4c1SNogah Frankel 			      void *params)
143393d8a4c1SNogah Frankel {
143493d8a4c1SNogah Frankel 	struct tc_prio_qopt_offload_params *p = params;
143593d8a4c1SNogah Frankel 
14367917f52aSPetr Machata 	__mlxsw_sp_qdisc_ets_unoffload(mlxsw_sp_port, mlxsw_sp_qdisc,
14377917f52aSPetr Machata 				       p->qstats);
143893d8a4c1SNogah Frankel }
143993d8a4c1SNogah Frankel 
144093d8a4c1SNogah Frankel static int
mlxsw_sp_qdisc_get_prio_stats(struct mlxsw_sp_port * mlxsw_sp_port,struct mlxsw_sp_qdisc * mlxsw_sp_qdisc,struct tc_qopt_offload_stats * stats_ptr)144193d8a4c1SNogah Frankel mlxsw_sp_qdisc_get_prio_stats(struct mlxsw_sp_port *mlxsw_sp_port,
144293d8a4c1SNogah Frankel 			      struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
144393d8a4c1SNogah Frankel 			      struct tc_qopt_offload_stats *stats_ptr)
144493d8a4c1SNogah Frankel {
1445cf9af379SPetr Machata 	struct mlxsw_sp_qdisc *tc_qdisc;
1446cf9af379SPetr Machata 	u64 tx_packets = 0;
1447cf9af379SPetr Machata 	u64 tx_bytes = 0;
1448cf9af379SPetr Machata 	u64 backlog = 0;
1449cf9af379SPetr Machata 	u64 drops = 0;
145093d8a4c1SNogah Frankel 	int i;
145193d8a4c1SNogah Frankel 
145251d52ed9SPetr Machata 	for (i = 0; i < mlxsw_sp_qdisc->num_classes; i++) {
145351d52ed9SPetr Machata 		tc_qdisc = &mlxsw_sp_qdisc->qdiscs[i];
1454cf9af379SPetr Machata 		mlxsw_sp_qdisc_collect_tc_stats(mlxsw_sp_port, tc_qdisc,
1455cf9af379SPetr Machata 						&tx_bytes, &tx_packets,
1456cf9af379SPetr Machata 						&drops, &backlog);
145793d8a4c1SNogah Frankel 	}
145893d8a4c1SNogah Frankel 
1459cf9af379SPetr Machata 	mlxsw_sp_qdisc_update_stats(mlxsw_sp_port->mlxsw_sp, mlxsw_sp_qdisc,
1460cf9af379SPetr Machata 				    tx_bytes, tx_packets, drops, backlog,
1461cf9af379SPetr Machata 				    stats_ptr);
146293d8a4c1SNogah Frankel 	return 0;
146393d8a4c1SNogah Frankel }
146493d8a4c1SNogah Frankel 
146593d8a4c1SNogah Frankel static void
mlxsw_sp_setup_tc_qdisc_prio_clean_stats(struct mlxsw_sp_port * mlxsw_sp_port,struct mlxsw_sp_qdisc * mlxsw_sp_qdisc)146693d8a4c1SNogah Frankel mlxsw_sp_setup_tc_qdisc_prio_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port,
146793d8a4c1SNogah Frankel 					 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
146893d8a4c1SNogah Frankel {
146993d8a4c1SNogah Frankel 	struct mlxsw_sp_qdisc_stats *stats_base;
147093d8a4c1SNogah Frankel 	struct mlxsw_sp_port_xstats *xstats;
147193d8a4c1SNogah Frankel 	struct rtnl_link_stats64 *stats;
147293d8a4c1SNogah Frankel 	int i;
147393d8a4c1SNogah Frankel 
147493d8a4c1SNogah Frankel 	xstats = &mlxsw_sp_port->periodic_hw_stats.xstats;
147593d8a4c1SNogah Frankel 	stats = &mlxsw_sp_port->periodic_hw_stats.stats;
147693d8a4c1SNogah Frankel 	stats_base = &mlxsw_sp_qdisc->stats_base;
147793d8a4c1SNogah Frankel 
147893d8a4c1SNogah Frankel 	stats_base->tx_packets = stats->tx_packets;
147993d8a4c1SNogah Frankel 	stats_base->tx_bytes = stats->tx_bytes;
148093d8a4c1SNogah Frankel 
148193d8a4c1SNogah Frankel 	stats_base->drops = 0;
148223f2b404SNogah Frankel 	for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
148385005b82SPetr Machata 		stats_base->drops += mlxsw_sp_xstats_tail_drop(xstats, i);
148423f2b404SNogah Frankel 		stats_base->drops += xstats->wred_drop[i];
148523f2b404SNogah Frankel 	}
148693d8a4c1SNogah Frankel 
148793d8a4c1SNogah Frankel 	mlxsw_sp_qdisc->stats_base.backlog = 0;
148893d8a4c1SNogah Frankel }
148993d8a4c1SNogah Frankel 
149051d52ed9SPetr Machata static struct mlxsw_sp_qdisc *
mlxsw_sp_qdisc_prio_find_class(struct mlxsw_sp_qdisc * mlxsw_sp_qdisc,u32 parent)149151d52ed9SPetr Machata mlxsw_sp_qdisc_prio_find_class(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
149251d52ed9SPetr Machata 			       u32 parent)
149351d52ed9SPetr Machata {
149451d52ed9SPetr Machata 	int child_index = TC_H_MIN(parent);
149551d52ed9SPetr Machata 	int band = child_index - 1;
149651d52ed9SPetr Machata 
149751d52ed9SPetr Machata 	if (band < 0 || band >= mlxsw_sp_qdisc->num_classes)
149851d52ed9SPetr Machata 		return NULL;
149951d52ed9SPetr Machata 	return &mlxsw_sp_qdisc->qdiscs[band];
150051d52ed9SPetr Machata }
150151d52ed9SPetr Machata 
150276ff72a7SPetr Machata static struct mlxsw_sp_qdisc_ets_band *
mlxsw_sp_qdisc_ets_get_band(struct mlxsw_sp_qdisc * mlxsw_sp_qdisc,struct mlxsw_sp_qdisc * child)150376ff72a7SPetr Machata mlxsw_sp_qdisc_ets_get_band(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
150476ff72a7SPetr Machata 			    struct mlxsw_sp_qdisc *child)
150576ff72a7SPetr Machata {
150676ff72a7SPetr Machata 	unsigned int band = child - mlxsw_sp_qdisc->qdiscs;
150776ff72a7SPetr Machata 
150876ff72a7SPetr Machata 	if (WARN_ON(band >= IEEE_8021QAZ_MAX_TCS))
150976ff72a7SPetr Machata 		band = 0;
151076ff72a7SPetr Machata 	return &mlxsw_sp_qdisc->ets_data->bands[band];
151176ff72a7SPetr Machata }
151276ff72a7SPetr Machata 
151376ff72a7SPetr Machata static u8
mlxsw_sp_qdisc_ets_get_prio_bitmap(struct mlxsw_sp_qdisc * mlxsw_sp_qdisc,struct mlxsw_sp_qdisc * child)151476ff72a7SPetr Machata mlxsw_sp_qdisc_ets_get_prio_bitmap(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
151576ff72a7SPetr Machata 				   struct mlxsw_sp_qdisc *child)
151676ff72a7SPetr Machata {
151776ff72a7SPetr Machata 	return mlxsw_sp_qdisc_ets_get_band(mlxsw_sp_qdisc, child)->prio_bitmap;
151876ff72a7SPetr Machata }
151976ff72a7SPetr Machata 
152076ff72a7SPetr Machata static int
mlxsw_sp_qdisc_ets_get_tclass_num(struct mlxsw_sp_qdisc * mlxsw_sp_qdisc,struct mlxsw_sp_qdisc * child)152176ff72a7SPetr Machata mlxsw_sp_qdisc_ets_get_tclass_num(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
152276ff72a7SPetr Machata 				  struct mlxsw_sp_qdisc *child)
152376ff72a7SPetr Machata {
152476ff72a7SPetr Machata 	return mlxsw_sp_qdisc_ets_get_band(mlxsw_sp_qdisc, child)->tclass_num;
152576ff72a7SPetr Machata }
152676ff72a7SPetr Machata 
152746a3615bSNogah Frankel static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_prio = {
152846a3615bSNogah Frankel 	.type = MLXSW_SP_QDISC_PRIO,
152946a3615bSNogah Frankel 	.check_params = mlxsw_sp_qdisc_prio_check_params,
153046a3615bSNogah Frankel 	.replace = mlxsw_sp_qdisc_prio_replace,
153193d8a4c1SNogah Frankel 	.unoffload = mlxsw_sp_qdisc_prio_unoffload,
153246a3615bSNogah Frankel 	.destroy = mlxsw_sp_qdisc_prio_destroy,
153393d8a4c1SNogah Frankel 	.get_stats = mlxsw_sp_qdisc_get_prio_stats,
153493d8a4c1SNogah Frankel 	.clean_stats = mlxsw_sp_setup_tc_qdisc_prio_clean_stats,
153551d52ed9SPetr Machata 	.find_class = mlxsw_sp_qdisc_prio_find_class,
15365cbd9602SPetr Machata 	.num_classes = IEEE_8021QAZ_MAX_TCS,
153776ff72a7SPetr Machata 	.get_prio_bitmap = mlxsw_sp_qdisc_ets_get_prio_bitmap,
153876ff72a7SPetr Machata 	.get_tclass_num = mlxsw_sp_qdisc_ets_get_tclass_num,
153946a3615bSNogah Frankel };
154046a3615bSNogah Frankel 
154119f405b9SPetr Machata static int
mlxsw_sp_qdisc_ets_check_params(struct mlxsw_sp_port * mlxsw_sp_port,void * params)154219f405b9SPetr Machata mlxsw_sp_qdisc_ets_check_params(struct mlxsw_sp_port *mlxsw_sp_port,
154319f405b9SPetr Machata 				void *params)
154419f405b9SPetr Machata {
154519f405b9SPetr Machata 	struct tc_ets_qopt_offload_replace_params *p = params;
154619f405b9SPetr Machata 
154719f405b9SPetr Machata 	return __mlxsw_sp_qdisc_ets_check_params(p->bands);
154819f405b9SPetr Machata }
154919f405b9SPetr Machata 
155019f405b9SPetr Machata static int
mlxsw_sp_qdisc_ets_replace(struct mlxsw_sp_port * mlxsw_sp_port,u32 handle,struct mlxsw_sp_qdisc * mlxsw_sp_qdisc,void * params)1551c4e372e2SPetr Machata mlxsw_sp_qdisc_ets_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
155219f405b9SPetr Machata 			   struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
155319f405b9SPetr Machata 			   void *params)
155419f405b9SPetr Machata {
155519f405b9SPetr Machata 	struct tc_ets_qopt_offload_replace_params *p = params;
155619f405b9SPetr Machata 
155751d52ed9SPetr Machata 	return __mlxsw_sp_qdisc_ets_replace(mlxsw_sp_port, mlxsw_sp_qdisc,
155851d52ed9SPetr Machata 					    handle, p->bands, p->quanta,
155951d52ed9SPetr Machata 					    p->weights, p->priomap);
156019f405b9SPetr Machata }
156119f405b9SPetr Machata 
156219f405b9SPetr Machata static void
mlxsw_sp_qdisc_ets_unoffload(struct mlxsw_sp_port * mlxsw_sp_port,struct mlxsw_sp_qdisc * mlxsw_sp_qdisc,void * params)156319f405b9SPetr Machata mlxsw_sp_qdisc_ets_unoffload(struct mlxsw_sp_port *mlxsw_sp_port,
156419f405b9SPetr Machata 			     struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
156519f405b9SPetr Machata 			     void *params)
156619f405b9SPetr Machata {
156719f405b9SPetr Machata 	struct tc_ets_qopt_offload_replace_params *p = params;
156819f405b9SPetr Machata 
156919f405b9SPetr Machata 	__mlxsw_sp_qdisc_ets_unoffload(mlxsw_sp_port, mlxsw_sp_qdisc,
157019f405b9SPetr Machata 				       p->qstats);
157119f405b9SPetr Machata }
157219f405b9SPetr Machata 
157319f405b9SPetr Machata static int
mlxsw_sp_qdisc_ets_destroy(struct mlxsw_sp_port * mlxsw_sp_port,struct mlxsw_sp_qdisc * mlxsw_sp_qdisc)157419f405b9SPetr Machata mlxsw_sp_qdisc_ets_destroy(struct mlxsw_sp_port *mlxsw_sp_port,
157519f405b9SPetr Machata 			   struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
157619f405b9SPetr Machata {
157751d52ed9SPetr Machata 	return __mlxsw_sp_qdisc_ets_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
157819f405b9SPetr Machata }
157919f405b9SPetr Machata 
158019f405b9SPetr Machata static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_ets = {
158119f405b9SPetr Machata 	.type = MLXSW_SP_QDISC_ETS,
158219f405b9SPetr Machata 	.check_params = mlxsw_sp_qdisc_ets_check_params,
158319f405b9SPetr Machata 	.replace = mlxsw_sp_qdisc_ets_replace,
158419f405b9SPetr Machata 	.unoffload = mlxsw_sp_qdisc_ets_unoffload,
158519f405b9SPetr Machata 	.destroy = mlxsw_sp_qdisc_ets_destroy,
158619f405b9SPetr Machata 	.get_stats = mlxsw_sp_qdisc_get_prio_stats,
158719f405b9SPetr Machata 	.clean_stats = mlxsw_sp_setup_tc_qdisc_prio_clean_stats,
158851d52ed9SPetr Machata 	.find_class = mlxsw_sp_qdisc_prio_find_class,
15895cbd9602SPetr Machata 	.num_classes = IEEE_8021QAZ_MAX_TCS,
159076ff72a7SPetr Machata 	.get_prio_bitmap = mlxsw_sp_qdisc_ets_get_prio_bitmap,
159176ff72a7SPetr Machata 	.get_tclass_num = mlxsw_sp_qdisc_ets_get_tclass_num,
159219f405b9SPetr Machata };
159319f405b9SPetr Machata 
15945bc146c9SPetr Machata /* Linux allows linking of Qdiscs to arbitrary classes (so long as the resulting
15955bc146c9SPetr Machata  * graph is free of cycles). These operations do not change the parent handle
15965bc146c9SPetr Machata  * though, which means it can be incomplete (if there is more than one class
15975bc146c9SPetr Machata  * where the Qdisc in question is grafted) or outright wrong (if the Qdisc was
15985bc146c9SPetr Machata  * linked to a different class and then removed from the original class).
15995bc146c9SPetr Machata  *
16005bc146c9SPetr Machata  * E.g. consider this sequence of operations:
16015bc146c9SPetr Machata  *
16025bc146c9SPetr Machata  *  # tc qdisc add dev swp1 root handle 1: prio
16035bc146c9SPetr Machata  *  # tc qdisc add dev swp1 parent 1:3 handle 13: red limit 1000000 avpkt 10000
16045bc146c9SPetr Machata  *  RED: set bandwidth to 10Mbit
16055bc146c9SPetr Machata  *  # tc qdisc link dev swp1 handle 13: parent 1:2
16065bc146c9SPetr Machata  *
16075bc146c9SPetr Machata  * At this point, both 1:2 and 1:3 have the same RED Qdisc instance as their
16085bc146c9SPetr Machata  * child. But RED will still only claim that 1:3 is its parent. If it's removed
16095bc146c9SPetr Machata  * from that band, its only parent will be 1:2, but it will continue to claim
16105bc146c9SPetr Machata  * that it is in fact 1:3.
16115bc146c9SPetr Machata  *
16125bc146c9SPetr Machata  * The notification for child Qdisc replace (e.g. TC_RED_REPLACE) comes before
16135bc146c9SPetr Machata  * the notification for parent graft (e.g. TC_PRIO_GRAFT). We take the replace
16145bc146c9SPetr Machata  * notification to offload the child Qdisc, based on its parent handle, and use
16155bc146c9SPetr Machata  * the graft operation to validate that the class where the child is actually
16165bc146c9SPetr Machata  * grafted corresponds to the parent handle. If the two don't match, we
16175bc146c9SPetr Machata  * unoffload the child.
161832dc5efcSNogah Frankel  */
mlxsw_sp_qdisc_graft(struct mlxsw_sp_port * mlxsw_sp_port,struct mlxsw_sp_qdisc * mlxsw_sp_qdisc,u8 band,u32 child_handle)1619be7e2a5aSPetr Machata static int mlxsw_sp_qdisc_graft(struct mlxsw_sp_port *mlxsw_sp_port,
162032dc5efcSNogah Frankel 				struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
16217917f52aSPetr Machata 				u8 band, u32 child_handle)
162232dc5efcSNogah Frankel {
162332dc5efcSNogah Frankel 	struct mlxsw_sp_qdisc *old_qdisc;
1624d566ed04SPetr Machata 	u32 parent;
162532dc5efcSNogah Frankel 
162651d52ed9SPetr Machata 	if (band < mlxsw_sp_qdisc->num_classes &&
162751d52ed9SPetr Machata 	    mlxsw_sp_qdisc->qdiscs[band].handle == child_handle)
162832dc5efcSNogah Frankel 		return 0;
162932dc5efcSNogah Frankel 
1630a2d6d7aeSDavid S. Miller 	if (!child_handle) {
16313971a535SPetr Machata 		/* This is an invisible FIFO replacing the original Qdisc.
16323971a535SPetr Machata 		 * Ignore it--the original Qdisc's destroy will follow.
16333971a535SPetr Machata 		 */
16343971a535SPetr Machata 		return 0;
16353971a535SPetr Machata 	}
16363971a535SPetr Machata 
163732dc5efcSNogah Frankel 	/* See if the grafted qdisc is already offloaded on any tclass. If so,
163832dc5efcSNogah Frankel 	 * unoffload it.
163932dc5efcSNogah Frankel 	 */
164032dc5efcSNogah Frankel 	old_qdisc = mlxsw_sp_qdisc_find_by_handle(mlxsw_sp_port,
16417917f52aSPetr Machata 						  child_handle);
164232dc5efcSNogah Frankel 	if (old_qdisc)
164332dc5efcSNogah Frankel 		mlxsw_sp_qdisc_destroy(mlxsw_sp_port, old_qdisc);
164432dc5efcSNogah Frankel 
1645d566ed04SPetr Machata 	parent = TC_H_MAKE(mlxsw_sp_qdisc->handle, band + 1);
1646d566ed04SPetr Machata 	mlxsw_sp_qdisc = mlxsw_sp_qdisc->ops->find_class(mlxsw_sp_qdisc,
1647d566ed04SPetr Machata 							 parent);
164851d52ed9SPetr Machata 	if (!WARN_ON(!mlxsw_sp_qdisc))
164951d52ed9SPetr Machata 		mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
165051d52ed9SPetr Machata 
165132dc5efcSNogah Frankel 	return -EOPNOTSUPP;
165232dc5efcSNogah Frankel }
165332dc5efcSNogah Frankel 
__mlxsw_sp_setup_tc_prio(struct mlxsw_sp_port * mlxsw_sp_port,struct tc_prio_qopt_offload * p)1654cff99e20SPetr Machata static int __mlxsw_sp_setup_tc_prio(struct mlxsw_sp_port *mlxsw_sp_port,
165546a3615bSNogah Frankel 				    struct tc_prio_qopt_offload *p)
165646a3615bSNogah Frankel {
165746a3615bSNogah Frankel 	struct mlxsw_sp_qdisc *mlxsw_sp_qdisc;
165846a3615bSNogah Frankel 
1659c2792f38SPetr Machata 	mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent);
1660eed4baebSNogah Frankel 	if (!mlxsw_sp_qdisc)
166146a3615bSNogah Frankel 		return -EOPNOTSUPP;
166246a3615bSNogah Frankel 
166346a3615bSNogah Frankel 	if (p->command == TC_PRIO_REPLACE)
166446a3615bSNogah Frankel 		return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle,
166546a3615bSNogah Frankel 					      mlxsw_sp_qdisc,
166646a3615bSNogah Frankel 					      &mlxsw_sp_qdisc_ops_prio,
166746a3615bSNogah Frankel 					      &p->replace_params);
166846a3615bSNogah Frankel 
1669290fe2c5SPetr Machata 	if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle))
167046a3615bSNogah Frankel 		return -EOPNOTSUPP;
167146a3615bSNogah Frankel 
167246a3615bSNogah Frankel 	switch (p->command) {
167346a3615bSNogah Frankel 	case TC_PRIO_DESTROY:
167446a3615bSNogah Frankel 		return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
167593d8a4c1SNogah Frankel 	case TC_PRIO_STATS:
167693d8a4c1SNogah Frankel 		return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc,
167793d8a4c1SNogah Frankel 						&p->stats);
167832dc5efcSNogah Frankel 	case TC_PRIO_GRAFT:
1679be7e2a5aSPetr Machata 		return mlxsw_sp_qdisc_graft(mlxsw_sp_port, mlxsw_sp_qdisc,
1680be7e2a5aSPetr Machata 					    p->graft_params.band,
1681be7e2a5aSPetr Machata 					    p->graft_params.child_handle);
168246a3615bSNogah Frankel 	default:
168346a3615bSNogah Frankel 		return -EOPNOTSUPP;
168446a3615bSNogah Frankel 	}
168546a3615bSNogah Frankel }
168646a3615bSNogah Frankel 
mlxsw_sp_setup_tc_prio(struct mlxsw_sp_port * mlxsw_sp_port,struct tc_prio_qopt_offload * p)1687cff99e20SPetr Machata int mlxsw_sp_setup_tc_prio(struct mlxsw_sp_port *mlxsw_sp_port,
1688cff99e20SPetr Machata 			   struct tc_prio_qopt_offload *p)
1689cff99e20SPetr Machata {
1690cff99e20SPetr Machata 	int err;
1691cff99e20SPetr Machata 
1692cff99e20SPetr Machata 	mutex_lock(&mlxsw_sp_port->qdisc->lock);
1693cff99e20SPetr Machata 	err = __mlxsw_sp_setup_tc_prio(mlxsw_sp_port, p);
1694cff99e20SPetr Machata 	mutex_unlock(&mlxsw_sp_port->qdisc->lock);
1695cff99e20SPetr Machata 
1696cff99e20SPetr Machata 	return err;
1697cff99e20SPetr Machata }
1698cff99e20SPetr Machata 
__mlxsw_sp_setup_tc_ets(struct mlxsw_sp_port * mlxsw_sp_port,struct tc_ets_qopt_offload * p)1699cff99e20SPetr Machata static int __mlxsw_sp_setup_tc_ets(struct mlxsw_sp_port *mlxsw_sp_port,
170019f405b9SPetr Machata 				   struct tc_ets_qopt_offload *p)
170119f405b9SPetr Machata {
170219f405b9SPetr Machata 	struct mlxsw_sp_qdisc *mlxsw_sp_qdisc;
170319f405b9SPetr Machata 
1704c2792f38SPetr Machata 	mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent);
170519f405b9SPetr Machata 	if (!mlxsw_sp_qdisc)
170619f405b9SPetr Machata 		return -EOPNOTSUPP;
170719f405b9SPetr Machata 
170819f405b9SPetr Machata 	if (p->command == TC_ETS_REPLACE)
170919f405b9SPetr Machata 		return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle,
171019f405b9SPetr Machata 					      mlxsw_sp_qdisc,
171119f405b9SPetr Machata 					      &mlxsw_sp_qdisc_ops_ets,
171219f405b9SPetr Machata 					      &p->replace_params);
171319f405b9SPetr Machata 
1714290fe2c5SPetr Machata 	if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle))
171519f405b9SPetr Machata 		return -EOPNOTSUPP;
171619f405b9SPetr Machata 
171719f405b9SPetr Machata 	switch (p->command) {
171819f405b9SPetr Machata 	case TC_ETS_DESTROY:
171919f405b9SPetr Machata 		return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
172019f405b9SPetr Machata 	case TC_ETS_STATS:
172119f405b9SPetr Machata 		return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc,
172219f405b9SPetr Machata 						&p->stats);
172319f405b9SPetr Machata 	case TC_ETS_GRAFT:
1724be7e2a5aSPetr Machata 		return mlxsw_sp_qdisc_graft(mlxsw_sp_port, mlxsw_sp_qdisc,
172519f405b9SPetr Machata 					    p->graft_params.band,
172619f405b9SPetr Machata 					    p->graft_params.child_handle);
172719f405b9SPetr Machata 	default:
172819f405b9SPetr Machata 		return -EOPNOTSUPP;
172919f405b9SPetr Machata 	}
173019f405b9SPetr Machata }
173119f405b9SPetr Machata 
mlxsw_sp_setup_tc_ets(struct mlxsw_sp_port * mlxsw_sp_port,struct tc_ets_qopt_offload * p)1732cff99e20SPetr Machata int mlxsw_sp_setup_tc_ets(struct mlxsw_sp_port *mlxsw_sp_port,
1733cff99e20SPetr Machata 			  struct tc_ets_qopt_offload *p)
1734cff99e20SPetr Machata {
1735cff99e20SPetr Machata 	int err;
1736cff99e20SPetr Machata 
1737cff99e20SPetr Machata 	mutex_lock(&mlxsw_sp_port->qdisc->lock);
1738cff99e20SPetr Machata 	err = __mlxsw_sp_setup_tc_ets(mlxsw_sp_port, p);
1739cff99e20SPetr Machata 	mutex_unlock(&mlxsw_sp_port->qdisc->lock);
1740cff99e20SPetr Machata 
1741cff99e20SPetr Machata 	return err;
1742cff99e20SPetr Machata }
1743cff99e20SPetr Machata 
1744f6668eacSPetr Machata struct mlxsw_sp_qevent_block {
1745f6668eacSPetr Machata 	struct list_head binding_list;
1746f6668eacSPetr Machata 	struct list_head mall_entry_list;
1747f6668eacSPetr Machata 	struct mlxsw_sp *mlxsw_sp;
1748f6668eacSPetr Machata };
1749f6668eacSPetr Machata 
1750f6668eacSPetr Machata struct mlxsw_sp_qevent_binding {
1751f6668eacSPetr Machata 	struct list_head list;
1752f6668eacSPetr Machata 	struct mlxsw_sp_port *mlxsw_sp_port;
1753f6668eacSPetr Machata 	u32 handle;
1754f6668eacSPetr Machata 	int tclass_num;
1755f6668eacSPetr Machata 	enum mlxsw_sp_span_trigger span_trigger;
1756099bf89dSPetr Machata 	unsigned int action_mask;
1757f6668eacSPetr Machata };
1758f6668eacSPetr Machata 
1759f6668eacSPetr Machata static LIST_HEAD(mlxsw_sp_qevent_block_cb_list);
1760f6668eacSPetr Machata 
mlxsw_sp_qevent_span_configure(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_mall_entry * mall_entry,struct mlxsw_sp_qevent_binding * qevent_binding,const struct mlxsw_sp_span_agent_parms * agent_parms,int * p_span_id)176154a92385SPetr Machata static int mlxsw_sp_qevent_span_configure(struct mlxsw_sp *mlxsw_sp,
1762f6668eacSPetr Machata 					  struct mlxsw_sp_mall_entry *mall_entry,
176354a92385SPetr Machata 					  struct mlxsw_sp_qevent_binding *qevent_binding,
176454a92385SPetr Machata 					  const struct mlxsw_sp_span_agent_parms *agent_parms,
176554a92385SPetr Machata 					  int *p_span_id)
1766f6668eacSPetr Machata {
17670908e42aSPetr Machata 	enum mlxsw_sp_span_trigger span_trigger = qevent_binding->span_trigger;
1768f6668eacSPetr Machata 	struct mlxsw_sp_port *mlxsw_sp_port = qevent_binding->mlxsw_sp_port;
1769f6668eacSPetr Machata 	struct mlxsw_sp_span_trigger_parms trigger_parms = {};
17700908e42aSPetr Machata 	bool ingress;
1771f6668eacSPetr Machata 	int span_id;
1772f6668eacSPetr Machata 	int err;
1773f6668eacSPetr Machata 
177454a92385SPetr Machata 	err = mlxsw_sp_span_agent_get(mlxsw_sp, &span_id, agent_parms);
1775f6668eacSPetr Machata 	if (err)
1776f6668eacSPetr Machata 		return err;
1777f6668eacSPetr Machata 
17780908e42aSPetr Machata 	ingress = mlxsw_sp_span_trigger_is_ingress(span_trigger);
17790908e42aSPetr Machata 	err = mlxsw_sp_span_analyzed_port_get(mlxsw_sp_port, ingress);
1780f6668eacSPetr Machata 	if (err)
1781f6668eacSPetr Machata 		goto err_analyzed_port_get;
1782f6668eacSPetr Machata 
1783f6668eacSPetr Machata 	trigger_parms.span_id = span_id;
17842dcbd920SIdo Schimmel 	trigger_parms.probability_rate = 1;
17850908e42aSPetr Machata 	err = mlxsw_sp_span_agent_bind(mlxsw_sp, span_trigger, mlxsw_sp_port,
1786f6668eacSPetr Machata 				       &trigger_parms);
1787f6668eacSPetr Machata 	if (err)
1788f6668eacSPetr Machata 		goto err_agent_bind;
1789f6668eacSPetr Machata 
17900908e42aSPetr Machata 	err = mlxsw_sp_span_trigger_enable(mlxsw_sp_port, span_trigger,
1791f6668eacSPetr Machata 					   qevent_binding->tclass_num);
1792f6668eacSPetr Machata 	if (err)
1793f6668eacSPetr Machata 		goto err_trigger_enable;
1794f6668eacSPetr Machata 
179554a92385SPetr Machata 	*p_span_id = span_id;
1796f6668eacSPetr Machata 	return 0;
1797f6668eacSPetr Machata 
1798f6668eacSPetr Machata err_trigger_enable:
17990908e42aSPetr Machata 	mlxsw_sp_span_agent_unbind(mlxsw_sp, span_trigger, mlxsw_sp_port,
1800f6668eacSPetr Machata 				   &trigger_parms);
1801f6668eacSPetr Machata err_agent_bind:
18020908e42aSPetr Machata 	mlxsw_sp_span_analyzed_port_put(mlxsw_sp_port, ingress);
1803f6668eacSPetr Machata err_analyzed_port_get:
1804f6668eacSPetr Machata 	mlxsw_sp_span_agent_put(mlxsw_sp, span_id);
1805f6668eacSPetr Machata 	return err;
1806f6668eacSPetr Machata }
1807f6668eacSPetr Machata 
mlxsw_sp_qevent_span_deconfigure(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_qevent_binding * qevent_binding,int span_id)180854a92385SPetr Machata static void mlxsw_sp_qevent_span_deconfigure(struct mlxsw_sp *mlxsw_sp,
180954a92385SPetr Machata 					     struct mlxsw_sp_qevent_binding *qevent_binding,
181054a92385SPetr Machata 					     int span_id)
1811f6668eacSPetr Machata {
18120908e42aSPetr Machata 	enum mlxsw_sp_span_trigger span_trigger = qevent_binding->span_trigger;
1813f6668eacSPetr Machata 	struct mlxsw_sp_port *mlxsw_sp_port = qevent_binding->mlxsw_sp_port;
1814f6668eacSPetr Machata 	struct mlxsw_sp_span_trigger_parms trigger_parms = {
181554a92385SPetr Machata 		.span_id = span_id,
1816f6668eacSPetr Machata 	};
18170908e42aSPetr Machata 	bool ingress;
1818f6668eacSPetr Machata 
18190908e42aSPetr Machata 	ingress = mlxsw_sp_span_trigger_is_ingress(span_trigger);
18200908e42aSPetr Machata 
18210908e42aSPetr Machata 	mlxsw_sp_span_trigger_disable(mlxsw_sp_port, span_trigger,
1822f6668eacSPetr Machata 				      qevent_binding->tclass_num);
18230908e42aSPetr Machata 	mlxsw_sp_span_agent_unbind(mlxsw_sp, span_trigger, mlxsw_sp_port,
1824f6668eacSPetr Machata 				   &trigger_parms);
18250908e42aSPetr Machata 	mlxsw_sp_span_analyzed_port_put(mlxsw_sp_port, ingress);
182654a92385SPetr Machata 	mlxsw_sp_span_agent_put(mlxsw_sp, span_id);
182754a92385SPetr Machata }
182854a92385SPetr Machata 
mlxsw_sp_qevent_mirror_configure(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_mall_entry * mall_entry,struct mlxsw_sp_qevent_binding * qevent_binding)182954a92385SPetr Machata static int mlxsw_sp_qevent_mirror_configure(struct mlxsw_sp *mlxsw_sp,
183054a92385SPetr Machata 					    struct mlxsw_sp_mall_entry *mall_entry,
183154a92385SPetr Machata 					    struct mlxsw_sp_qevent_binding *qevent_binding)
183254a92385SPetr Machata {
183354a92385SPetr Machata 	struct mlxsw_sp_span_agent_parms agent_parms = {
183454a92385SPetr Machata 		.to_dev = mall_entry->mirror.to_dev,
183554a92385SPetr Machata 	};
183654a92385SPetr Machata 
183754a92385SPetr Machata 	return mlxsw_sp_qevent_span_configure(mlxsw_sp, mall_entry, qevent_binding,
183854a92385SPetr Machata 					      &agent_parms, &mall_entry->mirror.span_id);
183954a92385SPetr Machata }
184054a92385SPetr Machata 
mlxsw_sp_qevent_mirror_deconfigure(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_mall_entry * mall_entry,struct mlxsw_sp_qevent_binding * qevent_binding)184154a92385SPetr Machata static void mlxsw_sp_qevent_mirror_deconfigure(struct mlxsw_sp *mlxsw_sp,
184254a92385SPetr Machata 					       struct mlxsw_sp_mall_entry *mall_entry,
184354a92385SPetr Machata 					       struct mlxsw_sp_qevent_binding *qevent_binding)
184454a92385SPetr Machata {
184554a92385SPetr Machata 	mlxsw_sp_qevent_span_deconfigure(mlxsw_sp, qevent_binding, mall_entry->mirror.span_id);
184654a92385SPetr Machata }
184754a92385SPetr Machata 
mlxsw_sp_qevent_trap_configure(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_mall_entry * mall_entry,struct mlxsw_sp_qevent_binding * qevent_binding)184854a92385SPetr Machata static int mlxsw_sp_qevent_trap_configure(struct mlxsw_sp *mlxsw_sp,
184954a92385SPetr Machata 					  struct mlxsw_sp_mall_entry *mall_entry,
185054a92385SPetr Machata 					  struct mlxsw_sp_qevent_binding *qevent_binding)
185154a92385SPetr Machata {
18525c7659ebSIdo Schimmel 	struct mlxsw_sp_span_agent_parms agent_parms = {
18535c7659ebSIdo Schimmel 		.session_id = MLXSW_SP_SPAN_SESSION_ID_BUFFER,
18545c7659ebSIdo Schimmel 	};
185554a92385SPetr Machata 	int err;
185654a92385SPetr Machata 
185754a92385SPetr Machata 	err = mlxsw_sp_trap_group_policer_hw_id_get(mlxsw_sp,
185854a92385SPetr Machata 						    DEVLINK_TRAP_GROUP_GENERIC_ID_BUFFER_DROPS,
185954a92385SPetr Machata 						    &agent_parms.policer_enable,
186054a92385SPetr Machata 						    &agent_parms.policer_id);
186154a92385SPetr Machata 	if (err)
186254a92385SPetr Machata 		return err;
186354a92385SPetr Machata 
186454a92385SPetr Machata 	return mlxsw_sp_qevent_span_configure(mlxsw_sp, mall_entry, qevent_binding,
186554a92385SPetr Machata 					      &agent_parms, &mall_entry->trap.span_id);
186654a92385SPetr Machata }
186754a92385SPetr Machata 
mlxsw_sp_qevent_trap_deconfigure(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_mall_entry * mall_entry,struct mlxsw_sp_qevent_binding * qevent_binding)186854a92385SPetr Machata static void mlxsw_sp_qevent_trap_deconfigure(struct mlxsw_sp *mlxsw_sp,
186954a92385SPetr Machata 					     struct mlxsw_sp_mall_entry *mall_entry,
187054a92385SPetr Machata 					     struct mlxsw_sp_qevent_binding *qevent_binding)
187154a92385SPetr Machata {
187254a92385SPetr Machata 	mlxsw_sp_qevent_span_deconfigure(mlxsw_sp, qevent_binding, mall_entry->trap.span_id);
1873f6668eacSPetr Machata }
1874f6668eacSPetr Machata 
1875a34dda72SPetr Machata static int
mlxsw_sp_qevent_entry_configure(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_mall_entry * mall_entry,struct mlxsw_sp_qevent_binding * qevent_binding,struct netlink_ext_ack * extack)1876a34dda72SPetr Machata mlxsw_sp_qevent_entry_configure(struct mlxsw_sp *mlxsw_sp,
1877f6668eacSPetr Machata 				struct mlxsw_sp_mall_entry *mall_entry,
1878a34dda72SPetr Machata 				struct mlxsw_sp_qevent_binding *qevent_binding,
1879a34dda72SPetr Machata 				struct netlink_ext_ack *extack)
1880f6668eacSPetr Machata {
1881099bf89dSPetr Machata 	if (!(BIT(mall_entry->type) & qevent_binding->action_mask)) {
1882099bf89dSPetr Machata 		NL_SET_ERR_MSG(extack, "Action not supported at this qevent");
1883099bf89dSPetr Machata 		return -EOPNOTSUPP;
1884099bf89dSPetr Machata 	}
1885099bf89dSPetr Machata 
1886f6668eacSPetr Machata 	switch (mall_entry->type) {
1887f6668eacSPetr Machata 	case MLXSW_SP_MALL_ACTION_TYPE_MIRROR:
1888f6668eacSPetr Machata 		return mlxsw_sp_qevent_mirror_configure(mlxsw_sp, mall_entry, qevent_binding);
188954a92385SPetr Machata 	case MLXSW_SP_MALL_ACTION_TYPE_TRAP:
189054a92385SPetr Machata 		return mlxsw_sp_qevent_trap_configure(mlxsw_sp, mall_entry, qevent_binding);
1891f6668eacSPetr Machata 	default:
1892f6668eacSPetr Machata 		/* This should have been validated away. */
1893f6668eacSPetr Machata 		WARN_ON(1);
1894f6668eacSPetr Machata 		return -EOPNOTSUPP;
1895f6668eacSPetr Machata 	}
1896f6668eacSPetr Machata }
1897f6668eacSPetr Machata 
mlxsw_sp_qevent_entry_deconfigure(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_mall_entry * mall_entry,struct mlxsw_sp_qevent_binding * qevent_binding)1898f6668eacSPetr Machata static void mlxsw_sp_qevent_entry_deconfigure(struct mlxsw_sp *mlxsw_sp,
1899f6668eacSPetr Machata 					      struct mlxsw_sp_mall_entry *mall_entry,
1900f6668eacSPetr Machata 					      struct mlxsw_sp_qevent_binding *qevent_binding)
1901f6668eacSPetr Machata {
1902f6668eacSPetr Machata 	switch (mall_entry->type) {
1903f6668eacSPetr Machata 	case MLXSW_SP_MALL_ACTION_TYPE_MIRROR:
1904f6668eacSPetr Machata 		return mlxsw_sp_qevent_mirror_deconfigure(mlxsw_sp, mall_entry, qevent_binding);
190554a92385SPetr Machata 	case MLXSW_SP_MALL_ACTION_TYPE_TRAP:
190654a92385SPetr Machata 		return mlxsw_sp_qevent_trap_deconfigure(mlxsw_sp, mall_entry, qevent_binding);
1907f6668eacSPetr Machata 	default:
1908f6668eacSPetr Machata 		WARN_ON(1);
1909f6668eacSPetr Machata 		return;
1910f6668eacSPetr Machata 	}
1911f6668eacSPetr Machata }
1912f6668eacSPetr Machata 
1913a34dda72SPetr Machata static int
mlxsw_sp_qevent_binding_configure(struct mlxsw_sp_qevent_block * qevent_block,struct mlxsw_sp_qevent_binding * qevent_binding,struct netlink_ext_ack * extack)1914a34dda72SPetr Machata mlxsw_sp_qevent_binding_configure(struct mlxsw_sp_qevent_block *qevent_block,
1915a34dda72SPetr Machata 				  struct mlxsw_sp_qevent_binding *qevent_binding,
1916a34dda72SPetr Machata 				  struct netlink_ext_ack *extack)
1917f6668eacSPetr Machata {
1918f6668eacSPetr Machata 	struct mlxsw_sp_mall_entry *mall_entry;
1919f6668eacSPetr Machata 	int err;
1920f6668eacSPetr Machata 
1921f6668eacSPetr Machata 	list_for_each_entry(mall_entry, &qevent_block->mall_entry_list, list) {
1922f6668eacSPetr Machata 		err = mlxsw_sp_qevent_entry_configure(qevent_block->mlxsw_sp, mall_entry,
1923a34dda72SPetr Machata 						      qevent_binding, extack);
1924f6668eacSPetr Machata 		if (err)
1925f6668eacSPetr Machata 			goto err_entry_configure;
1926f6668eacSPetr Machata 	}
1927f6668eacSPetr Machata 
1928f6668eacSPetr Machata 	return 0;
1929f6668eacSPetr Machata 
1930f6668eacSPetr Machata err_entry_configure:
1931f6668eacSPetr Machata 	list_for_each_entry_continue_reverse(mall_entry, &qevent_block->mall_entry_list, list)
1932f6668eacSPetr Machata 		mlxsw_sp_qevent_entry_deconfigure(qevent_block->mlxsw_sp, mall_entry,
1933f6668eacSPetr Machata 						  qevent_binding);
1934f6668eacSPetr Machata 	return err;
1935f6668eacSPetr Machata }
1936f6668eacSPetr Machata 
mlxsw_sp_qevent_binding_deconfigure(struct mlxsw_sp_qevent_block * qevent_block,struct mlxsw_sp_qevent_binding * qevent_binding)1937f6668eacSPetr Machata static void mlxsw_sp_qevent_binding_deconfigure(struct mlxsw_sp_qevent_block *qevent_block,
1938f6668eacSPetr Machata 						struct mlxsw_sp_qevent_binding *qevent_binding)
1939f6668eacSPetr Machata {
1940f6668eacSPetr Machata 	struct mlxsw_sp_mall_entry *mall_entry;
1941f6668eacSPetr Machata 
1942f6668eacSPetr Machata 	list_for_each_entry(mall_entry, &qevent_block->mall_entry_list, list)
1943f6668eacSPetr Machata 		mlxsw_sp_qevent_entry_deconfigure(qevent_block->mlxsw_sp, mall_entry,
1944f6668eacSPetr Machata 						  qevent_binding);
1945f6668eacSPetr Machata }
1946f6668eacSPetr Machata 
1947a34dda72SPetr Machata static int
mlxsw_sp_qevent_block_configure(struct mlxsw_sp_qevent_block * qevent_block,struct netlink_ext_ack * extack)1948a34dda72SPetr Machata mlxsw_sp_qevent_block_configure(struct mlxsw_sp_qevent_block *qevent_block,
1949a34dda72SPetr Machata 				struct netlink_ext_ack *extack)
1950f6668eacSPetr Machata {
1951f6668eacSPetr Machata 	struct mlxsw_sp_qevent_binding *qevent_binding;
1952f6668eacSPetr Machata 	int err;
1953f6668eacSPetr Machata 
1954f6668eacSPetr Machata 	list_for_each_entry(qevent_binding, &qevent_block->binding_list, list) {
1955a34dda72SPetr Machata 		err = mlxsw_sp_qevent_binding_configure(qevent_block,
1956a34dda72SPetr Machata 							qevent_binding,
1957a34dda72SPetr Machata 							extack);
1958f6668eacSPetr Machata 		if (err)
1959f6668eacSPetr Machata 			goto err_binding_configure;
1960f6668eacSPetr Machata 	}
1961f6668eacSPetr Machata 
1962f6668eacSPetr Machata 	return 0;
1963f6668eacSPetr Machata 
1964f6668eacSPetr Machata err_binding_configure:
1965f6668eacSPetr Machata 	list_for_each_entry_continue_reverse(qevent_binding, &qevent_block->binding_list, list)
1966f6668eacSPetr Machata 		mlxsw_sp_qevent_binding_deconfigure(qevent_block, qevent_binding);
1967f6668eacSPetr Machata 	return err;
1968f6668eacSPetr Machata }
1969f6668eacSPetr Machata 
mlxsw_sp_qevent_block_deconfigure(struct mlxsw_sp_qevent_block * qevent_block)1970f6668eacSPetr Machata static void mlxsw_sp_qevent_block_deconfigure(struct mlxsw_sp_qevent_block *qevent_block)
1971f6668eacSPetr Machata {
1972f6668eacSPetr Machata 	struct mlxsw_sp_qevent_binding *qevent_binding;
1973f6668eacSPetr Machata 
1974f6668eacSPetr Machata 	list_for_each_entry(qevent_binding, &qevent_block->binding_list, list)
1975f6668eacSPetr Machata 		mlxsw_sp_qevent_binding_deconfigure(qevent_block, qevent_binding);
1976f6668eacSPetr Machata }
1977f6668eacSPetr Machata 
1978f6668eacSPetr Machata static struct mlxsw_sp_mall_entry *
mlxsw_sp_qevent_mall_entry_find(struct mlxsw_sp_qevent_block * block,unsigned long cookie)1979f6668eacSPetr Machata mlxsw_sp_qevent_mall_entry_find(struct mlxsw_sp_qevent_block *block, unsigned long cookie)
1980f6668eacSPetr Machata {
1981f6668eacSPetr Machata 	struct mlxsw_sp_mall_entry *mall_entry;
1982f6668eacSPetr Machata 
1983f6668eacSPetr Machata 	list_for_each_entry(mall_entry, &block->mall_entry_list, list)
1984f6668eacSPetr Machata 		if (mall_entry->cookie == cookie)
1985f6668eacSPetr Machata 			return mall_entry;
1986f6668eacSPetr Machata 
1987f6668eacSPetr Machata 	return NULL;
1988f6668eacSPetr Machata }
1989f6668eacSPetr Machata 
mlxsw_sp_qevent_mall_replace(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_qevent_block * qevent_block,struct tc_cls_matchall_offload * f)1990f6668eacSPetr Machata static int mlxsw_sp_qevent_mall_replace(struct mlxsw_sp *mlxsw_sp,
1991f6668eacSPetr Machata 					struct mlxsw_sp_qevent_block *qevent_block,
1992f6668eacSPetr Machata 					struct tc_cls_matchall_offload *f)
1993f6668eacSPetr Machata {
1994f6668eacSPetr Machata 	struct mlxsw_sp_mall_entry *mall_entry;
1995f6668eacSPetr Machata 	struct flow_action_entry *act;
1996f6668eacSPetr Machata 	int err;
1997f6668eacSPetr Machata 
1998f6668eacSPetr Machata 	/* It should not currently be possible to replace a matchall rule. So
1999f6668eacSPetr Machata 	 * this must be a new rule.
2000f6668eacSPetr Machata 	 */
2001f6668eacSPetr Machata 	if (!list_empty(&qevent_block->mall_entry_list)) {
2002f6668eacSPetr Machata 		NL_SET_ERR_MSG(f->common.extack, "At most one filter supported");
2003f6668eacSPetr Machata 		return -EOPNOTSUPP;
2004f6668eacSPetr Machata 	}
2005f6668eacSPetr Machata 	if (f->rule->action.num_entries != 1) {
2006f6668eacSPetr Machata 		NL_SET_ERR_MSG(f->common.extack, "Only singular actions supported");
2007f6668eacSPetr Machata 		return -EOPNOTSUPP;
2008f6668eacSPetr Machata 	}
2009f6668eacSPetr Machata 	if (f->common.chain_index) {
2010f6668eacSPetr Machata 		NL_SET_ERR_MSG(f->common.extack, "Only chain 0 is supported");
2011f6668eacSPetr Machata 		return -EOPNOTSUPP;
2012f6668eacSPetr Machata 	}
2013f6668eacSPetr Machata 	if (f->common.protocol != htons(ETH_P_ALL)) {
2014f6668eacSPetr Machata 		NL_SET_ERR_MSG(f->common.extack, "Protocol matching not supported");
2015f6668eacSPetr Machata 		return -EOPNOTSUPP;
2016f6668eacSPetr Machata 	}
2017f6668eacSPetr Machata 
2018f6668eacSPetr Machata 	act = &f->rule->action.entries[0];
2019f6668eacSPetr Machata 	if (!(act->hw_stats & FLOW_ACTION_HW_STATS_DISABLED)) {
2020f6668eacSPetr Machata 		NL_SET_ERR_MSG(f->common.extack, "HW counters not supported on qevents");
2021f6668eacSPetr Machata 		return -EOPNOTSUPP;
2022f6668eacSPetr Machata 	}
2023f6668eacSPetr Machata 
2024f6668eacSPetr Machata 	mall_entry = kzalloc(sizeof(*mall_entry), GFP_KERNEL);
2025f6668eacSPetr Machata 	if (!mall_entry)
2026f6668eacSPetr Machata 		return -ENOMEM;
2027f6668eacSPetr Machata 	mall_entry->cookie = f->cookie;
2028f6668eacSPetr Machata 
2029f6668eacSPetr Machata 	if (act->id == FLOW_ACTION_MIRRED) {
2030f6668eacSPetr Machata 		mall_entry->type = MLXSW_SP_MALL_ACTION_TYPE_MIRROR;
2031f6668eacSPetr Machata 		mall_entry->mirror.to_dev = act->dev;
203254a92385SPetr Machata 	} else if (act->id == FLOW_ACTION_TRAP) {
203354a92385SPetr Machata 		mall_entry->type = MLXSW_SP_MALL_ACTION_TYPE_TRAP;
2034f6668eacSPetr Machata 	} else {
2035f6668eacSPetr Machata 		NL_SET_ERR_MSG(f->common.extack, "Unsupported action");
2036f6668eacSPetr Machata 		err = -EOPNOTSUPP;
2037f6668eacSPetr Machata 		goto err_unsupported_action;
2038f6668eacSPetr Machata 	}
2039f6668eacSPetr Machata 
2040f6668eacSPetr Machata 	list_add_tail(&mall_entry->list, &qevent_block->mall_entry_list);
2041f6668eacSPetr Machata 
2042a34dda72SPetr Machata 	err = mlxsw_sp_qevent_block_configure(qevent_block, f->common.extack);
2043f6668eacSPetr Machata 	if (err)
2044f6668eacSPetr Machata 		goto err_block_configure;
2045f6668eacSPetr Machata 
2046f6668eacSPetr Machata 	return 0;
2047f6668eacSPetr Machata 
2048f6668eacSPetr Machata err_block_configure:
2049f6668eacSPetr Machata 	list_del(&mall_entry->list);
2050f6668eacSPetr Machata err_unsupported_action:
2051f6668eacSPetr Machata 	kfree(mall_entry);
2052f6668eacSPetr Machata 	return err;
2053f6668eacSPetr Machata }
2054f6668eacSPetr Machata 
mlxsw_sp_qevent_mall_destroy(struct mlxsw_sp_qevent_block * qevent_block,struct tc_cls_matchall_offload * f)2055f6668eacSPetr Machata static void mlxsw_sp_qevent_mall_destroy(struct mlxsw_sp_qevent_block *qevent_block,
2056f6668eacSPetr Machata 					 struct tc_cls_matchall_offload *f)
2057f6668eacSPetr Machata {
2058f6668eacSPetr Machata 	struct mlxsw_sp_mall_entry *mall_entry;
2059f6668eacSPetr Machata 
2060f6668eacSPetr Machata 	mall_entry = mlxsw_sp_qevent_mall_entry_find(qevent_block, f->cookie);
2061f6668eacSPetr Machata 	if (!mall_entry)
2062f6668eacSPetr Machata 		return;
2063f6668eacSPetr Machata 
2064f6668eacSPetr Machata 	mlxsw_sp_qevent_block_deconfigure(qevent_block);
2065f6668eacSPetr Machata 
2066f6668eacSPetr Machata 	list_del(&mall_entry->list);
2067f6668eacSPetr Machata 	kfree(mall_entry);
2068f6668eacSPetr Machata }
2069f6668eacSPetr Machata 
mlxsw_sp_qevent_block_mall_cb(struct mlxsw_sp_qevent_block * qevent_block,struct tc_cls_matchall_offload * f)2070f6668eacSPetr Machata static int mlxsw_sp_qevent_block_mall_cb(struct mlxsw_sp_qevent_block *qevent_block,
2071f6668eacSPetr Machata 					 struct tc_cls_matchall_offload *f)
2072f6668eacSPetr Machata {
2073f6668eacSPetr Machata 	struct mlxsw_sp *mlxsw_sp = qevent_block->mlxsw_sp;
2074f6668eacSPetr Machata 
2075f6668eacSPetr Machata 	switch (f->command) {
2076f6668eacSPetr Machata 	case TC_CLSMATCHALL_REPLACE:
2077f6668eacSPetr Machata 		return mlxsw_sp_qevent_mall_replace(mlxsw_sp, qevent_block, f);
2078f6668eacSPetr Machata 	case TC_CLSMATCHALL_DESTROY:
2079f6668eacSPetr Machata 		mlxsw_sp_qevent_mall_destroy(qevent_block, f);
2080f6668eacSPetr Machata 		return 0;
2081f6668eacSPetr Machata 	default:
2082f6668eacSPetr Machata 		return -EOPNOTSUPP;
2083f6668eacSPetr Machata 	}
2084f6668eacSPetr Machata }
2085f6668eacSPetr Machata 
mlxsw_sp_qevent_block_cb(enum tc_setup_type type,void * type_data,void * cb_priv)2086f6668eacSPetr Machata static int mlxsw_sp_qevent_block_cb(enum tc_setup_type type, void *type_data, void *cb_priv)
2087f6668eacSPetr Machata {
2088f6668eacSPetr Machata 	struct mlxsw_sp_qevent_block *qevent_block = cb_priv;
2089f6668eacSPetr Machata 
2090f6668eacSPetr Machata 	switch (type) {
2091f6668eacSPetr Machata 	case TC_SETUP_CLSMATCHALL:
2092f6668eacSPetr Machata 		return mlxsw_sp_qevent_block_mall_cb(qevent_block, type_data);
2093f6668eacSPetr Machata 	default:
2094f6668eacSPetr Machata 		return -EOPNOTSUPP;
2095f6668eacSPetr Machata 	}
2096f6668eacSPetr Machata }
2097f6668eacSPetr Machata 
mlxsw_sp_qevent_block_create(struct mlxsw_sp * mlxsw_sp,struct net * net)2098f6668eacSPetr Machata static struct mlxsw_sp_qevent_block *mlxsw_sp_qevent_block_create(struct mlxsw_sp *mlxsw_sp,
2099f6668eacSPetr Machata 								  struct net *net)
2100f6668eacSPetr Machata {
2101f6668eacSPetr Machata 	struct mlxsw_sp_qevent_block *qevent_block;
2102f6668eacSPetr Machata 
2103f6668eacSPetr Machata 	qevent_block = kzalloc(sizeof(*qevent_block), GFP_KERNEL);
2104f6668eacSPetr Machata 	if (!qevent_block)
2105f6668eacSPetr Machata 		return NULL;
2106f6668eacSPetr Machata 
2107f6668eacSPetr Machata 	INIT_LIST_HEAD(&qevent_block->binding_list);
2108f6668eacSPetr Machata 	INIT_LIST_HEAD(&qevent_block->mall_entry_list);
2109f6668eacSPetr Machata 	qevent_block->mlxsw_sp = mlxsw_sp;
2110f6668eacSPetr Machata 	return qevent_block;
2111f6668eacSPetr Machata }
2112f6668eacSPetr Machata 
2113f6668eacSPetr Machata static void
mlxsw_sp_qevent_block_destroy(struct mlxsw_sp_qevent_block * qevent_block)2114f6668eacSPetr Machata mlxsw_sp_qevent_block_destroy(struct mlxsw_sp_qevent_block *qevent_block)
2115f6668eacSPetr Machata {
2116f6668eacSPetr Machata 	WARN_ON(!list_empty(&qevent_block->binding_list));
2117f6668eacSPetr Machata 	WARN_ON(!list_empty(&qevent_block->mall_entry_list));
2118f6668eacSPetr Machata 	kfree(qevent_block);
2119f6668eacSPetr Machata }
2120f6668eacSPetr Machata 
mlxsw_sp_qevent_block_release(void * cb_priv)2121f6668eacSPetr Machata static void mlxsw_sp_qevent_block_release(void *cb_priv)
2122f6668eacSPetr Machata {
2123f6668eacSPetr Machata 	struct mlxsw_sp_qevent_block *qevent_block = cb_priv;
2124f6668eacSPetr Machata 
2125f6668eacSPetr Machata 	mlxsw_sp_qevent_block_destroy(qevent_block);
2126f6668eacSPetr Machata }
2127f6668eacSPetr Machata 
2128f6668eacSPetr Machata static struct mlxsw_sp_qevent_binding *
mlxsw_sp_qevent_binding_create(struct mlxsw_sp_port * mlxsw_sp_port,u32 handle,int tclass_num,enum mlxsw_sp_span_trigger span_trigger,unsigned int action_mask)2129f6668eacSPetr Machata mlxsw_sp_qevent_binding_create(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, int tclass_num,
2130099bf89dSPetr Machata 			       enum mlxsw_sp_span_trigger span_trigger,
2131099bf89dSPetr Machata 			       unsigned int action_mask)
2132f6668eacSPetr Machata {
2133f6668eacSPetr Machata 	struct mlxsw_sp_qevent_binding *binding;
2134f6668eacSPetr Machata 
2135f6668eacSPetr Machata 	binding = kzalloc(sizeof(*binding), GFP_KERNEL);
2136f6668eacSPetr Machata 	if (!binding)
2137f6668eacSPetr Machata 		return ERR_PTR(-ENOMEM);
2138f6668eacSPetr Machata 
2139f6668eacSPetr Machata 	binding->mlxsw_sp_port = mlxsw_sp_port;
2140f6668eacSPetr Machata 	binding->handle = handle;
2141f6668eacSPetr Machata 	binding->tclass_num = tclass_num;
2142f6668eacSPetr Machata 	binding->span_trigger = span_trigger;
2143099bf89dSPetr Machata 	binding->action_mask = action_mask;
2144f6668eacSPetr Machata 	return binding;
2145f6668eacSPetr Machata }
2146f6668eacSPetr Machata 
2147f6668eacSPetr Machata static void
mlxsw_sp_qevent_binding_destroy(struct mlxsw_sp_qevent_binding * binding)2148f6668eacSPetr Machata mlxsw_sp_qevent_binding_destroy(struct mlxsw_sp_qevent_binding *binding)
2149f6668eacSPetr Machata {
2150f6668eacSPetr Machata 	kfree(binding);
2151f6668eacSPetr Machata }
2152f6668eacSPetr Machata 
2153f6668eacSPetr Machata static struct mlxsw_sp_qevent_binding *
mlxsw_sp_qevent_binding_lookup(struct mlxsw_sp_qevent_block * block,struct mlxsw_sp_port * mlxsw_sp_port,u32 handle,enum mlxsw_sp_span_trigger span_trigger)2154f6668eacSPetr Machata mlxsw_sp_qevent_binding_lookup(struct mlxsw_sp_qevent_block *block,
2155f6668eacSPetr Machata 			       struct mlxsw_sp_port *mlxsw_sp_port,
2156f6668eacSPetr Machata 			       u32 handle,
2157f6668eacSPetr Machata 			       enum mlxsw_sp_span_trigger span_trigger)
2158f6668eacSPetr Machata {
2159f6668eacSPetr Machata 	struct mlxsw_sp_qevent_binding *qevent_binding;
2160f6668eacSPetr Machata 
2161f6668eacSPetr Machata 	list_for_each_entry(qevent_binding, &block->binding_list, list)
2162f6668eacSPetr Machata 		if (qevent_binding->mlxsw_sp_port == mlxsw_sp_port &&
2163f6668eacSPetr Machata 		    qevent_binding->handle == handle &&
2164f6668eacSPetr Machata 		    qevent_binding->span_trigger == span_trigger)
2165f6668eacSPetr Machata 			return qevent_binding;
2166f6668eacSPetr Machata 	return NULL;
2167f6668eacSPetr Machata }
2168f6668eacSPetr Machata 
2169099bf89dSPetr Machata static int
mlxsw_sp_setup_tc_block_qevent_bind(struct mlxsw_sp_port * mlxsw_sp_port,struct flow_block_offload * f,enum mlxsw_sp_span_trigger span_trigger,unsigned int action_mask)2170099bf89dSPetr Machata mlxsw_sp_setup_tc_block_qevent_bind(struct mlxsw_sp_port *mlxsw_sp_port,
2171f6668eacSPetr Machata 				    struct flow_block_offload *f,
2172099bf89dSPetr Machata 				    enum mlxsw_sp_span_trigger span_trigger,
2173099bf89dSPetr Machata 				    unsigned int action_mask)
2174f6668eacSPetr Machata {
2175f6668eacSPetr Machata 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
2176f6668eacSPetr Machata 	struct mlxsw_sp_qevent_binding *qevent_binding;
2177f6668eacSPetr Machata 	struct mlxsw_sp_qevent_block *qevent_block;
2178f6668eacSPetr Machata 	struct flow_block_cb *block_cb;
2179f6668eacSPetr Machata 	struct mlxsw_sp_qdisc *qdisc;
2180f6668eacSPetr Machata 	bool register_block = false;
218176ff72a7SPetr Machata 	int tclass_num;
2182f6668eacSPetr Machata 	int err;
2183f6668eacSPetr Machata 
2184f6668eacSPetr Machata 	block_cb = flow_block_cb_lookup(f->block, mlxsw_sp_qevent_block_cb, mlxsw_sp);
2185f6668eacSPetr Machata 	if (!block_cb) {
2186f6668eacSPetr Machata 		qevent_block = mlxsw_sp_qevent_block_create(mlxsw_sp, f->net);
2187f6668eacSPetr Machata 		if (!qevent_block)
2188f6668eacSPetr Machata 			return -ENOMEM;
2189f6668eacSPetr Machata 		block_cb = flow_block_cb_alloc(mlxsw_sp_qevent_block_cb, mlxsw_sp, qevent_block,
2190f6668eacSPetr Machata 					       mlxsw_sp_qevent_block_release);
2191f6668eacSPetr Machata 		if (IS_ERR(block_cb)) {
2192f6668eacSPetr Machata 			mlxsw_sp_qevent_block_destroy(qevent_block);
2193f6668eacSPetr Machata 			return PTR_ERR(block_cb);
2194f6668eacSPetr Machata 		}
2195f6668eacSPetr Machata 		register_block = true;
2196f6668eacSPetr Machata 	} else {
2197f6668eacSPetr Machata 		qevent_block = flow_block_cb_priv(block_cb);
2198f6668eacSPetr Machata 	}
2199f6668eacSPetr Machata 	flow_block_cb_incref(block_cb);
2200f6668eacSPetr Machata 
2201f6668eacSPetr Machata 	qdisc = mlxsw_sp_qdisc_find_by_handle(mlxsw_sp_port, f->sch->handle);
2202f6668eacSPetr Machata 	if (!qdisc) {
2203f6668eacSPetr Machata 		NL_SET_ERR_MSG(f->extack, "Qdisc not offloaded");
2204f6668eacSPetr Machata 		err = -ENOENT;
2205f6668eacSPetr Machata 		goto err_find_qdisc;
2206f6668eacSPetr Machata 	}
2207f6668eacSPetr Machata 
2208f6668eacSPetr Machata 	if (WARN_ON(mlxsw_sp_qevent_binding_lookup(qevent_block, mlxsw_sp_port, f->sch->handle,
2209f6668eacSPetr Machata 						   span_trigger))) {
2210f6668eacSPetr Machata 		err = -EEXIST;
2211f6668eacSPetr Machata 		goto err_binding_exists;
2212f6668eacSPetr Machata 	}
2213f6668eacSPetr Machata 
221476ff72a7SPetr Machata 	tclass_num = mlxsw_sp_qdisc_get_tclass_num(mlxsw_sp_port, qdisc);
2215099bf89dSPetr Machata 	qevent_binding = mlxsw_sp_qevent_binding_create(mlxsw_sp_port,
2216099bf89dSPetr Machata 							f->sch->handle,
221776ff72a7SPetr Machata 							tclass_num,
2218099bf89dSPetr Machata 							span_trigger,
2219099bf89dSPetr Machata 							action_mask);
2220f6668eacSPetr Machata 	if (IS_ERR(qevent_binding)) {
2221f6668eacSPetr Machata 		err = PTR_ERR(qevent_binding);
2222f6668eacSPetr Machata 		goto err_binding_create;
2223f6668eacSPetr Machata 	}
2224f6668eacSPetr Machata 
2225a34dda72SPetr Machata 	err = mlxsw_sp_qevent_binding_configure(qevent_block, qevent_binding,
2226a34dda72SPetr Machata 						f->extack);
2227f6668eacSPetr Machata 	if (err)
2228f6668eacSPetr Machata 		goto err_binding_configure;
2229f6668eacSPetr Machata 
2230f6668eacSPetr Machata 	list_add(&qevent_binding->list, &qevent_block->binding_list);
2231f6668eacSPetr Machata 
2232f6668eacSPetr Machata 	if (register_block) {
2233f6668eacSPetr Machata 		flow_block_cb_add(block_cb, f);
2234f6668eacSPetr Machata 		list_add_tail(&block_cb->driver_list, &mlxsw_sp_qevent_block_cb_list);
2235f6668eacSPetr Machata 	}
2236f6668eacSPetr Machata 
2237f6668eacSPetr Machata 	return 0;
2238f6668eacSPetr Machata 
2239f6668eacSPetr Machata err_binding_configure:
2240f6668eacSPetr Machata 	mlxsw_sp_qevent_binding_destroy(qevent_binding);
2241f6668eacSPetr Machata err_binding_create:
2242f6668eacSPetr Machata err_binding_exists:
2243f6668eacSPetr Machata err_find_qdisc:
2244f6668eacSPetr Machata 	if (!flow_block_cb_decref(block_cb))
2245f6668eacSPetr Machata 		flow_block_cb_free(block_cb);
2246f6668eacSPetr Machata 	return err;
2247f6668eacSPetr Machata }
2248f6668eacSPetr Machata 
mlxsw_sp_setup_tc_block_qevent_unbind(struct mlxsw_sp_port * mlxsw_sp_port,struct flow_block_offload * f,enum mlxsw_sp_span_trigger span_trigger)2249f6668eacSPetr Machata static void mlxsw_sp_setup_tc_block_qevent_unbind(struct mlxsw_sp_port *mlxsw_sp_port,
2250f6668eacSPetr Machata 						  struct flow_block_offload *f,
2251f6668eacSPetr Machata 						  enum mlxsw_sp_span_trigger span_trigger)
2252f6668eacSPetr Machata {
2253f6668eacSPetr Machata 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
2254f6668eacSPetr Machata 	struct mlxsw_sp_qevent_binding *qevent_binding;
2255f6668eacSPetr Machata 	struct mlxsw_sp_qevent_block *qevent_block;
2256f6668eacSPetr Machata 	struct flow_block_cb *block_cb;
2257f6668eacSPetr Machata 
2258f6668eacSPetr Machata 	block_cb = flow_block_cb_lookup(f->block, mlxsw_sp_qevent_block_cb, mlxsw_sp);
2259f6668eacSPetr Machata 	if (!block_cb)
2260f6668eacSPetr Machata 		return;
2261f6668eacSPetr Machata 	qevent_block = flow_block_cb_priv(block_cb);
2262f6668eacSPetr Machata 
2263f6668eacSPetr Machata 	qevent_binding = mlxsw_sp_qevent_binding_lookup(qevent_block, mlxsw_sp_port, f->sch->handle,
2264f6668eacSPetr Machata 							span_trigger);
2265f6668eacSPetr Machata 	if (!qevent_binding)
2266f6668eacSPetr Machata 		return;
2267f6668eacSPetr Machata 
2268f6668eacSPetr Machata 	list_del(&qevent_binding->list);
2269f6668eacSPetr Machata 	mlxsw_sp_qevent_binding_deconfigure(qevent_block, qevent_binding);
2270f6668eacSPetr Machata 	mlxsw_sp_qevent_binding_destroy(qevent_binding);
2271f6668eacSPetr Machata 
2272f6668eacSPetr Machata 	if (!flow_block_cb_decref(block_cb)) {
2273f6668eacSPetr Machata 		flow_block_cb_remove(block_cb, f);
2274f6668eacSPetr Machata 		list_del(&block_cb->driver_list);
2275f6668eacSPetr Machata 	}
2276f6668eacSPetr Machata }
2277f6668eacSPetr Machata 
2278099bf89dSPetr Machata static int
mlxsw_sp_setup_tc_block_qevent(struct mlxsw_sp_port * mlxsw_sp_port,struct flow_block_offload * f,enum mlxsw_sp_span_trigger span_trigger,unsigned int action_mask)2279099bf89dSPetr Machata mlxsw_sp_setup_tc_block_qevent(struct mlxsw_sp_port *mlxsw_sp_port,
2280f6668eacSPetr Machata 			       struct flow_block_offload *f,
2281099bf89dSPetr Machata 			       enum mlxsw_sp_span_trigger span_trigger,
2282099bf89dSPetr Machata 			       unsigned int action_mask)
2283f6668eacSPetr Machata {
2284f6668eacSPetr Machata 	f->driver_block_list = &mlxsw_sp_qevent_block_cb_list;
2285f6668eacSPetr Machata 
2286f6668eacSPetr Machata 	switch (f->command) {
2287f6668eacSPetr Machata 	case FLOW_BLOCK_BIND:
2288099bf89dSPetr Machata 		return mlxsw_sp_setup_tc_block_qevent_bind(mlxsw_sp_port, f,
2289099bf89dSPetr Machata 							   span_trigger,
2290099bf89dSPetr Machata 							   action_mask);
2291f6668eacSPetr Machata 	case FLOW_BLOCK_UNBIND:
2292f6668eacSPetr Machata 		mlxsw_sp_setup_tc_block_qevent_unbind(mlxsw_sp_port, f, span_trigger);
2293f6668eacSPetr Machata 		return 0;
2294f6668eacSPetr Machata 	default:
2295f6668eacSPetr Machata 		return -EOPNOTSUPP;
2296f6668eacSPetr Machata 	}
2297f6668eacSPetr Machata }
2298f6668eacSPetr Machata 
mlxsw_sp_setup_tc_block_qevent_early_drop(struct mlxsw_sp_port * mlxsw_sp_port,struct flow_block_offload * f)2299f6668eacSPetr Machata int mlxsw_sp_setup_tc_block_qevent_early_drop(struct mlxsw_sp_port *mlxsw_sp_port,
2300f6668eacSPetr Machata 					      struct flow_block_offload *f)
2301f6668eacSPetr Machata {
2302099bf89dSPetr Machata 	unsigned int action_mask = BIT(MLXSW_SP_MALL_ACTION_TYPE_MIRROR) |
2303099bf89dSPetr Machata 				   BIT(MLXSW_SP_MALL_ACTION_TYPE_TRAP);
2304099bf89dSPetr Machata 
2305099bf89dSPetr Machata 	return mlxsw_sp_setup_tc_block_qevent(mlxsw_sp_port, f,
2306099bf89dSPetr Machata 					      MLXSW_SP_SPAN_TRIGGER_EARLY_DROP,
2307099bf89dSPetr Machata 					      action_mask);
2308f6668eacSPetr Machata }
2309f6668eacSPetr Machata 
mlxsw_sp_setup_tc_block_qevent_mark(struct mlxsw_sp_port * mlxsw_sp_port,struct flow_block_offload * f)23109c18eaf2SPetr Machata int mlxsw_sp_setup_tc_block_qevent_mark(struct mlxsw_sp_port *mlxsw_sp_port,
23119c18eaf2SPetr Machata 					struct flow_block_offload *f)
23129c18eaf2SPetr Machata {
23139c18eaf2SPetr Machata 	unsigned int action_mask = BIT(MLXSW_SP_MALL_ACTION_TYPE_MIRROR);
23149c18eaf2SPetr Machata 
23159c18eaf2SPetr Machata 	return mlxsw_sp_setup_tc_block_qevent(mlxsw_sp_port, f,
23169c18eaf2SPetr Machata 					      MLXSW_SP_SPAN_TRIGGER_ECN,
23179c18eaf2SPetr Machata 					      action_mask);
23189c18eaf2SPetr Machata }
23199c18eaf2SPetr Machata 
mlxsw_sp_tc_qdisc_init(struct mlxsw_sp_port * mlxsw_sp_port)2320371b437aSNogah Frankel int mlxsw_sp_tc_qdisc_init(struct mlxsw_sp_port *mlxsw_sp_port)
2321371b437aSNogah Frankel {
2322ee88450dSPetr Machata 	struct mlxsw_sp_qdisc_state *qdisc_state;
2323371b437aSNogah Frankel 
2324ee88450dSPetr Machata 	qdisc_state = kzalloc(sizeof(*qdisc_state), GFP_KERNEL);
2325ee88450dSPetr Machata 	if (!qdisc_state)
2326eed4baebSNogah Frankel 		return -ENOMEM;
2327ee88450dSPetr Machata 
2328cff99e20SPetr Machata 	mutex_init(&qdisc_state->lock);
2329ee88450dSPetr Machata 	mlxsw_sp_port->qdisc = qdisc_state;
2330ee88450dSPetr Machata 	return 0;
2331371b437aSNogah Frankel }
2332371b437aSNogah Frankel 
mlxsw_sp_tc_qdisc_fini(struct mlxsw_sp_port * mlxsw_sp_port)2333371b437aSNogah Frankel void mlxsw_sp_tc_qdisc_fini(struct mlxsw_sp_port *mlxsw_sp_port)
2334371b437aSNogah Frankel {
2335cff99e20SPetr Machata 	mutex_destroy(&mlxsw_sp_port->qdisc->lock);
2336ee88450dSPetr Machata 	kfree(mlxsw_sp_port->qdisc);
2337371b437aSNogah Frankel }
2338