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 			    struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
339cf6c9c7SNogah Frankel 			    void *params);
34c4e372e2SPetr Machata 	int (*replace)(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
359cf6c9c7SNogah Frankel 		       struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, void *params);
369a37a59fSNogah Frankel 	int (*destroy)(struct mlxsw_sp_port *mlxsw_sp_port,
379a37a59fSNogah Frankel 		       struct mlxsw_sp_qdisc *mlxsw_sp_qdisc);
38562ffbc4SNogah Frankel 	int (*get_stats)(struct mlxsw_sp_port *mlxsw_sp_port,
39562ffbc4SNogah Frankel 			 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
40562ffbc4SNogah Frankel 			 struct tc_qopt_offload_stats *stats_ptr);
41562ffbc4SNogah Frankel 	int (*get_xstats)(struct mlxsw_sp_port *mlxsw_sp_port,
42562ffbc4SNogah Frankel 			  struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
43562ffbc4SNogah Frankel 			  void *xstats_ptr);
449cf6c9c7SNogah Frankel 	void (*clean_stats)(struct mlxsw_sp_port *mlxsw_sp_port,
459cf6c9c7SNogah Frankel 			    struct mlxsw_sp_qdisc *mlxsw_sp_qdisc);
4693d8a4c1SNogah Frankel 	/* unoffload - to be used for a qdisc that stops being offloaded without
4793d8a4c1SNogah Frankel 	 * being destroyed.
4893d8a4c1SNogah Frankel 	 */
4993d8a4c1SNogah Frankel 	void (*unoffload)(struct mlxsw_sp_port *mlxsw_sp_port,
5093d8a4c1SNogah Frankel 			  struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, void *params);
51562ffbc4SNogah Frankel };
52562ffbc4SNogah Frankel 
53371b437aSNogah Frankel struct mlxsw_sp_qdisc {
54371b437aSNogah Frankel 	u32 handle;
55d56c8955SNogah Frankel 	u8 tclass_num;
561631ab2eSNogah Frankel 	u8 prio_bitmap;
57371b437aSNogah Frankel 	union {
584d1a4b84SNogah Frankel 		struct red_stats red;
594d1a4b84SNogah Frankel 	} xstats_base;
604d1a4b84SNogah Frankel 	struct mlxsw_sp_qdisc_stats {
61371b437aSNogah Frankel 		u64 tx_bytes;
62371b437aSNogah Frankel 		u64 tx_packets;
63371b437aSNogah Frankel 		u64 drops;
64371b437aSNogah Frankel 		u64 overlimits;
6593d8a4c1SNogah Frankel 		u64 backlog;
664d1a4b84SNogah Frankel 	} stats_base;
67562ffbc4SNogah Frankel 
68562ffbc4SNogah Frankel 	struct mlxsw_sp_qdisc_ops *ops;
69371b437aSNogah Frankel };
70371b437aSNogah Frankel 
71ee88450dSPetr Machata struct mlxsw_sp_qdisc_state {
72ee88450dSPetr Machata 	struct mlxsw_sp_qdisc root_qdisc;
73ee88450dSPetr Machata 	struct mlxsw_sp_qdisc tclass_qdiscs[IEEE_8021QAZ_MAX_TCS];
747bec1a45SPetr Machata 
757bec1a45SPetr Machata 	/* When a PRIO or ETS are added, the invisible FIFOs in their bands are
767bec1a45SPetr Machata 	 * created first. When notifications for these FIFOs arrive, it is not
777bec1a45SPetr Machata 	 * known what qdisc their parent handle refers to. It could be a
787bec1a45SPetr Machata 	 * newly-created PRIO that will replace the currently-offloaded one, or
797bec1a45SPetr Machata 	 * it could be e.g. a RED that will be attached below it.
807bec1a45SPetr Machata 	 *
817bec1a45SPetr Machata 	 * As the notifications start to arrive, use them to note what the
827bec1a45SPetr Machata 	 * future parent handle is, and keep track of which child FIFOs were
837bec1a45SPetr Machata 	 * seen. Then when the parent is known, retroactively offload those
847bec1a45SPetr Machata 	 * FIFOs.
857bec1a45SPetr Machata 	 */
867bec1a45SPetr Machata 	u32 future_handle;
877bec1a45SPetr Machata 	bool future_fifos[IEEE_8021QAZ_MAX_TCS];
88ee88450dSPetr Machata };
89ee88450dSPetr Machata 
90cba7158fSNogah Frankel static bool
91cba7158fSNogah Frankel mlxsw_sp_qdisc_compare(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, u32 handle,
92cba7158fSNogah Frankel 		       enum mlxsw_sp_qdisc_type type)
93cba7158fSNogah Frankel {
949cf6c9c7SNogah Frankel 	return mlxsw_sp_qdisc && mlxsw_sp_qdisc->ops &&
959cf6c9c7SNogah Frankel 	       mlxsw_sp_qdisc->ops->type == type &&
969cf6c9c7SNogah Frankel 	       mlxsw_sp_qdisc->handle == handle;
97cba7158fSNogah Frankel }
98cba7158fSNogah Frankel 
99eed4baebSNogah Frankel static struct mlxsw_sp_qdisc *
100eed4baebSNogah Frankel mlxsw_sp_qdisc_find(struct mlxsw_sp_port *mlxsw_sp_port, u32 parent,
101eed4baebSNogah Frankel 		    bool root_only)
102eed4baebSNogah Frankel {
103ee88450dSPetr Machata 	struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc;
104eed4baebSNogah Frankel 	int tclass, child_index;
105eed4baebSNogah Frankel 
106eed4baebSNogah Frankel 	if (parent == TC_H_ROOT)
107ee88450dSPetr Machata 		return &qdisc_state->root_qdisc;
108eed4baebSNogah Frankel 
109ee88450dSPetr Machata 	if (root_only || !qdisc_state ||
110ee88450dSPetr Machata 	    !qdisc_state->root_qdisc.ops ||
111ee88450dSPetr Machata 	    TC_H_MAJ(parent) != qdisc_state->root_qdisc.handle ||
112eed4baebSNogah Frankel 	    TC_H_MIN(parent) > IEEE_8021QAZ_MAX_TCS)
113eed4baebSNogah Frankel 		return NULL;
114eed4baebSNogah Frankel 
115eed4baebSNogah Frankel 	child_index = TC_H_MIN(parent);
116eed4baebSNogah Frankel 	tclass = MLXSW_SP_PRIO_CHILD_TO_TCLASS(child_index);
117ee88450dSPetr Machata 	return &qdisc_state->tclass_qdiscs[tclass];
118eed4baebSNogah Frankel }
119eed4baebSNogah Frankel 
12032dc5efcSNogah Frankel static struct mlxsw_sp_qdisc *
12132dc5efcSNogah Frankel mlxsw_sp_qdisc_find_by_handle(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle)
12232dc5efcSNogah Frankel {
123ee88450dSPetr Machata 	struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc;
12432dc5efcSNogah Frankel 	int i;
12532dc5efcSNogah Frankel 
126ee88450dSPetr Machata 	if (qdisc_state->root_qdisc.handle == handle)
127ee88450dSPetr Machata 		return &qdisc_state->root_qdisc;
12832dc5efcSNogah Frankel 
129ee88450dSPetr Machata 	if (qdisc_state->root_qdisc.handle == TC_H_UNSPEC)
13032dc5efcSNogah Frankel 		return NULL;
13132dc5efcSNogah Frankel 
13232dc5efcSNogah Frankel 	for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++)
133ee88450dSPetr Machata 		if (qdisc_state->tclass_qdiscs[i].handle == handle)
134ee88450dSPetr Machata 			return &qdisc_state->tclass_qdiscs[i];
13532dc5efcSNogah Frankel 
13632dc5efcSNogah Frankel 	return NULL;
13732dc5efcSNogah Frankel }
13832dc5efcSNogah Frankel 
13996f17e07SNogah Frankel static int
1409a37a59fSNogah Frankel mlxsw_sp_qdisc_destroy(struct mlxsw_sp_port *mlxsw_sp_port,
1419a37a59fSNogah Frankel 		       struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
1429a37a59fSNogah Frankel {
143509f04caSPetr Machata 	struct mlxsw_sp_qdisc *root_qdisc = &mlxsw_sp_port->qdisc->root_qdisc;
144509f04caSPetr Machata 	int err_hdroom = 0;
1459a37a59fSNogah Frankel 	int err = 0;
1469a37a59fSNogah Frankel 
1479a37a59fSNogah Frankel 	if (!mlxsw_sp_qdisc)
1489a37a59fSNogah Frankel 		return 0;
1499a37a59fSNogah Frankel 
150509f04caSPetr Machata 	if (root_qdisc == mlxsw_sp_qdisc) {
151509f04caSPetr Machata 		struct mlxsw_sp_hdroom hdroom = *mlxsw_sp_port->hdroom;
152509f04caSPetr Machata 
153509f04caSPetr Machata 		hdroom.mode = MLXSW_SP_HDROOM_MODE_DCB;
154509f04caSPetr Machata 		mlxsw_sp_hdroom_prios_reset_buf_idx(&hdroom);
155509f04caSPetr Machata 		mlxsw_sp_hdroom_bufs_reset_lossiness(&hdroom);
156509f04caSPetr Machata 		mlxsw_sp_hdroom_bufs_reset_sizes(mlxsw_sp_port, &hdroom);
157509f04caSPetr Machata 		err_hdroom = mlxsw_sp_hdroom_configure(mlxsw_sp_port, &hdroom);
158509f04caSPetr Machata 	}
159509f04caSPetr Machata 
1609a37a59fSNogah Frankel 	if (mlxsw_sp_qdisc->ops && mlxsw_sp_qdisc->ops->destroy)
1619a37a59fSNogah Frankel 		err = mlxsw_sp_qdisc->ops->destroy(mlxsw_sp_port,
1629a37a59fSNogah Frankel 						   mlxsw_sp_qdisc);
1639a37a59fSNogah Frankel 
1649a37a59fSNogah Frankel 	mlxsw_sp_qdisc->handle = TC_H_UNSPEC;
1659a37a59fSNogah Frankel 	mlxsw_sp_qdisc->ops = NULL;
166509f04caSPetr Machata 
167509f04caSPetr Machata 	return err_hdroom ?: err;
1689a37a59fSNogah Frankel }
1699a37a59fSNogah Frankel 
1709a37a59fSNogah Frankel static int
1719cf6c9c7SNogah Frankel mlxsw_sp_qdisc_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
1729cf6c9c7SNogah Frankel 		       struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
1739cf6c9c7SNogah Frankel 		       struct mlxsw_sp_qdisc_ops *ops, void *params)
1749cf6c9c7SNogah Frankel {
175509f04caSPetr Machata 	struct mlxsw_sp_qdisc *root_qdisc = &mlxsw_sp_port->qdisc->root_qdisc;
176509f04caSPetr Machata 	struct mlxsw_sp_hdroom orig_hdroom;
1779cf6c9c7SNogah Frankel 	int err;
1789cf6c9c7SNogah Frankel 
17956202ca4SNogah Frankel 	if (mlxsw_sp_qdisc->ops && mlxsw_sp_qdisc->ops->type != ops->type)
18056202ca4SNogah Frankel 		/* In case this location contained a different qdisc of the
18156202ca4SNogah Frankel 		 * same type we can override the old qdisc configuration.
18256202ca4SNogah Frankel 		 * Otherwise, we need to remove the old qdisc before setting the
18356202ca4SNogah Frankel 		 * new one.
18456202ca4SNogah Frankel 		 */
18556202ca4SNogah Frankel 		mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
186509f04caSPetr Machata 
187509f04caSPetr Machata 	orig_hdroom = *mlxsw_sp_port->hdroom;
188509f04caSPetr Machata 	if (root_qdisc == mlxsw_sp_qdisc) {
189509f04caSPetr Machata 		struct mlxsw_sp_hdroom hdroom = orig_hdroom;
190509f04caSPetr Machata 
191509f04caSPetr Machata 		hdroom.mode = MLXSW_SP_HDROOM_MODE_TC;
192509f04caSPetr Machata 		mlxsw_sp_hdroom_prios_reset_buf_idx(&hdroom);
193509f04caSPetr Machata 		mlxsw_sp_hdroom_bufs_reset_lossiness(&hdroom);
194509f04caSPetr Machata 		mlxsw_sp_hdroom_bufs_reset_sizes(mlxsw_sp_port, &hdroom);
195509f04caSPetr Machata 
196509f04caSPetr Machata 		err = mlxsw_sp_hdroom_configure(mlxsw_sp_port, &hdroom);
197509f04caSPetr Machata 		if (err)
198509f04caSPetr Machata 			goto err_hdroom_configure;
199509f04caSPetr Machata 	}
200509f04caSPetr Machata 
2019cf6c9c7SNogah Frankel 	err = ops->check_params(mlxsw_sp_port, mlxsw_sp_qdisc, params);
2029cf6c9c7SNogah Frankel 	if (err)
2039cf6c9c7SNogah Frankel 		goto err_bad_param;
2049cf6c9c7SNogah Frankel 
205c4e372e2SPetr Machata 	err = ops->replace(mlxsw_sp_port, handle, mlxsw_sp_qdisc, params);
2069cf6c9c7SNogah Frankel 	if (err)
2079cf6c9c7SNogah Frankel 		goto err_config;
2089cf6c9c7SNogah Frankel 
2097bec1a45SPetr Machata 	/* Check if the Qdisc changed. That includes a situation where an
2107bec1a45SPetr Machata 	 * invisible Qdisc replaces another one, or is being added for the
2117bec1a45SPetr Machata 	 * first time.
2127bec1a45SPetr Machata 	 */
2137bec1a45SPetr Machata 	if (mlxsw_sp_qdisc->handle != handle || handle == TC_H_UNSPEC) {
2149cf6c9c7SNogah Frankel 		mlxsw_sp_qdisc->ops = ops;
2159cf6c9c7SNogah Frankel 		if (ops->clean_stats)
2169cf6c9c7SNogah Frankel 			ops->clean_stats(mlxsw_sp_port, mlxsw_sp_qdisc);
2179cf6c9c7SNogah Frankel 	}
2189cf6c9c7SNogah Frankel 
2199cf6c9c7SNogah Frankel 	mlxsw_sp_qdisc->handle = handle;
2209cf6c9c7SNogah Frankel 	return 0;
2219cf6c9c7SNogah Frankel 
2229cf6c9c7SNogah Frankel err_bad_param:
2239cf6c9c7SNogah Frankel err_config:
224509f04caSPetr Machata 	mlxsw_sp_hdroom_configure(mlxsw_sp_port, &orig_hdroom);
225509f04caSPetr Machata err_hdroom_configure:
22693d8a4c1SNogah Frankel 	if (mlxsw_sp_qdisc->handle == handle && ops->unoffload)
22793d8a4c1SNogah Frankel 		ops->unoffload(mlxsw_sp_port, mlxsw_sp_qdisc, params);
22893d8a4c1SNogah Frankel 
2299cf6c9c7SNogah Frankel 	mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
2309cf6c9c7SNogah Frankel 	return err;
2319cf6c9c7SNogah Frankel }
2329cf6c9c7SNogah Frankel 
2339cf6c9c7SNogah Frankel static int
234562ffbc4SNogah Frankel mlxsw_sp_qdisc_get_stats(struct mlxsw_sp_port *mlxsw_sp_port,
235562ffbc4SNogah Frankel 			 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
236562ffbc4SNogah Frankel 			 struct tc_qopt_offload_stats *stats_ptr)
237562ffbc4SNogah Frankel {
238562ffbc4SNogah Frankel 	if (mlxsw_sp_qdisc && mlxsw_sp_qdisc->ops &&
239562ffbc4SNogah Frankel 	    mlxsw_sp_qdisc->ops->get_stats)
240562ffbc4SNogah Frankel 		return mlxsw_sp_qdisc->ops->get_stats(mlxsw_sp_port,
241562ffbc4SNogah Frankel 						      mlxsw_sp_qdisc,
242562ffbc4SNogah Frankel 						      stats_ptr);
243562ffbc4SNogah Frankel 
244562ffbc4SNogah Frankel 	return -EOPNOTSUPP;
245562ffbc4SNogah Frankel }
246562ffbc4SNogah Frankel 
247562ffbc4SNogah Frankel static int
248562ffbc4SNogah Frankel mlxsw_sp_qdisc_get_xstats(struct mlxsw_sp_port *mlxsw_sp_port,
249562ffbc4SNogah Frankel 			  struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
250562ffbc4SNogah Frankel 			  void *xstats_ptr)
251562ffbc4SNogah Frankel {
252562ffbc4SNogah Frankel 	if (mlxsw_sp_qdisc && mlxsw_sp_qdisc->ops &&
253562ffbc4SNogah Frankel 	    mlxsw_sp_qdisc->ops->get_xstats)
254562ffbc4SNogah Frankel 		return mlxsw_sp_qdisc->ops->get_xstats(mlxsw_sp_port,
255562ffbc4SNogah Frankel 						      mlxsw_sp_qdisc,
256562ffbc4SNogah Frankel 						      xstats_ptr);
257562ffbc4SNogah Frankel 
258562ffbc4SNogah Frankel 	return -EOPNOTSUPP;
259562ffbc4SNogah Frankel }
260562ffbc4SNogah Frankel 
26185005b82SPetr Machata static u64
26285005b82SPetr Machata mlxsw_sp_xstats_backlog(struct mlxsw_sp_port_xstats *xstats, int tclass_num)
26385005b82SPetr Machata {
26485005b82SPetr Machata 	return xstats->backlog[tclass_num] +
26585005b82SPetr Machata 	       xstats->backlog[tclass_num + 8];
26685005b82SPetr Machata }
26785005b82SPetr Machata 
26885005b82SPetr Machata static u64
26985005b82SPetr Machata mlxsw_sp_xstats_tail_drop(struct mlxsw_sp_port_xstats *xstats, int tclass_num)
27085005b82SPetr Machata {
27185005b82SPetr Machata 	return xstats->tail_drop[tclass_num] +
27285005b82SPetr Machata 	       xstats->tail_drop[tclass_num + 8];
27385005b82SPetr Machata }
27485005b82SPetr Machata 
27504cc0bf5SNogah Frankel static void
27604cc0bf5SNogah Frankel mlxsw_sp_qdisc_bstats_per_priority_get(struct mlxsw_sp_port_xstats *xstats,
27704cc0bf5SNogah Frankel 				       u8 prio_bitmap, u64 *tx_packets,
27804cc0bf5SNogah Frankel 				       u64 *tx_bytes)
27904cc0bf5SNogah Frankel {
28004cc0bf5SNogah Frankel 	int i;
28104cc0bf5SNogah Frankel 
28204cc0bf5SNogah Frankel 	*tx_packets = 0;
28304cc0bf5SNogah Frankel 	*tx_bytes = 0;
28404cc0bf5SNogah Frankel 	for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
28504cc0bf5SNogah Frankel 		if (prio_bitmap & BIT(i)) {
28604cc0bf5SNogah Frankel 			*tx_packets += xstats->tx_packets[i];
28704cc0bf5SNogah Frankel 			*tx_bytes += xstats->tx_bytes[i];
28804cc0bf5SNogah Frankel 		}
28904cc0bf5SNogah Frankel 	}
29004cc0bf5SNogah Frankel }
29104cc0bf5SNogah Frankel 
292cf9af379SPetr Machata static void
293cf9af379SPetr Machata mlxsw_sp_qdisc_collect_tc_stats(struct mlxsw_sp_port *mlxsw_sp_port,
294cf9af379SPetr Machata 				struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
295cf9af379SPetr Machata 				u64 *p_tx_bytes, u64 *p_tx_packets,
296cf9af379SPetr Machata 				u64 *p_drops, u64 *p_backlog)
297cf9af379SPetr Machata {
298cf9af379SPetr Machata 	u8 tclass_num = mlxsw_sp_qdisc->tclass_num;
299cf9af379SPetr Machata 	struct mlxsw_sp_port_xstats *xstats;
300cf9af379SPetr Machata 	u64 tx_bytes, tx_packets;
301cf9af379SPetr Machata 
302cf9af379SPetr Machata 	xstats = &mlxsw_sp_port->periodic_hw_stats.xstats;
303cf9af379SPetr Machata 	mlxsw_sp_qdisc_bstats_per_priority_get(xstats,
304cf9af379SPetr Machata 					       mlxsw_sp_qdisc->prio_bitmap,
305cf9af379SPetr Machata 					       &tx_packets, &tx_bytes);
306cf9af379SPetr Machata 
307cf9af379SPetr Machata 	*p_tx_packets += tx_packets;
308cf9af379SPetr Machata 	*p_tx_bytes += tx_bytes;
309cf9af379SPetr Machata 	*p_drops += xstats->wred_drop[tclass_num] +
310cf9af379SPetr Machata 		    mlxsw_sp_xstats_tail_drop(xstats, tclass_num);
311cf9af379SPetr Machata 	*p_backlog += mlxsw_sp_xstats_backlog(xstats, tclass_num);
312cf9af379SPetr Machata }
313cf9af379SPetr Machata 
314cf9af379SPetr Machata static void
315cf9af379SPetr Machata mlxsw_sp_qdisc_update_stats(struct mlxsw_sp *mlxsw_sp,
316cf9af379SPetr Machata 			    struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
317cf9af379SPetr Machata 			    u64 tx_bytes, u64 tx_packets,
318cf9af379SPetr Machata 			    u64 drops, u64 backlog,
319cf9af379SPetr Machata 			    struct tc_qopt_offload_stats *stats_ptr)
320cf9af379SPetr Machata {
321cf9af379SPetr Machata 	struct mlxsw_sp_qdisc_stats *stats_base = &mlxsw_sp_qdisc->stats_base;
322cf9af379SPetr Machata 
323cf9af379SPetr Machata 	tx_bytes -= stats_base->tx_bytes;
324cf9af379SPetr Machata 	tx_packets -= stats_base->tx_packets;
325cf9af379SPetr Machata 	drops -= stats_base->drops;
326cf9af379SPetr Machata 	backlog -= stats_base->backlog;
327cf9af379SPetr Machata 
328cf9af379SPetr Machata 	_bstats_update(stats_ptr->bstats, tx_bytes, tx_packets);
329cf9af379SPetr Machata 	stats_ptr->qstats->drops += drops;
330cf9af379SPetr Machata 	stats_ptr->qstats->backlog += mlxsw_sp_cells_bytes(mlxsw_sp, backlog);
331cf9af379SPetr Machata 
332cf9af379SPetr Machata 	stats_base->backlog += backlog;
333cf9af379SPetr Machata 	stats_base->drops += drops;
334cf9af379SPetr Machata 	stats_base->tx_bytes += tx_bytes;
335cf9af379SPetr Machata 	stats_base->tx_packets += tx_packets;
336cf9af379SPetr Machata }
337cf9af379SPetr Machata 
3383d0d5921SPetr Machata static void
3393d0d5921SPetr Machata mlxsw_sp_qdisc_get_tc_stats(struct mlxsw_sp_port *mlxsw_sp_port,
3403d0d5921SPetr Machata 			    struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
3413d0d5921SPetr Machata 			    struct tc_qopt_offload_stats *stats_ptr)
3423d0d5921SPetr Machata {
3433d0d5921SPetr Machata 	u64 tx_packets = 0;
3443d0d5921SPetr Machata 	u64 tx_bytes = 0;
3453d0d5921SPetr Machata 	u64 backlog = 0;
3463d0d5921SPetr Machata 	u64 drops = 0;
3473d0d5921SPetr Machata 
3483d0d5921SPetr Machata 	mlxsw_sp_qdisc_collect_tc_stats(mlxsw_sp_port, mlxsw_sp_qdisc,
3493d0d5921SPetr Machata 					&tx_bytes, &tx_packets,
3503d0d5921SPetr Machata 					&drops, &backlog);
3513d0d5921SPetr Machata 	mlxsw_sp_qdisc_update_stats(mlxsw_sp_port->mlxsw_sp, mlxsw_sp_qdisc,
3523d0d5921SPetr Machata 				    tx_bytes, tx_packets, drops, backlog,
3533d0d5921SPetr Machata 				    stats_ptr);
3543d0d5921SPetr Machata }
3553d0d5921SPetr Machata 
356562ffbc4SNogah Frankel static int
35796f17e07SNogah Frankel mlxsw_sp_tclass_congestion_enable(struct mlxsw_sp_port *mlxsw_sp_port,
35896f17e07SNogah Frankel 				  int tclass_num, u32 min, u32 max,
3598040c96bSPetr Machata 				  u32 probability, bool is_wred, bool is_ecn)
36096f17e07SNogah Frankel {
361db84924cSJiri Pirko 	char cwtpm_cmd[MLXSW_REG_CWTPM_LEN];
362db84924cSJiri Pirko 	char cwtp_cmd[MLXSW_REG_CWTP_LEN];
36396f17e07SNogah Frankel 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
36496f17e07SNogah Frankel 	int err;
36596f17e07SNogah Frankel 
36696f17e07SNogah Frankel 	mlxsw_reg_cwtp_pack(cwtp_cmd, mlxsw_sp_port->local_port, tclass_num);
36796f17e07SNogah Frankel 	mlxsw_reg_cwtp_profile_pack(cwtp_cmd, MLXSW_REG_CWTP_DEFAULT_PROFILE,
36896f17e07SNogah Frankel 				    roundup(min, MLXSW_REG_CWTP_MIN_VALUE),
36996f17e07SNogah Frankel 				    roundup(max, MLXSW_REG_CWTP_MIN_VALUE),
37096f17e07SNogah Frankel 				    probability);
37196f17e07SNogah Frankel 
37296f17e07SNogah Frankel 	err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(cwtp), cwtp_cmd);
37396f17e07SNogah Frankel 	if (err)
37496f17e07SNogah Frankel 		return err;
37596f17e07SNogah Frankel 
376db84924cSJiri Pirko 	mlxsw_reg_cwtpm_pack(cwtpm_cmd, mlxsw_sp_port->local_port, tclass_num,
3778040c96bSPetr Machata 			     MLXSW_REG_CWTP_DEFAULT_PROFILE, is_wred, is_ecn);
37896f17e07SNogah Frankel 
379db84924cSJiri Pirko 	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(cwtpm), cwtpm_cmd);
38096f17e07SNogah Frankel }
38196f17e07SNogah Frankel 
38296f17e07SNogah Frankel static int
38396f17e07SNogah Frankel mlxsw_sp_tclass_congestion_disable(struct mlxsw_sp_port *mlxsw_sp_port,
38496f17e07SNogah Frankel 				   int tclass_num)
38596f17e07SNogah Frankel {
38696f17e07SNogah Frankel 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
38796f17e07SNogah Frankel 	char cwtpm_cmd[MLXSW_REG_CWTPM_LEN];
38896f17e07SNogah Frankel 
38996f17e07SNogah Frankel 	mlxsw_reg_cwtpm_pack(cwtpm_cmd, mlxsw_sp_port->local_port, tclass_num,
39096f17e07SNogah Frankel 			     MLXSW_REG_CWTPM_RESET_PROFILE, false, false);
39196f17e07SNogah Frankel 	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(cwtpm), cwtpm_cmd);
39296f17e07SNogah Frankel }
39396f17e07SNogah Frankel 
394861fb829SNogah Frankel static void
395c2ed6db7SNogah Frankel mlxsw_sp_setup_tc_qdisc_red_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port,
396d56c8955SNogah Frankel 					struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
397861fb829SNogah Frankel {
398d56c8955SNogah Frankel 	u8 tclass_num = mlxsw_sp_qdisc->tclass_num;
3994d1a4b84SNogah Frankel 	struct mlxsw_sp_qdisc_stats *stats_base;
400861fb829SNogah Frankel 	struct mlxsw_sp_port_xstats *xstats;
4014d1a4b84SNogah Frankel 	struct red_stats *red_base;
402861fb829SNogah Frankel 
403861fb829SNogah Frankel 	xstats = &mlxsw_sp_port->periodic_hw_stats.xstats;
4044d1a4b84SNogah Frankel 	stats_base = &mlxsw_sp_qdisc->stats_base;
405c2ed6db7SNogah Frankel 	red_base = &mlxsw_sp_qdisc->xstats_base.red;
4063670756fSNogah Frankel 
40704cc0bf5SNogah Frankel 	mlxsw_sp_qdisc_bstats_per_priority_get(xstats,
40804cc0bf5SNogah Frankel 					       mlxsw_sp_qdisc->prio_bitmap,
40904cc0bf5SNogah Frankel 					       &stats_base->tx_packets,
41004cc0bf5SNogah Frankel 					       &stats_base->tx_bytes);
4114d1a4b84SNogah Frankel 	red_base->prob_drop = xstats->wred_drop[tclass_num];
41285005b82SPetr Machata 	red_base->pdrop = mlxsw_sp_xstats_tail_drop(xstats, tclass_num);
4133670756fSNogah Frankel 
414c2ed6db7SNogah Frankel 	stats_base->overlimits = red_base->prob_drop + red_base->prob_mark;
4154d1a4b84SNogah Frankel 	stats_base->drops = red_base->prob_drop + red_base->pdrop;
416416ef9b1SJakub Kicinski 
417416ef9b1SJakub Kicinski 	stats_base->backlog = 0;
418861fb829SNogah Frankel }
419861fb829SNogah Frankel 
42096f17e07SNogah Frankel static int
421cba7158fSNogah Frankel mlxsw_sp_qdisc_red_destroy(struct mlxsw_sp_port *mlxsw_sp_port,
422d56c8955SNogah Frankel 			   struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
42396f17e07SNogah Frankel {
424ee88450dSPetr Machata 	struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc;
425ee88450dSPetr Machata 	struct mlxsw_sp_qdisc *root_qdisc = &qdisc_state->root_qdisc;
426cc6e5c13SNogah Frankel 
427cc6e5c13SNogah Frankel 	if (root_qdisc != mlxsw_sp_qdisc)
428cc6e5c13SNogah Frankel 		root_qdisc->stats_base.backlog -=
429cc6e5c13SNogah Frankel 					mlxsw_sp_qdisc->stats_base.backlog;
430cc6e5c13SNogah Frankel 
4319a37a59fSNogah Frankel 	return mlxsw_sp_tclass_congestion_disable(mlxsw_sp_port,
432d56c8955SNogah Frankel 						  mlxsw_sp_qdisc->tclass_num);
43396f17e07SNogah Frankel }
43496f17e07SNogah Frankel 
43596f17e07SNogah Frankel static int
4369cf6c9c7SNogah Frankel mlxsw_sp_qdisc_red_check_params(struct mlxsw_sp_port *mlxsw_sp_port,
43796f17e07SNogah Frankel 				struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
4389cf6c9c7SNogah Frankel 				void *params)
43996f17e07SNogah Frankel {
44096f17e07SNogah Frankel 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
4419cf6c9c7SNogah Frankel 	struct tc_red_qopt_offload_params *p = params;
44296f17e07SNogah Frankel 
44396f17e07SNogah Frankel 	if (p->min > p->max) {
44496f17e07SNogah Frankel 		dev_err(mlxsw_sp->bus_info->dev,
44596f17e07SNogah Frankel 			"spectrum: RED: min %u is bigger then max %u\n", p->min,
44696f17e07SNogah Frankel 			p->max);
4479cf6c9c7SNogah Frankel 		return -EINVAL;
44896f17e07SNogah Frankel 	}
449914c4fc1SPetr Machata 	if (p->max > MLXSW_CORE_RES_GET(mlxsw_sp->core,
450914c4fc1SPetr Machata 					GUARANTEED_SHARED_BUFFER)) {
45196f17e07SNogah Frankel 		dev_err(mlxsw_sp->bus_info->dev,
45296f17e07SNogah Frankel 			"spectrum: RED: max value %u is too big\n", p->max);
4539cf6c9c7SNogah Frankel 		return -EINVAL;
45496f17e07SNogah Frankel 	}
45596f17e07SNogah Frankel 	if (p->min == 0 || p->max == 0) {
45696f17e07SNogah Frankel 		dev_err(mlxsw_sp->bus_info->dev,
45796f17e07SNogah Frankel 			"spectrum: RED: 0 value is illegal for min and max\n");
4589cf6c9c7SNogah Frankel 		return -EINVAL;
45996f17e07SNogah Frankel 	}
4609cf6c9c7SNogah Frankel 	return 0;
4619cf6c9c7SNogah Frankel }
4629cf6c9c7SNogah Frankel 
4639cf6c9c7SNogah Frankel static int
464c4e372e2SPetr Machata mlxsw_sp_qdisc_red_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
4659cf6c9c7SNogah Frankel 			   struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
4669cf6c9c7SNogah Frankel 			   void *params)
4679cf6c9c7SNogah Frankel {
4689cf6c9c7SNogah Frankel 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
4699cf6c9c7SNogah Frankel 	struct tc_red_qopt_offload_params *p = params;
4709cf6c9c7SNogah Frankel 	u8 tclass_num = mlxsw_sp_qdisc->tclass_num;
4719cf6c9c7SNogah Frankel 	u32 min, max;
4729cf6c9c7SNogah Frankel 	u64 prob;
47396f17e07SNogah Frankel 
47496f17e07SNogah Frankel 	/* calculate probability in percentage */
47596f17e07SNogah Frankel 	prob = p->probability;
47696f17e07SNogah Frankel 	prob *= 100;
47796f17e07SNogah Frankel 	prob = DIV_ROUND_UP(prob, 1 << 16);
47896f17e07SNogah Frankel 	prob = DIV_ROUND_UP(prob, 1 << 16);
47996f17e07SNogah Frankel 	min = mlxsw_sp_bytes_cells(mlxsw_sp, p->min);
48096f17e07SNogah Frankel 	max = mlxsw_sp_bytes_cells(mlxsw_sp, p->max);
4818040c96bSPetr Machata 	return mlxsw_sp_tclass_congestion_enable(mlxsw_sp_port, tclass_num,
4828040c96bSPetr Machata 						 min, max, prob,
4838040c96bSPetr Machata 						 !p->is_nodrop, p->is_ecn);
48496f17e07SNogah Frankel }
48596f17e07SNogah Frankel 
486416ef9b1SJakub Kicinski static void
487be1d5a8aSPetr Machata mlxsw_sp_qdisc_leaf_unoffload(struct mlxsw_sp_port *mlxsw_sp_port,
488be1d5a8aSPetr Machata 			      struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
489be1d5a8aSPetr Machata 			      struct gnet_stats_queue *qstats)
490be1d5a8aSPetr Machata {
491be1d5a8aSPetr Machata 	u64 backlog;
492be1d5a8aSPetr Machata 
493be1d5a8aSPetr Machata 	backlog = mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp,
494be1d5a8aSPetr Machata 				       mlxsw_sp_qdisc->stats_base.backlog);
495be1d5a8aSPetr Machata 	qstats->backlog -= backlog;
496be1d5a8aSPetr Machata 	mlxsw_sp_qdisc->stats_base.backlog = 0;
497be1d5a8aSPetr Machata }
498be1d5a8aSPetr Machata 
499be1d5a8aSPetr Machata static void
500416ef9b1SJakub Kicinski mlxsw_sp_qdisc_red_unoffload(struct mlxsw_sp_port *mlxsw_sp_port,
501416ef9b1SJakub Kicinski 			     struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
502416ef9b1SJakub Kicinski 			     void *params)
503416ef9b1SJakub Kicinski {
504416ef9b1SJakub Kicinski 	struct tc_red_qopt_offload_params *p = params;
505416ef9b1SJakub Kicinski 
506be1d5a8aSPetr Machata 	mlxsw_sp_qdisc_leaf_unoffload(mlxsw_sp_port, mlxsw_sp_qdisc, p->qstats);
507416ef9b1SJakub Kicinski }
508416ef9b1SJakub Kicinski 
509861fb829SNogah Frankel static int
510cba7158fSNogah Frankel mlxsw_sp_qdisc_get_red_xstats(struct mlxsw_sp_port *mlxsw_sp_port,
511861fb829SNogah Frankel 			      struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
512562ffbc4SNogah Frankel 			      void *xstats_ptr)
513861fb829SNogah Frankel {
5144d1a4b84SNogah Frankel 	struct red_stats *xstats_base = &mlxsw_sp_qdisc->xstats_base.red;
515d56c8955SNogah Frankel 	u8 tclass_num = mlxsw_sp_qdisc->tclass_num;
516861fb829SNogah Frankel 	struct mlxsw_sp_port_xstats *xstats;
517562ffbc4SNogah Frankel 	struct red_stats *res = xstats_ptr;
5188a29581eSPetr Machata 	int early_drops, pdrops;
519861fb829SNogah Frankel 
520861fb829SNogah Frankel 	xstats = &mlxsw_sp_port->periodic_hw_stats.xstats;
521861fb829SNogah Frankel 
522f8253df5SNogah Frankel 	early_drops = xstats->wred_drop[tclass_num] - xstats_base->prob_drop;
52385005b82SPetr Machata 	pdrops = mlxsw_sp_xstats_tail_drop(xstats, tclass_num) -
52485005b82SPetr Machata 		 xstats_base->pdrop;
525f8253df5SNogah Frankel 
526f8253df5SNogah Frankel 	res->pdrop += pdrops;
527f8253df5SNogah Frankel 	res->prob_drop += early_drops;
528f8253df5SNogah Frankel 
529f8253df5SNogah Frankel 	xstats_base->pdrop += pdrops;
530f8253df5SNogah Frankel 	xstats_base->prob_drop += early_drops;
531861fb829SNogah Frankel 	return 0;
532861fb829SNogah Frankel }
533861fb829SNogah Frankel 
5343670756fSNogah Frankel static int
535cba7158fSNogah Frankel mlxsw_sp_qdisc_get_red_stats(struct mlxsw_sp_port *mlxsw_sp_port,
5363670756fSNogah Frankel 			     struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
537562ffbc4SNogah Frankel 			     struct tc_qopt_offload_stats *stats_ptr)
5383670756fSNogah Frankel {
539d56c8955SNogah Frankel 	u8 tclass_num = mlxsw_sp_qdisc->tclass_num;
5404d1a4b84SNogah Frankel 	struct mlxsw_sp_qdisc_stats *stats_base;
5413670756fSNogah Frankel 	struct mlxsw_sp_port_xstats *xstats;
542cf9af379SPetr Machata 	u64 overlimits;
5433670756fSNogah Frankel 
5443670756fSNogah Frankel 	xstats = &mlxsw_sp_port->periodic_hw_stats.xstats;
5454d1a4b84SNogah Frankel 	stats_base = &mlxsw_sp_qdisc->stats_base;
5463670756fSNogah Frankel 
5473d0d5921SPetr Machata 	mlxsw_sp_qdisc_get_tc_stats(mlxsw_sp_port, mlxsw_sp_qdisc, stats_ptr);
5488a29581eSPetr Machata 	overlimits = xstats->wred_drop[tclass_num] - stats_base->overlimits;
5493670756fSNogah Frankel 
550562ffbc4SNogah Frankel 	stats_ptr->qstats->overlimits += overlimits;
5514d1a4b84SNogah Frankel 	stats_base->overlimits += overlimits;
552cf9af379SPetr Machata 
5533670756fSNogah Frankel 	return 0;
5543670756fSNogah Frankel }
5553670756fSNogah Frankel 
55696f17e07SNogah Frankel #define MLXSW_SP_PORT_DEFAULT_TCLASS 0
55796f17e07SNogah Frankel 
558562ffbc4SNogah Frankel static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_red = {
5599cf6c9c7SNogah Frankel 	.type = MLXSW_SP_QDISC_RED,
5609cf6c9c7SNogah Frankel 	.check_params = mlxsw_sp_qdisc_red_check_params,
5619cf6c9c7SNogah Frankel 	.replace = mlxsw_sp_qdisc_red_replace,
562416ef9b1SJakub Kicinski 	.unoffload = mlxsw_sp_qdisc_red_unoffload,
5639a37a59fSNogah Frankel 	.destroy = mlxsw_sp_qdisc_red_destroy,
564562ffbc4SNogah Frankel 	.get_stats = mlxsw_sp_qdisc_get_red_stats,
565562ffbc4SNogah Frankel 	.get_xstats = mlxsw_sp_qdisc_get_red_xstats,
5669cf6c9c7SNogah Frankel 	.clean_stats = mlxsw_sp_setup_tc_qdisc_red_clean_stats,
567562ffbc4SNogah Frankel };
568562ffbc4SNogah Frankel 
56996f17e07SNogah Frankel int mlxsw_sp_setup_tc_red(struct mlxsw_sp_port *mlxsw_sp_port,
57096f17e07SNogah Frankel 			  struct tc_red_qopt_offload *p)
57196f17e07SNogah Frankel {
57296f17e07SNogah Frankel 	struct mlxsw_sp_qdisc *mlxsw_sp_qdisc;
57396f17e07SNogah Frankel 
574eed4baebSNogah Frankel 	mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent, false);
575eed4baebSNogah Frankel 	if (!mlxsw_sp_qdisc)
57696f17e07SNogah Frankel 		return -EOPNOTSUPP;
57796f17e07SNogah Frankel 
578cba7158fSNogah Frankel 	if (p->command == TC_RED_REPLACE)
5799cf6c9c7SNogah Frankel 		return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle,
580562ffbc4SNogah Frankel 					      mlxsw_sp_qdisc,
581562ffbc4SNogah Frankel 					      &mlxsw_sp_qdisc_ops_red,
582562ffbc4SNogah Frankel 					      &p->set);
583cba7158fSNogah Frankel 
584cba7158fSNogah Frankel 	if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle,
585cba7158fSNogah Frankel 				    MLXSW_SP_QDISC_RED))
586cba7158fSNogah Frankel 		return -EOPNOTSUPP;
587cba7158fSNogah Frankel 
588cba7158fSNogah Frankel 	switch (p->command) {
58996f17e07SNogah Frankel 	case TC_RED_DESTROY:
5909a37a59fSNogah Frankel 		return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
591861fb829SNogah Frankel 	case TC_RED_XSTATS:
592562ffbc4SNogah Frankel 		return mlxsw_sp_qdisc_get_xstats(mlxsw_sp_port, mlxsw_sp_qdisc,
593861fb829SNogah Frankel 						 p->xstats);
5943670756fSNogah Frankel 	case TC_RED_STATS:
595562ffbc4SNogah Frankel 		return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc,
5963670756fSNogah Frankel 						&p->stats);
59796f17e07SNogah Frankel 	default:
59896f17e07SNogah Frankel 		return -EOPNOTSUPP;
59996f17e07SNogah Frankel 	}
60096f17e07SNogah Frankel }
601371b437aSNogah Frankel 
602a44f58c4SPetr Machata static void
603a44f58c4SPetr Machata mlxsw_sp_setup_tc_qdisc_leaf_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port,
604a44f58c4SPetr Machata 					 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
605a44f58c4SPetr Machata {
606a44f58c4SPetr Machata 	u64 backlog_cells = 0;
607a44f58c4SPetr Machata 	u64 tx_packets = 0;
608a44f58c4SPetr Machata 	u64 tx_bytes = 0;
609a44f58c4SPetr Machata 	u64 drops = 0;
610a44f58c4SPetr Machata 
611a44f58c4SPetr Machata 	mlxsw_sp_qdisc_collect_tc_stats(mlxsw_sp_port, mlxsw_sp_qdisc,
612a44f58c4SPetr Machata 					&tx_bytes, &tx_packets,
613a44f58c4SPetr Machata 					&drops, &backlog_cells);
614a44f58c4SPetr Machata 
615a44f58c4SPetr Machata 	mlxsw_sp_qdisc->stats_base.tx_packets = tx_packets;
616a44f58c4SPetr Machata 	mlxsw_sp_qdisc->stats_base.tx_bytes = tx_bytes;
617a44f58c4SPetr Machata 	mlxsw_sp_qdisc->stats_base.drops = drops;
618a44f58c4SPetr Machata 	mlxsw_sp_qdisc->stats_base.backlog = 0;
619a44f58c4SPetr Machata }
620a44f58c4SPetr Machata 
621a44f58c4SPetr Machata static int
622a44f58c4SPetr Machata mlxsw_sp_qdisc_tbf_destroy(struct mlxsw_sp_port *mlxsw_sp_port,
623a44f58c4SPetr Machata 			   struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
624a44f58c4SPetr Machata {
625ee88450dSPetr Machata 	struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc;
626ee88450dSPetr Machata 	struct mlxsw_sp_qdisc *root_qdisc = &qdisc_state->root_qdisc;
627a44f58c4SPetr Machata 
628a44f58c4SPetr Machata 	if (root_qdisc != mlxsw_sp_qdisc)
629a44f58c4SPetr Machata 		root_qdisc->stats_base.backlog -=
630a44f58c4SPetr Machata 					mlxsw_sp_qdisc->stats_base.backlog;
631a44f58c4SPetr Machata 
632a44f58c4SPetr Machata 	return mlxsw_sp_port_ets_maxrate_set(mlxsw_sp_port,
633a44f58c4SPetr Machata 					     MLXSW_REG_QEEC_HR_SUBGROUP,
634a44f58c4SPetr Machata 					     mlxsw_sp_qdisc->tclass_num, 0,
635a44f58c4SPetr Machata 					     MLXSW_REG_QEEC_MAS_DIS, 0);
636a44f58c4SPetr Machata }
637a44f58c4SPetr Machata 
638a44f58c4SPetr Machata static int
639a44f58c4SPetr Machata mlxsw_sp_qdisc_tbf_bs(struct mlxsw_sp_port *mlxsw_sp_port,
640a44f58c4SPetr Machata 		      u32 max_size, u8 *p_burst_size)
641a44f58c4SPetr Machata {
642a44f58c4SPetr Machata 	/* TBF burst size is configured in bytes. The ASIC burst size value is
643a44f58c4SPetr Machata 	 * ((2 ^ bs) * 512 bits. Convert the TBF bytes to 512-bit units.
644a44f58c4SPetr Machata 	 */
645a44f58c4SPetr Machata 	u32 bs512 = max_size / 64;
646a44f58c4SPetr Machata 	u8 bs = fls(bs512);
647a44f58c4SPetr Machata 
648a44f58c4SPetr Machata 	if (!bs)
649a44f58c4SPetr Machata 		return -EINVAL;
650a44f58c4SPetr Machata 	--bs;
651a44f58c4SPetr Machata 
652a44f58c4SPetr Machata 	/* Demand a power of two. */
653a44f58c4SPetr Machata 	if ((1 << bs) != bs512)
654a44f58c4SPetr Machata 		return -EINVAL;
655a44f58c4SPetr Machata 
656a44f58c4SPetr Machata 	if (bs < mlxsw_sp_port->mlxsw_sp->lowest_shaper_bs ||
657a44f58c4SPetr Machata 	    bs > MLXSW_REG_QEEC_HIGHEST_SHAPER_BS)
658a44f58c4SPetr Machata 		return -EINVAL;
659a44f58c4SPetr Machata 
660a44f58c4SPetr Machata 	*p_burst_size = bs;
661a44f58c4SPetr Machata 	return 0;
662a44f58c4SPetr Machata }
663a44f58c4SPetr Machata 
664a44f58c4SPetr Machata static u32
665a44f58c4SPetr Machata mlxsw_sp_qdisc_tbf_max_size(u8 bs)
666a44f58c4SPetr Machata {
667a44f58c4SPetr Machata 	return (1U << bs) * 64;
668a44f58c4SPetr Machata }
669a44f58c4SPetr Machata 
670a44f58c4SPetr Machata static u64
671a44f58c4SPetr Machata mlxsw_sp_qdisc_tbf_rate_kbps(struct tc_tbf_qopt_offload_replace_params *p)
672a44f58c4SPetr Machata {
673a44f58c4SPetr Machata 	/* TBF interface is in bytes/s, whereas Spectrum ASIC is configured in
674a44f58c4SPetr Machata 	 * Kbits/s.
675a44f58c4SPetr Machata 	 */
67691a7d4bfSNathan Chancellor 	return div_u64(p->rate.rate_bytes_ps, 1000) * 8;
677a44f58c4SPetr Machata }
678a44f58c4SPetr Machata 
679a44f58c4SPetr Machata static int
680a44f58c4SPetr Machata mlxsw_sp_qdisc_tbf_check_params(struct mlxsw_sp_port *mlxsw_sp_port,
681a44f58c4SPetr Machata 				struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
682a44f58c4SPetr Machata 				void *params)
683a44f58c4SPetr Machata {
684a44f58c4SPetr Machata 	struct tc_tbf_qopt_offload_replace_params *p = params;
685a44f58c4SPetr Machata 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
686a44f58c4SPetr Machata 	u64 rate_kbps = mlxsw_sp_qdisc_tbf_rate_kbps(p);
687a44f58c4SPetr Machata 	u8 burst_size;
688a44f58c4SPetr Machata 	int err;
689a44f58c4SPetr Machata 
690a44f58c4SPetr Machata 	if (rate_kbps >= MLXSW_REG_QEEC_MAS_DIS) {
691a44f58c4SPetr Machata 		dev_err(mlxsw_sp_port->mlxsw_sp->bus_info->dev,
692a44f58c4SPetr Machata 			"spectrum: TBF: rate of %lluKbps must be below %u\n",
693a44f58c4SPetr Machata 			rate_kbps, MLXSW_REG_QEEC_MAS_DIS);
694a44f58c4SPetr Machata 		return -EINVAL;
695a44f58c4SPetr Machata 	}
696a44f58c4SPetr Machata 
697a44f58c4SPetr Machata 	err = mlxsw_sp_qdisc_tbf_bs(mlxsw_sp_port, p->max_size, &burst_size);
698a44f58c4SPetr Machata 	if (err) {
699a44f58c4SPetr Machata 		u8 highest_shaper_bs = MLXSW_REG_QEEC_HIGHEST_SHAPER_BS;
700a44f58c4SPetr Machata 
701a44f58c4SPetr Machata 		dev_err(mlxsw_sp->bus_info->dev,
702a44f58c4SPetr Machata 			"spectrum: TBF: invalid burst size of %u, must be a power of two between %u and %u",
703a44f58c4SPetr Machata 			p->max_size,
704a44f58c4SPetr Machata 			mlxsw_sp_qdisc_tbf_max_size(mlxsw_sp->lowest_shaper_bs),
705a44f58c4SPetr Machata 			mlxsw_sp_qdisc_tbf_max_size(highest_shaper_bs));
706a44f58c4SPetr Machata 		return -EINVAL;
707a44f58c4SPetr Machata 	}
708a44f58c4SPetr Machata 
709a44f58c4SPetr Machata 	return 0;
710a44f58c4SPetr Machata }
711a44f58c4SPetr Machata 
712a44f58c4SPetr Machata static int
713c4e372e2SPetr Machata mlxsw_sp_qdisc_tbf_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
714a44f58c4SPetr Machata 			   struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
715a44f58c4SPetr Machata 			   void *params)
716a44f58c4SPetr Machata {
717a44f58c4SPetr Machata 	struct tc_tbf_qopt_offload_replace_params *p = params;
718a44f58c4SPetr Machata 	u64 rate_kbps = mlxsw_sp_qdisc_tbf_rate_kbps(p);
719a44f58c4SPetr Machata 	u8 burst_size;
720a44f58c4SPetr Machata 	int err;
721a44f58c4SPetr Machata 
722a44f58c4SPetr Machata 	err = mlxsw_sp_qdisc_tbf_bs(mlxsw_sp_port, p->max_size, &burst_size);
723a44f58c4SPetr Machata 	if (WARN_ON_ONCE(err))
724a44f58c4SPetr Machata 		/* check_params above was supposed to reject this value. */
725a44f58c4SPetr Machata 		return -EINVAL;
726a44f58c4SPetr Machata 
727a44f58c4SPetr Machata 	/* Configure subgroup shaper, so that both UC and MC traffic is subject
728a44f58c4SPetr Machata 	 * to shaping. That is unlike RED, however UC queue lengths are going to
729a44f58c4SPetr Machata 	 * be different than MC ones due to different pool and quota
730a44f58c4SPetr Machata 	 * configurations, so the configuration is not applicable. For shaper on
731a44f58c4SPetr Machata 	 * the other hand, subjecting the overall stream to the configured
732a44f58c4SPetr Machata 	 * shaper makes sense. Also note that that is what we do for
733a44f58c4SPetr Machata 	 * ieee_setmaxrate().
734a44f58c4SPetr Machata 	 */
735a44f58c4SPetr Machata 	return mlxsw_sp_port_ets_maxrate_set(mlxsw_sp_port,
736a44f58c4SPetr Machata 					     MLXSW_REG_QEEC_HR_SUBGROUP,
737a44f58c4SPetr Machata 					     mlxsw_sp_qdisc->tclass_num, 0,
738a44f58c4SPetr Machata 					     rate_kbps, burst_size);
739a44f58c4SPetr Machata }
740a44f58c4SPetr Machata 
741a44f58c4SPetr Machata static void
742a44f58c4SPetr Machata mlxsw_sp_qdisc_tbf_unoffload(struct mlxsw_sp_port *mlxsw_sp_port,
743a44f58c4SPetr Machata 			     struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
744a44f58c4SPetr Machata 			     void *params)
745a44f58c4SPetr Machata {
746a44f58c4SPetr Machata 	struct tc_tbf_qopt_offload_replace_params *p = params;
747a44f58c4SPetr Machata 
748a44f58c4SPetr Machata 	mlxsw_sp_qdisc_leaf_unoffload(mlxsw_sp_port, mlxsw_sp_qdisc, p->qstats);
749a44f58c4SPetr Machata }
750a44f58c4SPetr Machata 
751a44f58c4SPetr Machata static int
752a44f58c4SPetr Machata mlxsw_sp_qdisc_get_tbf_stats(struct mlxsw_sp_port *mlxsw_sp_port,
753a44f58c4SPetr Machata 			     struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
754a44f58c4SPetr Machata 			     struct tc_qopt_offload_stats *stats_ptr)
755a44f58c4SPetr Machata {
756a44f58c4SPetr Machata 	mlxsw_sp_qdisc_get_tc_stats(mlxsw_sp_port, mlxsw_sp_qdisc,
757a44f58c4SPetr Machata 				    stats_ptr);
758a44f58c4SPetr Machata 	return 0;
759a44f58c4SPetr Machata }
760a44f58c4SPetr Machata 
761a44f58c4SPetr Machata static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_tbf = {
762a44f58c4SPetr Machata 	.type = MLXSW_SP_QDISC_TBF,
763a44f58c4SPetr Machata 	.check_params = mlxsw_sp_qdisc_tbf_check_params,
764a44f58c4SPetr Machata 	.replace = mlxsw_sp_qdisc_tbf_replace,
765a44f58c4SPetr Machata 	.unoffload = mlxsw_sp_qdisc_tbf_unoffload,
766a44f58c4SPetr Machata 	.destroy = mlxsw_sp_qdisc_tbf_destroy,
767a44f58c4SPetr Machata 	.get_stats = mlxsw_sp_qdisc_get_tbf_stats,
768a44f58c4SPetr Machata 	.clean_stats = mlxsw_sp_setup_tc_qdisc_leaf_clean_stats,
769a44f58c4SPetr Machata };
770a44f58c4SPetr Machata 
771a44f58c4SPetr Machata int mlxsw_sp_setup_tc_tbf(struct mlxsw_sp_port *mlxsw_sp_port,
772a44f58c4SPetr Machata 			  struct tc_tbf_qopt_offload *p)
773a44f58c4SPetr Machata {
774a44f58c4SPetr Machata 	struct mlxsw_sp_qdisc *mlxsw_sp_qdisc;
775a44f58c4SPetr Machata 
776a44f58c4SPetr Machata 	mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent, false);
777a44f58c4SPetr Machata 	if (!mlxsw_sp_qdisc)
778a44f58c4SPetr Machata 		return -EOPNOTSUPP;
779a44f58c4SPetr Machata 
780a44f58c4SPetr Machata 	if (p->command == TC_TBF_REPLACE)
781a44f58c4SPetr Machata 		return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle,
782a44f58c4SPetr Machata 					      mlxsw_sp_qdisc,
783a44f58c4SPetr Machata 					      &mlxsw_sp_qdisc_ops_tbf,
784a44f58c4SPetr Machata 					      &p->replace_params);
785a44f58c4SPetr Machata 
786a44f58c4SPetr Machata 	if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle,
787a44f58c4SPetr Machata 				    MLXSW_SP_QDISC_TBF))
788a44f58c4SPetr Machata 		return -EOPNOTSUPP;
789a44f58c4SPetr Machata 
790a44f58c4SPetr Machata 	switch (p->command) {
791a44f58c4SPetr Machata 	case TC_TBF_DESTROY:
792a44f58c4SPetr Machata 		return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
793a44f58c4SPetr Machata 	case TC_TBF_STATS:
794a44f58c4SPetr Machata 		return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc,
795a44f58c4SPetr Machata 						&p->stats);
796a44f58c4SPetr Machata 	default:
797a44f58c4SPetr Machata 		return -EOPNOTSUPP;
798a44f58c4SPetr Machata 	}
799a44f58c4SPetr Machata }
800a44f58c4SPetr Machata 
80146a3615bSNogah Frankel static int
8027bec1a45SPetr Machata mlxsw_sp_qdisc_fifo_destroy(struct mlxsw_sp_port *mlxsw_sp_port,
8037bec1a45SPetr Machata 			    struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
8047bec1a45SPetr Machata {
8057bec1a45SPetr Machata 	struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc;
8067bec1a45SPetr Machata 	struct mlxsw_sp_qdisc *root_qdisc = &qdisc_state->root_qdisc;
8077bec1a45SPetr Machata 
8087bec1a45SPetr Machata 	if (root_qdisc != mlxsw_sp_qdisc)
8097bec1a45SPetr Machata 		root_qdisc->stats_base.backlog -=
8107bec1a45SPetr Machata 					mlxsw_sp_qdisc->stats_base.backlog;
8117bec1a45SPetr Machata 	return 0;
8127bec1a45SPetr Machata }
8137bec1a45SPetr Machata 
8147bec1a45SPetr Machata static int
8157bec1a45SPetr Machata mlxsw_sp_qdisc_fifo_check_params(struct mlxsw_sp_port *mlxsw_sp_port,
8167bec1a45SPetr Machata 				 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
8177bec1a45SPetr Machata 				 void *params)
8187bec1a45SPetr Machata {
8197bec1a45SPetr Machata 	return 0;
8207bec1a45SPetr Machata }
8217bec1a45SPetr Machata 
8227bec1a45SPetr Machata static int
8237bec1a45SPetr Machata mlxsw_sp_qdisc_fifo_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
8247bec1a45SPetr Machata 			    struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
8257bec1a45SPetr Machata 			    void *params)
8267bec1a45SPetr Machata {
8277bec1a45SPetr Machata 	return 0;
8287bec1a45SPetr Machata }
8297bec1a45SPetr Machata 
8307bec1a45SPetr Machata static int
8317bec1a45SPetr Machata mlxsw_sp_qdisc_get_fifo_stats(struct mlxsw_sp_port *mlxsw_sp_port,
8327bec1a45SPetr Machata 			      struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
8337bec1a45SPetr Machata 			      struct tc_qopt_offload_stats *stats_ptr)
8347bec1a45SPetr Machata {
8357bec1a45SPetr Machata 	mlxsw_sp_qdisc_get_tc_stats(mlxsw_sp_port, mlxsw_sp_qdisc,
8367bec1a45SPetr Machata 				    stats_ptr);
8377bec1a45SPetr Machata 	return 0;
8387bec1a45SPetr Machata }
8397bec1a45SPetr Machata 
8407bec1a45SPetr Machata static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_fifo = {
8417bec1a45SPetr Machata 	.type = MLXSW_SP_QDISC_FIFO,
8427bec1a45SPetr Machata 	.check_params = mlxsw_sp_qdisc_fifo_check_params,
8437bec1a45SPetr Machata 	.replace = mlxsw_sp_qdisc_fifo_replace,
8447bec1a45SPetr Machata 	.destroy = mlxsw_sp_qdisc_fifo_destroy,
8457bec1a45SPetr Machata 	.get_stats = mlxsw_sp_qdisc_get_fifo_stats,
8467bec1a45SPetr Machata 	.clean_stats = mlxsw_sp_setup_tc_qdisc_leaf_clean_stats,
8477bec1a45SPetr Machata };
8487bec1a45SPetr Machata 
8497bec1a45SPetr Machata int mlxsw_sp_setup_tc_fifo(struct mlxsw_sp_port *mlxsw_sp_port,
8507bec1a45SPetr Machata 			   struct tc_fifo_qopt_offload *p)
8517bec1a45SPetr Machata {
8527bec1a45SPetr Machata 	struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc;
8537bec1a45SPetr Machata 	struct mlxsw_sp_qdisc *mlxsw_sp_qdisc;
8547bec1a45SPetr Machata 	int tclass, child_index;
8557bec1a45SPetr Machata 	u32 parent_handle;
8567bec1a45SPetr Machata 
8577bec1a45SPetr Machata 	/* Invisible FIFOs are tracked in future_handle and future_fifos. Make
8587bec1a45SPetr Machata 	 * sure that not more than one qdisc is created for a port at a time.
8597bec1a45SPetr Machata 	 * RTNL is a simple proxy for that.
8607bec1a45SPetr Machata 	 */
8617bec1a45SPetr Machata 	ASSERT_RTNL();
8627bec1a45SPetr Machata 
8637bec1a45SPetr Machata 	mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent, false);
8647bec1a45SPetr Machata 	if (!mlxsw_sp_qdisc && p->handle == TC_H_UNSPEC) {
8657bec1a45SPetr Machata 		parent_handle = TC_H_MAJ(p->parent);
8667bec1a45SPetr Machata 		if (parent_handle != qdisc_state->future_handle) {
8677bec1a45SPetr Machata 			/* This notifications is for a different Qdisc than
8687bec1a45SPetr Machata 			 * previously. Wipe the future cache.
8697bec1a45SPetr Machata 			 */
8707bec1a45SPetr Machata 			memset(qdisc_state->future_fifos, 0,
8717bec1a45SPetr Machata 			       sizeof(qdisc_state->future_fifos));
8727bec1a45SPetr Machata 			qdisc_state->future_handle = parent_handle;
8737bec1a45SPetr Machata 		}
8747bec1a45SPetr Machata 
8757bec1a45SPetr Machata 		child_index = TC_H_MIN(p->parent);
8767bec1a45SPetr Machata 		tclass = MLXSW_SP_PRIO_CHILD_TO_TCLASS(child_index);
8777bec1a45SPetr Machata 		if (tclass < IEEE_8021QAZ_MAX_TCS) {
8787bec1a45SPetr Machata 			if (p->command == TC_FIFO_REPLACE)
8797bec1a45SPetr Machata 				qdisc_state->future_fifos[tclass] = true;
8807bec1a45SPetr Machata 			else if (p->command == TC_FIFO_DESTROY)
8817bec1a45SPetr Machata 				qdisc_state->future_fifos[tclass] = false;
8827bec1a45SPetr Machata 		}
8837bec1a45SPetr Machata 	}
8847bec1a45SPetr Machata 	if (!mlxsw_sp_qdisc)
8857bec1a45SPetr Machata 		return -EOPNOTSUPP;
8867bec1a45SPetr Machata 
8877bec1a45SPetr Machata 	if (p->command == TC_FIFO_REPLACE) {
8887bec1a45SPetr Machata 		return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle,
8897bec1a45SPetr Machata 					      mlxsw_sp_qdisc,
8907bec1a45SPetr Machata 					      &mlxsw_sp_qdisc_ops_fifo, NULL);
8917bec1a45SPetr Machata 	}
8927bec1a45SPetr Machata 
8937bec1a45SPetr Machata 	if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle,
8947bec1a45SPetr Machata 				    MLXSW_SP_QDISC_FIFO))
8957bec1a45SPetr Machata 		return -EOPNOTSUPP;
8967bec1a45SPetr Machata 
8977bec1a45SPetr Machata 	switch (p->command) {
8987bec1a45SPetr Machata 	case TC_FIFO_DESTROY:
8997bec1a45SPetr Machata 		if (p->handle == mlxsw_sp_qdisc->handle)
9007bec1a45SPetr Machata 			return mlxsw_sp_qdisc_destroy(mlxsw_sp_port,
9017bec1a45SPetr Machata 						      mlxsw_sp_qdisc);
9027bec1a45SPetr Machata 		return 0;
9037bec1a45SPetr Machata 	case TC_FIFO_STATS:
9047bec1a45SPetr Machata 		return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc,
9057bec1a45SPetr Machata 						&p->stats);
9067bec1a45SPetr Machata 	case TC_FIFO_REPLACE: /* Handled above. */
9077bec1a45SPetr Machata 		break;
9087bec1a45SPetr Machata 	}
9097bec1a45SPetr Machata 
9107bec1a45SPetr Machata 	return -EOPNOTSUPP;
9117bec1a45SPetr Machata }
9127bec1a45SPetr Machata 
9137bec1a45SPetr Machata static int
9147917f52aSPetr Machata __mlxsw_sp_qdisc_ets_destroy(struct mlxsw_sp_port *mlxsw_sp_port)
91546a3615bSNogah Frankel {
916ee88450dSPetr Machata 	struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc;
91746a3615bSNogah Frankel 	int i;
91846a3615bSNogah Frankel 
919eed4baebSNogah Frankel 	for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
92046a3615bSNogah Frankel 		mlxsw_sp_port_prio_tc_set(mlxsw_sp_port, i,
92146a3615bSNogah Frankel 					  MLXSW_SP_PORT_DEFAULT_TCLASS);
9227917f52aSPetr Machata 		mlxsw_sp_port_ets_set(mlxsw_sp_port,
9237917f52aSPetr Machata 				      MLXSW_REG_QEEC_HR_SUBGROUP,
9247917f52aSPetr Machata 				      i, 0, false, 0);
925eed4baebSNogah Frankel 		mlxsw_sp_qdisc_destroy(mlxsw_sp_port,
926ee88450dSPetr Machata 				       &qdisc_state->tclass_qdiscs[i]);
927ee88450dSPetr Machata 		qdisc_state->tclass_qdiscs[i].prio_bitmap = 0;
928eed4baebSNogah Frankel 	}
92946a3615bSNogah Frankel 
93046a3615bSNogah Frankel 	return 0;
93146a3615bSNogah Frankel }
93246a3615bSNogah Frankel 
93346a3615bSNogah Frankel static int
9347917f52aSPetr Machata mlxsw_sp_qdisc_prio_destroy(struct mlxsw_sp_port *mlxsw_sp_port,
9357917f52aSPetr Machata 			    struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
9367917f52aSPetr Machata {
9377917f52aSPetr Machata 	return __mlxsw_sp_qdisc_ets_destroy(mlxsw_sp_port);
9387917f52aSPetr Machata }
9397917f52aSPetr Machata 
9407917f52aSPetr Machata static int
9417917f52aSPetr Machata __mlxsw_sp_qdisc_ets_check_params(unsigned int nbands)
9427917f52aSPetr Machata {
9437917f52aSPetr Machata 	if (nbands > IEEE_8021QAZ_MAX_TCS)
9447917f52aSPetr Machata 		return -EOPNOTSUPP;
9457917f52aSPetr Machata 
9467917f52aSPetr Machata 	return 0;
9477917f52aSPetr Machata }
9487917f52aSPetr Machata 
9497917f52aSPetr Machata static int
95046a3615bSNogah Frankel mlxsw_sp_qdisc_prio_check_params(struct mlxsw_sp_port *mlxsw_sp_port,
95146a3615bSNogah Frankel 				 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
95246a3615bSNogah Frankel 				 void *params)
95346a3615bSNogah Frankel {
95446a3615bSNogah Frankel 	struct tc_prio_qopt_offload_params *p = params;
95546a3615bSNogah Frankel 
9567917f52aSPetr Machata 	return __mlxsw_sp_qdisc_ets_check_params(p->bands);
95746a3615bSNogah Frankel }
95846a3615bSNogah Frankel 
95946a3615bSNogah Frankel static int
960c4e372e2SPetr Machata __mlxsw_sp_qdisc_ets_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
9617917f52aSPetr Machata 			     unsigned int nbands,
9627917f52aSPetr Machata 			     const unsigned int *quanta,
9637917f52aSPetr Machata 			     const unsigned int *weights,
9647917f52aSPetr Machata 			     const u8 *priomap)
96546a3615bSNogah Frankel {
966ee88450dSPetr Machata 	struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc;
96704cc0bf5SNogah Frankel 	struct mlxsw_sp_qdisc *child_qdisc;
968cc6e5c13SNogah Frankel 	int tclass, i, band, backlog;
96904cc0bf5SNogah Frankel 	u8 old_priomap;
97046a3615bSNogah Frankel 	int err;
97146a3615bSNogah Frankel 
9727917f52aSPetr Machata 	for (band = 0; band < nbands; band++) {
97304cc0bf5SNogah Frankel 		tclass = MLXSW_SP_PRIO_BAND_TO_TCLASS(band);
974ee88450dSPetr Machata 		child_qdisc = &qdisc_state->tclass_qdiscs[tclass];
97504cc0bf5SNogah Frankel 		old_priomap = child_qdisc->prio_bitmap;
97604cc0bf5SNogah Frankel 		child_qdisc->prio_bitmap = 0;
9777917f52aSPetr Machata 
9787917f52aSPetr Machata 		err = mlxsw_sp_port_ets_set(mlxsw_sp_port,
9797917f52aSPetr Machata 					    MLXSW_REG_QEEC_HR_SUBGROUP,
9807917f52aSPetr Machata 					    tclass, 0, !!quanta[band],
9817917f52aSPetr Machata 					    weights[band]);
9827917f52aSPetr Machata 		if (err)
9837917f52aSPetr Machata 			return err;
9847917f52aSPetr Machata 
98546a3615bSNogah Frankel 		for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
9867917f52aSPetr Machata 			if (priomap[i] == band) {
98704cc0bf5SNogah Frankel 				child_qdisc->prio_bitmap |= BIT(i);
98804cc0bf5SNogah Frankel 				if (BIT(i) & old_priomap)
98904cc0bf5SNogah Frankel 					continue;
99004cc0bf5SNogah Frankel 				err = mlxsw_sp_port_prio_tc_set(mlxsw_sp_port,
99104cc0bf5SNogah Frankel 								i, tclass);
99246a3615bSNogah Frankel 				if (err)
99346a3615bSNogah Frankel 					return err;
99404cc0bf5SNogah Frankel 			}
99504cc0bf5SNogah Frankel 		}
99604cc0bf5SNogah Frankel 		if (old_priomap != child_qdisc->prio_bitmap &&
997cc6e5c13SNogah Frankel 		    child_qdisc->ops && child_qdisc->ops->clean_stats) {
998cc6e5c13SNogah Frankel 			backlog = child_qdisc->stats_base.backlog;
99904cc0bf5SNogah Frankel 			child_qdisc->ops->clean_stats(mlxsw_sp_port,
100004cc0bf5SNogah Frankel 						      child_qdisc);
1001cc6e5c13SNogah Frankel 			child_qdisc->stats_base.backlog = backlog;
1002cc6e5c13SNogah Frankel 		}
10037bec1a45SPetr Machata 
10047bec1a45SPetr Machata 		if (handle == qdisc_state->future_handle &&
10057bec1a45SPetr Machata 		    qdisc_state->future_fifos[tclass]) {
10067bec1a45SPetr Machata 			err = mlxsw_sp_qdisc_replace(mlxsw_sp_port, TC_H_UNSPEC,
10077bec1a45SPetr Machata 						     child_qdisc,
10087bec1a45SPetr Machata 						     &mlxsw_sp_qdisc_ops_fifo,
10097bec1a45SPetr Machata 						     NULL);
10107bec1a45SPetr Machata 			if (err)
10117bec1a45SPetr Machata 				return err;
10127bec1a45SPetr Machata 		}
101346a3615bSNogah Frankel 	}
101498ceb7b6SNogah Frankel 	for (; band < IEEE_8021QAZ_MAX_TCS; band++) {
101598ceb7b6SNogah Frankel 		tclass = MLXSW_SP_PRIO_BAND_TO_TCLASS(band);
1016ee88450dSPetr Machata 		child_qdisc = &qdisc_state->tclass_qdiscs[tclass];
101798ceb7b6SNogah Frankel 		child_qdisc->prio_bitmap = 0;
101898ceb7b6SNogah Frankel 		mlxsw_sp_qdisc_destroy(mlxsw_sp_port, child_qdisc);
10197917f52aSPetr Machata 		mlxsw_sp_port_ets_set(mlxsw_sp_port,
10207917f52aSPetr Machata 				      MLXSW_REG_QEEC_HR_SUBGROUP,
10217917f52aSPetr Machata 				      tclass, 0, false, 0);
102298ceb7b6SNogah Frankel 	}
10237bec1a45SPetr Machata 
10247bec1a45SPetr Machata 	qdisc_state->future_handle = TC_H_UNSPEC;
10257bec1a45SPetr Machata 	memset(qdisc_state->future_fifos, 0, sizeof(qdisc_state->future_fifos));
102646a3615bSNogah Frankel 	return 0;
102746a3615bSNogah Frankel }
102846a3615bSNogah Frankel 
10297917f52aSPetr Machata static int
1030c4e372e2SPetr Machata mlxsw_sp_qdisc_prio_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
10317917f52aSPetr Machata 			    struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
10327917f52aSPetr Machata 			    void *params)
10337917f52aSPetr Machata {
10347917f52aSPetr Machata 	struct tc_prio_qopt_offload_params *p = params;
10357917f52aSPetr Machata 	unsigned int zeroes[TCQ_ETS_MAX_BANDS] = {0};
10367917f52aSPetr Machata 
1037c4e372e2SPetr Machata 	return __mlxsw_sp_qdisc_ets_replace(mlxsw_sp_port, handle, p->bands,
10387917f52aSPetr Machata 					    zeroes, zeroes, p->priomap);
10397917f52aSPetr Machata }
10407917f52aSPetr Machata 
10417917f52aSPetr Machata static void
10427917f52aSPetr Machata __mlxsw_sp_qdisc_ets_unoffload(struct mlxsw_sp_port *mlxsw_sp_port,
10437917f52aSPetr Machata 			       struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
10447917f52aSPetr Machata 			       struct gnet_stats_queue *qstats)
10457917f52aSPetr Machata {
10467917f52aSPetr Machata 	u64 backlog;
10477917f52aSPetr Machata 
10487917f52aSPetr Machata 	backlog = mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp,
10497917f52aSPetr Machata 				       mlxsw_sp_qdisc->stats_base.backlog);
10507917f52aSPetr Machata 	qstats->backlog -= backlog;
10517917f52aSPetr Machata }
10527917f52aSPetr Machata 
1053e02f08a0SWei Yongjun static void
105493d8a4c1SNogah Frankel mlxsw_sp_qdisc_prio_unoffload(struct mlxsw_sp_port *mlxsw_sp_port,
105593d8a4c1SNogah Frankel 			      struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
105693d8a4c1SNogah Frankel 			      void *params)
105793d8a4c1SNogah Frankel {
105893d8a4c1SNogah Frankel 	struct tc_prio_qopt_offload_params *p = params;
105993d8a4c1SNogah Frankel 
10607917f52aSPetr Machata 	__mlxsw_sp_qdisc_ets_unoffload(mlxsw_sp_port, mlxsw_sp_qdisc,
10617917f52aSPetr Machata 				       p->qstats);
106293d8a4c1SNogah Frankel }
106393d8a4c1SNogah Frankel 
106493d8a4c1SNogah Frankel static int
106593d8a4c1SNogah Frankel mlxsw_sp_qdisc_get_prio_stats(struct mlxsw_sp_port *mlxsw_sp_port,
106693d8a4c1SNogah Frankel 			      struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
106793d8a4c1SNogah Frankel 			      struct tc_qopt_offload_stats *stats_ptr)
106893d8a4c1SNogah Frankel {
1069ee88450dSPetr Machata 	struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc;
1070cf9af379SPetr Machata 	struct mlxsw_sp_qdisc *tc_qdisc;
1071cf9af379SPetr Machata 	u64 tx_packets = 0;
1072cf9af379SPetr Machata 	u64 tx_bytes = 0;
1073cf9af379SPetr Machata 	u64 backlog = 0;
1074cf9af379SPetr Machata 	u64 drops = 0;
107593d8a4c1SNogah Frankel 	int i;
107693d8a4c1SNogah Frankel 
107793d8a4c1SNogah Frankel 	for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
1078ee88450dSPetr Machata 		tc_qdisc = &qdisc_state->tclass_qdiscs[i];
1079cf9af379SPetr Machata 		mlxsw_sp_qdisc_collect_tc_stats(mlxsw_sp_port, tc_qdisc,
1080cf9af379SPetr Machata 						&tx_bytes, &tx_packets,
1081cf9af379SPetr Machata 						&drops, &backlog);
108293d8a4c1SNogah Frankel 	}
108393d8a4c1SNogah Frankel 
1084cf9af379SPetr Machata 	mlxsw_sp_qdisc_update_stats(mlxsw_sp_port->mlxsw_sp, mlxsw_sp_qdisc,
1085cf9af379SPetr Machata 				    tx_bytes, tx_packets, drops, backlog,
1086cf9af379SPetr Machata 				    stats_ptr);
108793d8a4c1SNogah Frankel 	return 0;
108893d8a4c1SNogah Frankel }
108993d8a4c1SNogah Frankel 
109093d8a4c1SNogah Frankel static void
109193d8a4c1SNogah Frankel mlxsw_sp_setup_tc_qdisc_prio_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port,
109293d8a4c1SNogah Frankel 					 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
109393d8a4c1SNogah Frankel {
109493d8a4c1SNogah Frankel 	struct mlxsw_sp_qdisc_stats *stats_base;
109593d8a4c1SNogah Frankel 	struct mlxsw_sp_port_xstats *xstats;
109693d8a4c1SNogah Frankel 	struct rtnl_link_stats64 *stats;
109793d8a4c1SNogah Frankel 	int i;
109893d8a4c1SNogah Frankel 
109993d8a4c1SNogah Frankel 	xstats = &mlxsw_sp_port->periodic_hw_stats.xstats;
110093d8a4c1SNogah Frankel 	stats = &mlxsw_sp_port->periodic_hw_stats.stats;
110193d8a4c1SNogah Frankel 	stats_base = &mlxsw_sp_qdisc->stats_base;
110293d8a4c1SNogah Frankel 
110393d8a4c1SNogah Frankel 	stats_base->tx_packets = stats->tx_packets;
110493d8a4c1SNogah Frankel 	stats_base->tx_bytes = stats->tx_bytes;
110593d8a4c1SNogah Frankel 
110693d8a4c1SNogah Frankel 	stats_base->drops = 0;
110723f2b404SNogah Frankel 	for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
110885005b82SPetr Machata 		stats_base->drops += mlxsw_sp_xstats_tail_drop(xstats, i);
110923f2b404SNogah Frankel 		stats_base->drops += xstats->wred_drop[i];
111023f2b404SNogah Frankel 	}
111193d8a4c1SNogah Frankel 
111293d8a4c1SNogah Frankel 	mlxsw_sp_qdisc->stats_base.backlog = 0;
111393d8a4c1SNogah Frankel }
111493d8a4c1SNogah Frankel 
111546a3615bSNogah Frankel static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_prio = {
111646a3615bSNogah Frankel 	.type = MLXSW_SP_QDISC_PRIO,
111746a3615bSNogah Frankel 	.check_params = mlxsw_sp_qdisc_prio_check_params,
111846a3615bSNogah Frankel 	.replace = mlxsw_sp_qdisc_prio_replace,
111993d8a4c1SNogah Frankel 	.unoffload = mlxsw_sp_qdisc_prio_unoffload,
112046a3615bSNogah Frankel 	.destroy = mlxsw_sp_qdisc_prio_destroy,
112193d8a4c1SNogah Frankel 	.get_stats = mlxsw_sp_qdisc_get_prio_stats,
112293d8a4c1SNogah Frankel 	.clean_stats = mlxsw_sp_setup_tc_qdisc_prio_clean_stats,
112346a3615bSNogah Frankel };
112446a3615bSNogah Frankel 
112519f405b9SPetr Machata static int
112619f405b9SPetr Machata mlxsw_sp_qdisc_ets_check_params(struct mlxsw_sp_port *mlxsw_sp_port,
112719f405b9SPetr Machata 				struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
112819f405b9SPetr Machata 				void *params)
112919f405b9SPetr Machata {
113019f405b9SPetr Machata 	struct tc_ets_qopt_offload_replace_params *p = params;
113119f405b9SPetr Machata 
113219f405b9SPetr Machata 	return __mlxsw_sp_qdisc_ets_check_params(p->bands);
113319f405b9SPetr Machata }
113419f405b9SPetr Machata 
113519f405b9SPetr Machata static int
1136c4e372e2SPetr Machata mlxsw_sp_qdisc_ets_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
113719f405b9SPetr Machata 			   struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
113819f405b9SPetr Machata 			   void *params)
113919f405b9SPetr Machata {
114019f405b9SPetr Machata 	struct tc_ets_qopt_offload_replace_params *p = params;
114119f405b9SPetr Machata 
1142c4e372e2SPetr Machata 	return __mlxsw_sp_qdisc_ets_replace(mlxsw_sp_port, handle, p->bands,
114319f405b9SPetr Machata 					    p->quanta, p->weights, p->priomap);
114419f405b9SPetr Machata }
114519f405b9SPetr Machata 
114619f405b9SPetr Machata static void
114719f405b9SPetr Machata mlxsw_sp_qdisc_ets_unoffload(struct mlxsw_sp_port *mlxsw_sp_port,
114819f405b9SPetr Machata 			     struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
114919f405b9SPetr Machata 			     void *params)
115019f405b9SPetr Machata {
115119f405b9SPetr Machata 	struct tc_ets_qopt_offload_replace_params *p = params;
115219f405b9SPetr Machata 
115319f405b9SPetr Machata 	__mlxsw_sp_qdisc_ets_unoffload(mlxsw_sp_port, mlxsw_sp_qdisc,
115419f405b9SPetr Machata 				       p->qstats);
115519f405b9SPetr Machata }
115619f405b9SPetr Machata 
115719f405b9SPetr Machata static int
115819f405b9SPetr Machata mlxsw_sp_qdisc_ets_destroy(struct mlxsw_sp_port *mlxsw_sp_port,
115919f405b9SPetr Machata 			   struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
116019f405b9SPetr Machata {
116119f405b9SPetr Machata 	return __mlxsw_sp_qdisc_ets_destroy(mlxsw_sp_port);
116219f405b9SPetr Machata }
116319f405b9SPetr Machata 
116419f405b9SPetr Machata static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_ets = {
116519f405b9SPetr Machata 	.type = MLXSW_SP_QDISC_ETS,
116619f405b9SPetr Machata 	.check_params = mlxsw_sp_qdisc_ets_check_params,
116719f405b9SPetr Machata 	.replace = mlxsw_sp_qdisc_ets_replace,
116819f405b9SPetr Machata 	.unoffload = mlxsw_sp_qdisc_ets_unoffload,
116919f405b9SPetr Machata 	.destroy = mlxsw_sp_qdisc_ets_destroy,
117019f405b9SPetr Machata 	.get_stats = mlxsw_sp_qdisc_get_prio_stats,
117119f405b9SPetr Machata 	.clean_stats = mlxsw_sp_setup_tc_qdisc_prio_clean_stats,
117219f405b9SPetr Machata };
117319f405b9SPetr Machata 
11745bc146c9SPetr Machata /* Linux allows linking of Qdiscs to arbitrary classes (so long as the resulting
11755bc146c9SPetr Machata  * graph is free of cycles). These operations do not change the parent handle
11765bc146c9SPetr Machata  * though, which means it can be incomplete (if there is more than one class
11775bc146c9SPetr Machata  * where the Qdisc in question is grafted) or outright wrong (if the Qdisc was
11785bc146c9SPetr Machata  * linked to a different class and then removed from the original class).
11795bc146c9SPetr Machata  *
11805bc146c9SPetr Machata  * E.g. consider this sequence of operations:
11815bc146c9SPetr Machata  *
11825bc146c9SPetr Machata  *  # tc qdisc add dev swp1 root handle 1: prio
11835bc146c9SPetr Machata  *  # tc qdisc add dev swp1 parent 1:3 handle 13: red limit 1000000 avpkt 10000
11845bc146c9SPetr Machata  *  RED: set bandwidth to 10Mbit
11855bc146c9SPetr Machata  *  # tc qdisc link dev swp1 handle 13: parent 1:2
11865bc146c9SPetr Machata  *
11875bc146c9SPetr Machata  * At this point, both 1:2 and 1:3 have the same RED Qdisc instance as their
11885bc146c9SPetr Machata  * child. But RED will still only claim that 1:3 is its parent. If it's removed
11895bc146c9SPetr Machata  * from that band, its only parent will be 1:2, but it will continue to claim
11905bc146c9SPetr Machata  * that it is in fact 1:3.
11915bc146c9SPetr Machata  *
11925bc146c9SPetr Machata  * The notification for child Qdisc replace (e.g. TC_RED_REPLACE) comes before
11935bc146c9SPetr Machata  * the notification for parent graft (e.g. TC_PRIO_GRAFT). We take the replace
11945bc146c9SPetr Machata  * notification to offload the child Qdisc, based on its parent handle, and use
11955bc146c9SPetr Machata  * the graft operation to validate that the class where the child is actually
11965bc146c9SPetr Machata  * grafted corresponds to the parent handle. If the two don't match, we
11975bc146c9SPetr Machata  * unoffload the child.
119832dc5efcSNogah Frankel  */
119932dc5efcSNogah Frankel static int
12007917f52aSPetr Machata __mlxsw_sp_qdisc_ets_graft(struct mlxsw_sp_port *mlxsw_sp_port,
120132dc5efcSNogah Frankel 			   struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
12027917f52aSPetr Machata 			   u8 band, u32 child_handle)
120332dc5efcSNogah Frankel {
1204ee88450dSPetr Machata 	struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc;
12057917f52aSPetr Machata 	int tclass_num = MLXSW_SP_PRIO_BAND_TO_TCLASS(band);
120632dc5efcSNogah Frankel 	struct mlxsw_sp_qdisc *old_qdisc;
120732dc5efcSNogah Frankel 
12087917f52aSPetr Machata 	if (band < IEEE_8021QAZ_MAX_TCS &&
1209ee88450dSPetr Machata 	    qdisc_state->tclass_qdiscs[tclass_num].handle == child_handle)
121032dc5efcSNogah Frankel 		return 0;
121132dc5efcSNogah Frankel 
1212a2d6d7aeSDavid S. Miller 	if (!child_handle) {
12133971a535SPetr Machata 		/* This is an invisible FIFO replacing the original Qdisc.
12143971a535SPetr Machata 		 * Ignore it--the original Qdisc's destroy will follow.
12153971a535SPetr Machata 		 */
12163971a535SPetr Machata 		return 0;
12173971a535SPetr Machata 	}
12183971a535SPetr Machata 
121932dc5efcSNogah Frankel 	/* See if the grafted qdisc is already offloaded on any tclass. If so,
122032dc5efcSNogah Frankel 	 * unoffload it.
122132dc5efcSNogah Frankel 	 */
122232dc5efcSNogah Frankel 	old_qdisc = mlxsw_sp_qdisc_find_by_handle(mlxsw_sp_port,
12237917f52aSPetr Machata 						  child_handle);
122432dc5efcSNogah Frankel 	if (old_qdisc)
122532dc5efcSNogah Frankel 		mlxsw_sp_qdisc_destroy(mlxsw_sp_port, old_qdisc);
122632dc5efcSNogah Frankel 
122732dc5efcSNogah Frankel 	mlxsw_sp_qdisc_destroy(mlxsw_sp_port,
1228ee88450dSPetr Machata 			       &qdisc_state->tclass_qdiscs[tclass_num]);
122932dc5efcSNogah Frankel 	return -EOPNOTSUPP;
123032dc5efcSNogah Frankel }
123132dc5efcSNogah Frankel 
12327917f52aSPetr Machata static int
12337917f52aSPetr Machata mlxsw_sp_qdisc_prio_graft(struct mlxsw_sp_port *mlxsw_sp_port,
12347917f52aSPetr Machata 			  struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
12357917f52aSPetr Machata 			  struct tc_prio_qopt_offload_graft_params *p)
12367917f52aSPetr Machata {
12377917f52aSPetr Machata 	return __mlxsw_sp_qdisc_ets_graft(mlxsw_sp_port, mlxsw_sp_qdisc,
12387917f52aSPetr Machata 					  p->band, p->child_handle);
12397917f52aSPetr Machata }
12407917f52aSPetr Machata 
124146a3615bSNogah Frankel int mlxsw_sp_setup_tc_prio(struct mlxsw_sp_port *mlxsw_sp_port,
124246a3615bSNogah Frankel 			   struct tc_prio_qopt_offload *p)
124346a3615bSNogah Frankel {
124446a3615bSNogah Frankel 	struct mlxsw_sp_qdisc *mlxsw_sp_qdisc;
124546a3615bSNogah Frankel 
1246eed4baebSNogah Frankel 	mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent, true);
1247eed4baebSNogah Frankel 	if (!mlxsw_sp_qdisc)
124846a3615bSNogah Frankel 		return -EOPNOTSUPP;
124946a3615bSNogah Frankel 
125046a3615bSNogah Frankel 	if (p->command == TC_PRIO_REPLACE)
125146a3615bSNogah Frankel 		return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle,
125246a3615bSNogah Frankel 					      mlxsw_sp_qdisc,
125346a3615bSNogah Frankel 					      &mlxsw_sp_qdisc_ops_prio,
125446a3615bSNogah Frankel 					      &p->replace_params);
125546a3615bSNogah Frankel 
125646a3615bSNogah Frankel 	if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle,
125746a3615bSNogah Frankel 				    MLXSW_SP_QDISC_PRIO))
125846a3615bSNogah Frankel 		return -EOPNOTSUPP;
125946a3615bSNogah Frankel 
126046a3615bSNogah Frankel 	switch (p->command) {
126146a3615bSNogah Frankel 	case TC_PRIO_DESTROY:
126246a3615bSNogah Frankel 		return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
126393d8a4c1SNogah Frankel 	case TC_PRIO_STATS:
126493d8a4c1SNogah Frankel 		return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc,
126593d8a4c1SNogah Frankel 						&p->stats);
126632dc5efcSNogah Frankel 	case TC_PRIO_GRAFT:
126732dc5efcSNogah Frankel 		return mlxsw_sp_qdisc_prio_graft(mlxsw_sp_port, mlxsw_sp_qdisc,
126832dc5efcSNogah Frankel 						 &p->graft_params);
126946a3615bSNogah Frankel 	default:
127046a3615bSNogah Frankel 		return -EOPNOTSUPP;
127146a3615bSNogah Frankel 	}
127246a3615bSNogah Frankel }
127346a3615bSNogah Frankel 
127419f405b9SPetr Machata int mlxsw_sp_setup_tc_ets(struct mlxsw_sp_port *mlxsw_sp_port,
127519f405b9SPetr Machata 			  struct tc_ets_qopt_offload *p)
127619f405b9SPetr Machata {
127719f405b9SPetr Machata 	struct mlxsw_sp_qdisc *mlxsw_sp_qdisc;
127819f405b9SPetr Machata 
127919f405b9SPetr Machata 	mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent, true);
128019f405b9SPetr Machata 	if (!mlxsw_sp_qdisc)
128119f405b9SPetr Machata 		return -EOPNOTSUPP;
128219f405b9SPetr Machata 
128319f405b9SPetr Machata 	if (p->command == TC_ETS_REPLACE)
128419f405b9SPetr Machata 		return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle,
128519f405b9SPetr Machata 					      mlxsw_sp_qdisc,
128619f405b9SPetr Machata 					      &mlxsw_sp_qdisc_ops_ets,
128719f405b9SPetr Machata 					      &p->replace_params);
128819f405b9SPetr Machata 
128919f405b9SPetr Machata 	if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle,
129019f405b9SPetr Machata 				    MLXSW_SP_QDISC_ETS))
129119f405b9SPetr Machata 		return -EOPNOTSUPP;
129219f405b9SPetr Machata 
129319f405b9SPetr Machata 	switch (p->command) {
129419f405b9SPetr Machata 	case TC_ETS_DESTROY:
129519f405b9SPetr Machata 		return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
129619f405b9SPetr Machata 	case TC_ETS_STATS:
129719f405b9SPetr Machata 		return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc,
129819f405b9SPetr Machata 						&p->stats);
129919f405b9SPetr Machata 	case TC_ETS_GRAFT:
130019f405b9SPetr Machata 		return __mlxsw_sp_qdisc_ets_graft(mlxsw_sp_port, mlxsw_sp_qdisc,
130119f405b9SPetr Machata 						  p->graft_params.band,
130219f405b9SPetr Machata 						  p->graft_params.child_handle);
130319f405b9SPetr Machata 	default:
130419f405b9SPetr Machata 		return -EOPNOTSUPP;
130519f405b9SPetr Machata 	}
130619f405b9SPetr Machata }
130719f405b9SPetr Machata 
1308f6668eacSPetr Machata struct mlxsw_sp_qevent_block {
1309f6668eacSPetr Machata 	struct list_head binding_list;
1310f6668eacSPetr Machata 	struct list_head mall_entry_list;
1311f6668eacSPetr Machata 	struct mlxsw_sp *mlxsw_sp;
1312f6668eacSPetr Machata };
1313f6668eacSPetr Machata 
1314f6668eacSPetr Machata struct mlxsw_sp_qevent_binding {
1315f6668eacSPetr Machata 	struct list_head list;
1316f6668eacSPetr Machata 	struct mlxsw_sp_port *mlxsw_sp_port;
1317f6668eacSPetr Machata 	u32 handle;
1318f6668eacSPetr Machata 	int tclass_num;
1319f6668eacSPetr Machata 	enum mlxsw_sp_span_trigger span_trigger;
1320f6668eacSPetr Machata };
1321f6668eacSPetr Machata 
1322f6668eacSPetr Machata static LIST_HEAD(mlxsw_sp_qevent_block_cb_list);
1323f6668eacSPetr Machata 
132454a92385SPetr Machata static int mlxsw_sp_qevent_span_configure(struct mlxsw_sp *mlxsw_sp,
1325f6668eacSPetr Machata 					  struct mlxsw_sp_mall_entry *mall_entry,
132654a92385SPetr Machata 					  struct mlxsw_sp_qevent_binding *qevent_binding,
132754a92385SPetr Machata 					  const struct mlxsw_sp_span_agent_parms *agent_parms,
132854a92385SPetr Machata 					  int *p_span_id)
1329f6668eacSPetr Machata {
1330f6668eacSPetr Machata 	struct mlxsw_sp_port *mlxsw_sp_port = qevent_binding->mlxsw_sp_port;
1331f6668eacSPetr Machata 	struct mlxsw_sp_span_trigger_parms trigger_parms = {};
1332f6668eacSPetr Machata 	int span_id;
1333f6668eacSPetr Machata 	int err;
1334f6668eacSPetr Machata 
133554a92385SPetr Machata 	err = mlxsw_sp_span_agent_get(mlxsw_sp, &span_id, agent_parms);
1336f6668eacSPetr Machata 	if (err)
1337f6668eacSPetr Machata 		return err;
1338f6668eacSPetr Machata 
1339f6668eacSPetr Machata 	err = mlxsw_sp_span_analyzed_port_get(mlxsw_sp_port, true);
1340f6668eacSPetr Machata 	if (err)
1341f6668eacSPetr Machata 		goto err_analyzed_port_get;
1342f6668eacSPetr Machata 
1343f6668eacSPetr Machata 	trigger_parms.span_id = span_id;
1344f6668eacSPetr Machata 	err = mlxsw_sp_span_agent_bind(mlxsw_sp, qevent_binding->span_trigger, mlxsw_sp_port,
1345f6668eacSPetr Machata 				       &trigger_parms);
1346f6668eacSPetr Machata 	if (err)
1347f6668eacSPetr Machata 		goto err_agent_bind;
1348f6668eacSPetr Machata 
1349f6668eacSPetr Machata 	err = mlxsw_sp_span_trigger_enable(mlxsw_sp_port, qevent_binding->span_trigger,
1350f6668eacSPetr Machata 					   qevent_binding->tclass_num);
1351f6668eacSPetr Machata 	if (err)
1352f6668eacSPetr Machata 		goto err_trigger_enable;
1353f6668eacSPetr Machata 
135454a92385SPetr Machata 	*p_span_id = span_id;
1355f6668eacSPetr Machata 	return 0;
1356f6668eacSPetr Machata 
1357f6668eacSPetr Machata err_trigger_enable:
1358f6668eacSPetr Machata 	mlxsw_sp_span_agent_unbind(mlxsw_sp, qevent_binding->span_trigger, mlxsw_sp_port,
1359f6668eacSPetr Machata 				   &trigger_parms);
1360f6668eacSPetr Machata err_agent_bind:
1361f6668eacSPetr Machata 	mlxsw_sp_span_analyzed_port_put(mlxsw_sp_port, true);
1362f6668eacSPetr Machata err_analyzed_port_get:
1363f6668eacSPetr Machata 	mlxsw_sp_span_agent_put(mlxsw_sp, span_id);
1364f6668eacSPetr Machata 	return err;
1365f6668eacSPetr Machata }
1366f6668eacSPetr Machata 
136754a92385SPetr Machata static void mlxsw_sp_qevent_span_deconfigure(struct mlxsw_sp *mlxsw_sp,
136854a92385SPetr Machata 					     struct mlxsw_sp_qevent_binding *qevent_binding,
136954a92385SPetr Machata 					     int span_id)
1370f6668eacSPetr Machata {
1371f6668eacSPetr Machata 	struct mlxsw_sp_port *mlxsw_sp_port = qevent_binding->mlxsw_sp_port;
1372f6668eacSPetr Machata 	struct mlxsw_sp_span_trigger_parms trigger_parms = {
137354a92385SPetr Machata 		.span_id = span_id,
1374f6668eacSPetr Machata 	};
1375f6668eacSPetr Machata 
1376f6668eacSPetr Machata 	mlxsw_sp_span_trigger_disable(mlxsw_sp_port, qevent_binding->span_trigger,
1377f6668eacSPetr Machata 				      qevent_binding->tclass_num);
1378f6668eacSPetr Machata 	mlxsw_sp_span_agent_unbind(mlxsw_sp, qevent_binding->span_trigger, mlxsw_sp_port,
1379f6668eacSPetr Machata 				   &trigger_parms);
1380f6668eacSPetr Machata 	mlxsw_sp_span_analyzed_port_put(mlxsw_sp_port, true);
138154a92385SPetr Machata 	mlxsw_sp_span_agent_put(mlxsw_sp, span_id);
138254a92385SPetr Machata }
138354a92385SPetr Machata 
138454a92385SPetr Machata static int mlxsw_sp_qevent_mirror_configure(struct mlxsw_sp *mlxsw_sp,
138554a92385SPetr Machata 					    struct mlxsw_sp_mall_entry *mall_entry,
138654a92385SPetr Machata 					    struct mlxsw_sp_qevent_binding *qevent_binding)
138754a92385SPetr Machata {
138854a92385SPetr Machata 	struct mlxsw_sp_span_agent_parms agent_parms = {
138954a92385SPetr Machata 		.to_dev = mall_entry->mirror.to_dev,
139054a92385SPetr Machata 	};
139154a92385SPetr Machata 
139254a92385SPetr Machata 	return mlxsw_sp_qevent_span_configure(mlxsw_sp, mall_entry, qevent_binding,
139354a92385SPetr Machata 					      &agent_parms, &mall_entry->mirror.span_id);
139454a92385SPetr Machata }
139554a92385SPetr Machata 
139654a92385SPetr Machata static void mlxsw_sp_qevent_mirror_deconfigure(struct mlxsw_sp *mlxsw_sp,
139754a92385SPetr Machata 					       struct mlxsw_sp_mall_entry *mall_entry,
139854a92385SPetr Machata 					       struct mlxsw_sp_qevent_binding *qevent_binding)
139954a92385SPetr Machata {
140054a92385SPetr Machata 	mlxsw_sp_qevent_span_deconfigure(mlxsw_sp, qevent_binding, mall_entry->mirror.span_id);
140154a92385SPetr Machata }
140254a92385SPetr Machata 
140354a92385SPetr Machata static int mlxsw_sp_qevent_trap_configure(struct mlxsw_sp *mlxsw_sp,
140454a92385SPetr Machata 					  struct mlxsw_sp_mall_entry *mall_entry,
140554a92385SPetr Machata 					  struct mlxsw_sp_qevent_binding *qevent_binding)
140654a92385SPetr Machata {
140754a92385SPetr Machata 	struct mlxsw_sp_span_agent_parms agent_parms = {};
140854a92385SPetr Machata 	int err;
140954a92385SPetr Machata 
141054a92385SPetr Machata 	err = mlxsw_sp_trap_group_policer_hw_id_get(mlxsw_sp,
141154a92385SPetr Machata 						    DEVLINK_TRAP_GROUP_GENERIC_ID_BUFFER_DROPS,
141254a92385SPetr Machata 						    &agent_parms.policer_enable,
141354a92385SPetr Machata 						    &agent_parms.policer_id);
141454a92385SPetr Machata 	if (err)
141554a92385SPetr Machata 		return err;
141654a92385SPetr Machata 
141754a92385SPetr Machata 	return mlxsw_sp_qevent_span_configure(mlxsw_sp, mall_entry, qevent_binding,
141854a92385SPetr Machata 					      &agent_parms, &mall_entry->trap.span_id);
141954a92385SPetr Machata }
142054a92385SPetr Machata 
142154a92385SPetr Machata static void mlxsw_sp_qevent_trap_deconfigure(struct mlxsw_sp *mlxsw_sp,
142254a92385SPetr Machata 					     struct mlxsw_sp_mall_entry *mall_entry,
142354a92385SPetr Machata 					     struct mlxsw_sp_qevent_binding *qevent_binding)
142454a92385SPetr Machata {
142554a92385SPetr Machata 	mlxsw_sp_qevent_span_deconfigure(mlxsw_sp, qevent_binding, mall_entry->trap.span_id);
1426f6668eacSPetr Machata }
1427f6668eacSPetr Machata 
1428f6668eacSPetr Machata static int mlxsw_sp_qevent_entry_configure(struct mlxsw_sp *mlxsw_sp,
1429f6668eacSPetr Machata 					   struct mlxsw_sp_mall_entry *mall_entry,
1430f6668eacSPetr Machata 					   struct mlxsw_sp_qevent_binding *qevent_binding)
1431f6668eacSPetr Machata {
1432f6668eacSPetr Machata 	switch (mall_entry->type) {
1433f6668eacSPetr Machata 	case MLXSW_SP_MALL_ACTION_TYPE_MIRROR:
1434f6668eacSPetr Machata 		return mlxsw_sp_qevent_mirror_configure(mlxsw_sp, mall_entry, qevent_binding);
143554a92385SPetr Machata 	case MLXSW_SP_MALL_ACTION_TYPE_TRAP:
143654a92385SPetr Machata 		return mlxsw_sp_qevent_trap_configure(mlxsw_sp, mall_entry, qevent_binding);
1437f6668eacSPetr Machata 	default:
1438f6668eacSPetr Machata 		/* This should have been validated away. */
1439f6668eacSPetr Machata 		WARN_ON(1);
1440f6668eacSPetr Machata 		return -EOPNOTSUPP;
1441f6668eacSPetr Machata 	}
1442f6668eacSPetr Machata }
1443f6668eacSPetr Machata 
1444f6668eacSPetr Machata static void mlxsw_sp_qevent_entry_deconfigure(struct mlxsw_sp *mlxsw_sp,
1445f6668eacSPetr Machata 					      struct mlxsw_sp_mall_entry *mall_entry,
1446f6668eacSPetr Machata 					      struct mlxsw_sp_qevent_binding *qevent_binding)
1447f6668eacSPetr Machata {
1448f6668eacSPetr Machata 	switch (mall_entry->type) {
1449f6668eacSPetr Machata 	case MLXSW_SP_MALL_ACTION_TYPE_MIRROR:
1450f6668eacSPetr Machata 		return mlxsw_sp_qevent_mirror_deconfigure(mlxsw_sp, mall_entry, qevent_binding);
145154a92385SPetr Machata 	case MLXSW_SP_MALL_ACTION_TYPE_TRAP:
145254a92385SPetr Machata 		return mlxsw_sp_qevent_trap_deconfigure(mlxsw_sp, mall_entry, qevent_binding);
1453f6668eacSPetr Machata 	default:
1454f6668eacSPetr Machata 		WARN_ON(1);
1455f6668eacSPetr Machata 		return;
1456f6668eacSPetr Machata 	}
1457f6668eacSPetr Machata }
1458f6668eacSPetr Machata 
1459f6668eacSPetr Machata static int mlxsw_sp_qevent_binding_configure(struct mlxsw_sp_qevent_block *qevent_block,
1460f6668eacSPetr Machata 					     struct mlxsw_sp_qevent_binding *qevent_binding)
1461f6668eacSPetr Machata {
1462f6668eacSPetr Machata 	struct mlxsw_sp_mall_entry *mall_entry;
1463f6668eacSPetr Machata 	int err;
1464f6668eacSPetr Machata 
1465f6668eacSPetr Machata 	list_for_each_entry(mall_entry, &qevent_block->mall_entry_list, list) {
1466f6668eacSPetr Machata 		err = mlxsw_sp_qevent_entry_configure(qevent_block->mlxsw_sp, mall_entry,
1467f6668eacSPetr Machata 						      qevent_binding);
1468f6668eacSPetr Machata 		if (err)
1469f6668eacSPetr Machata 			goto err_entry_configure;
1470f6668eacSPetr Machata 	}
1471f6668eacSPetr Machata 
1472f6668eacSPetr Machata 	return 0;
1473f6668eacSPetr Machata 
1474f6668eacSPetr Machata err_entry_configure:
1475f6668eacSPetr Machata 	list_for_each_entry_continue_reverse(mall_entry, &qevent_block->mall_entry_list, list)
1476f6668eacSPetr Machata 		mlxsw_sp_qevent_entry_deconfigure(qevent_block->mlxsw_sp, mall_entry,
1477f6668eacSPetr Machata 						  qevent_binding);
1478f6668eacSPetr Machata 	return err;
1479f6668eacSPetr Machata }
1480f6668eacSPetr Machata 
1481f6668eacSPetr Machata static void mlxsw_sp_qevent_binding_deconfigure(struct mlxsw_sp_qevent_block *qevent_block,
1482f6668eacSPetr Machata 						struct mlxsw_sp_qevent_binding *qevent_binding)
1483f6668eacSPetr Machata {
1484f6668eacSPetr Machata 	struct mlxsw_sp_mall_entry *mall_entry;
1485f6668eacSPetr Machata 
1486f6668eacSPetr Machata 	list_for_each_entry(mall_entry, &qevent_block->mall_entry_list, list)
1487f6668eacSPetr Machata 		mlxsw_sp_qevent_entry_deconfigure(qevent_block->mlxsw_sp, mall_entry,
1488f6668eacSPetr Machata 						  qevent_binding);
1489f6668eacSPetr Machata }
1490f6668eacSPetr Machata 
1491f6668eacSPetr Machata static int mlxsw_sp_qevent_block_configure(struct mlxsw_sp_qevent_block *qevent_block)
1492f6668eacSPetr Machata {
1493f6668eacSPetr Machata 	struct mlxsw_sp_qevent_binding *qevent_binding;
1494f6668eacSPetr Machata 	int err;
1495f6668eacSPetr Machata 
1496f6668eacSPetr Machata 	list_for_each_entry(qevent_binding, &qevent_block->binding_list, list) {
1497f6668eacSPetr Machata 		err = mlxsw_sp_qevent_binding_configure(qevent_block, qevent_binding);
1498f6668eacSPetr Machata 		if (err)
1499f6668eacSPetr Machata 			goto err_binding_configure;
1500f6668eacSPetr Machata 	}
1501f6668eacSPetr Machata 
1502f6668eacSPetr Machata 	return 0;
1503f6668eacSPetr Machata 
1504f6668eacSPetr Machata err_binding_configure:
1505f6668eacSPetr Machata 	list_for_each_entry_continue_reverse(qevent_binding, &qevent_block->binding_list, list)
1506f6668eacSPetr Machata 		mlxsw_sp_qevent_binding_deconfigure(qevent_block, qevent_binding);
1507f6668eacSPetr Machata 	return err;
1508f6668eacSPetr Machata }
1509f6668eacSPetr Machata 
1510f6668eacSPetr Machata static void mlxsw_sp_qevent_block_deconfigure(struct mlxsw_sp_qevent_block *qevent_block)
1511f6668eacSPetr Machata {
1512f6668eacSPetr Machata 	struct mlxsw_sp_qevent_binding *qevent_binding;
1513f6668eacSPetr Machata 
1514f6668eacSPetr Machata 	list_for_each_entry(qevent_binding, &qevent_block->binding_list, list)
1515f6668eacSPetr Machata 		mlxsw_sp_qevent_binding_deconfigure(qevent_block, qevent_binding);
1516f6668eacSPetr Machata }
1517f6668eacSPetr Machata 
1518f6668eacSPetr Machata static struct mlxsw_sp_mall_entry *
1519f6668eacSPetr Machata mlxsw_sp_qevent_mall_entry_find(struct mlxsw_sp_qevent_block *block, unsigned long cookie)
1520f6668eacSPetr Machata {
1521f6668eacSPetr Machata 	struct mlxsw_sp_mall_entry *mall_entry;
1522f6668eacSPetr Machata 
1523f6668eacSPetr Machata 	list_for_each_entry(mall_entry, &block->mall_entry_list, list)
1524f6668eacSPetr Machata 		if (mall_entry->cookie == cookie)
1525f6668eacSPetr Machata 			return mall_entry;
1526f6668eacSPetr Machata 
1527f6668eacSPetr Machata 	return NULL;
1528f6668eacSPetr Machata }
1529f6668eacSPetr Machata 
1530f6668eacSPetr Machata static int mlxsw_sp_qevent_mall_replace(struct mlxsw_sp *mlxsw_sp,
1531f6668eacSPetr Machata 					struct mlxsw_sp_qevent_block *qevent_block,
1532f6668eacSPetr Machata 					struct tc_cls_matchall_offload *f)
1533f6668eacSPetr Machata {
1534f6668eacSPetr Machata 	struct mlxsw_sp_mall_entry *mall_entry;
1535f6668eacSPetr Machata 	struct flow_action_entry *act;
1536f6668eacSPetr Machata 	int err;
1537f6668eacSPetr Machata 
1538f6668eacSPetr Machata 	/* It should not currently be possible to replace a matchall rule. So
1539f6668eacSPetr Machata 	 * this must be a new rule.
1540f6668eacSPetr Machata 	 */
1541f6668eacSPetr Machata 	if (!list_empty(&qevent_block->mall_entry_list)) {
1542f6668eacSPetr Machata 		NL_SET_ERR_MSG(f->common.extack, "At most one filter supported");
1543f6668eacSPetr Machata 		return -EOPNOTSUPP;
1544f6668eacSPetr Machata 	}
1545f6668eacSPetr Machata 	if (f->rule->action.num_entries != 1) {
1546f6668eacSPetr Machata 		NL_SET_ERR_MSG(f->common.extack, "Only singular actions supported");
1547f6668eacSPetr Machata 		return -EOPNOTSUPP;
1548f6668eacSPetr Machata 	}
1549f6668eacSPetr Machata 	if (f->common.chain_index) {
1550f6668eacSPetr Machata 		NL_SET_ERR_MSG(f->common.extack, "Only chain 0 is supported");
1551f6668eacSPetr Machata 		return -EOPNOTSUPP;
1552f6668eacSPetr Machata 	}
1553f6668eacSPetr Machata 	if (f->common.protocol != htons(ETH_P_ALL)) {
1554f6668eacSPetr Machata 		NL_SET_ERR_MSG(f->common.extack, "Protocol matching not supported");
1555f6668eacSPetr Machata 		return -EOPNOTSUPP;
1556f6668eacSPetr Machata 	}
1557f6668eacSPetr Machata 
1558f6668eacSPetr Machata 	act = &f->rule->action.entries[0];
1559f6668eacSPetr Machata 	if (!(act->hw_stats & FLOW_ACTION_HW_STATS_DISABLED)) {
1560f6668eacSPetr Machata 		NL_SET_ERR_MSG(f->common.extack, "HW counters not supported on qevents");
1561f6668eacSPetr Machata 		return -EOPNOTSUPP;
1562f6668eacSPetr Machata 	}
1563f6668eacSPetr Machata 
1564f6668eacSPetr Machata 	mall_entry = kzalloc(sizeof(*mall_entry), GFP_KERNEL);
1565f6668eacSPetr Machata 	if (!mall_entry)
1566f6668eacSPetr Machata 		return -ENOMEM;
1567f6668eacSPetr Machata 	mall_entry->cookie = f->cookie;
1568f6668eacSPetr Machata 
1569f6668eacSPetr Machata 	if (act->id == FLOW_ACTION_MIRRED) {
1570f6668eacSPetr Machata 		mall_entry->type = MLXSW_SP_MALL_ACTION_TYPE_MIRROR;
1571f6668eacSPetr Machata 		mall_entry->mirror.to_dev = act->dev;
157254a92385SPetr Machata 	} else if (act->id == FLOW_ACTION_TRAP) {
157354a92385SPetr Machata 		mall_entry->type = MLXSW_SP_MALL_ACTION_TYPE_TRAP;
1574f6668eacSPetr Machata 	} else {
1575f6668eacSPetr Machata 		NL_SET_ERR_MSG(f->common.extack, "Unsupported action");
1576f6668eacSPetr Machata 		err = -EOPNOTSUPP;
1577f6668eacSPetr Machata 		goto err_unsupported_action;
1578f6668eacSPetr Machata 	}
1579f6668eacSPetr Machata 
1580f6668eacSPetr Machata 	list_add_tail(&mall_entry->list, &qevent_block->mall_entry_list);
1581f6668eacSPetr Machata 
1582f6668eacSPetr Machata 	err = mlxsw_sp_qevent_block_configure(qevent_block);
1583f6668eacSPetr Machata 	if (err)
1584f6668eacSPetr Machata 		goto err_block_configure;
1585f6668eacSPetr Machata 
1586f6668eacSPetr Machata 	return 0;
1587f6668eacSPetr Machata 
1588f6668eacSPetr Machata err_block_configure:
1589f6668eacSPetr Machata 	list_del(&mall_entry->list);
1590f6668eacSPetr Machata err_unsupported_action:
1591f6668eacSPetr Machata 	kfree(mall_entry);
1592f6668eacSPetr Machata 	return err;
1593f6668eacSPetr Machata }
1594f6668eacSPetr Machata 
1595f6668eacSPetr Machata static void mlxsw_sp_qevent_mall_destroy(struct mlxsw_sp_qevent_block *qevent_block,
1596f6668eacSPetr Machata 					 struct tc_cls_matchall_offload *f)
1597f6668eacSPetr Machata {
1598f6668eacSPetr Machata 	struct mlxsw_sp_mall_entry *mall_entry;
1599f6668eacSPetr Machata 
1600f6668eacSPetr Machata 	mall_entry = mlxsw_sp_qevent_mall_entry_find(qevent_block, f->cookie);
1601f6668eacSPetr Machata 	if (!mall_entry)
1602f6668eacSPetr Machata 		return;
1603f6668eacSPetr Machata 
1604f6668eacSPetr Machata 	mlxsw_sp_qevent_block_deconfigure(qevent_block);
1605f6668eacSPetr Machata 
1606f6668eacSPetr Machata 	list_del(&mall_entry->list);
1607f6668eacSPetr Machata 	kfree(mall_entry);
1608f6668eacSPetr Machata }
1609f6668eacSPetr Machata 
1610f6668eacSPetr Machata static int mlxsw_sp_qevent_block_mall_cb(struct mlxsw_sp_qevent_block *qevent_block,
1611f6668eacSPetr Machata 					 struct tc_cls_matchall_offload *f)
1612f6668eacSPetr Machata {
1613f6668eacSPetr Machata 	struct mlxsw_sp *mlxsw_sp = qevent_block->mlxsw_sp;
1614f6668eacSPetr Machata 
1615f6668eacSPetr Machata 	switch (f->command) {
1616f6668eacSPetr Machata 	case TC_CLSMATCHALL_REPLACE:
1617f6668eacSPetr Machata 		return mlxsw_sp_qevent_mall_replace(mlxsw_sp, qevent_block, f);
1618f6668eacSPetr Machata 	case TC_CLSMATCHALL_DESTROY:
1619f6668eacSPetr Machata 		mlxsw_sp_qevent_mall_destroy(qevent_block, f);
1620f6668eacSPetr Machata 		return 0;
1621f6668eacSPetr Machata 	default:
1622f6668eacSPetr Machata 		return -EOPNOTSUPP;
1623f6668eacSPetr Machata 	}
1624f6668eacSPetr Machata }
1625f6668eacSPetr Machata 
1626f6668eacSPetr Machata static int mlxsw_sp_qevent_block_cb(enum tc_setup_type type, void *type_data, void *cb_priv)
1627f6668eacSPetr Machata {
1628f6668eacSPetr Machata 	struct mlxsw_sp_qevent_block *qevent_block = cb_priv;
1629f6668eacSPetr Machata 
1630f6668eacSPetr Machata 	switch (type) {
1631f6668eacSPetr Machata 	case TC_SETUP_CLSMATCHALL:
1632f6668eacSPetr Machata 		return mlxsw_sp_qevent_block_mall_cb(qevent_block, type_data);
1633f6668eacSPetr Machata 	default:
1634f6668eacSPetr Machata 		return -EOPNOTSUPP;
1635f6668eacSPetr Machata 	}
1636f6668eacSPetr Machata }
1637f6668eacSPetr Machata 
1638f6668eacSPetr Machata static struct mlxsw_sp_qevent_block *mlxsw_sp_qevent_block_create(struct mlxsw_sp *mlxsw_sp,
1639f6668eacSPetr Machata 								  struct net *net)
1640f6668eacSPetr Machata {
1641f6668eacSPetr Machata 	struct mlxsw_sp_qevent_block *qevent_block;
1642f6668eacSPetr Machata 
1643f6668eacSPetr Machata 	qevent_block = kzalloc(sizeof(*qevent_block), GFP_KERNEL);
1644f6668eacSPetr Machata 	if (!qevent_block)
1645f6668eacSPetr Machata 		return NULL;
1646f6668eacSPetr Machata 
1647f6668eacSPetr Machata 	INIT_LIST_HEAD(&qevent_block->binding_list);
1648f6668eacSPetr Machata 	INIT_LIST_HEAD(&qevent_block->mall_entry_list);
1649f6668eacSPetr Machata 	qevent_block->mlxsw_sp = mlxsw_sp;
1650f6668eacSPetr Machata 	return qevent_block;
1651f6668eacSPetr Machata }
1652f6668eacSPetr Machata 
1653f6668eacSPetr Machata static void
1654f6668eacSPetr Machata mlxsw_sp_qevent_block_destroy(struct mlxsw_sp_qevent_block *qevent_block)
1655f6668eacSPetr Machata {
1656f6668eacSPetr Machata 	WARN_ON(!list_empty(&qevent_block->binding_list));
1657f6668eacSPetr Machata 	WARN_ON(!list_empty(&qevent_block->mall_entry_list));
1658f6668eacSPetr Machata 	kfree(qevent_block);
1659f6668eacSPetr Machata }
1660f6668eacSPetr Machata 
1661f6668eacSPetr Machata static void mlxsw_sp_qevent_block_release(void *cb_priv)
1662f6668eacSPetr Machata {
1663f6668eacSPetr Machata 	struct mlxsw_sp_qevent_block *qevent_block = cb_priv;
1664f6668eacSPetr Machata 
1665f6668eacSPetr Machata 	mlxsw_sp_qevent_block_destroy(qevent_block);
1666f6668eacSPetr Machata }
1667f6668eacSPetr Machata 
1668f6668eacSPetr Machata static struct mlxsw_sp_qevent_binding *
1669f6668eacSPetr Machata mlxsw_sp_qevent_binding_create(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, int tclass_num,
1670f6668eacSPetr Machata 			       enum mlxsw_sp_span_trigger span_trigger)
1671f6668eacSPetr Machata {
1672f6668eacSPetr Machata 	struct mlxsw_sp_qevent_binding *binding;
1673f6668eacSPetr Machata 
1674f6668eacSPetr Machata 	binding = kzalloc(sizeof(*binding), GFP_KERNEL);
1675f6668eacSPetr Machata 	if (!binding)
1676f6668eacSPetr Machata 		return ERR_PTR(-ENOMEM);
1677f6668eacSPetr Machata 
1678f6668eacSPetr Machata 	binding->mlxsw_sp_port = mlxsw_sp_port;
1679f6668eacSPetr Machata 	binding->handle = handle;
1680f6668eacSPetr Machata 	binding->tclass_num = tclass_num;
1681f6668eacSPetr Machata 	binding->span_trigger = span_trigger;
1682f6668eacSPetr Machata 	return binding;
1683f6668eacSPetr Machata }
1684f6668eacSPetr Machata 
1685f6668eacSPetr Machata static void
1686f6668eacSPetr Machata mlxsw_sp_qevent_binding_destroy(struct mlxsw_sp_qevent_binding *binding)
1687f6668eacSPetr Machata {
1688f6668eacSPetr Machata 	kfree(binding);
1689f6668eacSPetr Machata }
1690f6668eacSPetr Machata 
1691f6668eacSPetr Machata static struct mlxsw_sp_qevent_binding *
1692f6668eacSPetr Machata mlxsw_sp_qevent_binding_lookup(struct mlxsw_sp_qevent_block *block,
1693f6668eacSPetr Machata 			       struct mlxsw_sp_port *mlxsw_sp_port,
1694f6668eacSPetr Machata 			       u32 handle,
1695f6668eacSPetr Machata 			       enum mlxsw_sp_span_trigger span_trigger)
1696f6668eacSPetr Machata {
1697f6668eacSPetr Machata 	struct mlxsw_sp_qevent_binding *qevent_binding;
1698f6668eacSPetr Machata 
1699f6668eacSPetr Machata 	list_for_each_entry(qevent_binding, &block->binding_list, list)
1700f6668eacSPetr Machata 		if (qevent_binding->mlxsw_sp_port == mlxsw_sp_port &&
1701f6668eacSPetr Machata 		    qevent_binding->handle == handle &&
1702f6668eacSPetr Machata 		    qevent_binding->span_trigger == span_trigger)
1703f6668eacSPetr Machata 			return qevent_binding;
1704f6668eacSPetr Machata 	return NULL;
1705f6668eacSPetr Machata }
1706f6668eacSPetr Machata 
1707f6668eacSPetr Machata static int mlxsw_sp_setup_tc_block_qevent_bind(struct mlxsw_sp_port *mlxsw_sp_port,
1708f6668eacSPetr Machata 					       struct flow_block_offload *f,
1709f6668eacSPetr Machata 					       enum mlxsw_sp_span_trigger span_trigger)
1710f6668eacSPetr Machata {
1711f6668eacSPetr Machata 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
1712f6668eacSPetr Machata 	struct mlxsw_sp_qevent_binding *qevent_binding;
1713f6668eacSPetr Machata 	struct mlxsw_sp_qevent_block *qevent_block;
1714f6668eacSPetr Machata 	struct flow_block_cb *block_cb;
1715f6668eacSPetr Machata 	struct mlxsw_sp_qdisc *qdisc;
1716f6668eacSPetr Machata 	bool register_block = false;
1717f6668eacSPetr Machata 	int err;
1718f6668eacSPetr Machata 
1719f6668eacSPetr Machata 	block_cb = flow_block_cb_lookup(f->block, mlxsw_sp_qevent_block_cb, mlxsw_sp);
1720f6668eacSPetr Machata 	if (!block_cb) {
1721f6668eacSPetr Machata 		qevent_block = mlxsw_sp_qevent_block_create(mlxsw_sp, f->net);
1722f6668eacSPetr Machata 		if (!qevent_block)
1723f6668eacSPetr Machata 			return -ENOMEM;
1724f6668eacSPetr Machata 		block_cb = flow_block_cb_alloc(mlxsw_sp_qevent_block_cb, mlxsw_sp, qevent_block,
1725f6668eacSPetr Machata 					       mlxsw_sp_qevent_block_release);
1726f6668eacSPetr Machata 		if (IS_ERR(block_cb)) {
1727f6668eacSPetr Machata 			mlxsw_sp_qevent_block_destroy(qevent_block);
1728f6668eacSPetr Machata 			return PTR_ERR(block_cb);
1729f6668eacSPetr Machata 		}
1730f6668eacSPetr Machata 		register_block = true;
1731f6668eacSPetr Machata 	} else {
1732f6668eacSPetr Machata 		qevent_block = flow_block_cb_priv(block_cb);
1733f6668eacSPetr Machata 	}
1734f6668eacSPetr Machata 	flow_block_cb_incref(block_cb);
1735f6668eacSPetr Machata 
1736f6668eacSPetr Machata 	qdisc = mlxsw_sp_qdisc_find_by_handle(mlxsw_sp_port, f->sch->handle);
1737f6668eacSPetr Machata 	if (!qdisc) {
1738f6668eacSPetr Machata 		NL_SET_ERR_MSG(f->extack, "Qdisc not offloaded");
1739f6668eacSPetr Machata 		err = -ENOENT;
1740f6668eacSPetr Machata 		goto err_find_qdisc;
1741f6668eacSPetr Machata 	}
1742f6668eacSPetr Machata 
1743f6668eacSPetr Machata 	if (WARN_ON(mlxsw_sp_qevent_binding_lookup(qevent_block, mlxsw_sp_port, f->sch->handle,
1744f6668eacSPetr Machata 						   span_trigger))) {
1745f6668eacSPetr Machata 		err = -EEXIST;
1746f6668eacSPetr Machata 		goto err_binding_exists;
1747f6668eacSPetr Machata 	}
1748f6668eacSPetr Machata 
1749f6668eacSPetr Machata 	qevent_binding = mlxsw_sp_qevent_binding_create(mlxsw_sp_port, f->sch->handle,
1750f6668eacSPetr Machata 							qdisc->tclass_num, span_trigger);
1751f6668eacSPetr Machata 	if (IS_ERR(qevent_binding)) {
1752f6668eacSPetr Machata 		err = PTR_ERR(qevent_binding);
1753f6668eacSPetr Machata 		goto err_binding_create;
1754f6668eacSPetr Machata 	}
1755f6668eacSPetr Machata 
1756f6668eacSPetr Machata 	err = mlxsw_sp_qevent_binding_configure(qevent_block, qevent_binding);
1757f6668eacSPetr Machata 	if (err)
1758f6668eacSPetr Machata 		goto err_binding_configure;
1759f6668eacSPetr Machata 
1760f6668eacSPetr Machata 	list_add(&qevent_binding->list, &qevent_block->binding_list);
1761f6668eacSPetr Machata 
1762f6668eacSPetr Machata 	if (register_block) {
1763f6668eacSPetr Machata 		flow_block_cb_add(block_cb, f);
1764f6668eacSPetr Machata 		list_add_tail(&block_cb->driver_list, &mlxsw_sp_qevent_block_cb_list);
1765f6668eacSPetr Machata 	}
1766f6668eacSPetr Machata 
1767f6668eacSPetr Machata 	return 0;
1768f6668eacSPetr Machata 
1769f6668eacSPetr Machata err_binding_configure:
1770f6668eacSPetr Machata 	mlxsw_sp_qevent_binding_destroy(qevent_binding);
1771f6668eacSPetr Machata err_binding_create:
1772f6668eacSPetr Machata err_binding_exists:
1773f6668eacSPetr Machata err_find_qdisc:
1774f6668eacSPetr Machata 	if (!flow_block_cb_decref(block_cb))
1775f6668eacSPetr Machata 		flow_block_cb_free(block_cb);
1776f6668eacSPetr Machata 	return err;
1777f6668eacSPetr Machata }
1778f6668eacSPetr Machata 
1779f6668eacSPetr Machata static void mlxsw_sp_setup_tc_block_qevent_unbind(struct mlxsw_sp_port *mlxsw_sp_port,
1780f6668eacSPetr Machata 						  struct flow_block_offload *f,
1781f6668eacSPetr Machata 						  enum mlxsw_sp_span_trigger span_trigger)
1782f6668eacSPetr Machata {
1783f6668eacSPetr Machata 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
1784f6668eacSPetr Machata 	struct mlxsw_sp_qevent_binding *qevent_binding;
1785f6668eacSPetr Machata 	struct mlxsw_sp_qevent_block *qevent_block;
1786f6668eacSPetr Machata 	struct flow_block_cb *block_cb;
1787f6668eacSPetr Machata 
1788f6668eacSPetr Machata 	block_cb = flow_block_cb_lookup(f->block, mlxsw_sp_qevent_block_cb, mlxsw_sp);
1789f6668eacSPetr Machata 	if (!block_cb)
1790f6668eacSPetr Machata 		return;
1791f6668eacSPetr Machata 	qevent_block = flow_block_cb_priv(block_cb);
1792f6668eacSPetr Machata 
1793f6668eacSPetr Machata 	qevent_binding = mlxsw_sp_qevent_binding_lookup(qevent_block, mlxsw_sp_port, f->sch->handle,
1794f6668eacSPetr Machata 							span_trigger);
1795f6668eacSPetr Machata 	if (!qevent_binding)
1796f6668eacSPetr Machata 		return;
1797f6668eacSPetr Machata 
1798f6668eacSPetr Machata 	list_del(&qevent_binding->list);
1799f6668eacSPetr Machata 	mlxsw_sp_qevent_binding_deconfigure(qevent_block, qevent_binding);
1800f6668eacSPetr Machata 	mlxsw_sp_qevent_binding_destroy(qevent_binding);
1801f6668eacSPetr Machata 
1802f6668eacSPetr Machata 	if (!flow_block_cb_decref(block_cb)) {
1803f6668eacSPetr Machata 		flow_block_cb_remove(block_cb, f);
1804f6668eacSPetr Machata 		list_del(&block_cb->driver_list);
1805f6668eacSPetr Machata 	}
1806f6668eacSPetr Machata }
1807f6668eacSPetr Machata 
1808f6668eacSPetr Machata static int mlxsw_sp_setup_tc_block_qevent(struct mlxsw_sp_port *mlxsw_sp_port,
1809f6668eacSPetr Machata 					  struct flow_block_offload *f,
1810f6668eacSPetr Machata 					  enum mlxsw_sp_span_trigger span_trigger)
1811f6668eacSPetr Machata {
1812f6668eacSPetr Machata 	f->driver_block_list = &mlxsw_sp_qevent_block_cb_list;
1813f6668eacSPetr Machata 
1814f6668eacSPetr Machata 	switch (f->command) {
1815f6668eacSPetr Machata 	case FLOW_BLOCK_BIND:
1816f6668eacSPetr Machata 		return mlxsw_sp_setup_tc_block_qevent_bind(mlxsw_sp_port, f, span_trigger);
1817f6668eacSPetr Machata 	case FLOW_BLOCK_UNBIND:
1818f6668eacSPetr Machata 		mlxsw_sp_setup_tc_block_qevent_unbind(mlxsw_sp_port, f, span_trigger);
1819f6668eacSPetr Machata 		return 0;
1820f6668eacSPetr Machata 	default:
1821f6668eacSPetr Machata 		return -EOPNOTSUPP;
1822f6668eacSPetr Machata 	}
1823f6668eacSPetr Machata }
1824f6668eacSPetr Machata 
1825f6668eacSPetr Machata int mlxsw_sp_setup_tc_block_qevent_early_drop(struct mlxsw_sp_port *mlxsw_sp_port,
1826f6668eacSPetr Machata 					      struct flow_block_offload *f)
1827f6668eacSPetr Machata {
1828f6668eacSPetr Machata 	return mlxsw_sp_setup_tc_block_qevent(mlxsw_sp_port, f, MLXSW_SP_SPAN_TRIGGER_EARLY_DROP);
1829f6668eacSPetr Machata }
1830f6668eacSPetr Machata 
1831371b437aSNogah Frankel int mlxsw_sp_tc_qdisc_init(struct mlxsw_sp_port *mlxsw_sp_port)
1832371b437aSNogah Frankel {
1833ee88450dSPetr Machata 	struct mlxsw_sp_qdisc_state *qdisc_state;
1834eed4baebSNogah Frankel 	int i;
1835371b437aSNogah Frankel 
1836ee88450dSPetr Machata 	qdisc_state = kzalloc(sizeof(*qdisc_state), GFP_KERNEL);
1837ee88450dSPetr Machata 	if (!qdisc_state)
1838eed4baebSNogah Frankel 		return -ENOMEM;
1839ee88450dSPetr Machata 
1840ee88450dSPetr Machata 	qdisc_state->root_qdisc.prio_bitmap = 0xff;
1841ee88450dSPetr Machata 	qdisc_state->root_qdisc.tclass_num = MLXSW_SP_PORT_DEFAULT_TCLASS;
1842ee88450dSPetr Machata 	for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++)
1843ee88450dSPetr Machata 		qdisc_state->tclass_qdiscs[i].tclass_num = i;
1844ee88450dSPetr Machata 
1845ee88450dSPetr Machata 	mlxsw_sp_port->qdisc = qdisc_state;
1846ee88450dSPetr Machata 	return 0;
1847371b437aSNogah Frankel }
1848371b437aSNogah Frankel 
1849371b437aSNogah Frankel void mlxsw_sp_tc_qdisc_fini(struct mlxsw_sp_port *mlxsw_sp_port)
1850371b437aSNogah Frankel {
1851ee88450dSPetr Machata 	kfree(mlxsw_sp_port->qdisc);
1852371b437aSNogah Frankel }
1853