1// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
2/* Copyright (c) 2017-2018 Mellanox Technologies. All rights reserved */
3
4#include <linux/kernel.h>
5#include <linux/errno.h>
6#include <linux/netdevice.h>
7#include <net/pkt_cls.h>
8#include <net/red.h>
9
10#include "spectrum.h"
11#include "spectrum_span.h"
12#include "reg.h"
13
14#define MLXSW_SP_PRIO_BAND_TO_TCLASS(band) (IEEE_8021QAZ_MAX_TCS - band - 1)
15#define MLXSW_SP_PRIO_CHILD_TO_TCLASS(child) \
16	MLXSW_SP_PRIO_BAND_TO_TCLASS((child - 1))
17
18enum mlxsw_sp_qdisc_type {
19	MLXSW_SP_QDISC_NO_QDISC,
20	MLXSW_SP_QDISC_RED,
21	MLXSW_SP_QDISC_PRIO,
22	MLXSW_SP_QDISC_ETS,
23	MLXSW_SP_QDISC_TBF,
24	MLXSW_SP_QDISC_FIFO,
25};
26
27struct mlxsw_sp_qdisc;
28
29struct mlxsw_sp_qdisc_ops {
30	enum mlxsw_sp_qdisc_type type;
31	int (*check_params)(struct mlxsw_sp_port *mlxsw_sp_port,
32			    struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
33			    void *params);
34	int (*replace)(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
35		       struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, void *params);
36	int (*destroy)(struct mlxsw_sp_port *mlxsw_sp_port,
37		       struct mlxsw_sp_qdisc *mlxsw_sp_qdisc);
38	int (*get_stats)(struct mlxsw_sp_port *mlxsw_sp_port,
39			 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
40			 struct tc_qopt_offload_stats *stats_ptr);
41	int (*get_xstats)(struct mlxsw_sp_port *mlxsw_sp_port,
42			  struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
43			  void *xstats_ptr);
44	void (*clean_stats)(struct mlxsw_sp_port *mlxsw_sp_port,
45			    struct mlxsw_sp_qdisc *mlxsw_sp_qdisc);
46	/* unoffload - to be used for a qdisc that stops being offloaded without
47	 * being destroyed.
48	 */
49	void (*unoffload)(struct mlxsw_sp_port *mlxsw_sp_port,
50			  struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, void *params);
51};
52
53struct mlxsw_sp_qdisc {
54	u32 handle;
55	u8 tclass_num;
56	u8 prio_bitmap;
57	union {
58		struct red_stats red;
59	} xstats_base;
60	struct mlxsw_sp_qdisc_stats {
61		u64 tx_bytes;
62		u64 tx_packets;
63		u64 drops;
64		u64 overlimits;
65		u64 backlog;
66	} stats_base;
67
68	struct mlxsw_sp_qdisc_ops *ops;
69};
70
71struct mlxsw_sp_qdisc_state {
72	struct mlxsw_sp_qdisc root_qdisc;
73	struct mlxsw_sp_qdisc tclass_qdiscs[IEEE_8021QAZ_MAX_TCS];
74
75	/* When a PRIO or ETS are added, the invisible FIFOs in their bands are
76	 * created first. When notifications for these FIFOs arrive, it is not
77	 * known what qdisc their parent handle refers to. It could be a
78	 * newly-created PRIO that will replace the currently-offloaded one, or
79	 * it could be e.g. a RED that will be attached below it.
80	 *
81	 * As the notifications start to arrive, use them to note what the
82	 * future parent handle is, and keep track of which child FIFOs were
83	 * seen. Then when the parent is known, retroactively offload those
84	 * FIFOs.
85	 */
86	u32 future_handle;
87	bool future_fifos[IEEE_8021QAZ_MAX_TCS];
88};
89
90static bool
91mlxsw_sp_qdisc_compare(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, u32 handle,
92		       enum mlxsw_sp_qdisc_type type)
93{
94	return mlxsw_sp_qdisc && mlxsw_sp_qdisc->ops &&
95	       mlxsw_sp_qdisc->ops->type == type &&
96	       mlxsw_sp_qdisc->handle == handle;
97}
98
99static struct mlxsw_sp_qdisc *
100mlxsw_sp_qdisc_find(struct mlxsw_sp_port *mlxsw_sp_port, u32 parent,
101		    bool root_only)
102{
103	struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc;
104	int tclass, child_index;
105
106	if (parent == TC_H_ROOT)
107		return &qdisc_state->root_qdisc;
108
109	if (root_only || !qdisc_state ||
110	    !qdisc_state->root_qdisc.ops ||
111	    TC_H_MAJ(parent) != qdisc_state->root_qdisc.handle ||
112	    TC_H_MIN(parent) > IEEE_8021QAZ_MAX_TCS)
113		return NULL;
114
115	child_index = TC_H_MIN(parent);
116	tclass = MLXSW_SP_PRIO_CHILD_TO_TCLASS(child_index);
117	return &qdisc_state->tclass_qdiscs[tclass];
118}
119
120static struct mlxsw_sp_qdisc *
121mlxsw_sp_qdisc_find_by_handle(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle)
122{
123	struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc;
124	int i;
125
126	if (qdisc_state->root_qdisc.handle == handle)
127		return &qdisc_state->root_qdisc;
128
129	if (qdisc_state->root_qdisc.handle == TC_H_UNSPEC)
130		return NULL;
131
132	for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++)
133		if (qdisc_state->tclass_qdiscs[i].handle == handle)
134			return &qdisc_state->tclass_qdiscs[i];
135
136	return NULL;
137}
138
139static int
140mlxsw_sp_qdisc_destroy(struct mlxsw_sp_port *mlxsw_sp_port,
141		       struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
142{
143	struct mlxsw_sp_qdisc *root_qdisc = &mlxsw_sp_port->qdisc->root_qdisc;
144	int err_hdroom = 0;
145	int err = 0;
146
147	if (!mlxsw_sp_qdisc)
148		return 0;
149
150	if (root_qdisc == mlxsw_sp_qdisc) {
151		struct mlxsw_sp_hdroom hdroom = *mlxsw_sp_port->hdroom;
152
153		hdroom.mode = MLXSW_SP_HDROOM_MODE_DCB;
154		mlxsw_sp_hdroom_prios_reset_buf_idx(&hdroom);
155		mlxsw_sp_hdroom_bufs_reset_lossiness(&hdroom);
156		mlxsw_sp_hdroom_bufs_reset_sizes(mlxsw_sp_port, &hdroom);
157		err_hdroom = mlxsw_sp_hdroom_configure(mlxsw_sp_port, &hdroom);
158	}
159
160	if (mlxsw_sp_qdisc->ops && mlxsw_sp_qdisc->ops->destroy)
161		err = mlxsw_sp_qdisc->ops->destroy(mlxsw_sp_port,
162						   mlxsw_sp_qdisc);
163
164	mlxsw_sp_qdisc->handle = TC_H_UNSPEC;
165	mlxsw_sp_qdisc->ops = NULL;
166
167	return err_hdroom ?: err;
168}
169
170static int
171mlxsw_sp_qdisc_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
172		       struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
173		       struct mlxsw_sp_qdisc_ops *ops, void *params)
174{
175	struct mlxsw_sp_qdisc *root_qdisc = &mlxsw_sp_port->qdisc->root_qdisc;
176	struct mlxsw_sp_hdroom orig_hdroom;
177	int err;
178
179	if (mlxsw_sp_qdisc->ops && mlxsw_sp_qdisc->ops->type != ops->type)
180		/* In case this location contained a different qdisc of the
181		 * same type we can override the old qdisc configuration.
182		 * Otherwise, we need to remove the old qdisc before setting the
183		 * new one.
184		 */
185		mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
186
187	orig_hdroom = *mlxsw_sp_port->hdroom;
188	if (root_qdisc == mlxsw_sp_qdisc) {
189		struct mlxsw_sp_hdroom hdroom = orig_hdroom;
190
191		hdroom.mode = MLXSW_SP_HDROOM_MODE_TC;
192		mlxsw_sp_hdroom_prios_reset_buf_idx(&hdroom);
193		mlxsw_sp_hdroom_bufs_reset_lossiness(&hdroom);
194		mlxsw_sp_hdroom_bufs_reset_sizes(mlxsw_sp_port, &hdroom);
195
196		err = mlxsw_sp_hdroom_configure(mlxsw_sp_port, &hdroom);
197		if (err)
198			goto err_hdroom_configure;
199	}
200
201	err = ops->check_params(mlxsw_sp_port, mlxsw_sp_qdisc, params);
202	if (err)
203		goto err_bad_param;
204
205	err = ops->replace(mlxsw_sp_port, handle, mlxsw_sp_qdisc, params);
206	if (err)
207		goto err_config;
208
209	/* Check if the Qdisc changed. That includes a situation where an
210	 * invisible Qdisc replaces another one, or is being added for the
211	 * first time.
212	 */
213	if (mlxsw_sp_qdisc->handle != handle || handle == TC_H_UNSPEC) {
214		mlxsw_sp_qdisc->ops = ops;
215		if (ops->clean_stats)
216			ops->clean_stats(mlxsw_sp_port, mlxsw_sp_qdisc);
217	}
218
219	mlxsw_sp_qdisc->handle = handle;
220	return 0;
221
222err_bad_param:
223err_config:
224	mlxsw_sp_hdroom_configure(mlxsw_sp_port, &orig_hdroom);
225err_hdroom_configure:
226	if (mlxsw_sp_qdisc->handle == handle && ops->unoffload)
227		ops->unoffload(mlxsw_sp_port, mlxsw_sp_qdisc, params);
228
229	mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
230	return err;
231}
232
233static int
234mlxsw_sp_qdisc_get_stats(struct mlxsw_sp_port *mlxsw_sp_port,
235			 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
236			 struct tc_qopt_offload_stats *stats_ptr)
237{
238	if (mlxsw_sp_qdisc && mlxsw_sp_qdisc->ops &&
239	    mlxsw_sp_qdisc->ops->get_stats)
240		return mlxsw_sp_qdisc->ops->get_stats(mlxsw_sp_port,
241						      mlxsw_sp_qdisc,
242						      stats_ptr);
243
244	return -EOPNOTSUPP;
245}
246
247static int
248mlxsw_sp_qdisc_get_xstats(struct mlxsw_sp_port *mlxsw_sp_port,
249			  struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
250			  void *xstats_ptr)
251{
252	if (mlxsw_sp_qdisc && mlxsw_sp_qdisc->ops &&
253	    mlxsw_sp_qdisc->ops->get_xstats)
254		return mlxsw_sp_qdisc->ops->get_xstats(mlxsw_sp_port,
255						      mlxsw_sp_qdisc,
256						      xstats_ptr);
257
258	return -EOPNOTSUPP;
259}
260
261static u64
262mlxsw_sp_xstats_backlog(struct mlxsw_sp_port_xstats *xstats, int tclass_num)
263{
264	return xstats->backlog[tclass_num] +
265	       xstats->backlog[tclass_num + 8];
266}
267
268static u64
269mlxsw_sp_xstats_tail_drop(struct mlxsw_sp_port_xstats *xstats, int tclass_num)
270{
271	return xstats->tail_drop[tclass_num] +
272	       xstats->tail_drop[tclass_num + 8];
273}
274
275static void
276mlxsw_sp_qdisc_bstats_per_priority_get(struct mlxsw_sp_port_xstats *xstats,
277				       u8 prio_bitmap, u64 *tx_packets,
278				       u64 *tx_bytes)
279{
280	int i;
281
282	*tx_packets = 0;
283	*tx_bytes = 0;
284	for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
285		if (prio_bitmap & BIT(i)) {
286			*tx_packets += xstats->tx_packets[i];
287			*tx_bytes += xstats->tx_bytes[i];
288		}
289	}
290}
291
292static void
293mlxsw_sp_qdisc_collect_tc_stats(struct mlxsw_sp_port *mlxsw_sp_port,
294				struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
295				u64 *p_tx_bytes, u64 *p_tx_packets,
296				u64 *p_drops, u64 *p_backlog)
297{
298	u8 tclass_num = mlxsw_sp_qdisc->tclass_num;
299	struct mlxsw_sp_port_xstats *xstats;
300	u64 tx_bytes, tx_packets;
301
302	xstats = &mlxsw_sp_port->periodic_hw_stats.xstats;
303	mlxsw_sp_qdisc_bstats_per_priority_get(xstats,
304					       mlxsw_sp_qdisc->prio_bitmap,
305					       &tx_packets, &tx_bytes);
306
307	*p_tx_packets += tx_packets;
308	*p_tx_bytes += tx_bytes;
309	*p_drops += xstats->wred_drop[tclass_num] +
310		    mlxsw_sp_xstats_tail_drop(xstats, tclass_num);
311	*p_backlog += mlxsw_sp_xstats_backlog(xstats, tclass_num);
312}
313
314static void
315mlxsw_sp_qdisc_update_stats(struct mlxsw_sp *mlxsw_sp,
316			    struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
317			    u64 tx_bytes, u64 tx_packets,
318			    u64 drops, u64 backlog,
319			    struct tc_qopt_offload_stats *stats_ptr)
320{
321	struct mlxsw_sp_qdisc_stats *stats_base = &mlxsw_sp_qdisc->stats_base;
322
323	tx_bytes -= stats_base->tx_bytes;
324	tx_packets -= stats_base->tx_packets;
325	drops -= stats_base->drops;
326	backlog -= stats_base->backlog;
327
328	_bstats_update(stats_ptr->bstats, tx_bytes, tx_packets);
329	stats_ptr->qstats->drops += drops;
330	stats_ptr->qstats->backlog += mlxsw_sp_cells_bytes(mlxsw_sp, backlog);
331
332	stats_base->backlog += backlog;
333	stats_base->drops += drops;
334	stats_base->tx_bytes += tx_bytes;
335	stats_base->tx_packets += tx_packets;
336}
337
338static void
339mlxsw_sp_qdisc_get_tc_stats(struct mlxsw_sp_port *mlxsw_sp_port,
340			    struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
341			    struct tc_qopt_offload_stats *stats_ptr)
342{
343	u64 tx_packets = 0;
344	u64 tx_bytes = 0;
345	u64 backlog = 0;
346	u64 drops = 0;
347
348	mlxsw_sp_qdisc_collect_tc_stats(mlxsw_sp_port, mlxsw_sp_qdisc,
349					&tx_bytes, &tx_packets,
350					&drops, &backlog);
351	mlxsw_sp_qdisc_update_stats(mlxsw_sp_port->mlxsw_sp, mlxsw_sp_qdisc,
352				    tx_bytes, tx_packets, drops, backlog,
353				    stats_ptr);
354}
355
356static int
357mlxsw_sp_tclass_congestion_enable(struct mlxsw_sp_port *mlxsw_sp_port,
358				  int tclass_num, u32 min, u32 max,
359				  u32 probability, bool is_wred, bool is_ecn)
360{
361	char cwtpm_cmd[MLXSW_REG_CWTPM_LEN];
362	char cwtp_cmd[MLXSW_REG_CWTP_LEN];
363	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
364	int err;
365
366	mlxsw_reg_cwtp_pack(cwtp_cmd, mlxsw_sp_port->local_port, tclass_num);
367	mlxsw_reg_cwtp_profile_pack(cwtp_cmd, MLXSW_REG_CWTP_DEFAULT_PROFILE,
368				    roundup(min, MLXSW_REG_CWTP_MIN_VALUE),
369				    roundup(max, MLXSW_REG_CWTP_MIN_VALUE),
370				    probability);
371
372	err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(cwtp), cwtp_cmd);
373	if (err)
374		return err;
375
376	mlxsw_reg_cwtpm_pack(cwtpm_cmd, mlxsw_sp_port->local_port, tclass_num,
377			     MLXSW_REG_CWTP_DEFAULT_PROFILE, is_wred, is_ecn);
378
379	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(cwtpm), cwtpm_cmd);
380}
381
382static int
383mlxsw_sp_tclass_congestion_disable(struct mlxsw_sp_port *mlxsw_sp_port,
384				   int tclass_num)
385{
386	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
387	char cwtpm_cmd[MLXSW_REG_CWTPM_LEN];
388
389	mlxsw_reg_cwtpm_pack(cwtpm_cmd, mlxsw_sp_port->local_port, tclass_num,
390			     MLXSW_REG_CWTPM_RESET_PROFILE, false, false);
391	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(cwtpm), cwtpm_cmd);
392}
393
394static void
395mlxsw_sp_setup_tc_qdisc_red_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port,
396					struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
397{
398	u8 tclass_num = mlxsw_sp_qdisc->tclass_num;
399	struct mlxsw_sp_qdisc_stats *stats_base;
400	struct mlxsw_sp_port_xstats *xstats;
401	struct red_stats *red_base;
402
403	xstats = &mlxsw_sp_port->periodic_hw_stats.xstats;
404	stats_base = &mlxsw_sp_qdisc->stats_base;
405	red_base = &mlxsw_sp_qdisc->xstats_base.red;
406
407	mlxsw_sp_qdisc_bstats_per_priority_get(xstats,
408					       mlxsw_sp_qdisc->prio_bitmap,
409					       &stats_base->tx_packets,
410					       &stats_base->tx_bytes);
411	red_base->prob_drop = xstats->wred_drop[tclass_num];
412	red_base->pdrop = mlxsw_sp_xstats_tail_drop(xstats, tclass_num);
413
414	stats_base->overlimits = red_base->prob_drop + red_base->prob_mark;
415	stats_base->drops = red_base->prob_drop + red_base->pdrop;
416
417	stats_base->backlog = 0;
418}
419
420static int
421mlxsw_sp_qdisc_red_destroy(struct mlxsw_sp_port *mlxsw_sp_port,
422			   struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
423{
424	struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc;
425	struct mlxsw_sp_qdisc *root_qdisc = &qdisc_state->root_qdisc;
426
427	if (root_qdisc != mlxsw_sp_qdisc)
428		root_qdisc->stats_base.backlog -=
429					mlxsw_sp_qdisc->stats_base.backlog;
430
431	return mlxsw_sp_tclass_congestion_disable(mlxsw_sp_port,
432						  mlxsw_sp_qdisc->tclass_num);
433}
434
435static int
436mlxsw_sp_qdisc_red_check_params(struct mlxsw_sp_port *mlxsw_sp_port,
437				struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
438				void *params)
439{
440	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
441	struct tc_red_qopt_offload_params *p = params;
442
443	if (p->min > p->max) {
444		dev_err(mlxsw_sp->bus_info->dev,
445			"spectrum: RED: min %u is bigger then max %u\n", p->min,
446			p->max);
447		return -EINVAL;
448	}
449	if (p->max > MLXSW_CORE_RES_GET(mlxsw_sp->core,
450					GUARANTEED_SHARED_BUFFER)) {
451		dev_err(mlxsw_sp->bus_info->dev,
452			"spectrum: RED: max value %u is too big\n", p->max);
453		return -EINVAL;
454	}
455	if (p->min == 0 || p->max == 0) {
456		dev_err(mlxsw_sp->bus_info->dev,
457			"spectrum: RED: 0 value is illegal for min and max\n");
458		return -EINVAL;
459	}
460	return 0;
461}
462
463static int
464mlxsw_sp_qdisc_red_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
465			   struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
466			   void *params)
467{
468	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
469	struct tc_red_qopt_offload_params *p = params;
470	u8 tclass_num = mlxsw_sp_qdisc->tclass_num;
471	u32 min, max;
472	u64 prob;
473
474	/* calculate probability in percentage */
475	prob = p->probability;
476	prob *= 100;
477	prob = DIV_ROUND_UP(prob, 1 << 16);
478	prob = DIV_ROUND_UP(prob, 1 << 16);
479	min = mlxsw_sp_bytes_cells(mlxsw_sp, p->min);
480	max = mlxsw_sp_bytes_cells(mlxsw_sp, p->max);
481	return mlxsw_sp_tclass_congestion_enable(mlxsw_sp_port, tclass_num,
482						 min, max, prob,
483						 !p->is_nodrop, p->is_ecn);
484}
485
486static void
487mlxsw_sp_qdisc_leaf_unoffload(struct mlxsw_sp_port *mlxsw_sp_port,
488			      struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
489			      struct gnet_stats_queue *qstats)
490{
491	u64 backlog;
492
493	backlog = mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp,
494				       mlxsw_sp_qdisc->stats_base.backlog);
495	qstats->backlog -= backlog;
496	mlxsw_sp_qdisc->stats_base.backlog = 0;
497}
498
499static void
500mlxsw_sp_qdisc_red_unoffload(struct mlxsw_sp_port *mlxsw_sp_port,
501			     struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
502			     void *params)
503{
504	struct tc_red_qopt_offload_params *p = params;
505
506	mlxsw_sp_qdisc_leaf_unoffload(mlxsw_sp_port, mlxsw_sp_qdisc, p->qstats);
507}
508
509static int
510mlxsw_sp_qdisc_get_red_xstats(struct mlxsw_sp_port *mlxsw_sp_port,
511			      struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
512			      void *xstats_ptr)
513{
514	struct red_stats *xstats_base = &mlxsw_sp_qdisc->xstats_base.red;
515	u8 tclass_num = mlxsw_sp_qdisc->tclass_num;
516	struct mlxsw_sp_port_xstats *xstats;
517	struct red_stats *res = xstats_ptr;
518	int early_drops, pdrops;
519
520	xstats = &mlxsw_sp_port->periodic_hw_stats.xstats;
521
522	early_drops = xstats->wred_drop[tclass_num] - xstats_base->prob_drop;
523	pdrops = mlxsw_sp_xstats_tail_drop(xstats, tclass_num) -
524		 xstats_base->pdrop;
525
526	res->pdrop += pdrops;
527	res->prob_drop += early_drops;
528
529	xstats_base->pdrop += pdrops;
530	xstats_base->prob_drop += early_drops;
531	return 0;
532}
533
534static int
535mlxsw_sp_qdisc_get_red_stats(struct mlxsw_sp_port *mlxsw_sp_port,
536			     struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
537			     struct tc_qopt_offload_stats *stats_ptr)
538{
539	u8 tclass_num = mlxsw_sp_qdisc->tclass_num;
540	struct mlxsw_sp_qdisc_stats *stats_base;
541	struct mlxsw_sp_port_xstats *xstats;
542	u64 overlimits;
543
544	xstats = &mlxsw_sp_port->periodic_hw_stats.xstats;
545	stats_base = &mlxsw_sp_qdisc->stats_base;
546
547	mlxsw_sp_qdisc_get_tc_stats(mlxsw_sp_port, mlxsw_sp_qdisc, stats_ptr);
548	overlimits = xstats->wred_drop[tclass_num] - stats_base->overlimits;
549
550	stats_ptr->qstats->overlimits += overlimits;
551	stats_base->overlimits += overlimits;
552
553	return 0;
554}
555
556#define MLXSW_SP_PORT_DEFAULT_TCLASS 0
557
558static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_red = {
559	.type = MLXSW_SP_QDISC_RED,
560	.check_params = mlxsw_sp_qdisc_red_check_params,
561	.replace = mlxsw_sp_qdisc_red_replace,
562	.unoffload = mlxsw_sp_qdisc_red_unoffload,
563	.destroy = mlxsw_sp_qdisc_red_destroy,
564	.get_stats = mlxsw_sp_qdisc_get_red_stats,
565	.get_xstats = mlxsw_sp_qdisc_get_red_xstats,
566	.clean_stats = mlxsw_sp_setup_tc_qdisc_red_clean_stats,
567};
568
569int mlxsw_sp_setup_tc_red(struct mlxsw_sp_port *mlxsw_sp_port,
570			  struct tc_red_qopt_offload *p)
571{
572	struct mlxsw_sp_qdisc *mlxsw_sp_qdisc;
573
574	mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent, false);
575	if (!mlxsw_sp_qdisc)
576		return -EOPNOTSUPP;
577
578	if (p->command == TC_RED_REPLACE)
579		return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle,
580					      mlxsw_sp_qdisc,
581					      &mlxsw_sp_qdisc_ops_red,
582					      &p->set);
583
584	if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle,
585				    MLXSW_SP_QDISC_RED))
586		return -EOPNOTSUPP;
587
588	switch (p->command) {
589	case TC_RED_DESTROY:
590		return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
591	case TC_RED_XSTATS:
592		return mlxsw_sp_qdisc_get_xstats(mlxsw_sp_port, mlxsw_sp_qdisc,
593						 p->xstats);
594	case TC_RED_STATS:
595		return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc,
596						&p->stats);
597	default:
598		return -EOPNOTSUPP;
599	}
600}
601
602static void
603mlxsw_sp_setup_tc_qdisc_leaf_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port,
604					 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
605{
606	u64 backlog_cells = 0;
607	u64 tx_packets = 0;
608	u64 tx_bytes = 0;
609	u64 drops = 0;
610
611	mlxsw_sp_qdisc_collect_tc_stats(mlxsw_sp_port, mlxsw_sp_qdisc,
612					&tx_bytes, &tx_packets,
613					&drops, &backlog_cells);
614
615	mlxsw_sp_qdisc->stats_base.tx_packets = tx_packets;
616	mlxsw_sp_qdisc->stats_base.tx_bytes = tx_bytes;
617	mlxsw_sp_qdisc->stats_base.drops = drops;
618	mlxsw_sp_qdisc->stats_base.backlog = 0;
619}
620
621static int
622mlxsw_sp_qdisc_tbf_destroy(struct mlxsw_sp_port *mlxsw_sp_port,
623			   struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
624{
625	struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc;
626	struct mlxsw_sp_qdisc *root_qdisc = &qdisc_state->root_qdisc;
627
628	if (root_qdisc != mlxsw_sp_qdisc)
629		root_qdisc->stats_base.backlog -=
630					mlxsw_sp_qdisc->stats_base.backlog;
631
632	return mlxsw_sp_port_ets_maxrate_set(mlxsw_sp_port,
633					     MLXSW_REG_QEEC_HR_SUBGROUP,
634					     mlxsw_sp_qdisc->tclass_num, 0,
635					     MLXSW_REG_QEEC_MAS_DIS, 0);
636}
637
638static int
639mlxsw_sp_qdisc_tbf_bs(struct mlxsw_sp_port *mlxsw_sp_port,
640		      u32 max_size, u8 *p_burst_size)
641{
642	/* TBF burst size is configured in bytes. The ASIC burst size value is
643	 * ((2 ^ bs) * 512 bits. Convert the TBF bytes to 512-bit units.
644	 */
645	u32 bs512 = max_size / 64;
646	u8 bs = fls(bs512);
647
648	if (!bs)
649		return -EINVAL;
650	--bs;
651
652	/* Demand a power of two. */
653	if ((1 << bs) != bs512)
654		return -EINVAL;
655
656	if (bs < mlxsw_sp_port->mlxsw_sp->lowest_shaper_bs ||
657	    bs > MLXSW_REG_QEEC_HIGHEST_SHAPER_BS)
658		return -EINVAL;
659
660	*p_burst_size = bs;
661	return 0;
662}
663
664static u32
665mlxsw_sp_qdisc_tbf_max_size(u8 bs)
666{
667	return (1U << bs) * 64;
668}
669
670static u64
671mlxsw_sp_qdisc_tbf_rate_kbps(struct tc_tbf_qopt_offload_replace_params *p)
672{
673	/* TBF interface is in bytes/s, whereas Spectrum ASIC is configured in
674	 * Kbits/s.
675	 */
676	return div_u64(p->rate.rate_bytes_ps, 1000) * 8;
677}
678
679static int
680mlxsw_sp_qdisc_tbf_check_params(struct mlxsw_sp_port *mlxsw_sp_port,
681				struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
682				void *params)
683{
684	struct tc_tbf_qopt_offload_replace_params *p = params;
685	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
686	u64 rate_kbps = mlxsw_sp_qdisc_tbf_rate_kbps(p);
687	u8 burst_size;
688	int err;
689
690	if (rate_kbps >= MLXSW_REG_QEEC_MAS_DIS) {
691		dev_err(mlxsw_sp_port->mlxsw_sp->bus_info->dev,
692			"spectrum: TBF: rate of %lluKbps must be below %u\n",
693			rate_kbps, MLXSW_REG_QEEC_MAS_DIS);
694		return -EINVAL;
695	}
696
697	err = mlxsw_sp_qdisc_tbf_bs(mlxsw_sp_port, p->max_size, &burst_size);
698	if (err) {
699		u8 highest_shaper_bs = MLXSW_REG_QEEC_HIGHEST_SHAPER_BS;
700
701		dev_err(mlxsw_sp->bus_info->dev,
702			"spectrum: TBF: invalid burst size of %u, must be a power of two between %u and %u",
703			p->max_size,
704			mlxsw_sp_qdisc_tbf_max_size(mlxsw_sp->lowest_shaper_bs),
705			mlxsw_sp_qdisc_tbf_max_size(highest_shaper_bs));
706		return -EINVAL;
707	}
708
709	return 0;
710}
711
712static int
713mlxsw_sp_qdisc_tbf_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
714			   struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
715			   void *params)
716{
717	struct tc_tbf_qopt_offload_replace_params *p = params;
718	u64 rate_kbps = mlxsw_sp_qdisc_tbf_rate_kbps(p);
719	u8 burst_size;
720	int err;
721
722	err = mlxsw_sp_qdisc_tbf_bs(mlxsw_sp_port, p->max_size, &burst_size);
723	if (WARN_ON_ONCE(err))
724		/* check_params above was supposed to reject this value. */
725		return -EINVAL;
726
727	/* Configure subgroup shaper, so that both UC and MC traffic is subject
728	 * to shaping. That is unlike RED, however UC queue lengths are going to
729	 * be different than MC ones due to different pool and quota
730	 * configurations, so the configuration is not applicable. For shaper on
731	 * the other hand, subjecting the overall stream to the configured
732	 * shaper makes sense. Also note that that is what we do for
733	 * ieee_setmaxrate().
734	 */
735	return mlxsw_sp_port_ets_maxrate_set(mlxsw_sp_port,
736					     MLXSW_REG_QEEC_HR_SUBGROUP,
737					     mlxsw_sp_qdisc->tclass_num, 0,
738					     rate_kbps, burst_size);
739}
740
741static void
742mlxsw_sp_qdisc_tbf_unoffload(struct mlxsw_sp_port *mlxsw_sp_port,
743			     struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
744			     void *params)
745{
746	struct tc_tbf_qopt_offload_replace_params *p = params;
747
748	mlxsw_sp_qdisc_leaf_unoffload(mlxsw_sp_port, mlxsw_sp_qdisc, p->qstats);
749}
750
751static int
752mlxsw_sp_qdisc_get_tbf_stats(struct mlxsw_sp_port *mlxsw_sp_port,
753			     struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
754			     struct tc_qopt_offload_stats *stats_ptr)
755{
756	mlxsw_sp_qdisc_get_tc_stats(mlxsw_sp_port, mlxsw_sp_qdisc,
757				    stats_ptr);
758	return 0;
759}
760
761static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_tbf = {
762	.type = MLXSW_SP_QDISC_TBF,
763	.check_params = mlxsw_sp_qdisc_tbf_check_params,
764	.replace = mlxsw_sp_qdisc_tbf_replace,
765	.unoffload = mlxsw_sp_qdisc_tbf_unoffload,
766	.destroy = mlxsw_sp_qdisc_tbf_destroy,
767	.get_stats = mlxsw_sp_qdisc_get_tbf_stats,
768	.clean_stats = mlxsw_sp_setup_tc_qdisc_leaf_clean_stats,
769};
770
771int mlxsw_sp_setup_tc_tbf(struct mlxsw_sp_port *mlxsw_sp_port,
772			  struct tc_tbf_qopt_offload *p)
773{
774	struct mlxsw_sp_qdisc *mlxsw_sp_qdisc;
775
776	mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent, false);
777	if (!mlxsw_sp_qdisc)
778		return -EOPNOTSUPP;
779
780	if (p->command == TC_TBF_REPLACE)
781		return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle,
782					      mlxsw_sp_qdisc,
783					      &mlxsw_sp_qdisc_ops_tbf,
784					      &p->replace_params);
785
786	if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle,
787				    MLXSW_SP_QDISC_TBF))
788		return -EOPNOTSUPP;
789
790	switch (p->command) {
791	case TC_TBF_DESTROY:
792		return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
793	case TC_TBF_STATS:
794		return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc,
795						&p->stats);
796	default:
797		return -EOPNOTSUPP;
798	}
799}
800
801static int
802mlxsw_sp_qdisc_fifo_destroy(struct mlxsw_sp_port *mlxsw_sp_port,
803			    struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
804{
805	struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc;
806	struct mlxsw_sp_qdisc *root_qdisc = &qdisc_state->root_qdisc;
807
808	if (root_qdisc != mlxsw_sp_qdisc)
809		root_qdisc->stats_base.backlog -=
810					mlxsw_sp_qdisc->stats_base.backlog;
811	return 0;
812}
813
814static int
815mlxsw_sp_qdisc_fifo_check_params(struct mlxsw_sp_port *mlxsw_sp_port,
816				 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
817				 void *params)
818{
819	return 0;
820}
821
822static int
823mlxsw_sp_qdisc_fifo_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
824			    struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
825			    void *params)
826{
827	return 0;
828}
829
830static int
831mlxsw_sp_qdisc_get_fifo_stats(struct mlxsw_sp_port *mlxsw_sp_port,
832			      struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
833			      struct tc_qopt_offload_stats *stats_ptr)
834{
835	mlxsw_sp_qdisc_get_tc_stats(mlxsw_sp_port, mlxsw_sp_qdisc,
836				    stats_ptr);
837	return 0;
838}
839
840static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_fifo = {
841	.type = MLXSW_SP_QDISC_FIFO,
842	.check_params = mlxsw_sp_qdisc_fifo_check_params,
843	.replace = mlxsw_sp_qdisc_fifo_replace,
844	.destroy = mlxsw_sp_qdisc_fifo_destroy,
845	.get_stats = mlxsw_sp_qdisc_get_fifo_stats,
846	.clean_stats = mlxsw_sp_setup_tc_qdisc_leaf_clean_stats,
847};
848
849int mlxsw_sp_setup_tc_fifo(struct mlxsw_sp_port *mlxsw_sp_port,
850			   struct tc_fifo_qopt_offload *p)
851{
852	struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc;
853	struct mlxsw_sp_qdisc *mlxsw_sp_qdisc;
854	int tclass, child_index;
855	u32 parent_handle;
856
857	/* Invisible FIFOs are tracked in future_handle and future_fifos. Make
858	 * sure that not more than one qdisc is created for a port at a time.
859	 * RTNL is a simple proxy for that.
860	 */
861	ASSERT_RTNL();
862
863	mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent, false);
864	if (!mlxsw_sp_qdisc && p->handle == TC_H_UNSPEC) {
865		parent_handle = TC_H_MAJ(p->parent);
866		if (parent_handle != qdisc_state->future_handle) {
867			/* This notifications is for a different Qdisc than
868			 * previously. Wipe the future cache.
869			 */
870			memset(qdisc_state->future_fifos, 0,
871			       sizeof(qdisc_state->future_fifos));
872			qdisc_state->future_handle = parent_handle;
873		}
874
875		child_index = TC_H_MIN(p->parent);
876		tclass = MLXSW_SP_PRIO_CHILD_TO_TCLASS(child_index);
877		if (tclass < IEEE_8021QAZ_MAX_TCS) {
878			if (p->command == TC_FIFO_REPLACE)
879				qdisc_state->future_fifos[tclass] = true;
880			else if (p->command == TC_FIFO_DESTROY)
881				qdisc_state->future_fifos[tclass] = false;
882		}
883	}
884	if (!mlxsw_sp_qdisc)
885		return -EOPNOTSUPP;
886
887	if (p->command == TC_FIFO_REPLACE) {
888		return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle,
889					      mlxsw_sp_qdisc,
890					      &mlxsw_sp_qdisc_ops_fifo, NULL);
891	}
892
893	if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle,
894				    MLXSW_SP_QDISC_FIFO))
895		return -EOPNOTSUPP;
896
897	switch (p->command) {
898	case TC_FIFO_DESTROY:
899		if (p->handle == mlxsw_sp_qdisc->handle)
900			return mlxsw_sp_qdisc_destroy(mlxsw_sp_port,
901						      mlxsw_sp_qdisc);
902		return 0;
903	case TC_FIFO_STATS:
904		return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc,
905						&p->stats);
906	case TC_FIFO_REPLACE: /* Handled above. */
907		break;
908	}
909
910	return -EOPNOTSUPP;
911}
912
913static int
914__mlxsw_sp_qdisc_ets_destroy(struct mlxsw_sp_port *mlxsw_sp_port)
915{
916	struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc;
917	int i;
918
919	for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
920		mlxsw_sp_port_prio_tc_set(mlxsw_sp_port, i,
921					  MLXSW_SP_PORT_DEFAULT_TCLASS);
922		mlxsw_sp_port_ets_set(mlxsw_sp_port,
923				      MLXSW_REG_QEEC_HR_SUBGROUP,
924				      i, 0, false, 0);
925		mlxsw_sp_qdisc_destroy(mlxsw_sp_port,
926				       &qdisc_state->tclass_qdiscs[i]);
927		qdisc_state->tclass_qdiscs[i].prio_bitmap = 0;
928	}
929
930	return 0;
931}
932
933static int
934mlxsw_sp_qdisc_prio_destroy(struct mlxsw_sp_port *mlxsw_sp_port,
935			    struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
936{
937	return __mlxsw_sp_qdisc_ets_destroy(mlxsw_sp_port);
938}
939
940static int
941__mlxsw_sp_qdisc_ets_check_params(unsigned int nbands)
942{
943	if (nbands > IEEE_8021QAZ_MAX_TCS)
944		return -EOPNOTSUPP;
945
946	return 0;
947}
948
949static int
950mlxsw_sp_qdisc_prio_check_params(struct mlxsw_sp_port *mlxsw_sp_port,
951				 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
952				 void *params)
953{
954	struct tc_prio_qopt_offload_params *p = params;
955
956	return __mlxsw_sp_qdisc_ets_check_params(p->bands);
957}
958
959static int
960__mlxsw_sp_qdisc_ets_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
961			     unsigned int nbands,
962			     const unsigned int *quanta,
963			     const unsigned int *weights,
964			     const u8 *priomap)
965{
966	struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc;
967	struct mlxsw_sp_qdisc *child_qdisc;
968	int tclass, i, band, backlog;
969	u8 old_priomap;
970	int err;
971
972	for (band = 0; band < nbands; band++) {
973		tclass = MLXSW_SP_PRIO_BAND_TO_TCLASS(band);
974		child_qdisc = &qdisc_state->tclass_qdiscs[tclass];
975		old_priomap = child_qdisc->prio_bitmap;
976		child_qdisc->prio_bitmap = 0;
977
978		err = mlxsw_sp_port_ets_set(mlxsw_sp_port,
979					    MLXSW_REG_QEEC_HR_SUBGROUP,
980					    tclass, 0, !!quanta[band],
981					    weights[band]);
982		if (err)
983			return err;
984
985		for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
986			if (priomap[i] == band) {
987				child_qdisc->prio_bitmap |= BIT(i);
988				if (BIT(i) & old_priomap)
989					continue;
990				err = mlxsw_sp_port_prio_tc_set(mlxsw_sp_port,
991								i, tclass);
992				if (err)
993					return err;
994			}
995		}
996		if (old_priomap != child_qdisc->prio_bitmap &&
997		    child_qdisc->ops && child_qdisc->ops->clean_stats) {
998			backlog = child_qdisc->stats_base.backlog;
999			child_qdisc->ops->clean_stats(mlxsw_sp_port,
1000						      child_qdisc);
1001			child_qdisc->stats_base.backlog = backlog;
1002		}
1003
1004		if (handle == qdisc_state->future_handle &&
1005		    qdisc_state->future_fifos[tclass]) {
1006			err = mlxsw_sp_qdisc_replace(mlxsw_sp_port, TC_H_UNSPEC,
1007						     child_qdisc,
1008						     &mlxsw_sp_qdisc_ops_fifo,
1009						     NULL);
1010			if (err)
1011				return err;
1012		}
1013	}
1014	for (; band < IEEE_8021QAZ_MAX_TCS; band++) {
1015		tclass = MLXSW_SP_PRIO_BAND_TO_TCLASS(band);
1016		child_qdisc = &qdisc_state->tclass_qdiscs[tclass];
1017		child_qdisc->prio_bitmap = 0;
1018		mlxsw_sp_qdisc_destroy(mlxsw_sp_port, child_qdisc);
1019		mlxsw_sp_port_ets_set(mlxsw_sp_port,
1020				      MLXSW_REG_QEEC_HR_SUBGROUP,
1021				      tclass, 0, false, 0);
1022	}
1023
1024	qdisc_state->future_handle = TC_H_UNSPEC;
1025	memset(qdisc_state->future_fifos, 0, sizeof(qdisc_state->future_fifos));
1026	return 0;
1027}
1028
1029static int
1030mlxsw_sp_qdisc_prio_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
1031			    struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
1032			    void *params)
1033{
1034	struct tc_prio_qopt_offload_params *p = params;
1035	unsigned int zeroes[TCQ_ETS_MAX_BANDS] = {0};
1036
1037	return __mlxsw_sp_qdisc_ets_replace(mlxsw_sp_port, handle, p->bands,
1038					    zeroes, zeroes, p->priomap);
1039}
1040
1041static void
1042__mlxsw_sp_qdisc_ets_unoffload(struct mlxsw_sp_port *mlxsw_sp_port,
1043			       struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
1044			       struct gnet_stats_queue *qstats)
1045{
1046	u64 backlog;
1047
1048	backlog = mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp,
1049				       mlxsw_sp_qdisc->stats_base.backlog);
1050	qstats->backlog -= backlog;
1051}
1052
1053static void
1054mlxsw_sp_qdisc_prio_unoffload(struct mlxsw_sp_port *mlxsw_sp_port,
1055			      struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
1056			      void *params)
1057{
1058	struct tc_prio_qopt_offload_params *p = params;
1059
1060	__mlxsw_sp_qdisc_ets_unoffload(mlxsw_sp_port, mlxsw_sp_qdisc,
1061				       p->qstats);
1062}
1063
1064static int
1065mlxsw_sp_qdisc_get_prio_stats(struct mlxsw_sp_port *mlxsw_sp_port,
1066			      struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
1067			      struct tc_qopt_offload_stats *stats_ptr)
1068{
1069	struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc;
1070	struct mlxsw_sp_qdisc *tc_qdisc;
1071	u64 tx_packets = 0;
1072	u64 tx_bytes = 0;
1073	u64 backlog = 0;
1074	u64 drops = 0;
1075	int i;
1076
1077	for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
1078		tc_qdisc = &qdisc_state->tclass_qdiscs[i];
1079		mlxsw_sp_qdisc_collect_tc_stats(mlxsw_sp_port, tc_qdisc,
1080						&tx_bytes, &tx_packets,
1081						&drops, &backlog);
1082	}
1083
1084	mlxsw_sp_qdisc_update_stats(mlxsw_sp_port->mlxsw_sp, mlxsw_sp_qdisc,
1085				    tx_bytes, tx_packets, drops, backlog,
1086				    stats_ptr);
1087	return 0;
1088}
1089
1090static void
1091mlxsw_sp_setup_tc_qdisc_prio_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port,
1092					 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
1093{
1094	struct mlxsw_sp_qdisc_stats *stats_base;
1095	struct mlxsw_sp_port_xstats *xstats;
1096	struct rtnl_link_stats64 *stats;
1097	int i;
1098
1099	xstats = &mlxsw_sp_port->periodic_hw_stats.xstats;
1100	stats = &mlxsw_sp_port->periodic_hw_stats.stats;
1101	stats_base = &mlxsw_sp_qdisc->stats_base;
1102
1103	stats_base->tx_packets = stats->tx_packets;
1104	stats_base->tx_bytes = stats->tx_bytes;
1105
1106	stats_base->drops = 0;
1107	for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
1108		stats_base->drops += mlxsw_sp_xstats_tail_drop(xstats, i);
1109		stats_base->drops += xstats->wred_drop[i];
1110	}
1111
1112	mlxsw_sp_qdisc->stats_base.backlog = 0;
1113}
1114
1115static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_prio = {
1116	.type = MLXSW_SP_QDISC_PRIO,
1117	.check_params = mlxsw_sp_qdisc_prio_check_params,
1118	.replace = mlxsw_sp_qdisc_prio_replace,
1119	.unoffload = mlxsw_sp_qdisc_prio_unoffload,
1120	.destroy = mlxsw_sp_qdisc_prio_destroy,
1121	.get_stats = mlxsw_sp_qdisc_get_prio_stats,
1122	.clean_stats = mlxsw_sp_setup_tc_qdisc_prio_clean_stats,
1123};
1124
1125static int
1126mlxsw_sp_qdisc_ets_check_params(struct mlxsw_sp_port *mlxsw_sp_port,
1127				struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
1128				void *params)
1129{
1130	struct tc_ets_qopt_offload_replace_params *p = params;
1131
1132	return __mlxsw_sp_qdisc_ets_check_params(p->bands);
1133}
1134
1135static int
1136mlxsw_sp_qdisc_ets_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
1137			   struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
1138			   void *params)
1139{
1140	struct tc_ets_qopt_offload_replace_params *p = params;
1141
1142	return __mlxsw_sp_qdisc_ets_replace(mlxsw_sp_port, handle, p->bands,
1143					    p->quanta, p->weights, p->priomap);
1144}
1145
1146static void
1147mlxsw_sp_qdisc_ets_unoffload(struct mlxsw_sp_port *mlxsw_sp_port,
1148			     struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
1149			     void *params)
1150{
1151	struct tc_ets_qopt_offload_replace_params *p = params;
1152
1153	__mlxsw_sp_qdisc_ets_unoffload(mlxsw_sp_port, mlxsw_sp_qdisc,
1154				       p->qstats);
1155}
1156
1157static int
1158mlxsw_sp_qdisc_ets_destroy(struct mlxsw_sp_port *mlxsw_sp_port,
1159			   struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
1160{
1161	return __mlxsw_sp_qdisc_ets_destroy(mlxsw_sp_port);
1162}
1163
1164static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_ets = {
1165	.type = MLXSW_SP_QDISC_ETS,
1166	.check_params = mlxsw_sp_qdisc_ets_check_params,
1167	.replace = mlxsw_sp_qdisc_ets_replace,
1168	.unoffload = mlxsw_sp_qdisc_ets_unoffload,
1169	.destroy = mlxsw_sp_qdisc_ets_destroy,
1170	.get_stats = mlxsw_sp_qdisc_get_prio_stats,
1171	.clean_stats = mlxsw_sp_setup_tc_qdisc_prio_clean_stats,
1172};
1173
1174/* Linux allows linking of Qdiscs to arbitrary classes (so long as the resulting
1175 * graph is free of cycles). These operations do not change the parent handle
1176 * though, which means it can be incomplete (if there is more than one class
1177 * where the Qdisc in question is grafted) or outright wrong (if the Qdisc was
1178 * linked to a different class and then removed from the original class).
1179 *
1180 * E.g. consider this sequence of operations:
1181 *
1182 *  # tc qdisc add dev swp1 root handle 1: prio
1183 *  # tc qdisc add dev swp1 parent 1:3 handle 13: red limit 1000000 avpkt 10000
1184 *  RED: set bandwidth to 10Mbit
1185 *  # tc qdisc link dev swp1 handle 13: parent 1:2
1186 *
1187 * At this point, both 1:2 and 1:3 have the same RED Qdisc instance as their
1188 * child. But RED will still only claim that 1:3 is its parent. If it's removed
1189 * from that band, its only parent will be 1:2, but it will continue to claim
1190 * that it is in fact 1:3.
1191 *
1192 * The notification for child Qdisc replace (e.g. TC_RED_REPLACE) comes before
1193 * the notification for parent graft (e.g. TC_PRIO_GRAFT). We take the replace
1194 * notification to offload the child Qdisc, based on its parent handle, and use
1195 * the graft operation to validate that the class where the child is actually
1196 * grafted corresponds to the parent handle. If the two don't match, we
1197 * unoffload the child.
1198 */
1199static int
1200__mlxsw_sp_qdisc_ets_graft(struct mlxsw_sp_port *mlxsw_sp_port,
1201			   struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
1202			   u8 band, u32 child_handle)
1203{
1204	struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc;
1205	int tclass_num = MLXSW_SP_PRIO_BAND_TO_TCLASS(band);
1206	struct mlxsw_sp_qdisc *old_qdisc;
1207
1208	if (band < IEEE_8021QAZ_MAX_TCS &&
1209	    qdisc_state->tclass_qdiscs[tclass_num].handle == child_handle)
1210		return 0;
1211
1212	if (!child_handle) {
1213		/* This is an invisible FIFO replacing the original Qdisc.
1214		 * Ignore it--the original Qdisc's destroy will follow.
1215		 */
1216		return 0;
1217	}
1218
1219	/* See if the grafted qdisc is already offloaded on any tclass. If so,
1220	 * unoffload it.
1221	 */
1222	old_qdisc = mlxsw_sp_qdisc_find_by_handle(mlxsw_sp_port,
1223						  child_handle);
1224	if (old_qdisc)
1225		mlxsw_sp_qdisc_destroy(mlxsw_sp_port, old_qdisc);
1226
1227	mlxsw_sp_qdisc_destroy(mlxsw_sp_port,
1228			       &qdisc_state->tclass_qdiscs[tclass_num]);
1229	return -EOPNOTSUPP;
1230}
1231
1232static int
1233mlxsw_sp_qdisc_prio_graft(struct mlxsw_sp_port *mlxsw_sp_port,
1234			  struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
1235			  struct tc_prio_qopt_offload_graft_params *p)
1236{
1237	return __mlxsw_sp_qdisc_ets_graft(mlxsw_sp_port, mlxsw_sp_qdisc,
1238					  p->band, p->child_handle);
1239}
1240
1241int mlxsw_sp_setup_tc_prio(struct mlxsw_sp_port *mlxsw_sp_port,
1242			   struct tc_prio_qopt_offload *p)
1243{
1244	struct mlxsw_sp_qdisc *mlxsw_sp_qdisc;
1245
1246	mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent, true);
1247	if (!mlxsw_sp_qdisc)
1248		return -EOPNOTSUPP;
1249
1250	if (p->command == TC_PRIO_REPLACE)
1251		return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle,
1252					      mlxsw_sp_qdisc,
1253					      &mlxsw_sp_qdisc_ops_prio,
1254					      &p->replace_params);
1255
1256	if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle,
1257				    MLXSW_SP_QDISC_PRIO))
1258		return -EOPNOTSUPP;
1259
1260	switch (p->command) {
1261	case TC_PRIO_DESTROY:
1262		return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
1263	case TC_PRIO_STATS:
1264		return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc,
1265						&p->stats);
1266	case TC_PRIO_GRAFT:
1267		return mlxsw_sp_qdisc_prio_graft(mlxsw_sp_port, mlxsw_sp_qdisc,
1268						 &p->graft_params);
1269	default:
1270		return -EOPNOTSUPP;
1271	}
1272}
1273
1274int mlxsw_sp_setup_tc_ets(struct mlxsw_sp_port *mlxsw_sp_port,
1275			  struct tc_ets_qopt_offload *p)
1276{
1277	struct mlxsw_sp_qdisc *mlxsw_sp_qdisc;
1278
1279	mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent, true);
1280	if (!mlxsw_sp_qdisc)
1281		return -EOPNOTSUPP;
1282
1283	if (p->command == TC_ETS_REPLACE)
1284		return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle,
1285					      mlxsw_sp_qdisc,
1286					      &mlxsw_sp_qdisc_ops_ets,
1287					      &p->replace_params);
1288
1289	if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle,
1290				    MLXSW_SP_QDISC_ETS))
1291		return -EOPNOTSUPP;
1292
1293	switch (p->command) {
1294	case TC_ETS_DESTROY:
1295		return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
1296	case TC_ETS_STATS:
1297		return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc,
1298						&p->stats);
1299	case TC_ETS_GRAFT:
1300		return __mlxsw_sp_qdisc_ets_graft(mlxsw_sp_port, mlxsw_sp_qdisc,
1301						  p->graft_params.band,
1302						  p->graft_params.child_handle);
1303	default:
1304		return -EOPNOTSUPP;
1305	}
1306}
1307
1308struct mlxsw_sp_qevent_block {
1309	struct list_head binding_list;
1310	struct list_head mall_entry_list;
1311	struct mlxsw_sp *mlxsw_sp;
1312};
1313
1314struct mlxsw_sp_qevent_binding {
1315	struct list_head list;
1316	struct mlxsw_sp_port *mlxsw_sp_port;
1317	u32 handle;
1318	int tclass_num;
1319	enum mlxsw_sp_span_trigger span_trigger;
1320};
1321
1322static LIST_HEAD(mlxsw_sp_qevent_block_cb_list);
1323
1324static int mlxsw_sp_qevent_span_configure(struct mlxsw_sp *mlxsw_sp,
1325					  struct mlxsw_sp_mall_entry *mall_entry,
1326					  struct mlxsw_sp_qevent_binding *qevent_binding,
1327					  const struct mlxsw_sp_span_agent_parms *agent_parms,
1328					  int *p_span_id)
1329{
1330	struct mlxsw_sp_port *mlxsw_sp_port = qevent_binding->mlxsw_sp_port;
1331	struct mlxsw_sp_span_trigger_parms trigger_parms = {};
1332	int span_id;
1333	int err;
1334
1335	err = mlxsw_sp_span_agent_get(mlxsw_sp, &span_id, agent_parms);
1336	if (err)
1337		return err;
1338
1339	err = mlxsw_sp_span_analyzed_port_get(mlxsw_sp_port, true);
1340	if (err)
1341		goto err_analyzed_port_get;
1342
1343	trigger_parms.span_id = span_id;
1344	err = mlxsw_sp_span_agent_bind(mlxsw_sp, qevent_binding->span_trigger, mlxsw_sp_port,
1345				       &trigger_parms);
1346	if (err)
1347		goto err_agent_bind;
1348
1349	err = mlxsw_sp_span_trigger_enable(mlxsw_sp_port, qevent_binding->span_trigger,
1350					   qevent_binding->tclass_num);
1351	if (err)
1352		goto err_trigger_enable;
1353
1354	*p_span_id = span_id;
1355	return 0;
1356
1357err_trigger_enable:
1358	mlxsw_sp_span_agent_unbind(mlxsw_sp, qevent_binding->span_trigger, mlxsw_sp_port,
1359				   &trigger_parms);
1360err_agent_bind:
1361	mlxsw_sp_span_analyzed_port_put(mlxsw_sp_port, true);
1362err_analyzed_port_get:
1363	mlxsw_sp_span_agent_put(mlxsw_sp, span_id);
1364	return err;
1365}
1366
1367static void mlxsw_sp_qevent_span_deconfigure(struct mlxsw_sp *mlxsw_sp,
1368					     struct mlxsw_sp_qevent_binding *qevent_binding,
1369					     int span_id)
1370{
1371	struct mlxsw_sp_port *mlxsw_sp_port = qevent_binding->mlxsw_sp_port;
1372	struct mlxsw_sp_span_trigger_parms trigger_parms = {
1373		.span_id = span_id,
1374	};
1375
1376	mlxsw_sp_span_trigger_disable(mlxsw_sp_port, qevent_binding->span_trigger,
1377				      qevent_binding->tclass_num);
1378	mlxsw_sp_span_agent_unbind(mlxsw_sp, qevent_binding->span_trigger, mlxsw_sp_port,
1379				   &trigger_parms);
1380	mlxsw_sp_span_analyzed_port_put(mlxsw_sp_port, true);
1381	mlxsw_sp_span_agent_put(mlxsw_sp, span_id);
1382}
1383
1384static int mlxsw_sp_qevent_mirror_configure(struct mlxsw_sp *mlxsw_sp,
1385					    struct mlxsw_sp_mall_entry *mall_entry,
1386					    struct mlxsw_sp_qevent_binding *qevent_binding)
1387{
1388	struct mlxsw_sp_span_agent_parms agent_parms = {
1389		.to_dev = mall_entry->mirror.to_dev,
1390	};
1391
1392	return mlxsw_sp_qevent_span_configure(mlxsw_sp, mall_entry, qevent_binding,
1393					      &agent_parms, &mall_entry->mirror.span_id);
1394}
1395
1396static void mlxsw_sp_qevent_mirror_deconfigure(struct mlxsw_sp *mlxsw_sp,
1397					       struct mlxsw_sp_mall_entry *mall_entry,
1398					       struct mlxsw_sp_qevent_binding *qevent_binding)
1399{
1400	mlxsw_sp_qevent_span_deconfigure(mlxsw_sp, qevent_binding, mall_entry->mirror.span_id);
1401}
1402
1403static int mlxsw_sp_qevent_trap_configure(struct mlxsw_sp *mlxsw_sp,
1404					  struct mlxsw_sp_mall_entry *mall_entry,
1405					  struct mlxsw_sp_qevent_binding *qevent_binding)
1406{
1407	struct mlxsw_sp_span_agent_parms agent_parms = {};
1408	int err;
1409
1410	err = mlxsw_sp_trap_group_policer_hw_id_get(mlxsw_sp,
1411						    DEVLINK_TRAP_GROUP_GENERIC_ID_BUFFER_DROPS,
1412						    &agent_parms.policer_enable,
1413						    &agent_parms.policer_id);
1414	if (err)
1415		return err;
1416
1417	return mlxsw_sp_qevent_span_configure(mlxsw_sp, mall_entry, qevent_binding,
1418					      &agent_parms, &mall_entry->trap.span_id);
1419}
1420
1421static void mlxsw_sp_qevent_trap_deconfigure(struct mlxsw_sp *mlxsw_sp,
1422					     struct mlxsw_sp_mall_entry *mall_entry,
1423					     struct mlxsw_sp_qevent_binding *qevent_binding)
1424{
1425	mlxsw_sp_qevent_span_deconfigure(mlxsw_sp, qevent_binding, mall_entry->trap.span_id);
1426}
1427
1428static int mlxsw_sp_qevent_entry_configure(struct mlxsw_sp *mlxsw_sp,
1429					   struct mlxsw_sp_mall_entry *mall_entry,
1430					   struct mlxsw_sp_qevent_binding *qevent_binding)
1431{
1432	switch (mall_entry->type) {
1433	case MLXSW_SP_MALL_ACTION_TYPE_MIRROR:
1434		return mlxsw_sp_qevent_mirror_configure(mlxsw_sp, mall_entry, qevent_binding);
1435	case MLXSW_SP_MALL_ACTION_TYPE_TRAP:
1436		return mlxsw_sp_qevent_trap_configure(mlxsw_sp, mall_entry, qevent_binding);
1437	default:
1438		/* This should have been validated away. */
1439		WARN_ON(1);
1440		return -EOPNOTSUPP;
1441	}
1442}
1443
1444static void mlxsw_sp_qevent_entry_deconfigure(struct mlxsw_sp *mlxsw_sp,
1445					      struct mlxsw_sp_mall_entry *mall_entry,
1446					      struct mlxsw_sp_qevent_binding *qevent_binding)
1447{
1448	switch (mall_entry->type) {
1449	case MLXSW_SP_MALL_ACTION_TYPE_MIRROR:
1450		return mlxsw_sp_qevent_mirror_deconfigure(mlxsw_sp, mall_entry, qevent_binding);
1451	case MLXSW_SP_MALL_ACTION_TYPE_TRAP:
1452		return mlxsw_sp_qevent_trap_deconfigure(mlxsw_sp, mall_entry, qevent_binding);
1453	default:
1454		WARN_ON(1);
1455		return;
1456	}
1457}
1458
1459static int mlxsw_sp_qevent_binding_configure(struct mlxsw_sp_qevent_block *qevent_block,
1460					     struct mlxsw_sp_qevent_binding *qevent_binding)
1461{
1462	struct mlxsw_sp_mall_entry *mall_entry;
1463	int err;
1464
1465	list_for_each_entry(mall_entry, &qevent_block->mall_entry_list, list) {
1466		err = mlxsw_sp_qevent_entry_configure(qevent_block->mlxsw_sp, mall_entry,
1467						      qevent_binding);
1468		if (err)
1469			goto err_entry_configure;
1470	}
1471
1472	return 0;
1473
1474err_entry_configure:
1475	list_for_each_entry_continue_reverse(mall_entry, &qevent_block->mall_entry_list, list)
1476		mlxsw_sp_qevent_entry_deconfigure(qevent_block->mlxsw_sp, mall_entry,
1477						  qevent_binding);
1478	return err;
1479}
1480
1481static void mlxsw_sp_qevent_binding_deconfigure(struct mlxsw_sp_qevent_block *qevent_block,
1482						struct mlxsw_sp_qevent_binding *qevent_binding)
1483{
1484	struct mlxsw_sp_mall_entry *mall_entry;
1485
1486	list_for_each_entry(mall_entry, &qevent_block->mall_entry_list, list)
1487		mlxsw_sp_qevent_entry_deconfigure(qevent_block->mlxsw_sp, mall_entry,
1488						  qevent_binding);
1489}
1490
1491static int mlxsw_sp_qevent_block_configure(struct mlxsw_sp_qevent_block *qevent_block)
1492{
1493	struct mlxsw_sp_qevent_binding *qevent_binding;
1494	int err;
1495
1496	list_for_each_entry(qevent_binding, &qevent_block->binding_list, list) {
1497		err = mlxsw_sp_qevent_binding_configure(qevent_block, qevent_binding);
1498		if (err)
1499			goto err_binding_configure;
1500	}
1501
1502	return 0;
1503
1504err_binding_configure:
1505	list_for_each_entry_continue_reverse(qevent_binding, &qevent_block->binding_list, list)
1506		mlxsw_sp_qevent_binding_deconfigure(qevent_block, qevent_binding);
1507	return err;
1508}
1509
1510static void mlxsw_sp_qevent_block_deconfigure(struct mlxsw_sp_qevent_block *qevent_block)
1511{
1512	struct mlxsw_sp_qevent_binding *qevent_binding;
1513
1514	list_for_each_entry(qevent_binding, &qevent_block->binding_list, list)
1515		mlxsw_sp_qevent_binding_deconfigure(qevent_block, qevent_binding);
1516}
1517
1518static struct mlxsw_sp_mall_entry *
1519mlxsw_sp_qevent_mall_entry_find(struct mlxsw_sp_qevent_block *block, unsigned long cookie)
1520{
1521	struct mlxsw_sp_mall_entry *mall_entry;
1522
1523	list_for_each_entry(mall_entry, &block->mall_entry_list, list)
1524		if (mall_entry->cookie == cookie)
1525			return mall_entry;
1526
1527	return NULL;
1528}
1529
1530static int mlxsw_sp_qevent_mall_replace(struct mlxsw_sp *mlxsw_sp,
1531					struct mlxsw_sp_qevent_block *qevent_block,
1532					struct tc_cls_matchall_offload *f)
1533{
1534	struct mlxsw_sp_mall_entry *mall_entry;
1535	struct flow_action_entry *act;
1536	int err;
1537
1538	/* It should not currently be possible to replace a matchall rule. So
1539	 * this must be a new rule.
1540	 */
1541	if (!list_empty(&qevent_block->mall_entry_list)) {
1542		NL_SET_ERR_MSG(f->common.extack, "At most one filter supported");
1543		return -EOPNOTSUPP;
1544	}
1545	if (f->rule->action.num_entries != 1) {
1546		NL_SET_ERR_MSG(f->common.extack, "Only singular actions supported");
1547		return -EOPNOTSUPP;
1548	}
1549	if (f->common.chain_index) {
1550		NL_SET_ERR_MSG(f->common.extack, "Only chain 0 is supported");
1551		return -EOPNOTSUPP;
1552	}
1553	if (f->common.protocol != htons(ETH_P_ALL)) {
1554		NL_SET_ERR_MSG(f->common.extack, "Protocol matching not supported");
1555		return -EOPNOTSUPP;
1556	}
1557
1558	act = &f->rule->action.entries[0];
1559	if (!(act->hw_stats & FLOW_ACTION_HW_STATS_DISABLED)) {
1560		NL_SET_ERR_MSG(f->common.extack, "HW counters not supported on qevents");
1561		return -EOPNOTSUPP;
1562	}
1563
1564	mall_entry = kzalloc(sizeof(*mall_entry), GFP_KERNEL);
1565	if (!mall_entry)
1566		return -ENOMEM;
1567	mall_entry->cookie = f->cookie;
1568
1569	if (act->id == FLOW_ACTION_MIRRED) {
1570		mall_entry->type = MLXSW_SP_MALL_ACTION_TYPE_MIRROR;
1571		mall_entry->mirror.to_dev = act->dev;
1572	} else if (act->id == FLOW_ACTION_TRAP) {
1573		mall_entry->type = MLXSW_SP_MALL_ACTION_TYPE_TRAP;
1574	} else {
1575		NL_SET_ERR_MSG(f->common.extack, "Unsupported action");
1576		err = -EOPNOTSUPP;
1577		goto err_unsupported_action;
1578	}
1579
1580	list_add_tail(&mall_entry->list, &qevent_block->mall_entry_list);
1581
1582	err = mlxsw_sp_qevent_block_configure(qevent_block);
1583	if (err)
1584		goto err_block_configure;
1585
1586	return 0;
1587
1588err_block_configure:
1589	list_del(&mall_entry->list);
1590err_unsupported_action:
1591	kfree(mall_entry);
1592	return err;
1593}
1594
1595static void mlxsw_sp_qevent_mall_destroy(struct mlxsw_sp_qevent_block *qevent_block,
1596					 struct tc_cls_matchall_offload *f)
1597{
1598	struct mlxsw_sp_mall_entry *mall_entry;
1599
1600	mall_entry = mlxsw_sp_qevent_mall_entry_find(qevent_block, f->cookie);
1601	if (!mall_entry)
1602		return;
1603
1604	mlxsw_sp_qevent_block_deconfigure(qevent_block);
1605
1606	list_del(&mall_entry->list);
1607	kfree(mall_entry);
1608}
1609
1610static int mlxsw_sp_qevent_block_mall_cb(struct mlxsw_sp_qevent_block *qevent_block,
1611					 struct tc_cls_matchall_offload *f)
1612{
1613	struct mlxsw_sp *mlxsw_sp = qevent_block->mlxsw_sp;
1614
1615	switch (f->command) {
1616	case TC_CLSMATCHALL_REPLACE:
1617		return mlxsw_sp_qevent_mall_replace(mlxsw_sp, qevent_block, f);
1618	case TC_CLSMATCHALL_DESTROY:
1619		mlxsw_sp_qevent_mall_destroy(qevent_block, f);
1620		return 0;
1621	default:
1622		return -EOPNOTSUPP;
1623	}
1624}
1625
1626static int mlxsw_sp_qevent_block_cb(enum tc_setup_type type, void *type_data, void *cb_priv)
1627{
1628	struct mlxsw_sp_qevent_block *qevent_block = cb_priv;
1629
1630	switch (type) {
1631	case TC_SETUP_CLSMATCHALL:
1632		return mlxsw_sp_qevent_block_mall_cb(qevent_block, type_data);
1633	default:
1634		return -EOPNOTSUPP;
1635	}
1636}
1637
1638static struct mlxsw_sp_qevent_block *mlxsw_sp_qevent_block_create(struct mlxsw_sp *mlxsw_sp,
1639								  struct net *net)
1640{
1641	struct mlxsw_sp_qevent_block *qevent_block;
1642
1643	qevent_block = kzalloc(sizeof(*qevent_block), GFP_KERNEL);
1644	if (!qevent_block)
1645		return NULL;
1646
1647	INIT_LIST_HEAD(&qevent_block->binding_list);
1648	INIT_LIST_HEAD(&qevent_block->mall_entry_list);
1649	qevent_block->mlxsw_sp = mlxsw_sp;
1650	return qevent_block;
1651}
1652
1653static void
1654mlxsw_sp_qevent_block_destroy(struct mlxsw_sp_qevent_block *qevent_block)
1655{
1656	WARN_ON(!list_empty(&qevent_block->binding_list));
1657	WARN_ON(!list_empty(&qevent_block->mall_entry_list));
1658	kfree(qevent_block);
1659}
1660
1661static void mlxsw_sp_qevent_block_release(void *cb_priv)
1662{
1663	struct mlxsw_sp_qevent_block *qevent_block = cb_priv;
1664
1665	mlxsw_sp_qevent_block_destroy(qevent_block);
1666}
1667
1668static struct mlxsw_sp_qevent_binding *
1669mlxsw_sp_qevent_binding_create(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, int tclass_num,
1670			       enum mlxsw_sp_span_trigger span_trigger)
1671{
1672	struct mlxsw_sp_qevent_binding *binding;
1673
1674	binding = kzalloc(sizeof(*binding), GFP_KERNEL);
1675	if (!binding)
1676		return ERR_PTR(-ENOMEM);
1677
1678	binding->mlxsw_sp_port = mlxsw_sp_port;
1679	binding->handle = handle;
1680	binding->tclass_num = tclass_num;
1681	binding->span_trigger = span_trigger;
1682	return binding;
1683}
1684
1685static void
1686mlxsw_sp_qevent_binding_destroy(struct mlxsw_sp_qevent_binding *binding)
1687{
1688	kfree(binding);
1689}
1690
1691static struct mlxsw_sp_qevent_binding *
1692mlxsw_sp_qevent_binding_lookup(struct mlxsw_sp_qevent_block *block,
1693			       struct mlxsw_sp_port *mlxsw_sp_port,
1694			       u32 handle,
1695			       enum mlxsw_sp_span_trigger span_trigger)
1696{
1697	struct mlxsw_sp_qevent_binding *qevent_binding;
1698
1699	list_for_each_entry(qevent_binding, &block->binding_list, list)
1700		if (qevent_binding->mlxsw_sp_port == mlxsw_sp_port &&
1701		    qevent_binding->handle == handle &&
1702		    qevent_binding->span_trigger == span_trigger)
1703			return qevent_binding;
1704	return NULL;
1705}
1706
1707static int mlxsw_sp_setup_tc_block_qevent_bind(struct mlxsw_sp_port *mlxsw_sp_port,
1708					       struct flow_block_offload *f,
1709					       enum mlxsw_sp_span_trigger span_trigger)
1710{
1711	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
1712	struct mlxsw_sp_qevent_binding *qevent_binding;
1713	struct mlxsw_sp_qevent_block *qevent_block;
1714	struct flow_block_cb *block_cb;
1715	struct mlxsw_sp_qdisc *qdisc;
1716	bool register_block = false;
1717	int err;
1718
1719	block_cb = flow_block_cb_lookup(f->block, mlxsw_sp_qevent_block_cb, mlxsw_sp);
1720	if (!block_cb) {
1721		qevent_block = mlxsw_sp_qevent_block_create(mlxsw_sp, f->net);
1722		if (!qevent_block)
1723			return -ENOMEM;
1724		block_cb = flow_block_cb_alloc(mlxsw_sp_qevent_block_cb, mlxsw_sp, qevent_block,
1725					       mlxsw_sp_qevent_block_release);
1726		if (IS_ERR(block_cb)) {
1727			mlxsw_sp_qevent_block_destroy(qevent_block);
1728			return PTR_ERR(block_cb);
1729		}
1730		register_block = true;
1731	} else {
1732		qevent_block = flow_block_cb_priv(block_cb);
1733	}
1734	flow_block_cb_incref(block_cb);
1735
1736	qdisc = mlxsw_sp_qdisc_find_by_handle(mlxsw_sp_port, f->sch->handle);
1737	if (!qdisc) {
1738		NL_SET_ERR_MSG(f->extack, "Qdisc not offloaded");
1739		err = -ENOENT;
1740		goto err_find_qdisc;
1741	}
1742
1743	if (WARN_ON(mlxsw_sp_qevent_binding_lookup(qevent_block, mlxsw_sp_port, f->sch->handle,
1744						   span_trigger))) {
1745		err = -EEXIST;
1746		goto err_binding_exists;
1747	}
1748
1749	qevent_binding = mlxsw_sp_qevent_binding_create(mlxsw_sp_port, f->sch->handle,
1750							qdisc->tclass_num, span_trigger);
1751	if (IS_ERR(qevent_binding)) {
1752		err = PTR_ERR(qevent_binding);
1753		goto err_binding_create;
1754	}
1755
1756	err = mlxsw_sp_qevent_binding_configure(qevent_block, qevent_binding);
1757	if (err)
1758		goto err_binding_configure;
1759
1760	list_add(&qevent_binding->list, &qevent_block->binding_list);
1761
1762	if (register_block) {
1763		flow_block_cb_add(block_cb, f);
1764		list_add_tail(&block_cb->driver_list, &mlxsw_sp_qevent_block_cb_list);
1765	}
1766
1767	return 0;
1768
1769err_binding_configure:
1770	mlxsw_sp_qevent_binding_destroy(qevent_binding);
1771err_binding_create:
1772err_binding_exists:
1773err_find_qdisc:
1774	if (!flow_block_cb_decref(block_cb))
1775		flow_block_cb_free(block_cb);
1776	return err;
1777}
1778
1779static void mlxsw_sp_setup_tc_block_qevent_unbind(struct mlxsw_sp_port *mlxsw_sp_port,
1780						  struct flow_block_offload *f,
1781						  enum mlxsw_sp_span_trigger span_trigger)
1782{
1783	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
1784	struct mlxsw_sp_qevent_binding *qevent_binding;
1785	struct mlxsw_sp_qevent_block *qevent_block;
1786	struct flow_block_cb *block_cb;
1787
1788	block_cb = flow_block_cb_lookup(f->block, mlxsw_sp_qevent_block_cb, mlxsw_sp);
1789	if (!block_cb)
1790		return;
1791	qevent_block = flow_block_cb_priv(block_cb);
1792
1793	qevent_binding = mlxsw_sp_qevent_binding_lookup(qevent_block, mlxsw_sp_port, f->sch->handle,
1794							span_trigger);
1795	if (!qevent_binding)
1796		return;
1797
1798	list_del(&qevent_binding->list);
1799	mlxsw_sp_qevent_binding_deconfigure(qevent_block, qevent_binding);
1800	mlxsw_sp_qevent_binding_destroy(qevent_binding);
1801
1802	if (!flow_block_cb_decref(block_cb)) {
1803		flow_block_cb_remove(block_cb, f);
1804		list_del(&block_cb->driver_list);
1805	}
1806}
1807
1808static int mlxsw_sp_setup_tc_block_qevent(struct mlxsw_sp_port *mlxsw_sp_port,
1809					  struct flow_block_offload *f,
1810					  enum mlxsw_sp_span_trigger span_trigger)
1811{
1812	f->driver_block_list = &mlxsw_sp_qevent_block_cb_list;
1813
1814	switch (f->command) {
1815	case FLOW_BLOCK_BIND:
1816		return mlxsw_sp_setup_tc_block_qevent_bind(mlxsw_sp_port, f, span_trigger);
1817	case FLOW_BLOCK_UNBIND:
1818		mlxsw_sp_setup_tc_block_qevent_unbind(mlxsw_sp_port, f, span_trigger);
1819		return 0;
1820	default:
1821		return -EOPNOTSUPP;
1822	}
1823}
1824
1825int mlxsw_sp_setup_tc_block_qevent_early_drop(struct mlxsw_sp_port *mlxsw_sp_port,
1826					      struct flow_block_offload *f)
1827{
1828	return mlxsw_sp_setup_tc_block_qevent(mlxsw_sp_port, f, MLXSW_SP_SPAN_TRIGGER_EARLY_DROP);
1829}
1830
1831int mlxsw_sp_tc_qdisc_init(struct mlxsw_sp_port *mlxsw_sp_port)
1832{
1833	struct mlxsw_sp_qdisc_state *qdisc_state;
1834	int i;
1835
1836	qdisc_state = kzalloc(sizeof(*qdisc_state), GFP_KERNEL);
1837	if (!qdisc_state)
1838		return -ENOMEM;
1839
1840	qdisc_state->root_qdisc.prio_bitmap = 0xff;
1841	qdisc_state->root_qdisc.tclass_num = MLXSW_SP_PORT_DEFAULT_TCLASS;
1842	for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++)
1843		qdisc_state->tclass_qdiscs[i].tclass_num = i;
1844
1845	mlxsw_sp_port->qdisc = qdisc_state;
1846	return 0;
1847}
1848
1849void mlxsw_sp_tc_qdisc_fini(struct mlxsw_sp_port *mlxsw_sp_port)
1850{
1851	kfree(mlxsw_sp_port->qdisc);
1852}
1853