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);
50*51d52ed9SPetr Machata 	struct mlxsw_sp_qdisc *(*find_class)(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
51*51d52ed9SPetr Machata 					     u32 parent);
52562ffbc4SNogah Frankel };
53562ffbc4SNogah Frankel 
54371b437aSNogah Frankel struct mlxsw_sp_qdisc {
55371b437aSNogah Frankel 	u32 handle;
56017a131cSPetr Machata 	int tclass_num;
571631ab2eSNogah Frankel 	u8 prio_bitmap;
58371b437aSNogah Frankel 	union {
594d1a4b84SNogah Frankel 		struct red_stats red;
604d1a4b84SNogah Frankel 	} xstats_base;
614d1a4b84SNogah Frankel 	struct mlxsw_sp_qdisc_stats {
62371b437aSNogah Frankel 		u64 tx_bytes;
63371b437aSNogah Frankel 		u64 tx_packets;
64371b437aSNogah Frankel 		u64 drops;
65371b437aSNogah Frankel 		u64 overlimits;
6693d8a4c1SNogah Frankel 		u64 backlog;
674d1a4b84SNogah Frankel 	} stats_base;
68562ffbc4SNogah Frankel 
69562ffbc4SNogah Frankel 	struct mlxsw_sp_qdisc_ops *ops;
70b21832b5SPetr Machata 	struct mlxsw_sp_qdisc *parent;
71*51d52ed9SPetr Machata 	struct mlxsw_sp_qdisc *qdiscs;
72*51d52ed9SPetr Machata 	unsigned int num_classes;
73371b437aSNogah Frankel };
74371b437aSNogah Frankel 
75ee88450dSPetr Machata struct mlxsw_sp_qdisc_state {
76ee88450dSPetr Machata 	struct mlxsw_sp_qdisc root_qdisc;
77ee88450dSPetr Machata 	struct mlxsw_sp_qdisc tclass_qdiscs[IEEE_8021QAZ_MAX_TCS];
787bec1a45SPetr Machata 
797bec1a45SPetr Machata 	/* When a PRIO or ETS are added, the invisible FIFOs in their bands are
807bec1a45SPetr Machata 	 * created first. When notifications for these FIFOs arrive, it is not
817bec1a45SPetr Machata 	 * known what qdisc their parent handle refers to. It could be a
827bec1a45SPetr Machata 	 * newly-created PRIO that will replace the currently-offloaded one, or
837bec1a45SPetr Machata 	 * it could be e.g. a RED that will be attached below it.
847bec1a45SPetr Machata 	 *
857bec1a45SPetr Machata 	 * As the notifications start to arrive, use them to note what the
867bec1a45SPetr Machata 	 * future parent handle is, and keep track of which child FIFOs were
877bec1a45SPetr Machata 	 * seen. Then when the parent is known, retroactively offload those
887bec1a45SPetr Machata 	 * FIFOs.
897bec1a45SPetr Machata 	 */
907bec1a45SPetr Machata 	u32 future_handle;
917bec1a45SPetr Machata 	bool future_fifos[IEEE_8021QAZ_MAX_TCS];
92ee88450dSPetr Machata };
93ee88450dSPetr Machata 
94cba7158fSNogah Frankel static bool
95290fe2c5SPetr Machata mlxsw_sp_qdisc_compare(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, u32 handle)
96cba7158fSNogah Frankel {
97290fe2c5SPetr Machata 	return mlxsw_sp_qdisc->ops && mlxsw_sp_qdisc->handle == handle;
98cba7158fSNogah Frankel }
99cba7158fSNogah Frankel 
100eed4baebSNogah Frankel static struct mlxsw_sp_qdisc *
101*51d52ed9SPetr Machata mlxsw_sp_qdisc_walk(struct mlxsw_sp_qdisc *qdisc,
102*51d52ed9SPetr Machata 		    struct mlxsw_sp_qdisc *(*pre)(struct mlxsw_sp_qdisc *,
103*51d52ed9SPetr Machata 						  void *),
104*51d52ed9SPetr Machata 		    void *data)
105*51d52ed9SPetr Machata {
106*51d52ed9SPetr Machata 	struct mlxsw_sp_qdisc *tmp;
107*51d52ed9SPetr Machata 	unsigned int i;
108*51d52ed9SPetr Machata 
109*51d52ed9SPetr Machata 	if (pre) {
110*51d52ed9SPetr Machata 		tmp = pre(qdisc, data);
111*51d52ed9SPetr Machata 		if (tmp)
112*51d52ed9SPetr Machata 			return tmp;
113*51d52ed9SPetr Machata 	}
114*51d52ed9SPetr Machata 
115*51d52ed9SPetr Machata 	if (qdisc->ops) {
116*51d52ed9SPetr Machata 		for (i = 0; i < qdisc->num_classes; i++) {
117*51d52ed9SPetr Machata 			tmp = &qdisc->qdiscs[i];
118*51d52ed9SPetr Machata 			if (qdisc->ops) {
119*51d52ed9SPetr Machata 				tmp = mlxsw_sp_qdisc_walk(tmp, pre, data);
120*51d52ed9SPetr Machata 				if (tmp)
121*51d52ed9SPetr Machata 					return tmp;
122*51d52ed9SPetr Machata 			}
123*51d52ed9SPetr Machata 		}
124*51d52ed9SPetr Machata 	}
125*51d52ed9SPetr Machata 
126*51d52ed9SPetr Machata 	return NULL;
127*51d52ed9SPetr Machata }
128*51d52ed9SPetr Machata 
129*51d52ed9SPetr Machata static struct mlxsw_sp_qdisc *
130*51d52ed9SPetr Machata mlxsw_sp_qdisc_walk_cb_find(struct mlxsw_sp_qdisc *qdisc, void *data)
131*51d52ed9SPetr Machata {
132*51d52ed9SPetr Machata 	u32 parent = *(u32 *)data;
133*51d52ed9SPetr Machata 
134*51d52ed9SPetr Machata 	if (qdisc->ops && TC_H_MAJ(qdisc->handle) == TC_H_MAJ(parent)) {
135*51d52ed9SPetr Machata 		if (qdisc->ops->find_class)
136*51d52ed9SPetr Machata 			return qdisc->ops->find_class(qdisc, parent);
137*51d52ed9SPetr Machata 	}
138*51d52ed9SPetr Machata 
139*51d52ed9SPetr Machata 	return NULL;
140*51d52ed9SPetr Machata }
141*51d52ed9SPetr Machata 
142*51d52ed9SPetr Machata static struct mlxsw_sp_qdisc *
143eed4baebSNogah Frankel mlxsw_sp_qdisc_find(struct mlxsw_sp_port *mlxsw_sp_port, u32 parent,
144eed4baebSNogah Frankel 		    bool root_only)
145eed4baebSNogah Frankel {
146ee88450dSPetr Machata 	struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc;
147eed4baebSNogah Frankel 
148*51d52ed9SPetr Machata 	if (!qdisc_state)
149*51d52ed9SPetr Machata 		return NULL;
150eed4baebSNogah Frankel 	if (parent == TC_H_ROOT)
151ee88450dSPetr Machata 		return &qdisc_state->root_qdisc;
152*51d52ed9SPetr Machata 	if (root_only)
153eed4baebSNogah Frankel 		return NULL;
154*51d52ed9SPetr Machata 	return mlxsw_sp_qdisc_walk(&qdisc_state->root_qdisc,
155*51d52ed9SPetr Machata 				   mlxsw_sp_qdisc_walk_cb_find, &parent);
156*51d52ed9SPetr Machata }
157eed4baebSNogah Frankel 
158*51d52ed9SPetr Machata static struct mlxsw_sp_qdisc *
159*51d52ed9SPetr Machata mlxsw_sp_qdisc_walk_cb_find_by_handle(struct mlxsw_sp_qdisc *qdisc, void *data)
160*51d52ed9SPetr Machata {
161*51d52ed9SPetr Machata 	u32 handle = *(u32 *)data;
162*51d52ed9SPetr Machata 
163*51d52ed9SPetr Machata 	if (qdisc->ops && qdisc->handle == handle)
164*51d52ed9SPetr Machata 		return qdisc;
165*51d52ed9SPetr Machata 	return NULL;
166eed4baebSNogah Frankel }
167eed4baebSNogah Frankel 
16832dc5efcSNogah Frankel static struct mlxsw_sp_qdisc *
16932dc5efcSNogah Frankel mlxsw_sp_qdisc_find_by_handle(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle)
17032dc5efcSNogah Frankel {
171ee88450dSPetr Machata 	struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc;
17232dc5efcSNogah Frankel 
173*51d52ed9SPetr Machata 	if (!qdisc_state)
17432dc5efcSNogah Frankel 		return NULL;
175*51d52ed9SPetr Machata 	return mlxsw_sp_qdisc_walk(&qdisc_state->root_qdisc,
176*51d52ed9SPetr Machata 				   mlxsw_sp_qdisc_walk_cb_find_by_handle,
177*51d52ed9SPetr Machata 				   &handle);
17832dc5efcSNogah Frankel }
17932dc5efcSNogah Frankel 
180b21832b5SPetr Machata static void
181b21832b5SPetr Machata mlxsw_sp_qdisc_reduce_parent_backlog(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
182b21832b5SPetr Machata {
183b21832b5SPetr Machata 	struct mlxsw_sp_qdisc *tmp;
184b21832b5SPetr Machata 
185b21832b5SPetr Machata 	for (tmp = mlxsw_sp_qdisc->parent; tmp; tmp = tmp->parent)
186b21832b5SPetr Machata 		tmp->stats_base.backlog -= mlxsw_sp_qdisc->stats_base.backlog;
187b21832b5SPetr Machata }
188b21832b5SPetr Machata 
18996f17e07SNogah Frankel static int
1909a37a59fSNogah Frankel mlxsw_sp_qdisc_destroy(struct mlxsw_sp_port *mlxsw_sp_port,
1919a37a59fSNogah Frankel 		       struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
1929a37a59fSNogah Frankel {
193509f04caSPetr Machata 	struct mlxsw_sp_qdisc *root_qdisc = &mlxsw_sp_port->qdisc->root_qdisc;
194509f04caSPetr Machata 	int err_hdroom = 0;
1959a37a59fSNogah Frankel 	int err = 0;
1969a37a59fSNogah Frankel 
1979a37a59fSNogah Frankel 	if (!mlxsw_sp_qdisc)
1989a37a59fSNogah Frankel 		return 0;
1999a37a59fSNogah Frankel 
200509f04caSPetr Machata 	if (root_qdisc == mlxsw_sp_qdisc) {
201509f04caSPetr Machata 		struct mlxsw_sp_hdroom hdroom = *mlxsw_sp_port->hdroom;
202509f04caSPetr Machata 
203509f04caSPetr Machata 		hdroom.mode = MLXSW_SP_HDROOM_MODE_DCB;
204509f04caSPetr Machata 		mlxsw_sp_hdroom_prios_reset_buf_idx(&hdroom);
205509f04caSPetr Machata 		mlxsw_sp_hdroom_bufs_reset_lossiness(&hdroom);
206509f04caSPetr Machata 		mlxsw_sp_hdroom_bufs_reset_sizes(mlxsw_sp_port, &hdroom);
207509f04caSPetr Machata 		err_hdroom = mlxsw_sp_hdroom_configure(mlxsw_sp_port, &hdroom);
208509f04caSPetr Machata 	}
209509f04caSPetr Machata 
210b21832b5SPetr Machata 	if (!mlxsw_sp_qdisc->ops)
211b21832b5SPetr Machata 		return 0;
212b21832b5SPetr Machata 
213b21832b5SPetr Machata 	mlxsw_sp_qdisc_reduce_parent_backlog(mlxsw_sp_qdisc);
214b21832b5SPetr Machata 	if (mlxsw_sp_qdisc->ops->destroy)
2159a37a59fSNogah Frankel 		err = mlxsw_sp_qdisc->ops->destroy(mlxsw_sp_port,
2169a37a59fSNogah Frankel 						   mlxsw_sp_qdisc);
2179a37a59fSNogah Frankel 
2189a37a59fSNogah Frankel 	mlxsw_sp_qdisc->handle = TC_H_UNSPEC;
2199a37a59fSNogah Frankel 	mlxsw_sp_qdisc->ops = NULL;
220509f04caSPetr Machata 
221509f04caSPetr Machata 	return err_hdroom ?: err;
2229a37a59fSNogah Frankel }
2239a37a59fSNogah Frankel 
2249a37a59fSNogah Frankel static int
2259cf6c9c7SNogah Frankel mlxsw_sp_qdisc_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
2269cf6c9c7SNogah Frankel 		       struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
2279cf6c9c7SNogah Frankel 		       struct mlxsw_sp_qdisc_ops *ops, void *params)
2289cf6c9c7SNogah Frankel {
229509f04caSPetr Machata 	struct mlxsw_sp_qdisc *root_qdisc = &mlxsw_sp_port->qdisc->root_qdisc;
230509f04caSPetr Machata 	struct mlxsw_sp_hdroom orig_hdroom;
2319cf6c9c7SNogah Frankel 	int err;
2329cf6c9c7SNogah Frankel 
23356202ca4SNogah Frankel 	if (mlxsw_sp_qdisc->ops && mlxsw_sp_qdisc->ops->type != ops->type)
23456202ca4SNogah Frankel 		/* In case this location contained a different qdisc of the
23556202ca4SNogah Frankel 		 * same type we can override the old qdisc configuration.
23656202ca4SNogah Frankel 		 * Otherwise, we need to remove the old qdisc before setting the
23756202ca4SNogah Frankel 		 * new one.
23856202ca4SNogah Frankel 		 */
23956202ca4SNogah Frankel 		mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
240509f04caSPetr Machata 
241509f04caSPetr Machata 	orig_hdroom = *mlxsw_sp_port->hdroom;
242509f04caSPetr Machata 	if (root_qdisc == mlxsw_sp_qdisc) {
243509f04caSPetr Machata 		struct mlxsw_sp_hdroom hdroom = orig_hdroom;
244509f04caSPetr Machata 
245509f04caSPetr Machata 		hdroom.mode = MLXSW_SP_HDROOM_MODE_TC;
246509f04caSPetr Machata 		mlxsw_sp_hdroom_prios_reset_buf_idx(&hdroom);
247509f04caSPetr Machata 		mlxsw_sp_hdroom_bufs_reset_lossiness(&hdroom);
248509f04caSPetr Machata 		mlxsw_sp_hdroom_bufs_reset_sizes(mlxsw_sp_port, &hdroom);
249509f04caSPetr Machata 
250509f04caSPetr Machata 		err = mlxsw_sp_hdroom_configure(mlxsw_sp_port, &hdroom);
251509f04caSPetr Machata 		if (err)
252509f04caSPetr Machata 			goto err_hdroom_configure;
253509f04caSPetr Machata 	}
254509f04caSPetr Machata 
25517c0e6d1SPetr Machata 	err = ops->check_params(mlxsw_sp_port, params);
2569cf6c9c7SNogah Frankel 	if (err)
2579cf6c9c7SNogah Frankel 		goto err_bad_param;
2589cf6c9c7SNogah Frankel 
259c4e372e2SPetr Machata 	err = ops->replace(mlxsw_sp_port, handle, mlxsw_sp_qdisc, params);
2609cf6c9c7SNogah Frankel 	if (err)
2619cf6c9c7SNogah Frankel 		goto err_config;
2629cf6c9c7SNogah Frankel 
2637bec1a45SPetr Machata 	/* Check if the Qdisc changed. That includes a situation where an
2647bec1a45SPetr Machata 	 * invisible Qdisc replaces another one, or is being added for the
2657bec1a45SPetr Machata 	 * first time.
2667bec1a45SPetr Machata 	 */
2677bec1a45SPetr Machata 	if (mlxsw_sp_qdisc->handle != handle || handle == TC_H_UNSPEC) {
2689cf6c9c7SNogah Frankel 		mlxsw_sp_qdisc->ops = ops;
2699cf6c9c7SNogah Frankel 		if (ops->clean_stats)
2709cf6c9c7SNogah Frankel 			ops->clean_stats(mlxsw_sp_port, mlxsw_sp_qdisc);
2719cf6c9c7SNogah Frankel 	}
2729cf6c9c7SNogah Frankel 
2739cf6c9c7SNogah Frankel 	mlxsw_sp_qdisc->handle = handle;
2749cf6c9c7SNogah Frankel 	return 0;
2759cf6c9c7SNogah Frankel 
2769cf6c9c7SNogah Frankel err_bad_param:
2779cf6c9c7SNogah Frankel err_config:
278509f04caSPetr Machata 	mlxsw_sp_hdroom_configure(mlxsw_sp_port, &orig_hdroom);
279509f04caSPetr Machata err_hdroom_configure:
28093d8a4c1SNogah Frankel 	if (mlxsw_sp_qdisc->handle == handle && ops->unoffload)
28193d8a4c1SNogah Frankel 		ops->unoffload(mlxsw_sp_port, mlxsw_sp_qdisc, params);
28293d8a4c1SNogah Frankel 
2839cf6c9c7SNogah Frankel 	mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
2849cf6c9c7SNogah Frankel 	return err;
2859cf6c9c7SNogah Frankel }
2869cf6c9c7SNogah Frankel 
2879cf6c9c7SNogah Frankel static int
288562ffbc4SNogah Frankel mlxsw_sp_qdisc_get_stats(struct mlxsw_sp_port *mlxsw_sp_port,
289562ffbc4SNogah Frankel 			 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
290562ffbc4SNogah Frankel 			 struct tc_qopt_offload_stats *stats_ptr)
291562ffbc4SNogah Frankel {
292562ffbc4SNogah Frankel 	if (mlxsw_sp_qdisc && mlxsw_sp_qdisc->ops &&
293562ffbc4SNogah Frankel 	    mlxsw_sp_qdisc->ops->get_stats)
294562ffbc4SNogah Frankel 		return mlxsw_sp_qdisc->ops->get_stats(mlxsw_sp_port,
295562ffbc4SNogah Frankel 						      mlxsw_sp_qdisc,
296562ffbc4SNogah Frankel 						      stats_ptr);
297562ffbc4SNogah Frankel 
298562ffbc4SNogah Frankel 	return -EOPNOTSUPP;
299562ffbc4SNogah Frankel }
300562ffbc4SNogah Frankel 
301562ffbc4SNogah Frankel static int
302562ffbc4SNogah Frankel mlxsw_sp_qdisc_get_xstats(struct mlxsw_sp_port *mlxsw_sp_port,
303562ffbc4SNogah Frankel 			  struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
304562ffbc4SNogah Frankel 			  void *xstats_ptr)
305562ffbc4SNogah Frankel {
306562ffbc4SNogah Frankel 	if (mlxsw_sp_qdisc && mlxsw_sp_qdisc->ops &&
307562ffbc4SNogah Frankel 	    mlxsw_sp_qdisc->ops->get_xstats)
308562ffbc4SNogah Frankel 		return mlxsw_sp_qdisc->ops->get_xstats(mlxsw_sp_port,
309562ffbc4SNogah Frankel 						      mlxsw_sp_qdisc,
310562ffbc4SNogah Frankel 						      xstats_ptr);
311562ffbc4SNogah Frankel 
312562ffbc4SNogah Frankel 	return -EOPNOTSUPP;
313562ffbc4SNogah Frankel }
314562ffbc4SNogah Frankel 
31585005b82SPetr Machata static u64
31685005b82SPetr Machata mlxsw_sp_xstats_backlog(struct mlxsw_sp_port_xstats *xstats, int tclass_num)
31785005b82SPetr Machata {
31885005b82SPetr Machata 	return xstats->backlog[tclass_num] +
31985005b82SPetr Machata 	       xstats->backlog[tclass_num + 8];
32085005b82SPetr Machata }
32185005b82SPetr Machata 
32285005b82SPetr Machata static u64
32385005b82SPetr Machata mlxsw_sp_xstats_tail_drop(struct mlxsw_sp_port_xstats *xstats, int tclass_num)
32485005b82SPetr Machata {
32585005b82SPetr Machata 	return xstats->tail_drop[tclass_num] +
32685005b82SPetr Machata 	       xstats->tail_drop[tclass_num + 8];
32785005b82SPetr Machata }
32885005b82SPetr Machata 
32904cc0bf5SNogah Frankel static void
33004cc0bf5SNogah Frankel mlxsw_sp_qdisc_bstats_per_priority_get(struct mlxsw_sp_port_xstats *xstats,
33104cc0bf5SNogah Frankel 				       u8 prio_bitmap, u64 *tx_packets,
33204cc0bf5SNogah Frankel 				       u64 *tx_bytes)
33304cc0bf5SNogah Frankel {
33404cc0bf5SNogah Frankel 	int i;
33504cc0bf5SNogah Frankel 
33604cc0bf5SNogah Frankel 	*tx_packets = 0;
33704cc0bf5SNogah Frankel 	*tx_bytes = 0;
33804cc0bf5SNogah Frankel 	for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
33904cc0bf5SNogah Frankel 		if (prio_bitmap & BIT(i)) {
34004cc0bf5SNogah Frankel 			*tx_packets += xstats->tx_packets[i];
34104cc0bf5SNogah Frankel 			*tx_bytes += xstats->tx_bytes[i];
34204cc0bf5SNogah Frankel 		}
34304cc0bf5SNogah Frankel 	}
34404cc0bf5SNogah Frankel }
34504cc0bf5SNogah Frankel 
346cf9af379SPetr Machata static void
347cf9af379SPetr Machata mlxsw_sp_qdisc_collect_tc_stats(struct mlxsw_sp_port *mlxsw_sp_port,
348cf9af379SPetr Machata 				struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
349cf9af379SPetr Machata 				u64 *p_tx_bytes, u64 *p_tx_packets,
350cf9af379SPetr Machata 				u64 *p_drops, u64 *p_backlog)
351cf9af379SPetr Machata {
352017a131cSPetr Machata 	int tclass_num = mlxsw_sp_qdisc->tclass_num;
353cf9af379SPetr Machata 	struct mlxsw_sp_port_xstats *xstats;
354cf9af379SPetr Machata 	u64 tx_bytes, tx_packets;
355cf9af379SPetr Machata 
356cf9af379SPetr Machata 	xstats = &mlxsw_sp_port->periodic_hw_stats.xstats;
357cf9af379SPetr Machata 	mlxsw_sp_qdisc_bstats_per_priority_get(xstats,
358cf9af379SPetr Machata 					       mlxsw_sp_qdisc->prio_bitmap,
359cf9af379SPetr Machata 					       &tx_packets, &tx_bytes);
360cf9af379SPetr Machata 
361cf9af379SPetr Machata 	*p_tx_packets += tx_packets;
362cf9af379SPetr Machata 	*p_tx_bytes += tx_bytes;
363cf9af379SPetr Machata 	*p_drops += xstats->wred_drop[tclass_num] +
364cf9af379SPetr Machata 		    mlxsw_sp_xstats_tail_drop(xstats, tclass_num);
365cf9af379SPetr Machata 	*p_backlog += mlxsw_sp_xstats_backlog(xstats, tclass_num);
366cf9af379SPetr Machata }
367cf9af379SPetr Machata 
368cf9af379SPetr Machata static void
369cf9af379SPetr Machata mlxsw_sp_qdisc_update_stats(struct mlxsw_sp *mlxsw_sp,
370cf9af379SPetr Machata 			    struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
371cf9af379SPetr Machata 			    u64 tx_bytes, u64 tx_packets,
372cf9af379SPetr Machata 			    u64 drops, u64 backlog,
373cf9af379SPetr Machata 			    struct tc_qopt_offload_stats *stats_ptr)
374cf9af379SPetr Machata {
375cf9af379SPetr Machata 	struct mlxsw_sp_qdisc_stats *stats_base = &mlxsw_sp_qdisc->stats_base;
376cf9af379SPetr Machata 
377cf9af379SPetr Machata 	tx_bytes -= stats_base->tx_bytes;
378cf9af379SPetr Machata 	tx_packets -= stats_base->tx_packets;
379cf9af379SPetr Machata 	drops -= stats_base->drops;
380cf9af379SPetr Machata 	backlog -= stats_base->backlog;
381cf9af379SPetr Machata 
382cf9af379SPetr Machata 	_bstats_update(stats_ptr->bstats, tx_bytes, tx_packets);
383cf9af379SPetr Machata 	stats_ptr->qstats->drops += drops;
384cf9af379SPetr Machata 	stats_ptr->qstats->backlog += mlxsw_sp_cells_bytes(mlxsw_sp, backlog);
385cf9af379SPetr Machata 
386cf9af379SPetr Machata 	stats_base->backlog += backlog;
387cf9af379SPetr Machata 	stats_base->drops += drops;
388cf9af379SPetr Machata 	stats_base->tx_bytes += tx_bytes;
389cf9af379SPetr Machata 	stats_base->tx_packets += tx_packets;
390cf9af379SPetr Machata }
391cf9af379SPetr Machata 
3923d0d5921SPetr Machata static void
3933d0d5921SPetr Machata mlxsw_sp_qdisc_get_tc_stats(struct mlxsw_sp_port *mlxsw_sp_port,
3943d0d5921SPetr Machata 			    struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
3953d0d5921SPetr Machata 			    struct tc_qopt_offload_stats *stats_ptr)
3963d0d5921SPetr Machata {
3973d0d5921SPetr Machata 	u64 tx_packets = 0;
3983d0d5921SPetr Machata 	u64 tx_bytes = 0;
3993d0d5921SPetr Machata 	u64 backlog = 0;
4003d0d5921SPetr Machata 	u64 drops = 0;
4013d0d5921SPetr Machata 
4023d0d5921SPetr Machata 	mlxsw_sp_qdisc_collect_tc_stats(mlxsw_sp_port, mlxsw_sp_qdisc,
4033d0d5921SPetr Machata 					&tx_bytes, &tx_packets,
4043d0d5921SPetr Machata 					&drops, &backlog);
4053d0d5921SPetr Machata 	mlxsw_sp_qdisc_update_stats(mlxsw_sp_port->mlxsw_sp, mlxsw_sp_qdisc,
4063d0d5921SPetr Machata 				    tx_bytes, tx_packets, drops, backlog,
4073d0d5921SPetr Machata 				    stats_ptr);
4083d0d5921SPetr Machata }
4093d0d5921SPetr Machata 
410562ffbc4SNogah Frankel static int
41196f17e07SNogah Frankel mlxsw_sp_tclass_congestion_enable(struct mlxsw_sp_port *mlxsw_sp_port,
41296f17e07SNogah Frankel 				  int tclass_num, u32 min, u32 max,
4138040c96bSPetr Machata 				  u32 probability, bool is_wred, bool is_ecn)
41496f17e07SNogah Frankel {
415db84924cSJiri Pirko 	char cwtpm_cmd[MLXSW_REG_CWTPM_LEN];
416db84924cSJiri Pirko 	char cwtp_cmd[MLXSW_REG_CWTP_LEN];
41796f17e07SNogah Frankel 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
41896f17e07SNogah Frankel 	int err;
41996f17e07SNogah Frankel 
42096f17e07SNogah Frankel 	mlxsw_reg_cwtp_pack(cwtp_cmd, mlxsw_sp_port->local_port, tclass_num);
42196f17e07SNogah Frankel 	mlxsw_reg_cwtp_profile_pack(cwtp_cmd, MLXSW_REG_CWTP_DEFAULT_PROFILE,
42296f17e07SNogah Frankel 				    roundup(min, MLXSW_REG_CWTP_MIN_VALUE),
42396f17e07SNogah Frankel 				    roundup(max, MLXSW_REG_CWTP_MIN_VALUE),
42496f17e07SNogah Frankel 				    probability);
42596f17e07SNogah Frankel 
42696f17e07SNogah Frankel 	err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(cwtp), cwtp_cmd);
42796f17e07SNogah Frankel 	if (err)
42896f17e07SNogah Frankel 		return err;
42996f17e07SNogah Frankel 
430db84924cSJiri Pirko 	mlxsw_reg_cwtpm_pack(cwtpm_cmd, mlxsw_sp_port->local_port, tclass_num,
4318040c96bSPetr Machata 			     MLXSW_REG_CWTP_DEFAULT_PROFILE, is_wred, is_ecn);
43296f17e07SNogah Frankel 
433db84924cSJiri Pirko 	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(cwtpm), cwtpm_cmd);
43496f17e07SNogah Frankel }
43596f17e07SNogah Frankel 
43696f17e07SNogah Frankel static int
43796f17e07SNogah Frankel mlxsw_sp_tclass_congestion_disable(struct mlxsw_sp_port *mlxsw_sp_port,
43896f17e07SNogah Frankel 				   int tclass_num)
43996f17e07SNogah Frankel {
44096f17e07SNogah Frankel 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
44196f17e07SNogah Frankel 	char cwtpm_cmd[MLXSW_REG_CWTPM_LEN];
44296f17e07SNogah Frankel 
44396f17e07SNogah Frankel 	mlxsw_reg_cwtpm_pack(cwtpm_cmd, mlxsw_sp_port->local_port, tclass_num,
44496f17e07SNogah Frankel 			     MLXSW_REG_CWTPM_RESET_PROFILE, false, false);
44596f17e07SNogah Frankel 	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(cwtpm), cwtpm_cmd);
44696f17e07SNogah Frankel }
44796f17e07SNogah Frankel 
448861fb829SNogah Frankel static void
449c2ed6db7SNogah Frankel mlxsw_sp_setup_tc_qdisc_red_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port,
450d56c8955SNogah Frankel 					struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
451861fb829SNogah Frankel {
452017a131cSPetr Machata 	int tclass_num = mlxsw_sp_qdisc->tclass_num;
4534d1a4b84SNogah Frankel 	struct mlxsw_sp_qdisc_stats *stats_base;
454861fb829SNogah Frankel 	struct mlxsw_sp_port_xstats *xstats;
4554d1a4b84SNogah Frankel 	struct red_stats *red_base;
456861fb829SNogah Frankel 
457861fb829SNogah Frankel 	xstats = &mlxsw_sp_port->periodic_hw_stats.xstats;
4584d1a4b84SNogah Frankel 	stats_base = &mlxsw_sp_qdisc->stats_base;
459c2ed6db7SNogah Frankel 	red_base = &mlxsw_sp_qdisc->xstats_base.red;
4603670756fSNogah Frankel 
46104cc0bf5SNogah Frankel 	mlxsw_sp_qdisc_bstats_per_priority_get(xstats,
46204cc0bf5SNogah Frankel 					       mlxsw_sp_qdisc->prio_bitmap,
46304cc0bf5SNogah Frankel 					       &stats_base->tx_packets,
46404cc0bf5SNogah Frankel 					       &stats_base->tx_bytes);
4654d1a4b84SNogah Frankel 	red_base->prob_drop = xstats->wred_drop[tclass_num];
46685005b82SPetr Machata 	red_base->pdrop = mlxsw_sp_xstats_tail_drop(xstats, tclass_num);
4673670756fSNogah Frankel 
468c2ed6db7SNogah Frankel 	stats_base->overlimits = red_base->prob_drop + red_base->prob_mark;
4694d1a4b84SNogah Frankel 	stats_base->drops = red_base->prob_drop + red_base->pdrop;
470416ef9b1SJakub Kicinski 
471416ef9b1SJakub Kicinski 	stats_base->backlog = 0;
472861fb829SNogah Frankel }
473861fb829SNogah Frankel 
47496f17e07SNogah Frankel static int
475cba7158fSNogah Frankel mlxsw_sp_qdisc_red_destroy(struct mlxsw_sp_port *mlxsw_sp_port,
476d56c8955SNogah Frankel 			   struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
47796f17e07SNogah Frankel {
4789a37a59fSNogah Frankel 	return mlxsw_sp_tclass_congestion_disable(mlxsw_sp_port,
479d56c8955SNogah Frankel 						  mlxsw_sp_qdisc->tclass_num);
48096f17e07SNogah Frankel }
48196f17e07SNogah Frankel 
48296f17e07SNogah Frankel static int
4839cf6c9c7SNogah Frankel mlxsw_sp_qdisc_red_check_params(struct mlxsw_sp_port *mlxsw_sp_port,
4849cf6c9c7SNogah Frankel 				void *params)
48596f17e07SNogah Frankel {
48696f17e07SNogah Frankel 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
4879cf6c9c7SNogah Frankel 	struct tc_red_qopt_offload_params *p = params;
48896f17e07SNogah Frankel 
48996f17e07SNogah Frankel 	if (p->min > p->max) {
49096f17e07SNogah Frankel 		dev_err(mlxsw_sp->bus_info->dev,
49196f17e07SNogah Frankel 			"spectrum: RED: min %u is bigger then max %u\n", p->min,
49296f17e07SNogah Frankel 			p->max);
4939cf6c9c7SNogah Frankel 		return -EINVAL;
49496f17e07SNogah Frankel 	}
495914c4fc1SPetr Machata 	if (p->max > MLXSW_CORE_RES_GET(mlxsw_sp->core,
496914c4fc1SPetr Machata 					GUARANTEED_SHARED_BUFFER)) {
49796f17e07SNogah Frankel 		dev_err(mlxsw_sp->bus_info->dev,
49896f17e07SNogah Frankel 			"spectrum: RED: max value %u is too big\n", p->max);
4999cf6c9c7SNogah Frankel 		return -EINVAL;
50096f17e07SNogah Frankel 	}
50196f17e07SNogah Frankel 	if (p->min == 0 || p->max == 0) {
50296f17e07SNogah Frankel 		dev_err(mlxsw_sp->bus_info->dev,
50396f17e07SNogah Frankel 			"spectrum: RED: 0 value is illegal for min and max\n");
5049cf6c9c7SNogah Frankel 		return -EINVAL;
50596f17e07SNogah Frankel 	}
5069cf6c9c7SNogah Frankel 	return 0;
5079cf6c9c7SNogah Frankel }
5089cf6c9c7SNogah Frankel 
5099cf6c9c7SNogah Frankel static int
510c4e372e2SPetr Machata mlxsw_sp_qdisc_red_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
5119cf6c9c7SNogah Frankel 			   struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
5129cf6c9c7SNogah Frankel 			   void *params)
5139cf6c9c7SNogah Frankel {
5149cf6c9c7SNogah Frankel 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
5159cf6c9c7SNogah Frankel 	struct tc_red_qopt_offload_params *p = params;
516017a131cSPetr Machata 	int tclass_num = mlxsw_sp_qdisc->tclass_num;
5179cf6c9c7SNogah Frankel 	u32 min, max;
5189cf6c9c7SNogah Frankel 	u64 prob;
51996f17e07SNogah Frankel 
52096f17e07SNogah Frankel 	/* calculate probability in percentage */
52196f17e07SNogah Frankel 	prob = p->probability;
52296f17e07SNogah Frankel 	prob *= 100;
52396f17e07SNogah Frankel 	prob = DIV_ROUND_UP(prob, 1 << 16);
52496f17e07SNogah Frankel 	prob = DIV_ROUND_UP(prob, 1 << 16);
52596f17e07SNogah Frankel 	min = mlxsw_sp_bytes_cells(mlxsw_sp, p->min);
52696f17e07SNogah Frankel 	max = mlxsw_sp_bytes_cells(mlxsw_sp, p->max);
5278040c96bSPetr Machata 	return mlxsw_sp_tclass_congestion_enable(mlxsw_sp_port, tclass_num,
5288040c96bSPetr Machata 						 min, max, prob,
5298040c96bSPetr Machata 						 !p->is_nodrop, p->is_ecn);
53096f17e07SNogah Frankel }
53196f17e07SNogah Frankel 
532416ef9b1SJakub Kicinski static void
533be1d5a8aSPetr Machata mlxsw_sp_qdisc_leaf_unoffload(struct mlxsw_sp_port *mlxsw_sp_port,
534be1d5a8aSPetr Machata 			      struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
535be1d5a8aSPetr Machata 			      struct gnet_stats_queue *qstats)
536be1d5a8aSPetr Machata {
537be1d5a8aSPetr Machata 	u64 backlog;
538be1d5a8aSPetr Machata 
539be1d5a8aSPetr Machata 	backlog = mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp,
540be1d5a8aSPetr Machata 				       mlxsw_sp_qdisc->stats_base.backlog);
541be1d5a8aSPetr Machata 	qstats->backlog -= backlog;
542be1d5a8aSPetr Machata 	mlxsw_sp_qdisc->stats_base.backlog = 0;
543be1d5a8aSPetr Machata }
544be1d5a8aSPetr Machata 
545be1d5a8aSPetr Machata static void
546416ef9b1SJakub Kicinski mlxsw_sp_qdisc_red_unoffload(struct mlxsw_sp_port *mlxsw_sp_port,
547416ef9b1SJakub Kicinski 			     struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
548416ef9b1SJakub Kicinski 			     void *params)
549416ef9b1SJakub Kicinski {
550416ef9b1SJakub Kicinski 	struct tc_red_qopt_offload_params *p = params;
551416ef9b1SJakub Kicinski 
552be1d5a8aSPetr Machata 	mlxsw_sp_qdisc_leaf_unoffload(mlxsw_sp_port, mlxsw_sp_qdisc, p->qstats);
553416ef9b1SJakub Kicinski }
554416ef9b1SJakub Kicinski 
555861fb829SNogah Frankel static int
556cba7158fSNogah Frankel mlxsw_sp_qdisc_get_red_xstats(struct mlxsw_sp_port *mlxsw_sp_port,
557861fb829SNogah Frankel 			      struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
558562ffbc4SNogah Frankel 			      void *xstats_ptr)
559861fb829SNogah Frankel {
5604d1a4b84SNogah Frankel 	struct red_stats *xstats_base = &mlxsw_sp_qdisc->xstats_base.red;
561017a131cSPetr Machata 	int tclass_num = mlxsw_sp_qdisc->tclass_num;
562861fb829SNogah Frankel 	struct mlxsw_sp_port_xstats *xstats;
563562ffbc4SNogah Frankel 	struct red_stats *res = xstats_ptr;
5648a29581eSPetr Machata 	int early_drops, pdrops;
565861fb829SNogah Frankel 
566861fb829SNogah Frankel 	xstats = &mlxsw_sp_port->periodic_hw_stats.xstats;
567861fb829SNogah Frankel 
568f8253df5SNogah Frankel 	early_drops = xstats->wred_drop[tclass_num] - xstats_base->prob_drop;
56985005b82SPetr Machata 	pdrops = mlxsw_sp_xstats_tail_drop(xstats, tclass_num) -
57085005b82SPetr Machata 		 xstats_base->pdrop;
571f8253df5SNogah Frankel 
572f8253df5SNogah Frankel 	res->pdrop += pdrops;
573f8253df5SNogah Frankel 	res->prob_drop += early_drops;
574f8253df5SNogah Frankel 
575f8253df5SNogah Frankel 	xstats_base->pdrop += pdrops;
576f8253df5SNogah Frankel 	xstats_base->prob_drop += early_drops;
577861fb829SNogah Frankel 	return 0;
578861fb829SNogah Frankel }
579861fb829SNogah Frankel 
5803670756fSNogah Frankel static int
581cba7158fSNogah Frankel mlxsw_sp_qdisc_get_red_stats(struct mlxsw_sp_port *mlxsw_sp_port,
5823670756fSNogah Frankel 			     struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
583562ffbc4SNogah Frankel 			     struct tc_qopt_offload_stats *stats_ptr)
5843670756fSNogah Frankel {
585017a131cSPetr Machata 	int tclass_num = mlxsw_sp_qdisc->tclass_num;
5864d1a4b84SNogah Frankel 	struct mlxsw_sp_qdisc_stats *stats_base;
5873670756fSNogah Frankel 	struct mlxsw_sp_port_xstats *xstats;
588cf9af379SPetr Machata 	u64 overlimits;
5893670756fSNogah Frankel 
5903670756fSNogah Frankel 	xstats = &mlxsw_sp_port->periodic_hw_stats.xstats;
5914d1a4b84SNogah Frankel 	stats_base = &mlxsw_sp_qdisc->stats_base;
5923670756fSNogah Frankel 
5933d0d5921SPetr Machata 	mlxsw_sp_qdisc_get_tc_stats(mlxsw_sp_port, mlxsw_sp_qdisc, stats_ptr);
5948a29581eSPetr Machata 	overlimits = xstats->wred_drop[tclass_num] - stats_base->overlimits;
5953670756fSNogah Frankel 
596562ffbc4SNogah Frankel 	stats_ptr->qstats->overlimits += overlimits;
5974d1a4b84SNogah Frankel 	stats_base->overlimits += overlimits;
598cf9af379SPetr Machata 
5993670756fSNogah Frankel 	return 0;
6003670756fSNogah Frankel }
6013670756fSNogah Frankel 
602*51d52ed9SPetr Machata static struct mlxsw_sp_qdisc *
603*51d52ed9SPetr Machata mlxsw_sp_qdisc_leaf_find_class(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
604*51d52ed9SPetr Machata 			       u32 parent)
605*51d52ed9SPetr Machata {
606*51d52ed9SPetr Machata 	return NULL;
607*51d52ed9SPetr Machata }
608*51d52ed9SPetr Machata 
60996f17e07SNogah Frankel #define MLXSW_SP_PORT_DEFAULT_TCLASS 0
61096f17e07SNogah Frankel 
611562ffbc4SNogah Frankel static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_red = {
6129cf6c9c7SNogah Frankel 	.type = MLXSW_SP_QDISC_RED,
6139cf6c9c7SNogah Frankel 	.check_params = mlxsw_sp_qdisc_red_check_params,
6149cf6c9c7SNogah Frankel 	.replace = mlxsw_sp_qdisc_red_replace,
615416ef9b1SJakub Kicinski 	.unoffload = mlxsw_sp_qdisc_red_unoffload,
6169a37a59fSNogah Frankel 	.destroy = mlxsw_sp_qdisc_red_destroy,
617562ffbc4SNogah Frankel 	.get_stats = mlxsw_sp_qdisc_get_red_stats,
618562ffbc4SNogah Frankel 	.get_xstats = mlxsw_sp_qdisc_get_red_xstats,
6199cf6c9c7SNogah Frankel 	.clean_stats = mlxsw_sp_setup_tc_qdisc_red_clean_stats,
620*51d52ed9SPetr Machata 	.find_class = mlxsw_sp_qdisc_leaf_find_class,
621562ffbc4SNogah Frankel };
622562ffbc4SNogah Frankel 
62396f17e07SNogah Frankel int mlxsw_sp_setup_tc_red(struct mlxsw_sp_port *mlxsw_sp_port,
62496f17e07SNogah Frankel 			  struct tc_red_qopt_offload *p)
62596f17e07SNogah Frankel {
62696f17e07SNogah Frankel 	struct mlxsw_sp_qdisc *mlxsw_sp_qdisc;
62796f17e07SNogah Frankel 
628eed4baebSNogah Frankel 	mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent, false);
629eed4baebSNogah Frankel 	if (!mlxsw_sp_qdisc)
63096f17e07SNogah Frankel 		return -EOPNOTSUPP;
63196f17e07SNogah Frankel 
632cba7158fSNogah Frankel 	if (p->command == TC_RED_REPLACE)
6339cf6c9c7SNogah Frankel 		return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle,
634562ffbc4SNogah Frankel 					      mlxsw_sp_qdisc,
635562ffbc4SNogah Frankel 					      &mlxsw_sp_qdisc_ops_red,
636562ffbc4SNogah Frankel 					      &p->set);
637cba7158fSNogah Frankel 
638290fe2c5SPetr Machata 	if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle))
639cba7158fSNogah Frankel 		return -EOPNOTSUPP;
640cba7158fSNogah Frankel 
641cba7158fSNogah Frankel 	switch (p->command) {
64296f17e07SNogah Frankel 	case TC_RED_DESTROY:
6439a37a59fSNogah Frankel 		return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
644861fb829SNogah Frankel 	case TC_RED_XSTATS:
645562ffbc4SNogah Frankel 		return mlxsw_sp_qdisc_get_xstats(mlxsw_sp_port, mlxsw_sp_qdisc,
646861fb829SNogah Frankel 						 p->xstats);
6473670756fSNogah Frankel 	case TC_RED_STATS:
648562ffbc4SNogah Frankel 		return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc,
6493670756fSNogah Frankel 						&p->stats);
65096f17e07SNogah Frankel 	default:
65196f17e07SNogah Frankel 		return -EOPNOTSUPP;
65296f17e07SNogah Frankel 	}
65396f17e07SNogah Frankel }
654371b437aSNogah Frankel 
655a44f58c4SPetr Machata static void
656a44f58c4SPetr Machata mlxsw_sp_setup_tc_qdisc_leaf_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port,
657a44f58c4SPetr Machata 					 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
658a44f58c4SPetr Machata {
659a44f58c4SPetr Machata 	u64 backlog_cells = 0;
660a44f58c4SPetr Machata 	u64 tx_packets = 0;
661a44f58c4SPetr Machata 	u64 tx_bytes = 0;
662a44f58c4SPetr Machata 	u64 drops = 0;
663a44f58c4SPetr Machata 
664a44f58c4SPetr Machata 	mlxsw_sp_qdisc_collect_tc_stats(mlxsw_sp_port, mlxsw_sp_qdisc,
665a44f58c4SPetr Machata 					&tx_bytes, &tx_packets,
666a44f58c4SPetr Machata 					&drops, &backlog_cells);
667a44f58c4SPetr Machata 
668a44f58c4SPetr Machata 	mlxsw_sp_qdisc->stats_base.tx_packets = tx_packets;
669a44f58c4SPetr Machata 	mlxsw_sp_qdisc->stats_base.tx_bytes = tx_bytes;
670a44f58c4SPetr Machata 	mlxsw_sp_qdisc->stats_base.drops = drops;
671a44f58c4SPetr Machata 	mlxsw_sp_qdisc->stats_base.backlog = 0;
672a44f58c4SPetr Machata }
673a44f58c4SPetr Machata 
674a44f58c4SPetr Machata static int
675a44f58c4SPetr Machata mlxsw_sp_qdisc_tbf_destroy(struct mlxsw_sp_port *mlxsw_sp_port,
676a44f58c4SPetr Machata 			   struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
677a44f58c4SPetr Machata {
678a44f58c4SPetr Machata 	return mlxsw_sp_port_ets_maxrate_set(mlxsw_sp_port,
679a44f58c4SPetr Machata 					     MLXSW_REG_QEEC_HR_SUBGROUP,
680a44f58c4SPetr Machata 					     mlxsw_sp_qdisc->tclass_num, 0,
681a44f58c4SPetr Machata 					     MLXSW_REG_QEEC_MAS_DIS, 0);
682a44f58c4SPetr Machata }
683a44f58c4SPetr Machata 
684a44f58c4SPetr Machata static int
685a44f58c4SPetr Machata mlxsw_sp_qdisc_tbf_bs(struct mlxsw_sp_port *mlxsw_sp_port,
686a44f58c4SPetr Machata 		      u32 max_size, u8 *p_burst_size)
687a44f58c4SPetr Machata {
688a44f58c4SPetr Machata 	/* TBF burst size is configured in bytes. The ASIC burst size value is
689a44f58c4SPetr Machata 	 * ((2 ^ bs) * 512 bits. Convert the TBF bytes to 512-bit units.
690a44f58c4SPetr Machata 	 */
691a44f58c4SPetr Machata 	u32 bs512 = max_size / 64;
692a44f58c4SPetr Machata 	u8 bs = fls(bs512);
693a44f58c4SPetr Machata 
694a44f58c4SPetr Machata 	if (!bs)
695a44f58c4SPetr Machata 		return -EINVAL;
696a44f58c4SPetr Machata 	--bs;
697a44f58c4SPetr Machata 
698a44f58c4SPetr Machata 	/* Demand a power of two. */
699a44f58c4SPetr Machata 	if ((1 << bs) != bs512)
700a44f58c4SPetr Machata 		return -EINVAL;
701a44f58c4SPetr Machata 
702a44f58c4SPetr Machata 	if (bs < mlxsw_sp_port->mlxsw_sp->lowest_shaper_bs ||
703a44f58c4SPetr Machata 	    bs > MLXSW_REG_QEEC_HIGHEST_SHAPER_BS)
704a44f58c4SPetr Machata 		return -EINVAL;
705a44f58c4SPetr Machata 
706a44f58c4SPetr Machata 	*p_burst_size = bs;
707a44f58c4SPetr Machata 	return 0;
708a44f58c4SPetr Machata }
709a44f58c4SPetr Machata 
710a44f58c4SPetr Machata static u32
711a44f58c4SPetr Machata mlxsw_sp_qdisc_tbf_max_size(u8 bs)
712a44f58c4SPetr Machata {
713a44f58c4SPetr Machata 	return (1U << bs) * 64;
714a44f58c4SPetr Machata }
715a44f58c4SPetr Machata 
716a44f58c4SPetr Machata static u64
717a44f58c4SPetr Machata mlxsw_sp_qdisc_tbf_rate_kbps(struct tc_tbf_qopt_offload_replace_params *p)
718a44f58c4SPetr Machata {
719a44f58c4SPetr Machata 	/* TBF interface is in bytes/s, whereas Spectrum ASIC is configured in
720a44f58c4SPetr Machata 	 * Kbits/s.
721a44f58c4SPetr Machata 	 */
72291a7d4bfSNathan Chancellor 	return div_u64(p->rate.rate_bytes_ps, 1000) * 8;
723a44f58c4SPetr Machata }
724a44f58c4SPetr Machata 
725a44f58c4SPetr Machata static int
726a44f58c4SPetr Machata mlxsw_sp_qdisc_tbf_check_params(struct mlxsw_sp_port *mlxsw_sp_port,
727a44f58c4SPetr Machata 				void *params)
728a44f58c4SPetr Machata {
729a44f58c4SPetr Machata 	struct tc_tbf_qopt_offload_replace_params *p = params;
730a44f58c4SPetr Machata 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
731a44f58c4SPetr Machata 	u64 rate_kbps = mlxsw_sp_qdisc_tbf_rate_kbps(p);
732a44f58c4SPetr Machata 	u8 burst_size;
733a44f58c4SPetr Machata 	int err;
734a44f58c4SPetr Machata 
735a44f58c4SPetr Machata 	if (rate_kbps >= MLXSW_REG_QEEC_MAS_DIS) {
736a44f58c4SPetr Machata 		dev_err(mlxsw_sp_port->mlxsw_sp->bus_info->dev,
737a44f58c4SPetr Machata 			"spectrum: TBF: rate of %lluKbps must be below %u\n",
738a44f58c4SPetr Machata 			rate_kbps, MLXSW_REG_QEEC_MAS_DIS);
739a44f58c4SPetr Machata 		return -EINVAL;
740a44f58c4SPetr Machata 	}
741a44f58c4SPetr Machata 
742a44f58c4SPetr Machata 	err = mlxsw_sp_qdisc_tbf_bs(mlxsw_sp_port, p->max_size, &burst_size);
743a44f58c4SPetr Machata 	if (err) {
744a44f58c4SPetr Machata 		u8 highest_shaper_bs = MLXSW_REG_QEEC_HIGHEST_SHAPER_BS;
745a44f58c4SPetr Machata 
746a44f58c4SPetr Machata 		dev_err(mlxsw_sp->bus_info->dev,
747a44f58c4SPetr Machata 			"spectrum: TBF: invalid burst size of %u, must be a power of two between %u and %u",
748a44f58c4SPetr Machata 			p->max_size,
749a44f58c4SPetr Machata 			mlxsw_sp_qdisc_tbf_max_size(mlxsw_sp->lowest_shaper_bs),
750a44f58c4SPetr Machata 			mlxsw_sp_qdisc_tbf_max_size(highest_shaper_bs));
751a44f58c4SPetr Machata 		return -EINVAL;
752a44f58c4SPetr Machata 	}
753a44f58c4SPetr Machata 
754a44f58c4SPetr Machata 	return 0;
755a44f58c4SPetr Machata }
756a44f58c4SPetr Machata 
757a44f58c4SPetr Machata static int
758c4e372e2SPetr Machata mlxsw_sp_qdisc_tbf_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
759a44f58c4SPetr Machata 			   struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
760a44f58c4SPetr Machata 			   void *params)
761a44f58c4SPetr Machata {
762a44f58c4SPetr Machata 	struct tc_tbf_qopt_offload_replace_params *p = params;
763a44f58c4SPetr Machata 	u64 rate_kbps = mlxsw_sp_qdisc_tbf_rate_kbps(p);
764a44f58c4SPetr Machata 	u8 burst_size;
765a44f58c4SPetr Machata 	int err;
766a44f58c4SPetr Machata 
767a44f58c4SPetr Machata 	err = mlxsw_sp_qdisc_tbf_bs(mlxsw_sp_port, p->max_size, &burst_size);
768a44f58c4SPetr Machata 	if (WARN_ON_ONCE(err))
769a44f58c4SPetr Machata 		/* check_params above was supposed to reject this value. */
770a44f58c4SPetr Machata 		return -EINVAL;
771a44f58c4SPetr Machata 
772a44f58c4SPetr Machata 	/* Configure subgroup shaper, so that both UC and MC traffic is subject
773a44f58c4SPetr Machata 	 * to shaping. That is unlike RED, however UC queue lengths are going to
774a44f58c4SPetr Machata 	 * be different than MC ones due to different pool and quota
775a44f58c4SPetr Machata 	 * configurations, so the configuration is not applicable. For shaper on
776a44f58c4SPetr Machata 	 * the other hand, subjecting the overall stream to the configured
777a44f58c4SPetr Machata 	 * shaper makes sense. Also note that that is what we do for
778a44f58c4SPetr Machata 	 * ieee_setmaxrate().
779a44f58c4SPetr Machata 	 */
780a44f58c4SPetr Machata 	return mlxsw_sp_port_ets_maxrate_set(mlxsw_sp_port,
781a44f58c4SPetr Machata 					     MLXSW_REG_QEEC_HR_SUBGROUP,
782a44f58c4SPetr Machata 					     mlxsw_sp_qdisc->tclass_num, 0,
783a44f58c4SPetr Machata 					     rate_kbps, burst_size);
784a44f58c4SPetr Machata }
785a44f58c4SPetr Machata 
786a44f58c4SPetr Machata static void
787a44f58c4SPetr Machata mlxsw_sp_qdisc_tbf_unoffload(struct mlxsw_sp_port *mlxsw_sp_port,
788a44f58c4SPetr Machata 			     struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
789a44f58c4SPetr Machata 			     void *params)
790a44f58c4SPetr Machata {
791a44f58c4SPetr Machata 	struct tc_tbf_qopt_offload_replace_params *p = params;
792a44f58c4SPetr Machata 
793a44f58c4SPetr Machata 	mlxsw_sp_qdisc_leaf_unoffload(mlxsw_sp_port, mlxsw_sp_qdisc, p->qstats);
794a44f58c4SPetr Machata }
795a44f58c4SPetr Machata 
796a44f58c4SPetr Machata static int
797a44f58c4SPetr Machata mlxsw_sp_qdisc_get_tbf_stats(struct mlxsw_sp_port *mlxsw_sp_port,
798a44f58c4SPetr Machata 			     struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
799a44f58c4SPetr Machata 			     struct tc_qopt_offload_stats *stats_ptr)
800a44f58c4SPetr Machata {
801a44f58c4SPetr Machata 	mlxsw_sp_qdisc_get_tc_stats(mlxsw_sp_port, mlxsw_sp_qdisc,
802a44f58c4SPetr Machata 				    stats_ptr);
803a44f58c4SPetr Machata 	return 0;
804a44f58c4SPetr Machata }
805a44f58c4SPetr Machata 
806a44f58c4SPetr Machata static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_tbf = {
807a44f58c4SPetr Machata 	.type = MLXSW_SP_QDISC_TBF,
808a44f58c4SPetr Machata 	.check_params = mlxsw_sp_qdisc_tbf_check_params,
809a44f58c4SPetr Machata 	.replace = mlxsw_sp_qdisc_tbf_replace,
810a44f58c4SPetr Machata 	.unoffload = mlxsw_sp_qdisc_tbf_unoffload,
811a44f58c4SPetr Machata 	.destroy = mlxsw_sp_qdisc_tbf_destroy,
812a44f58c4SPetr Machata 	.get_stats = mlxsw_sp_qdisc_get_tbf_stats,
813a44f58c4SPetr Machata 	.clean_stats = mlxsw_sp_setup_tc_qdisc_leaf_clean_stats,
814*51d52ed9SPetr Machata 	.find_class = mlxsw_sp_qdisc_leaf_find_class,
815a44f58c4SPetr Machata };
816a44f58c4SPetr Machata 
817a44f58c4SPetr Machata int mlxsw_sp_setup_tc_tbf(struct mlxsw_sp_port *mlxsw_sp_port,
818a44f58c4SPetr Machata 			  struct tc_tbf_qopt_offload *p)
819a44f58c4SPetr Machata {
820a44f58c4SPetr Machata 	struct mlxsw_sp_qdisc *mlxsw_sp_qdisc;
821a44f58c4SPetr Machata 
822a44f58c4SPetr Machata 	mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent, false);
823a44f58c4SPetr Machata 	if (!mlxsw_sp_qdisc)
824a44f58c4SPetr Machata 		return -EOPNOTSUPP;
825a44f58c4SPetr Machata 
826a44f58c4SPetr Machata 	if (p->command == TC_TBF_REPLACE)
827a44f58c4SPetr Machata 		return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle,
828a44f58c4SPetr Machata 					      mlxsw_sp_qdisc,
829a44f58c4SPetr Machata 					      &mlxsw_sp_qdisc_ops_tbf,
830a44f58c4SPetr Machata 					      &p->replace_params);
831a44f58c4SPetr Machata 
832290fe2c5SPetr Machata 	if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle))
833a44f58c4SPetr Machata 		return -EOPNOTSUPP;
834a44f58c4SPetr Machata 
835a44f58c4SPetr Machata 	switch (p->command) {
836a44f58c4SPetr Machata 	case TC_TBF_DESTROY:
837a44f58c4SPetr Machata 		return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
838a44f58c4SPetr Machata 	case TC_TBF_STATS:
839a44f58c4SPetr Machata 		return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc,
840a44f58c4SPetr Machata 						&p->stats);
841a44f58c4SPetr Machata 	default:
842a44f58c4SPetr Machata 		return -EOPNOTSUPP;
843a44f58c4SPetr Machata 	}
844a44f58c4SPetr Machata }
845a44f58c4SPetr Machata 
84646a3615bSNogah Frankel static int
8477bec1a45SPetr Machata mlxsw_sp_qdisc_fifo_check_params(struct mlxsw_sp_port *mlxsw_sp_port,
8487bec1a45SPetr Machata 				 void *params)
8497bec1a45SPetr Machata {
8507bec1a45SPetr Machata 	return 0;
8517bec1a45SPetr Machata }
8527bec1a45SPetr Machata 
8537bec1a45SPetr Machata static int
8547bec1a45SPetr Machata mlxsw_sp_qdisc_fifo_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
8557bec1a45SPetr Machata 			    struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
8567bec1a45SPetr Machata 			    void *params)
8577bec1a45SPetr Machata {
8587bec1a45SPetr Machata 	return 0;
8597bec1a45SPetr Machata }
8607bec1a45SPetr Machata 
8617bec1a45SPetr Machata static int
8627bec1a45SPetr Machata mlxsw_sp_qdisc_get_fifo_stats(struct mlxsw_sp_port *mlxsw_sp_port,
8637bec1a45SPetr Machata 			      struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
8647bec1a45SPetr Machata 			      struct tc_qopt_offload_stats *stats_ptr)
8657bec1a45SPetr Machata {
8667bec1a45SPetr Machata 	mlxsw_sp_qdisc_get_tc_stats(mlxsw_sp_port, mlxsw_sp_qdisc,
8677bec1a45SPetr Machata 				    stats_ptr);
8687bec1a45SPetr Machata 	return 0;
8697bec1a45SPetr Machata }
8707bec1a45SPetr Machata 
8717bec1a45SPetr Machata static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_fifo = {
8727bec1a45SPetr Machata 	.type = MLXSW_SP_QDISC_FIFO,
8737bec1a45SPetr Machata 	.check_params = mlxsw_sp_qdisc_fifo_check_params,
8747bec1a45SPetr Machata 	.replace = mlxsw_sp_qdisc_fifo_replace,
8757bec1a45SPetr Machata 	.get_stats = mlxsw_sp_qdisc_get_fifo_stats,
8767bec1a45SPetr Machata 	.clean_stats = mlxsw_sp_setup_tc_qdisc_leaf_clean_stats,
8777bec1a45SPetr Machata };
8787bec1a45SPetr Machata 
8797bec1a45SPetr Machata int mlxsw_sp_setup_tc_fifo(struct mlxsw_sp_port *mlxsw_sp_port,
8807bec1a45SPetr Machata 			   struct tc_fifo_qopt_offload *p)
8817bec1a45SPetr Machata {
8827bec1a45SPetr Machata 	struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc;
8837bec1a45SPetr Machata 	struct mlxsw_sp_qdisc *mlxsw_sp_qdisc;
8847bec1a45SPetr Machata 	int tclass, child_index;
8857bec1a45SPetr Machata 	u32 parent_handle;
8867bec1a45SPetr Machata 
8877bec1a45SPetr Machata 	/* Invisible FIFOs are tracked in future_handle and future_fifos. Make
8887bec1a45SPetr Machata 	 * sure that not more than one qdisc is created for a port at a time.
8897bec1a45SPetr Machata 	 * RTNL is a simple proxy for that.
8907bec1a45SPetr Machata 	 */
8917bec1a45SPetr Machata 	ASSERT_RTNL();
8927bec1a45SPetr Machata 
8937bec1a45SPetr Machata 	mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent, false);
8947bec1a45SPetr Machata 	if (!mlxsw_sp_qdisc && p->handle == TC_H_UNSPEC) {
8957bec1a45SPetr Machata 		parent_handle = TC_H_MAJ(p->parent);
8967bec1a45SPetr Machata 		if (parent_handle != qdisc_state->future_handle) {
8977bec1a45SPetr Machata 			/* This notifications is for a different Qdisc than
8987bec1a45SPetr Machata 			 * previously. Wipe the future cache.
8997bec1a45SPetr Machata 			 */
9007bec1a45SPetr Machata 			memset(qdisc_state->future_fifos, 0,
9017bec1a45SPetr Machata 			       sizeof(qdisc_state->future_fifos));
9027bec1a45SPetr Machata 			qdisc_state->future_handle = parent_handle;
9037bec1a45SPetr Machata 		}
9047bec1a45SPetr Machata 
9057bec1a45SPetr Machata 		child_index = TC_H_MIN(p->parent);
9067bec1a45SPetr Machata 		tclass = MLXSW_SP_PRIO_CHILD_TO_TCLASS(child_index);
9077bec1a45SPetr Machata 		if (tclass < IEEE_8021QAZ_MAX_TCS) {
9087bec1a45SPetr Machata 			if (p->command == TC_FIFO_REPLACE)
9097bec1a45SPetr Machata 				qdisc_state->future_fifos[tclass] = true;
9107bec1a45SPetr Machata 			else if (p->command == TC_FIFO_DESTROY)
9117bec1a45SPetr Machata 				qdisc_state->future_fifos[tclass] = false;
9127bec1a45SPetr Machata 		}
9137bec1a45SPetr Machata 	}
9147bec1a45SPetr Machata 	if (!mlxsw_sp_qdisc)
9157bec1a45SPetr Machata 		return -EOPNOTSUPP;
9167bec1a45SPetr Machata 
9177bec1a45SPetr Machata 	if (p->command == TC_FIFO_REPLACE) {
9187bec1a45SPetr Machata 		return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle,
9197bec1a45SPetr Machata 					      mlxsw_sp_qdisc,
9207bec1a45SPetr Machata 					      &mlxsw_sp_qdisc_ops_fifo, NULL);
9217bec1a45SPetr Machata 	}
9227bec1a45SPetr Machata 
923290fe2c5SPetr Machata 	if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle))
9247bec1a45SPetr Machata 		return -EOPNOTSUPP;
9257bec1a45SPetr Machata 
9267bec1a45SPetr Machata 	switch (p->command) {
9277bec1a45SPetr Machata 	case TC_FIFO_DESTROY:
928549f2aaeSPetr Machata 		return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
9297bec1a45SPetr Machata 	case TC_FIFO_STATS:
9307bec1a45SPetr Machata 		return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc,
9317bec1a45SPetr Machata 						&p->stats);
9327bec1a45SPetr Machata 	case TC_FIFO_REPLACE: /* Handled above. */
9337bec1a45SPetr Machata 		break;
9347bec1a45SPetr Machata 	}
9357bec1a45SPetr Machata 
9367bec1a45SPetr Machata 	return -EOPNOTSUPP;
9377bec1a45SPetr Machata }
9387bec1a45SPetr Machata 
939*51d52ed9SPetr Machata static int __mlxsw_sp_qdisc_ets_destroy(struct mlxsw_sp_port *mlxsw_sp_port,
940*51d52ed9SPetr Machata 					struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
94146a3615bSNogah Frankel {
94246a3615bSNogah Frankel 	int i;
94346a3615bSNogah Frankel 
944*51d52ed9SPetr Machata 	for (i = 0; i < mlxsw_sp_qdisc->num_classes; i++) {
94546a3615bSNogah Frankel 		mlxsw_sp_port_prio_tc_set(mlxsw_sp_port, i,
94646a3615bSNogah Frankel 					  MLXSW_SP_PORT_DEFAULT_TCLASS);
9477917f52aSPetr Machata 		mlxsw_sp_port_ets_set(mlxsw_sp_port,
9487917f52aSPetr Machata 				      MLXSW_REG_QEEC_HR_SUBGROUP,
9497917f52aSPetr Machata 				      i, 0, false, 0);
950eed4baebSNogah Frankel 		mlxsw_sp_qdisc_destroy(mlxsw_sp_port,
951*51d52ed9SPetr Machata 				       &mlxsw_sp_qdisc->qdiscs[i]);
952*51d52ed9SPetr Machata 		mlxsw_sp_qdisc->qdiscs[i].prio_bitmap = 0;
953eed4baebSNogah Frankel 	}
95446a3615bSNogah Frankel 
95546a3615bSNogah Frankel 	return 0;
95646a3615bSNogah Frankel }
95746a3615bSNogah Frankel 
95846a3615bSNogah Frankel static int
9597917f52aSPetr Machata mlxsw_sp_qdisc_prio_destroy(struct mlxsw_sp_port *mlxsw_sp_port,
9607917f52aSPetr Machata 			    struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
9617917f52aSPetr Machata {
962*51d52ed9SPetr Machata 	return __mlxsw_sp_qdisc_ets_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
9637917f52aSPetr Machata }
9647917f52aSPetr Machata 
9657917f52aSPetr Machata static int
9667917f52aSPetr Machata __mlxsw_sp_qdisc_ets_check_params(unsigned int nbands)
9677917f52aSPetr Machata {
9687917f52aSPetr Machata 	if (nbands > IEEE_8021QAZ_MAX_TCS)
9697917f52aSPetr Machata 		return -EOPNOTSUPP;
9707917f52aSPetr Machata 
9717917f52aSPetr Machata 	return 0;
9727917f52aSPetr Machata }
9737917f52aSPetr Machata 
9747917f52aSPetr Machata static int
97546a3615bSNogah Frankel mlxsw_sp_qdisc_prio_check_params(struct mlxsw_sp_port *mlxsw_sp_port,
97646a3615bSNogah Frankel 				 void *params)
97746a3615bSNogah Frankel {
97846a3615bSNogah Frankel 	struct tc_prio_qopt_offload_params *p = params;
97946a3615bSNogah Frankel 
9807917f52aSPetr Machata 	return __mlxsw_sp_qdisc_ets_check_params(p->bands);
98146a3615bSNogah Frankel }
98246a3615bSNogah Frankel 
98346a3615bSNogah Frankel static int
984*51d52ed9SPetr Machata __mlxsw_sp_qdisc_ets_replace(struct mlxsw_sp_port *mlxsw_sp_port,
985*51d52ed9SPetr Machata 			     struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
986*51d52ed9SPetr Machata 			     u32 handle, unsigned int nbands,
9877917f52aSPetr Machata 			     const unsigned int *quanta,
9887917f52aSPetr Machata 			     const unsigned int *weights,
9897917f52aSPetr Machata 			     const u8 *priomap)
99046a3615bSNogah Frankel {
991ee88450dSPetr Machata 	struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc;
99204cc0bf5SNogah Frankel 	struct mlxsw_sp_qdisc *child_qdisc;
993cc6e5c13SNogah Frankel 	int tclass, i, band, backlog;
99404cc0bf5SNogah Frankel 	u8 old_priomap;
99546a3615bSNogah Frankel 	int err;
99646a3615bSNogah Frankel 
9977917f52aSPetr Machata 	for (band = 0; band < nbands; band++) {
99804cc0bf5SNogah Frankel 		tclass = MLXSW_SP_PRIO_BAND_TO_TCLASS(band);
999*51d52ed9SPetr Machata 		child_qdisc = &mlxsw_sp_qdisc->qdiscs[band];
100004cc0bf5SNogah Frankel 		old_priomap = child_qdisc->prio_bitmap;
100104cc0bf5SNogah Frankel 		child_qdisc->prio_bitmap = 0;
10027917f52aSPetr Machata 
10037917f52aSPetr Machata 		err = mlxsw_sp_port_ets_set(mlxsw_sp_port,
10047917f52aSPetr Machata 					    MLXSW_REG_QEEC_HR_SUBGROUP,
10057917f52aSPetr Machata 					    tclass, 0, !!quanta[band],
10067917f52aSPetr Machata 					    weights[band]);
10077917f52aSPetr Machata 		if (err)
10087917f52aSPetr Machata 			return err;
10097917f52aSPetr Machata 
101046a3615bSNogah Frankel 		for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
10117917f52aSPetr Machata 			if (priomap[i] == band) {
101204cc0bf5SNogah Frankel 				child_qdisc->prio_bitmap |= BIT(i);
101304cc0bf5SNogah Frankel 				if (BIT(i) & old_priomap)
101404cc0bf5SNogah Frankel 					continue;
101504cc0bf5SNogah Frankel 				err = mlxsw_sp_port_prio_tc_set(mlxsw_sp_port,
101604cc0bf5SNogah Frankel 								i, tclass);
101746a3615bSNogah Frankel 				if (err)
101846a3615bSNogah Frankel 					return err;
101904cc0bf5SNogah Frankel 			}
102004cc0bf5SNogah Frankel 		}
102104cc0bf5SNogah Frankel 		if (old_priomap != child_qdisc->prio_bitmap &&
1022cc6e5c13SNogah Frankel 		    child_qdisc->ops && child_qdisc->ops->clean_stats) {
1023cc6e5c13SNogah Frankel 			backlog = child_qdisc->stats_base.backlog;
102404cc0bf5SNogah Frankel 			child_qdisc->ops->clean_stats(mlxsw_sp_port,
102504cc0bf5SNogah Frankel 						      child_qdisc);
1026cc6e5c13SNogah Frankel 			child_qdisc->stats_base.backlog = backlog;
1027cc6e5c13SNogah Frankel 		}
10287bec1a45SPetr Machata 
10297bec1a45SPetr Machata 		if (handle == qdisc_state->future_handle &&
10307bec1a45SPetr Machata 		    qdisc_state->future_fifos[tclass]) {
10317bec1a45SPetr Machata 			err = mlxsw_sp_qdisc_replace(mlxsw_sp_port, TC_H_UNSPEC,
10327bec1a45SPetr Machata 						     child_qdisc,
10337bec1a45SPetr Machata 						     &mlxsw_sp_qdisc_ops_fifo,
10347bec1a45SPetr Machata 						     NULL);
10357bec1a45SPetr Machata 			if (err)
10367bec1a45SPetr Machata 				return err;
10377bec1a45SPetr Machata 		}
103846a3615bSNogah Frankel 	}
103998ceb7b6SNogah Frankel 	for (; band < IEEE_8021QAZ_MAX_TCS; band++) {
104098ceb7b6SNogah Frankel 		tclass = MLXSW_SP_PRIO_BAND_TO_TCLASS(band);
1041*51d52ed9SPetr Machata 		child_qdisc = &mlxsw_sp_qdisc->qdiscs[band];
104298ceb7b6SNogah Frankel 		child_qdisc->prio_bitmap = 0;
104398ceb7b6SNogah Frankel 		mlxsw_sp_qdisc_destroy(mlxsw_sp_port, child_qdisc);
10447917f52aSPetr Machata 		mlxsw_sp_port_ets_set(mlxsw_sp_port,
10457917f52aSPetr Machata 				      MLXSW_REG_QEEC_HR_SUBGROUP,
10467917f52aSPetr Machata 				      tclass, 0, false, 0);
104798ceb7b6SNogah Frankel 	}
10487bec1a45SPetr Machata 
10497bec1a45SPetr Machata 	qdisc_state->future_handle = TC_H_UNSPEC;
10507bec1a45SPetr Machata 	memset(qdisc_state->future_fifos, 0, sizeof(qdisc_state->future_fifos));
105146a3615bSNogah Frankel 	return 0;
105246a3615bSNogah Frankel }
105346a3615bSNogah Frankel 
10547917f52aSPetr Machata static int
1055c4e372e2SPetr Machata mlxsw_sp_qdisc_prio_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
10567917f52aSPetr Machata 			    struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
10577917f52aSPetr Machata 			    void *params)
10587917f52aSPetr Machata {
10597917f52aSPetr Machata 	struct tc_prio_qopt_offload_params *p = params;
10607917f52aSPetr Machata 	unsigned int zeroes[TCQ_ETS_MAX_BANDS] = {0};
10617917f52aSPetr Machata 
1062*51d52ed9SPetr Machata 	return __mlxsw_sp_qdisc_ets_replace(mlxsw_sp_port, mlxsw_sp_qdisc,
1063*51d52ed9SPetr Machata 					    handle, p->bands, zeroes,
1064*51d52ed9SPetr Machata 					    zeroes, p->priomap);
10657917f52aSPetr Machata }
10667917f52aSPetr Machata 
10677917f52aSPetr Machata static void
10687917f52aSPetr Machata __mlxsw_sp_qdisc_ets_unoffload(struct mlxsw_sp_port *mlxsw_sp_port,
10697917f52aSPetr Machata 			       struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
10707917f52aSPetr Machata 			       struct gnet_stats_queue *qstats)
10717917f52aSPetr Machata {
10727917f52aSPetr Machata 	u64 backlog;
10737917f52aSPetr Machata 
10747917f52aSPetr Machata 	backlog = mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp,
10757917f52aSPetr Machata 				       mlxsw_sp_qdisc->stats_base.backlog);
10767917f52aSPetr Machata 	qstats->backlog -= backlog;
10777917f52aSPetr Machata }
10787917f52aSPetr Machata 
1079e02f08a0SWei Yongjun static void
108093d8a4c1SNogah Frankel mlxsw_sp_qdisc_prio_unoffload(struct mlxsw_sp_port *mlxsw_sp_port,
108193d8a4c1SNogah Frankel 			      struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
108293d8a4c1SNogah Frankel 			      void *params)
108393d8a4c1SNogah Frankel {
108493d8a4c1SNogah Frankel 	struct tc_prio_qopt_offload_params *p = params;
108593d8a4c1SNogah Frankel 
10867917f52aSPetr Machata 	__mlxsw_sp_qdisc_ets_unoffload(mlxsw_sp_port, mlxsw_sp_qdisc,
10877917f52aSPetr Machata 				       p->qstats);
108893d8a4c1SNogah Frankel }
108993d8a4c1SNogah Frankel 
109093d8a4c1SNogah Frankel static int
109193d8a4c1SNogah Frankel mlxsw_sp_qdisc_get_prio_stats(struct mlxsw_sp_port *mlxsw_sp_port,
109293d8a4c1SNogah Frankel 			      struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
109393d8a4c1SNogah Frankel 			      struct tc_qopt_offload_stats *stats_ptr)
109493d8a4c1SNogah Frankel {
1095cf9af379SPetr Machata 	struct mlxsw_sp_qdisc *tc_qdisc;
1096cf9af379SPetr Machata 	u64 tx_packets = 0;
1097cf9af379SPetr Machata 	u64 tx_bytes = 0;
1098cf9af379SPetr Machata 	u64 backlog = 0;
1099cf9af379SPetr Machata 	u64 drops = 0;
110093d8a4c1SNogah Frankel 	int i;
110193d8a4c1SNogah Frankel 
1102*51d52ed9SPetr Machata 	for (i = 0; i < mlxsw_sp_qdisc->num_classes; i++) {
1103*51d52ed9SPetr Machata 		tc_qdisc = &mlxsw_sp_qdisc->qdiscs[i];
1104cf9af379SPetr Machata 		mlxsw_sp_qdisc_collect_tc_stats(mlxsw_sp_port, tc_qdisc,
1105cf9af379SPetr Machata 						&tx_bytes, &tx_packets,
1106cf9af379SPetr Machata 						&drops, &backlog);
110793d8a4c1SNogah Frankel 	}
110893d8a4c1SNogah Frankel 
1109cf9af379SPetr Machata 	mlxsw_sp_qdisc_update_stats(mlxsw_sp_port->mlxsw_sp, mlxsw_sp_qdisc,
1110cf9af379SPetr Machata 				    tx_bytes, tx_packets, drops, backlog,
1111cf9af379SPetr Machata 				    stats_ptr);
111293d8a4c1SNogah Frankel 	return 0;
111393d8a4c1SNogah Frankel }
111493d8a4c1SNogah Frankel 
111593d8a4c1SNogah Frankel static void
111693d8a4c1SNogah Frankel mlxsw_sp_setup_tc_qdisc_prio_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port,
111793d8a4c1SNogah Frankel 					 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
111893d8a4c1SNogah Frankel {
111993d8a4c1SNogah Frankel 	struct mlxsw_sp_qdisc_stats *stats_base;
112093d8a4c1SNogah Frankel 	struct mlxsw_sp_port_xstats *xstats;
112193d8a4c1SNogah Frankel 	struct rtnl_link_stats64 *stats;
112293d8a4c1SNogah Frankel 	int i;
112393d8a4c1SNogah Frankel 
112493d8a4c1SNogah Frankel 	xstats = &mlxsw_sp_port->periodic_hw_stats.xstats;
112593d8a4c1SNogah Frankel 	stats = &mlxsw_sp_port->periodic_hw_stats.stats;
112693d8a4c1SNogah Frankel 	stats_base = &mlxsw_sp_qdisc->stats_base;
112793d8a4c1SNogah Frankel 
112893d8a4c1SNogah Frankel 	stats_base->tx_packets = stats->tx_packets;
112993d8a4c1SNogah Frankel 	stats_base->tx_bytes = stats->tx_bytes;
113093d8a4c1SNogah Frankel 
113193d8a4c1SNogah Frankel 	stats_base->drops = 0;
113223f2b404SNogah Frankel 	for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
113385005b82SPetr Machata 		stats_base->drops += mlxsw_sp_xstats_tail_drop(xstats, i);
113423f2b404SNogah Frankel 		stats_base->drops += xstats->wred_drop[i];
113523f2b404SNogah Frankel 	}
113693d8a4c1SNogah Frankel 
113793d8a4c1SNogah Frankel 	mlxsw_sp_qdisc->stats_base.backlog = 0;
113893d8a4c1SNogah Frankel }
113993d8a4c1SNogah Frankel 
1140*51d52ed9SPetr Machata static struct mlxsw_sp_qdisc *
1141*51d52ed9SPetr Machata mlxsw_sp_qdisc_prio_find_class(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
1142*51d52ed9SPetr Machata 			       u32 parent)
1143*51d52ed9SPetr Machata {
1144*51d52ed9SPetr Machata 	int child_index = TC_H_MIN(parent);
1145*51d52ed9SPetr Machata 	int band = child_index - 1;
1146*51d52ed9SPetr Machata 
1147*51d52ed9SPetr Machata 	if (band < 0 || band >= mlxsw_sp_qdisc->num_classes)
1148*51d52ed9SPetr Machata 		return NULL;
1149*51d52ed9SPetr Machata 	return &mlxsw_sp_qdisc->qdiscs[band];
1150*51d52ed9SPetr Machata }
1151*51d52ed9SPetr Machata 
115246a3615bSNogah Frankel static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_prio = {
115346a3615bSNogah Frankel 	.type = MLXSW_SP_QDISC_PRIO,
115446a3615bSNogah Frankel 	.check_params = mlxsw_sp_qdisc_prio_check_params,
115546a3615bSNogah Frankel 	.replace = mlxsw_sp_qdisc_prio_replace,
115693d8a4c1SNogah Frankel 	.unoffload = mlxsw_sp_qdisc_prio_unoffload,
115746a3615bSNogah Frankel 	.destroy = mlxsw_sp_qdisc_prio_destroy,
115893d8a4c1SNogah Frankel 	.get_stats = mlxsw_sp_qdisc_get_prio_stats,
115993d8a4c1SNogah Frankel 	.clean_stats = mlxsw_sp_setup_tc_qdisc_prio_clean_stats,
1160*51d52ed9SPetr Machata 	.find_class = mlxsw_sp_qdisc_prio_find_class,
116146a3615bSNogah Frankel };
116246a3615bSNogah Frankel 
116319f405b9SPetr Machata static int
116419f405b9SPetr Machata mlxsw_sp_qdisc_ets_check_params(struct mlxsw_sp_port *mlxsw_sp_port,
116519f405b9SPetr Machata 				void *params)
116619f405b9SPetr Machata {
116719f405b9SPetr Machata 	struct tc_ets_qopt_offload_replace_params *p = params;
116819f405b9SPetr Machata 
116919f405b9SPetr Machata 	return __mlxsw_sp_qdisc_ets_check_params(p->bands);
117019f405b9SPetr Machata }
117119f405b9SPetr Machata 
117219f405b9SPetr Machata static int
1173c4e372e2SPetr Machata mlxsw_sp_qdisc_ets_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
117419f405b9SPetr Machata 			   struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
117519f405b9SPetr Machata 			   void *params)
117619f405b9SPetr Machata {
117719f405b9SPetr Machata 	struct tc_ets_qopt_offload_replace_params *p = params;
117819f405b9SPetr Machata 
1179*51d52ed9SPetr Machata 	return __mlxsw_sp_qdisc_ets_replace(mlxsw_sp_port, mlxsw_sp_qdisc,
1180*51d52ed9SPetr Machata 					    handle, p->bands, p->quanta,
1181*51d52ed9SPetr Machata 					    p->weights, p->priomap);
118219f405b9SPetr Machata }
118319f405b9SPetr Machata 
118419f405b9SPetr Machata static void
118519f405b9SPetr Machata mlxsw_sp_qdisc_ets_unoffload(struct mlxsw_sp_port *mlxsw_sp_port,
118619f405b9SPetr Machata 			     struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
118719f405b9SPetr Machata 			     void *params)
118819f405b9SPetr Machata {
118919f405b9SPetr Machata 	struct tc_ets_qopt_offload_replace_params *p = params;
119019f405b9SPetr Machata 
119119f405b9SPetr Machata 	__mlxsw_sp_qdisc_ets_unoffload(mlxsw_sp_port, mlxsw_sp_qdisc,
119219f405b9SPetr Machata 				       p->qstats);
119319f405b9SPetr Machata }
119419f405b9SPetr Machata 
119519f405b9SPetr Machata static int
119619f405b9SPetr Machata mlxsw_sp_qdisc_ets_destroy(struct mlxsw_sp_port *mlxsw_sp_port,
119719f405b9SPetr Machata 			   struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
119819f405b9SPetr Machata {
1199*51d52ed9SPetr Machata 	return __mlxsw_sp_qdisc_ets_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
120019f405b9SPetr Machata }
120119f405b9SPetr Machata 
120219f405b9SPetr Machata static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_ets = {
120319f405b9SPetr Machata 	.type = MLXSW_SP_QDISC_ETS,
120419f405b9SPetr Machata 	.check_params = mlxsw_sp_qdisc_ets_check_params,
120519f405b9SPetr Machata 	.replace = mlxsw_sp_qdisc_ets_replace,
120619f405b9SPetr Machata 	.unoffload = mlxsw_sp_qdisc_ets_unoffload,
120719f405b9SPetr Machata 	.destroy = mlxsw_sp_qdisc_ets_destroy,
120819f405b9SPetr Machata 	.get_stats = mlxsw_sp_qdisc_get_prio_stats,
120919f405b9SPetr Machata 	.clean_stats = mlxsw_sp_setup_tc_qdisc_prio_clean_stats,
1210*51d52ed9SPetr Machata 	.find_class = mlxsw_sp_qdisc_prio_find_class,
121119f405b9SPetr Machata };
121219f405b9SPetr Machata 
12135bc146c9SPetr Machata /* Linux allows linking of Qdiscs to arbitrary classes (so long as the resulting
12145bc146c9SPetr Machata  * graph is free of cycles). These operations do not change the parent handle
12155bc146c9SPetr Machata  * though, which means it can be incomplete (if there is more than one class
12165bc146c9SPetr Machata  * where the Qdisc in question is grafted) or outright wrong (if the Qdisc was
12175bc146c9SPetr Machata  * linked to a different class and then removed from the original class).
12185bc146c9SPetr Machata  *
12195bc146c9SPetr Machata  * E.g. consider this sequence of operations:
12205bc146c9SPetr Machata  *
12215bc146c9SPetr Machata  *  # tc qdisc add dev swp1 root handle 1: prio
12225bc146c9SPetr Machata  *  # tc qdisc add dev swp1 parent 1:3 handle 13: red limit 1000000 avpkt 10000
12235bc146c9SPetr Machata  *  RED: set bandwidth to 10Mbit
12245bc146c9SPetr Machata  *  # tc qdisc link dev swp1 handle 13: parent 1:2
12255bc146c9SPetr Machata  *
12265bc146c9SPetr Machata  * At this point, both 1:2 and 1:3 have the same RED Qdisc instance as their
12275bc146c9SPetr Machata  * child. But RED will still only claim that 1:3 is its parent. If it's removed
12285bc146c9SPetr Machata  * from that band, its only parent will be 1:2, but it will continue to claim
12295bc146c9SPetr Machata  * that it is in fact 1:3.
12305bc146c9SPetr Machata  *
12315bc146c9SPetr Machata  * The notification for child Qdisc replace (e.g. TC_RED_REPLACE) comes before
12325bc146c9SPetr Machata  * the notification for parent graft (e.g. TC_PRIO_GRAFT). We take the replace
12335bc146c9SPetr Machata  * notification to offload the child Qdisc, based on its parent handle, and use
12345bc146c9SPetr Machata  * the graft operation to validate that the class where the child is actually
12355bc146c9SPetr Machata  * grafted corresponds to the parent handle. If the two don't match, we
12365bc146c9SPetr Machata  * unoffload the child.
123732dc5efcSNogah Frankel  */
123832dc5efcSNogah Frankel static int
12397917f52aSPetr Machata __mlxsw_sp_qdisc_ets_graft(struct mlxsw_sp_port *mlxsw_sp_port,
124032dc5efcSNogah Frankel 			   struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
12417917f52aSPetr Machata 			   u8 band, u32 child_handle)
124232dc5efcSNogah Frankel {
124332dc5efcSNogah Frankel 	struct mlxsw_sp_qdisc *old_qdisc;
124432dc5efcSNogah Frankel 
1245*51d52ed9SPetr Machata 	if (band < mlxsw_sp_qdisc->num_classes &&
1246*51d52ed9SPetr Machata 	    mlxsw_sp_qdisc->qdiscs[band].handle == child_handle)
124732dc5efcSNogah Frankel 		return 0;
124832dc5efcSNogah Frankel 
1249a2d6d7aeSDavid S. Miller 	if (!child_handle) {
12503971a535SPetr Machata 		/* This is an invisible FIFO replacing the original Qdisc.
12513971a535SPetr Machata 		 * Ignore it--the original Qdisc's destroy will follow.
12523971a535SPetr Machata 		 */
12533971a535SPetr Machata 		return 0;
12543971a535SPetr Machata 	}
12553971a535SPetr Machata 
125632dc5efcSNogah Frankel 	/* See if the grafted qdisc is already offloaded on any tclass. If so,
125732dc5efcSNogah Frankel 	 * unoffload it.
125832dc5efcSNogah Frankel 	 */
125932dc5efcSNogah Frankel 	old_qdisc = mlxsw_sp_qdisc_find_by_handle(mlxsw_sp_port,
12607917f52aSPetr Machata 						  child_handle);
126132dc5efcSNogah Frankel 	if (old_qdisc)
126232dc5efcSNogah Frankel 		mlxsw_sp_qdisc_destroy(mlxsw_sp_port, old_qdisc);
126332dc5efcSNogah Frankel 
1264*51d52ed9SPetr Machata 	mlxsw_sp_qdisc = mlxsw_sp_qdisc->ops->find_class(mlxsw_sp_qdisc, band);
1265*51d52ed9SPetr Machata 	if (!WARN_ON(!mlxsw_sp_qdisc))
1266*51d52ed9SPetr Machata 		mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
1267*51d52ed9SPetr Machata 
126832dc5efcSNogah Frankel 	return -EOPNOTSUPP;
126932dc5efcSNogah Frankel }
127032dc5efcSNogah Frankel 
12717917f52aSPetr Machata static int
12727917f52aSPetr Machata mlxsw_sp_qdisc_prio_graft(struct mlxsw_sp_port *mlxsw_sp_port,
12737917f52aSPetr Machata 			  struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
12747917f52aSPetr Machata 			  struct tc_prio_qopt_offload_graft_params *p)
12757917f52aSPetr Machata {
12767917f52aSPetr Machata 	return __mlxsw_sp_qdisc_ets_graft(mlxsw_sp_port, mlxsw_sp_qdisc,
12777917f52aSPetr Machata 					  p->band, p->child_handle);
12787917f52aSPetr Machata }
12797917f52aSPetr Machata 
128046a3615bSNogah Frankel int mlxsw_sp_setup_tc_prio(struct mlxsw_sp_port *mlxsw_sp_port,
128146a3615bSNogah Frankel 			   struct tc_prio_qopt_offload *p)
128246a3615bSNogah Frankel {
128346a3615bSNogah Frankel 	struct mlxsw_sp_qdisc *mlxsw_sp_qdisc;
128446a3615bSNogah Frankel 
1285eed4baebSNogah Frankel 	mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent, true);
1286eed4baebSNogah Frankel 	if (!mlxsw_sp_qdisc)
128746a3615bSNogah Frankel 		return -EOPNOTSUPP;
128846a3615bSNogah Frankel 
128946a3615bSNogah Frankel 	if (p->command == TC_PRIO_REPLACE)
129046a3615bSNogah Frankel 		return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle,
129146a3615bSNogah Frankel 					      mlxsw_sp_qdisc,
129246a3615bSNogah Frankel 					      &mlxsw_sp_qdisc_ops_prio,
129346a3615bSNogah Frankel 					      &p->replace_params);
129446a3615bSNogah Frankel 
1295290fe2c5SPetr Machata 	if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle))
129646a3615bSNogah Frankel 		return -EOPNOTSUPP;
129746a3615bSNogah Frankel 
129846a3615bSNogah Frankel 	switch (p->command) {
129946a3615bSNogah Frankel 	case TC_PRIO_DESTROY:
130046a3615bSNogah Frankel 		return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
130193d8a4c1SNogah Frankel 	case TC_PRIO_STATS:
130293d8a4c1SNogah Frankel 		return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc,
130393d8a4c1SNogah Frankel 						&p->stats);
130432dc5efcSNogah Frankel 	case TC_PRIO_GRAFT:
130532dc5efcSNogah Frankel 		return mlxsw_sp_qdisc_prio_graft(mlxsw_sp_port, mlxsw_sp_qdisc,
130632dc5efcSNogah Frankel 						 &p->graft_params);
130746a3615bSNogah Frankel 	default:
130846a3615bSNogah Frankel 		return -EOPNOTSUPP;
130946a3615bSNogah Frankel 	}
131046a3615bSNogah Frankel }
131146a3615bSNogah Frankel 
131219f405b9SPetr Machata int mlxsw_sp_setup_tc_ets(struct mlxsw_sp_port *mlxsw_sp_port,
131319f405b9SPetr Machata 			  struct tc_ets_qopt_offload *p)
131419f405b9SPetr Machata {
131519f405b9SPetr Machata 	struct mlxsw_sp_qdisc *mlxsw_sp_qdisc;
131619f405b9SPetr Machata 
131719f405b9SPetr Machata 	mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent, true);
131819f405b9SPetr Machata 	if (!mlxsw_sp_qdisc)
131919f405b9SPetr Machata 		return -EOPNOTSUPP;
132019f405b9SPetr Machata 
132119f405b9SPetr Machata 	if (p->command == TC_ETS_REPLACE)
132219f405b9SPetr Machata 		return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle,
132319f405b9SPetr Machata 					      mlxsw_sp_qdisc,
132419f405b9SPetr Machata 					      &mlxsw_sp_qdisc_ops_ets,
132519f405b9SPetr Machata 					      &p->replace_params);
132619f405b9SPetr Machata 
1327290fe2c5SPetr Machata 	if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle))
132819f405b9SPetr Machata 		return -EOPNOTSUPP;
132919f405b9SPetr Machata 
133019f405b9SPetr Machata 	switch (p->command) {
133119f405b9SPetr Machata 	case TC_ETS_DESTROY:
133219f405b9SPetr Machata 		return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
133319f405b9SPetr Machata 	case TC_ETS_STATS:
133419f405b9SPetr Machata 		return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc,
133519f405b9SPetr Machata 						&p->stats);
133619f405b9SPetr Machata 	case TC_ETS_GRAFT:
133719f405b9SPetr Machata 		return __mlxsw_sp_qdisc_ets_graft(mlxsw_sp_port, mlxsw_sp_qdisc,
133819f405b9SPetr Machata 						  p->graft_params.band,
133919f405b9SPetr Machata 						  p->graft_params.child_handle);
134019f405b9SPetr Machata 	default:
134119f405b9SPetr Machata 		return -EOPNOTSUPP;
134219f405b9SPetr Machata 	}
134319f405b9SPetr Machata }
134419f405b9SPetr Machata 
1345f6668eacSPetr Machata struct mlxsw_sp_qevent_block {
1346f6668eacSPetr Machata 	struct list_head binding_list;
1347f6668eacSPetr Machata 	struct list_head mall_entry_list;
1348f6668eacSPetr Machata 	struct mlxsw_sp *mlxsw_sp;
1349f6668eacSPetr Machata };
1350f6668eacSPetr Machata 
1351f6668eacSPetr Machata struct mlxsw_sp_qevent_binding {
1352f6668eacSPetr Machata 	struct list_head list;
1353f6668eacSPetr Machata 	struct mlxsw_sp_port *mlxsw_sp_port;
1354f6668eacSPetr Machata 	u32 handle;
1355f6668eacSPetr Machata 	int tclass_num;
1356f6668eacSPetr Machata 	enum mlxsw_sp_span_trigger span_trigger;
1357f6668eacSPetr Machata };
1358f6668eacSPetr Machata 
1359f6668eacSPetr Machata static LIST_HEAD(mlxsw_sp_qevent_block_cb_list);
1360f6668eacSPetr Machata 
136154a92385SPetr Machata static int mlxsw_sp_qevent_span_configure(struct mlxsw_sp *mlxsw_sp,
1362f6668eacSPetr Machata 					  struct mlxsw_sp_mall_entry *mall_entry,
136354a92385SPetr Machata 					  struct mlxsw_sp_qevent_binding *qevent_binding,
136454a92385SPetr Machata 					  const struct mlxsw_sp_span_agent_parms *agent_parms,
136554a92385SPetr Machata 					  int *p_span_id)
1366f6668eacSPetr Machata {
1367f6668eacSPetr Machata 	struct mlxsw_sp_port *mlxsw_sp_port = qevent_binding->mlxsw_sp_port;
1368f6668eacSPetr Machata 	struct mlxsw_sp_span_trigger_parms trigger_parms = {};
1369f6668eacSPetr Machata 	int span_id;
1370f6668eacSPetr Machata 	int err;
1371f6668eacSPetr Machata 
137254a92385SPetr Machata 	err = mlxsw_sp_span_agent_get(mlxsw_sp, &span_id, agent_parms);
1373f6668eacSPetr Machata 	if (err)
1374f6668eacSPetr Machata 		return err;
1375f6668eacSPetr Machata 
1376f6668eacSPetr Machata 	err = mlxsw_sp_span_analyzed_port_get(mlxsw_sp_port, true);
1377f6668eacSPetr Machata 	if (err)
1378f6668eacSPetr Machata 		goto err_analyzed_port_get;
1379f6668eacSPetr Machata 
1380f6668eacSPetr Machata 	trigger_parms.span_id = span_id;
13812dcbd920SIdo Schimmel 	trigger_parms.probability_rate = 1;
1382f6668eacSPetr Machata 	err = mlxsw_sp_span_agent_bind(mlxsw_sp, qevent_binding->span_trigger, mlxsw_sp_port,
1383f6668eacSPetr Machata 				       &trigger_parms);
1384f6668eacSPetr Machata 	if (err)
1385f6668eacSPetr Machata 		goto err_agent_bind;
1386f6668eacSPetr Machata 
1387f6668eacSPetr Machata 	err = mlxsw_sp_span_trigger_enable(mlxsw_sp_port, qevent_binding->span_trigger,
1388f6668eacSPetr Machata 					   qevent_binding->tclass_num);
1389f6668eacSPetr Machata 	if (err)
1390f6668eacSPetr Machata 		goto err_trigger_enable;
1391f6668eacSPetr Machata 
139254a92385SPetr Machata 	*p_span_id = span_id;
1393f6668eacSPetr Machata 	return 0;
1394f6668eacSPetr Machata 
1395f6668eacSPetr Machata err_trigger_enable:
1396f6668eacSPetr Machata 	mlxsw_sp_span_agent_unbind(mlxsw_sp, qevent_binding->span_trigger, mlxsw_sp_port,
1397f6668eacSPetr Machata 				   &trigger_parms);
1398f6668eacSPetr Machata err_agent_bind:
1399f6668eacSPetr Machata 	mlxsw_sp_span_analyzed_port_put(mlxsw_sp_port, true);
1400f6668eacSPetr Machata err_analyzed_port_get:
1401f6668eacSPetr Machata 	mlxsw_sp_span_agent_put(mlxsw_sp, span_id);
1402f6668eacSPetr Machata 	return err;
1403f6668eacSPetr Machata }
1404f6668eacSPetr Machata 
140554a92385SPetr Machata static void mlxsw_sp_qevent_span_deconfigure(struct mlxsw_sp *mlxsw_sp,
140654a92385SPetr Machata 					     struct mlxsw_sp_qevent_binding *qevent_binding,
140754a92385SPetr Machata 					     int span_id)
1408f6668eacSPetr Machata {
1409f6668eacSPetr Machata 	struct mlxsw_sp_port *mlxsw_sp_port = qevent_binding->mlxsw_sp_port;
1410f6668eacSPetr Machata 	struct mlxsw_sp_span_trigger_parms trigger_parms = {
141154a92385SPetr Machata 		.span_id = span_id,
1412f6668eacSPetr Machata 	};
1413f6668eacSPetr Machata 
1414f6668eacSPetr Machata 	mlxsw_sp_span_trigger_disable(mlxsw_sp_port, qevent_binding->span_trigger,
1415f6668eacSPetr Machata 				      qevent_binding->tclass_num);
1416f6668eacSPetr Machata 	mlxsw_sp_span_agent_unbind(mlxsw_sp, qevent_binding->span_trigger, mlxsw_sp_port,
1417f6668eacSPetr Machata 				   &trigger_parms);
1418f6668eacSPetr Machata 	mlxsw_sp_span_analyzed_port_put(mlxsw_sp_port, true);
141954a92385SPetr Machata 	mlxsw_sp_span_agent_put(mlxsw_sp, span_id);
142054a92385SPetr Machata }
142154a92385SPetr Machata 
142254a92385SPetr Machata static int mlxsw_sp_qevent_mirror_configure(struct mlxsw_sp *mlxsw_sp,
142354a92385SPetr Machata 					    struct mlxsw_sp_mall_entry *mall_entry,
142454a92385SPetr Machata 					    struct mlxsw_sp_qevent_binding *qevent_binding)
142554a92385SPetr Machata {
142654a92385SPetr Machata 	struct mlxsw_sp_span_agent_parms agent_parms = {
142754a92385SPetr Machata 		.to_dev = mall_entry->mirror.to_dev,
142854a92385SPetr Machata 	};
142954a92385SPetr Machata 
143054a92385SPetr Machata 	return mlxsw_sp_qevent_span_configure(mlxsw_sp, mall_entry, qevent_binding,
143154a92385SPetr Machata 					      &agent_parms, &mall_entry->mirror.span_id);
143254a92385SPetr Machata }
143354a92385SPetr Machata 
143454a92385SPetr Machata static void mlxsw_sp_qevent_mirror_deconfigure(struct mlxsw_sp *mlxsw_sp,
143554a92385SPetr Machata 					       struct mlxsw_sp_mall_entry *mall_entry,
143654a92385SPetr Machata 					       struct mlxsw_sp_qevent_binding *qevent_binding)
143754a92385SPetr Machata {
143854a92385SPetr Machata 	mlxsw_sp_qevent_span_deconfigure(mlxsw_sp, qevent_binding, mall_entry->mirror.span_id);
143954a92385SPetr Machata }
144054a92385SPetr Machata 
144154a92385SPetr Machata static int mlxsw_sp_qevent_trap_configure(struct mlxsw_sp *mlxsw_sp,
144254a92385SPetr Machata 					  struct mlxsw_sp_mall_entry *mall_entry,
144354a92385SPetr Machata 					  struct mlxsw_sp_qevent_binding *qevent_binding)
144454a92385SPetr Machata {
14455c7659ebSIdo Schimmel 	struct mlxsw_sp_span_agent_parms agent_parms = {
14465c7659ebSIdo Schimmel 		.session_id = MLXSW_SP_SPAN_SESSION_ID_BUFFER,
14475c7659ebSIdo Schimmel 	};
144854a92385SPetr Machata 	int err;
144954a92385SPetr Machata 
145054a92385SPetr Machata 	err = mlxsw_sp_trap_group_policer_hw_id_get(mlxsw_sp,
145154a92385SPetr Machata 						    DEVLINK_TRAP_GROUP_GENERIC_ID_BUFFER_DROPS,
145254a92385SPetr Machata 						    &agent_parms.policer_enable,
145354a92385SPetr Machata 						    &agent_parms.policer_id);
145454a92385SPetr Machata 	if (err)
145554a92385SPetr Machata 		return err;
145654a92385SPetr Machata 
145754a92385SPetr Machata 	return mlxsw_sp_qevent_span_configure(mlxsw_sp, mall_entry, qevent_binding,
145854a92385SPetr Machata 					      &agent_parms, &mall_entry->trap.span_id);
145954a92385SPetr Machata }
146054a92385SPetr Machata 
146154a92385SPetr Machata static void mlxsw_sp_qevent_trap_deconfigure(struct mlxsw_sp *mlxsw_sp,
146254a92385SPetr Machata 					     struct mlxsw_sp_mall_entry *mall_entry,
146354a92385SPetr Machata 					     struct mlxsw_sp_qevent_binding *qevent_binding)
146454a92385SPetr Machata {
146554a92385SPetr Machata 	mlxsw_sp_qevent_span_deconfigure(mlxsw_sp, qevent_binding, mall_entry->trap.span_id);
1466f6668eacSPetr Machata }
1467f6668eacSPetr Machata 
1468f6668eacSPetr Machata static int mlxsw_sp_qevent_entry_configure(struct mlxsw_sp *mlxsw_sp,
1469f6668eacSPetr Machata 					   struct mlxsw_sp_mall_entry *mall_entry,
1470f6668eacSPetr Machata 					   struct mlxsw_sp_qevent_binding *qevent_binding)
1471f6668eacSPetr Machata {
1472f6668eacSPetr Machata 	switch (mall_entry->type) {
1473f6668eacSPetr Machata 	case MLXSW_SP_MALL_ACTION_TYPE_MIRROR:
1474f6668eacSPetr Machata 		return mlxsw_sp_qevent_mirror_configure(mlxsw_sp, mall_entry, qevent_binding);
147554a92385SPetr Machata 	case MLXSW_SP_MALL_ACTION_TYPE_TRAP:
147654a92385SPetr Machata 		return mlxsw_sp_qevent_trap_configure(mlxsw_sp, mall_entry, qevent_binding);
1477f6668eacSPetr Machata 	default:
1478f6668eacSPetr Machata 		/* This should have been validated away. */
1479f6668eacSPetr Machata 		WARN_ON(1);
1480f6668eacSPetr Machata 		return -EOPNOTSUPP;
1481f6668eacSPetr Machata 	}
1482f6668eacSPetr Machata }
1483f6668eacSPetr Machata 
1484f6668eacSPetr Machata static void mlxsw_sp_qevent_entry_deconfigure(struct mlxsw_sp *mlxsw_sp,
1485f6668eacSPetr Machata 					      struct mlxsw_sp_mall_entry *mall_entry,
1486f6668eacSPetr Machata 					      struct mlxsw_sp_qevent_binding *qevent_binding)
1487f6668eacSPetr Machata {
1488f6668eacSPetr Machata 	switch (mall_entry->type) {
1489f6668eacSPetr Machata 	case MLXSW_SP_MALL_ACTION_TYPE_MIRROR:
1490f6668eacSPetr Machata 		return mlxsw_sp_qevent_mirror_deconfigure(mlxsw_sp, mall_entry, qevent_binding);
149154a92385SPetr Machata 	case MLXSW_SP_MALL_ACTION_TYPE_TRAP:
149254a92385SPetr Machata 		return mlxsw_sp_qevent_trap_deconfigure(mlxsw_sp, mall_entry, qevent_binding);
1493f6668eacSPetr Machata 	default:
1494f6668eacSPetr Machata 		WARN_ON(1);
1495f6668eacSPetr Machata 		return;
1496f6668eacSPetr Machata 	}
1497f6668eacSPetr Machata }
1498f6668eacSPetr Machata 
1499f6668eacSPetr Machata static int mlxsw_sp_qevent_binding_configure(struct mlxsw_sp_qevent_block *qevent_block,
1500f6668eacSPetr Machata 					     struct mlxsw_sp_qevent_binding *qevent_binding)
1501f6668eacSPetr Machata {
1502f6668eacSPetr Machata 	struct mlxsw_sp_mall_entry *mall_entry;
1503f6668eacSPetr Machata 	int err;
1504f6668eacSPetr Machata 
1505f6668eacSPetr Machata 	list_for_each_entry(mall_entry, &qevent_block->mall_entry_list, list) {
1506f6668eacSPetr Machata 		err = mlxsw_sp_qevent_entry_configure(qevent_block->mlxsw_sp, mall_entry,
1507f6668eacSPetr Machata 						      qevent_binding);
1508f6668eacSPetr Machata 		if (err)
1509f6668eacSPetr Machata 			goto err_entry_configure;
1510f6668eacSPetr Machata 	}
1511f6668eacSPetr Machata 
1512f6668eacSPetr Machata 	return 0;
1513f6668eacSPetr Machata 
1514f6668eacSPetr Machata err_entry_configure:
1515f6668eacSPetr Machata 	list_for_each_entry_continue_reverse(mall_entry, &qevent_block->mall_entry_list, list)
1516f6668eacSPetr Machata 		mlxsw_sp_qevent_entry_deconfigure(qevent_block->mlxsw_sp, mall_entry,
1517f6668eacSPetr Machata 						  qevent_binding);
1518f6668eacSPetr Machata 	return err;
1519f6668eacSPetr Machata }
1520f6668eacSPetr Machata 
1521f6668eacSPetr Machata static void mlxsw_sp_qevent_binding_deconfigure(struct mlxsw_sp_qevent_block *qevent_block,
1522f6668eacSPetr Machata 						struct mlxsw_sp_qevent_binding *qevent_binding)
1523f6668eacSPetr Machata {
1524f6668eacSPetr Machata 	struct mlxsw_sp_mall_entry *mall_entry;
1525f6668eacSPetr Machata 
1526f6668eacSPetr Machata 	list_for_each_entry(mall_entry, &qevent_block->mall_entry_list, list)
1527f6668eacSPetr Machata 		mlxsw_sp_qevent_entry_deconfigure(qevent_block->mlxsw_sp, mall_entry,
1528f6668eacSPetr Machata 						  qevent_binding);
1529f6668eacSPetr Machata }
1530f6668eacSPetr Machata 
1531f6668eacSPetr Machata static int mlxsw_sp_qevent_block_configure(struct mlxsw_sp_qevent_block *qevent_block)
1532f6668eacSPetr Machata {
1533f6668eacSPetr Machata 	struct mlxsw_sp_qevent_binding *qevent_binding;
1534f6668eacSPetr Machata 	int err;
1535f6668eacSPetr Machata 
1536f6668eacSPetr Machata 	list_for_each_entry(qevent_binding, &qevent_block->binding_list, list) {
1537f6668eacSPetr Machata 		err = mlxsw_sp_qevent_binding_configure(qevent_block, qevent_binding);
1538f6668eacSPetr Machata 		if (err)
1539f6668eacSPetr Machata 			goto err_binding_configure;
1540f6668eacSPetr Machata 	}
1541f6668eacSPetr Machata 
1542f6668eacSPetr Machata 	return 0;
1543f6668eacSPetr Machata 
1544f6668eacSPetr Machata err_binding_configure:
1545f6668eacSPetr Machata 	list_for_each_entry_continue_reverse(qevent_binding, &qevent_block->binding_list, list)
1546f6668eacSPetr Machata 		mlxsw_sp_qevent_binding_deconfigure(qevent_block, qevent_binding);
1547f6668eacSPetr Machata 	return err;
1548f6668eacSPetr Machata }
1549f6668eacSPetr Machata 
1550f6668eacSPetr Machata static void mlxsw_sp_qevent_block_deconfigure(struct mlxsw_sp_qevent_block *qevent_block)
1551f6668eacSPetr Machata {
1552f6668eacSPetr Machata 	struct mlxsw_sp_qevent_binding *qevent_binding;
1553f6668eacSPetr Machata 
1554f6668eacSPetr Machata 	list_for_each_entry(qevent_binding, &qevent_block->binding_list, list)
1555f6668eacSPetr Machata 		mlxsw_sp_qevent_binding_deconfigure(qevent_block, qevent_binding);
1556f6668eacSPetr Machata }
1557f6668eacSPetr Machata 
1558f6668eacSPetr Machata static struct mlxsw_sp_mall_entry *
1559f6668eacSPetr Machata mlxsw_sp_qevent_mall_entry_find(struct mlxsw_sp_qevent_block *block, unsigned long cookie)
1560f6668eacSPetr Machata {
1561f6668eacSPetr Machata 	struct mlxsw_sp_mall_entry *mall_entry;
1562f6668eacSPetr Machata 
1563f6668eacSPetr Machata 	list_for_each_entry(mall_entry, &block->mall_entry_list, list)
1564f6668eacSPetr Machata 		if (mall_entry->cookie == cookie)
1565f6668eacSPetr Machata 			return mall_entry;
1566f6668eacSPetr Machata 
1567f6668eacSPetr Machata 	return NULL;
1568f6668eacSPetr Machata }
1569f6668eacSPetr Machata 
1570f6668eacSPetr Machata static int mlxsw_sp_qevent_mall_replace(struct mlxsw_sp *mlxsw_sp,
1571f6668eacSPetr Machata 					struct mlxsw_sp_qevent_block *qevent_block,
1572f6668eacSPetr Machata 					struct tc_cls_matchall_offload *f)
1573f6668eacSPetr Machata {
1574f6668eacSPetr Machata 	struct mlxsw_sp_mall_entry *mall_entry;
1575f6668eacSPetr Machata 	struct flow_action_entry *act;
1576f6668eacSPetr Machata 	int err;
1577f6668eacSPetr Machata 
1578f6668eacSPetr Machata 	/* It should not currently be possible to replace a matchall rule. So
1579f6668eacSPetr Machata 	 * this must be a new rule.
1580f6668eacSPetr Machata 	 */
1581f6668eacSPetr Machata 	if (!list_empty(&qevent_block->mall_entry_list)) {
1582f6668eacSPetr Machata 		NL_SET_ERR_MSG(f->common.extack, "At most one filter supported");
1583f6668eacSPetr Machata 		return -EOPNOTSUPP;
1584f6668eacSPetr Machata 	}
1585f6668eacSPetr Machata 	if (f->rule->action.num_entries != 1) {
1586f6668eacSPetr Machata 		NL_SET_ERR_MSG(f->common.extack, "Only singular actions supported");
1587f6668eacSPetr Machata 		return -EOPNOTSUPP;
1588f6668eacSPetr Machata 	}
1589f6668eacSPetr Machata 	if (f->common.chain_index) {
1590f6668eacSPetr Machata 		NL_SET_ERR_MSG(f->common.extack, "Only chain 0 is supported");
1591f6668eacSPetr Machata 		return -EOPNOTSUPP;
1592f6668eacSPetr Machata 	}
1593f6668eacSPetr Machata 	if (f->common.protocol != htons(ETH_P_ALL)) {
1594f6668eacSPetr Machata 		NL_SET_ERR_MSG(f->common.extack, "Protocol matching not supported");
1595f6668eacSPetr Machata 		return -EOPNOTSUPP;
1596f6668eacSPetr Machata 	}
1597f6668eacSPetr Machata 
1598f6668eacSPetr Machata 	act = &f->rule->action.entries[0];
1599f6668eacSPetr Machata 	if (!(act->hw_stats & FLOW_ACTION_HW_STATS_DISABLED)) {
1600f6668eacSPetr Machata 		NL_SET_ERR_MSG(f->common.extack, "HW counters not supported on qevents");
1601f6668eacSPetr Machata 		return -EOPNOTSUPP;
1602f6668eacSPetr Machata 	}
1603f6668eacSPetr Machata 
1604f6668eacSPetr Machata 	mall_entry = kzalloc(sizeof(*mall_entry), GFP_KERNEL);
1605f6668eacSPetr Machata 	if (!mall_entry)
1606f6668eacSPetr Machata 		return -ENOMEM;
1607f6668eacSPetr Machata 	mall_entry->cookie = f->cookie;
1608f6668eacSPetr Machata 
1609f6668eacSPetr Machata 	if (act->id == FLOW_ACTION_MIRRED) {
1610f6668eacSPetr Machata 		mall_entry->type = MLXSW_SP_MALL_ACTION_TYPE_MIRROR;
1611f6668eacSPetr Machata 		mall_entry->mirror.to_dev = act->dev;
161254a92385SPetr Machata 	} else if (act->id == FLOW_ACTION_TRAP) {
161354a92385SPetr Machata 		mall_entry->type = MLXSW_SP_MALL_ACTION_TYPE_TRAP;
1614f6668eacSPetr Machata 	} else {
1615f6668eacSPetr Machata 		NL_SET_ERR_MSG(f->common.extack, "Unsupported action");
1616f6668eacSPetr Machata 		err = -EOPNOTSUPP;
1617f6668eacSPetr Machata 		goto err_unsupported_action;
1618f6668eacSPetr Machata 	}
1619f6668eacSPetr Machata 
1620f6668eacSPetr Machata 	list_add_tail(&mall_entry->list, &qevent_block->mall_entry_list);
1621f6668eacSPetr Machata 
1622f6668eacSPetr Machata 	err = mlxsw_sp_qevent_block_configure(qevent_block);
1623f6668eacSPetr Machata 	if (err)
1624f6668eacSPetr Machata 		goto err_block_configure;
1625f6668eacSPetr Machata 
1626f6668eacSPetr Machata 	return 0;
1627f6668eacSPetr Machata 
1628f6668eacSPetr Machata err_block_configure:
1629f6668eacSPetr Machata 	list_del(&mall_entry->list);
1630f6668eacSPetr Machata err_unsupported_action:
1631f6668eacSPetr Machata 	kfree(mall_entry);
1632f6668eacSPetr Machata 	return err;
1633f6668eacSPetr Machata }
1634f6668eacSPetr Machata 
1635f6668eacSPetr Machata static void mlxsw_sp_qevent_mall_destroy(struct mlxsw_sp_qevent_block *qevent_block,
1636f6668eacSPetr Machata 					 struct tc_cls_matchall_offload *f)
1637f6668eacSPetr Machata {
1638f6668eacSPetr Machata 	struct mlxsw_sp_mall_entry *mall_entry;
1639f6668eacSPetr Machata 
1640f6668eacSPetr Machata 	mall_entry = mlxsw_sp_qevent_mall_entry_find(qevent_block, f->cookie);
1641f6668eacSPetr Machata 	if (!mall_entry)
1642f6668eacSPetr Machata 		return;
1643f6668eacSPetr Machata 
1644f6668eacSPetr Machata 	mlxsw_sp_qevent_block_deconfigure(qevent_block);
1645f6668eacSPetr Machata 
1646f6668eacSPetr Machata 	list_del(&mall_entry->list);
1647f6668eacSPetr Machata 	kfree(mall_entry);
1648f6668eacSPetr Machata }
1649f6668eacSPetr Machata 
1650f6668eacSPetr Machata static int mlxsw_sp_qevent_block_mall_cb(struct mlxsw_sp_qevent_block *qevent_block,
1651f6668eacSPetr Machata 					 struct tc_cls_matchall_offload *f)
1652f6668eacSPetr Machata {
1653f6668eacSPetr Machata 	struct mlxsw_sp *mlxsw_sp = qevent_block->mlxsw_sp;
1654f6668eacSPetr Machata 
1655f6668eacSPetr Machata 	switch (f->command) {
1656f6668eacSPetr Machata 	case TC_CLSMATCHALL_REPLACE:
1657f6668eacSPetr Machata 		return mlxsw_sp_qevent_mall_replace(mlxsw_sp, qevent_block, f);
1658f6668eacSPetr Machata 	case TC_CLSMATCHALL_DESTROY:
1659f6668eacSPetr Machata 		mlxsw_sp_qevent_mall_destroy(qevent_block, f);
1660f6668eacSPetr Machata 		return 0;
1661f6668eacSPetr Machata 	default:
1662f6668eacSPetr Machata 		return -EOPNOTSUPP;
1663f6668eacSPetr Machata 	}
1664f6668eacSPetr Machata }
1665f6668eacSPetr Machata 
1666f6668eacSPetr Machata static int mlxsw_sp_qevent_block_cb(enum tc_setup_type type, void *type_data, void *cb_priv)
1667f6668eacSPetr Machata {
1668f6668eacSPetr Machata 	struct mlxsw_sp_qevent_block *qevent_block = cb_priv;
1669f6668eacSPetr Machata 
1670f6668eacSPetr Machata 	switch (type) {
1671f6668eacSPetr Machata 	case TC_SETUP_CLSMATCHALL:
1672f6668eacSPetr Machata 		return mlxsw_sp_qevent_block_mall_cb(qevent_block, type_data);
1673f6668eacSPetr Machata 	default:
1674f6668eacSPetr Machata 		return -EOPNOTSUPP;
1675f6668eacSPetr Machata 	}
1676f6668eacSPetr Machata }
1677f6668eacSPetr Machata 
1678f6668eacSPetr Machata static struct mlxsw_sp_qevent_block *mlxsw_sp_qevent_block_create(struct mlxsw_sp *mlxsw_sp,
1679f6668eacSPetr Machata 								  struct net *net)
1680f6668eacSPetr Machata {
1681f6668eacSPetr Machata 	struct mlxsw_sp_qevent_block *qevent_block;
1682f6668eacSPetr Machata 
1683f6668eacSPetr Machata 	qevent_block = kzalloc(sizeof(*qevent_block), GFP_KERNEL);
1684f6668eacSPetr Machata 	if (!qevent_block)
1685f6668eacSPetr Machata 		return NULL;
1686f6668eacSPetr Machata 
1687f6668eacSPetr Machata 	INIT_LIST_HEAD(&qevent_block->binding_list);
1688f6668eacSPetr Machata 	INIT_LIST_HEAD(&qevent_block->mall_entry_list);
1689f6668eacSPetr Machata 	qevent_block->mlxsw_sp = mlxsw_sp;
1690f6668eacSPetr Machata 	return qevent_block;
1691f6668eacSPetr Machata }
1692f6668eacSPetr Machata 
1693f6668eacSPetr Machata static void
1694f6668eacSPetr Machata mlxsw_sp_qevent_block_destroy(struct mlxsw_sp_qevent_block *qevent_block)
1695f6668eacSPetr Machata {
1696f6668eacSPetr Machata 	WARN_ON(!list_empty(&qevent_block->binding_list));
1697f6668eacSPetr Machata 	WARN_ON(!list_empty(&qevent_block->mall_entry_list));
1698f6668eacSPetr Machata 	kfree(qevent_block);
1699f6668eacSPetr Machata }
1700f6668eacSPetr Machata 
1701f6668eacSPetr Machata static void mlxsw_sp_qevent_block_release(void *cb_priv)
1702f6668eacSPetr Machata {
1703f6668eacSPetr Machata 	struct mlxsw_sp_qevent_block *qevent_block = cb_priv;
1704f6668eacSPetr Machata 
1705f6668eacSPetr Machata 	mlxsw_sp_qevent_block_destroy(qevent_block);
1706f6668eacSPetr Machata }
1707f6668eacSPetr Machata 
1708f6668eacSPetr Machata static struct mlxsw_sp_qevent_binding *
1709f6668eacSPetr Machata mlxsw_sp_qevent_binding_create(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, int tclass_num,
1710f6668eacSPetr Machata 			       enum mlxsw_sp_span_trigger span_trigger)
1711f6668eacSPetr Machata {
1712f6668eacSPetr Machata 	struct mlxsw_sp_qevent_binding *binding;
1713f6668eacSPetr Machata 
1714f6668eacSPetr Machata 	binding = kzalloc(sizeof(*binding), GFP_KERNEL);
1715f6668eacSPetr Machata 	if (!binding)
1716f6668eacSPetr Machata 		return ERR_PTR(-ENOMEM);
1717f6668eacSPetr Machata 
1718f6668eacSPetr Machata 	binding->mlxsw_sp_port = mlxsw_sp_port;
1719f6668eacSPetr Machata 	binding->handle = handle;
1720f6668eacSPetr Machata 	binding->tclass_num = tclass_num;
1721f6668eacSPetr Machata 	binding->span_trigger = span_trigger;
1722f6668eacSPetr Machata 	return binding;
1723f6668eacSPetr Machata }
1724f6668eacSPetr Machata 
1725f6668eacSPetr Machata static void
1726f6668eacSPetr Machata mlxsw_sp_qevent_binding_destroy(struct mlxsw_sp_qevent_binding *binding)
1727f6668eacSPetr Machata {
1728f6668eacSPetr Machata 	kfree(binding);
1729f6668eacSPetr Machata }
1730f6668eacSPetr Machata 
1731f6668eacSPetr Machata static struct mlxsw_sp_qevent_binding *
1732f6668eacSPetr Machata mlxsw_sp_qevent_binding_lookup(struct mlxsw_sp_qevent_block *block,
1733f6668eacSPetr Machata 			       struct mlxsw_sp_port *mlxsw_sp_port,
1734f6668eacSPetr Machata 			       u32 handle,
1735f6668eacSPetr Machata 			       enum mlxsw_sp_span_trigger span_trigger)
1736f6668eacSPetr Machata {
1737f6668eacSPetr Machata 	struct mlxsw_sp_qevent_binding *qevent_binding;
1738f6668eacSPetr Machata 
1739f6668eacSPetr Machata 	list_for_each_entry(qevent_binding, &block->binding_list, list)
1740f6668eacSPetr Machata 		if (qevent_binding->mlxsw_sp_port == mlxsw_sp_port &&
1741f6668eacSPetr Machata 		    qevent_binding->handle == handle &&
1742f6668eacSPetr Machata 		    qevent_binding->span_trigger == span_trigger)
1743f6668eacSPetr Machata 			return qevent_binding;
1744f6668eacSPetr Machata 	return NULL;
1745f6668eacSPetr Machata }
1746f6668eacSPetr Machata 
1747f6668eacSPetr Machata static int mlxsw_sp_setup_tc_block_qevent_bind(struct mlxsw_sp_port *mlxsw_sp_port,
1748f6668eacSPetr Machata 					       struct flow_block_offload *f,
1749f6668eacSPetr Machata 					       enum mlxsw_sp_span_trigger span_trigger)
1750f6668eacSPetr Machata {
1751f6668eacSPetr Machata 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
1752f6668eacSPetr Machata 	struct mlxsw_sp_qevent_binding *qevent_binding;
1753f6668eacSPetr Machata 	struct mlxsw_sp_qevent_block *qevent_block;
1754f6668eacSPetr Machata 	struct flow_block_cb *block_cb;
1755f6668eacSPetr Machata 	struct mlxsw_sp_qdisc *qdisc;
1756f6668eacSPetr Machata 	bool register_block = false;
1757f6668eacSPetr Machata 	int err;
1758f6668eacSPetr Machata 
1759f6668eacSPetr Machata 	block_cb = flow_block_cb_lookup(f->block, mlxsw_sp_qevent_block_cb, mlxsw_sp);
1760f6668eacSPetr Machata 	if (!block_cb) {
1761f6668eacSPetr Machata 		qevent_block = mlxsw_sp_qevent_block_create(mlxsw_sp, f->net);
1762f6668eacSPetr Machata 		if (!qevent_block)
1763f6668eacSPetr Machata 			return -ENOMEM;
1764f6668eacSPetr Machata 		block_cb = flow_block_cb_alloc(mlxsw_sp_qevent_block_cb, mlxsw_sp, qevent_block,
1765f6668eacSPetr Machata 					       mlxsw_sp_qevent_block_release);
1766f6668eacSPetr Machata 		if (IS_ERR(block_cb)) {
1767f6668eacSPetr Machata 			mlxsw_sp_qevent_block_destroy(qevent_block);
1768f6668eacSPetr Machata 			return PTR_ERR(block_cb);
1769f6668eacSPetr Machata 		}
1770f6668eacSPetr Machata 		register_block = true;
1771f6668eacSPetr Machata 	} else {
1772f6668eacSPetr Machata 		qevent_block = flow_block_cb_priv(block_cb);
1773f6668eacSPetr Machata 	}
1774f6668eacSPetr Machata 	flow_block_cb_incref(block_cb);
1775f6668eacSPetr Machata 
1776f6668eacSPetr Machata 	qdisc = mlxsw_sp_qdisc_find_by_handle(mlxsw_sp_port, f->sch->handle);
1777f6668eacSPetr Machata 	if (!qdisc) {
1778f6668eacSPetr Machata 		NL_SET_ERR_MSG(f->extack, "Qdisc not offloaded");
1779f6668eacSPetr Machata 		err = -ENOENT;
1780f6668eacSPetr Machata 		goto err_find_qdisc;
1781f6668eacSPetr Machata 	}
1782f6668eacSPetr Machata 
1783f6668eacSPetr Machata 	if (WARN_ON(mlxsw_sp_qevent_binding_lookup(qevent_block, mlxsw_sp_port, f->sch->handle,
1784f6668eacSPetr Machata 						   span_trigger))) {
1785f6668eacSPetr Machata 		err = -EEXIST;
1786f6668eacSPetr Machata 		goto err_binding_exists;
1787f6668eacSPetr Machata 	}
1788f6668eacSPetr Machata 
1789f6668eacSPetr Machata 	qevent_binding = mlxsw_sp_qevent_binding_create(mlxsw_sp_port, f->sch->handle,
1790f6668eacSPetr Machata 							qdisc->tclass_num, span_trigger);
1791f6668eacSPetr Machata 	if (IS_ERR(qevent_binding)) {
1792f6668eacSPetr Machata 		err = PTR_ERR(qevent_binding);
1793f6668eacSPetr Machata 		goto err_binding_create;
1794f6668eacSPetr Machata 	}
1795f6668eacSPetr Machata 
1796f6668eacSPetr Machata 	err = mlxsw_sp_qevent_binding_configure(qevent_block, qevent_binding);
1797f6668eacSPetr Machata 	if (err)
1798f6668eacSPetr Machata 		goto err_binding_configure;
1799f6668eacSPetr Machata 
1800f6668eacSPetr Machata 	list_add(&qevent_binding->list, &qevent_block->binding_list);
1801f6668eacSPetr Machata 
1802f6668eacSPetr Machata 	if (register_block) {
1803f6668eacSPetr Machata 		flow_block_cb_add(block_cb, f);
1804f6668eacSPetr Machata 		list_add_tail(&block_cb->driver_list, &mlxsw_sp_qevent_block_cb_list);
1805f6668eacSPetr Machata 	}
1806f6668eacSPetr Machata 
1807f6668eacSPetr Machata 	return 0;
1808f6668eacSPetr Machata 
1809f6668eacSPetr Machata err_binding_configure:
1810f6668eacSPetr Machata 	mlxsw_sp_qevent_binding_destroy(qevent_binding);
1811f6668eacSPetr Machata err_binding_create:
1812f6668eacSPetr Machata err_binding_exists:
1813f6668eacSPetr Machata err_find_qdisc:
1814f6668eacSPetr Machata 	if (!flow_block_cb_decref(block_cb))
1815f6668eacSPetr Machata 		flow_block_cb_free(block_cb);
1816f6668eacSPetr Machata 	return err;
1817f6668eacSPetr Machata }
1818f6668eacSPetr Machata 
1819f6668eacSPetr Machata static void mlxsw_sp_setup_tc_block_qevent_unbind(struct mlxsw_sp_port *mlxsw_sp_port,
1820f6668eacSPetr Machata 						  struct flow_block_offload *f,
1821f6668eacSPetr Machata 						  enum mlxsw_sp_span_trigger span_trigger)
1822f6668eacSPetr Machata {
1823f6668eacSPetr Machata 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
1824f6668eacSPetr Machata 	struct mlxsw_sp_qevent_binding *qevent_binding;
1825f6668eacSPetr Machata 	struct mlxsw_sp_qevent_block *qevent_block;
1826f6668eacSPetr Machata 	struct flow_block_cb *block_cb;
1827f6668eacSPetr Machata 
1828f6668eacSPetr Machata 	block_cb = flow_block_cb_lookup(f->block, mlxsw_sp_qevent_block_cb, mlxsw_sp);
1829f6668eacSPetr Machata 	if (!block_cb)
1830f6668eacSPetr Machata 		return;
1831f6668eacSPetr Machata 	qevent_block = flow_block_cb_priv(block_cb);
1832f6668eacSPetr Machata 
1833f6668eacSPetr Machata 	qevent_binding = mlxsw_sp_qevent_binding_lookup(qevent_block, mlxsw_sp_port, f->sch->handle,
1834f6668eacSPetr Machata 							span_trigger);
1835f6668eacSPetr Machata 	if (!qevent_binding)
1836f6668eacSPetr Machata 		return;
1837f6668eacSPetr Machata 
1838f6668eacSPetr Machata 	list_del(&qevent_binding->list);
1839f6668eacSPetr Machata 	mlxsw_sp_qevent_binding_deconfigure(qevent_block, qevent_binding);
1840f6668eacSPetr Machata 	mlxsw_sp_qevent_binding_destroy(qevent_binding);
1841f6668eacSPetr Machata 
1842f6668eacSPetr Machata 	if (!flow_block_cb_decref(block_cb)) {
1843f6668eacSPetr Machata 		flow_block_cb_remove(block_cb, f);
1844f6668eacSPetr Machata 		list_del(&block_cb->driver_list);
1845f6668eacSPetr Machata 	}
1846f6668eacSPetr Machata }
1847f6668eacSPetr Machata 
1848f6668eacSPetr Machata static int mlxsw_sp_setup_tc_block_qevent(struct mlxsw_sp_port *mlxsw_sp_port,
1849f6668eacSPetr Machata 					  struct flow_block_offload *f,
1850f6668eacSPetr Machata 					  enum mlxsw_sp_span_trigger span_trigger)
1851f6668eacSPetr Machata {
1852f6668eacSPetr Machata 	f->driver_block_list = &mlxsw_sp_qevent_block_cb_list;
1853f6668eacSPetr Machata 
1854f6668eacSPetr Machata 	switch (f->command) {
1855f6668eacSPetr Machata 	case FLOW_BLOCK_BIND:
1856f6668eacSPetr Machata 		return mlxsw_sp_setup_tc_block_qevent_bind(mlxsw_sp_port, f, span_trigger);
1857f6668eacSPetr Machata 	case FLOW_BLOCK_UNBIND:
1858f6668eacSPetr Machata 		mlxsw_sp_setup_tc_block_qevent_unbind(mlxsw_sp_port, f, span_trigger);
1859f6668eacSPetr Machata 		return 0;
1860f6668eacSPetr Machata 	default:
1861f6668eacSPetr Machata 		return -EOPNOTSUPP;
1862f6668eacSPetr Machata 	}
1863f6668eacSPetr Machata }
1864f6668eacSPetr Machata 
1865f6668eacSPetr Machata int mlxsw_sp_setup_tc_block_qevent_early_drop(struct mlxsw_sp_port *mlxsw_sp_port,
1866f6668eacSPetr Machata 					      struct flow_block_offload *f)
1867f6668eacSPetr Machata {
1868f6668eacSPetr Machata 	return mlxsw_sp_setup_tc_block_qevent(mlxsw_sp_port, f, MLXSW_SP_SPAN_TRIGGER_EARLY_DROP);
1869f6668eacSPetr Machata }
1870f6668eacSPetr Machata 
1871371b437aSNogah Frankel int mlxsw_sp_tc_qdisc_init(struct mlxsw_sp_port *mlxsw_sp_port)
1872371b437aSNogah Frankel {
1873ee88450dSPetr Machata 	struct mlxsw_sp_qdisc_state *qdisc_state;
1874eed4baebSNogah Frankel 	int i;
1875371b437aSNogah Frankel 
1876ee88450dSPetr Machata 	qdisc_state = kzalloc(sizeof(*qdisc_state), GFP_KERNEL);
1877ee88450dSPetr Machata 	if (!qdisc_state)
1878eed4baebSNogah Frankel 		return -ENOMEM;
1879ee88450dSPetr Machata 
1880ee88450dSPetr Machata 	qdisc_state->root_qdisc.prio_bitmap = 0xff;
1881ee88450dSPetr Machata 	qdisc_state->root_qdisc.tclass_num = MLXSW_SP_PORT_DEFAULT_TCLASS;
1882*51d52ed9SPetr Machata 	qdisc_state->root_qdisc.qdiscs = qdisc_state->tclass_qdiscs;
1883*51d52ed9SPetr Machata 	qdisc_state->root_qdisc.num_classes = IEEE_8021QAZ_MAX_TCS;
1884b21832b5SPetr Machata 	for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
1885*51d52ed9SPetr Machata 		int tclass_num = MLXSW_SP_PRIO_BAND_TO_TCLASS(i);
1886*51d52ed9SPetr Machata 
1887*51d52ed9SPetr Machata 		qdisc_state->tclass_qdiscs[i].tclass_num = tclass_num;
1888b21832b5SPetr Machata 		qdisc_state->tclass_qdiscs[i].parent = &qdisc_state->root_qdisc;
1889b21832b5SPetr Machata 	}
1890ee88450dSPetr Machata 
1891ee88450dSPetr Machata 	mlxsw_sp_port->qdisc = qdisc_state;
1892ee88450dSPetr Machata 	return 0;
1893371b437aSNogah Frankel }
1894371b437aSNogah Frankel 
1895371b437aSNogah Frankel void mlxsw_sp_tc_qdisc_fini(struct mlxsw_sp_port *mlxsw_sp_port)
1896371b437aSNogah Frankel {
1897ee88450dSPetr Machata 	kfree(mlxsw_sp_port->qdisc);
1898371b437aSNogah Frankel }
1899