1 /*
2  * drivers/net/ethernet/mellanox/mlxsw/spectrum_qdisc.c
3  * Copyright (c) 2017 Mellanox Technologies. All rights reserved.
4  * Copyright (c) 2017 Nogah Frankel <nogahf@mellanox.com>
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. Neither the names of the copyright holders nor the names of its
15  *    contributors may be used to endorse or promote products derived from
16  *    this software without specific prior written permission.
17  *
18  * Alternatively, this software may be distributed under the terms of the
19  * GNU General Public License ("GPL") version 2 as published by the Free
20  * Software Foundation.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  */
34 
35 #include <linux/kernel.h>
36 #include <linux/errno.h>
37 #include <linux/netdevice.h>
38 #include <net/pkt_cls.h>
39 #include <net/red.h>
40 
41 #include "spectrum.h"
42 #include "reg.h"
43 
44 #define MLXSW_SP_PRIO_BAND_TO_TCLASS(band) (IEEE_8021QAZ_MAX_TCS - band - 1)
45 #define MLXSW_SP_PRIO_CHILD_TO_TCLASS(child) \
46 	MLXSW_SP_PRIO_BAND_TO_TCLASS((child - 1))
47 
48 enum mlxsw_sp_qdisc_type {
49 	MLXSW_SP_QDISC_NO_QDISC,
50 	MLXSW_SP_QDISC_RED,
51 	MLXSW_SP_QDISC_PRIO,
52 };
53 
54 struct mlxsw_sp_qdisc_ops {
55 	enum mlxsw_sp_qdisc_type type;
56 	int (*check_params)(struct mlxsw_sp_port *mlxsw_sp_port,
57 			    struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
58 			    void *params);
59 	int (*replace)(struct mlxsw_sp_port *mlxsw_sp_port,
60 		       struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, void *params);
61 	int (*destroy)(struct mlxsw_sp_port *mlxsw_sp_port,
62 		       struct mlxsw_sp_qdisc *mlxsw_sp_qdisc);
63 	int (*get_stats)(struct mlxsw_sp_port *mlxsw_sp_port,
64 			 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
65 			 struct tc_qopt_offload_stats *stats_ptr);
66 	int (*get_xstats)(struct mlxsw_sp_port *mlxsw_sp_port,
67 			  struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
68 			  void *xstats_ptr);
69 	void (*clean_stats)(struct mlxsw_sp_port *mlxsw_sp_port,
70 			    struct mlxsw_sp_qdisc *mlxsw_sp_qdisc);
71 	/* unoffload - to be used for a qdisc that stops being offloaded without
72 	 * being destroyed.
73 	 */
74 	void (*unoffload)(struct mlxsw_sp_port *mlxsw_sp_port,
75 			  struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, void *params);
76 };
77 
78 struct mlxsw_sp_qdisc {
79 	u32 handle;
80 	u8 tclass_num;
81 	u8 prio_bitmap;
82 	union {
83 		struct red_stats red;
84 	} xstats_base;
85 	struct mlxsw_sp_qdisc_stats {
86 		u64 tx_bytes;
87 		u64 tx_packets;
88 		u64 drops;
89 		u64 overlimits;
90 		u64 backlog;
91 	} stats_base;
92 
93 	struct mlxsw_sp_qdisc_ops *ops;
94 };
95 
96 static bool
97 mlxsw_sp_qdisc_compare(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, u32 handle,
98 		       enum mlxsw_sp_qdisc_type type)
99 {
100 	return mlxsw_sp_qdisc && mlxsw_sp_qdisc->ops &&
101 	       mlxsw_sp_qdisc->ops->type == type &&
102 	       mlxsw_sp_qdisc->handle == handle;
103 }
104 
105 static struct mlxsw_sp_qdisc *
106 mlxsw_sp_qdisc_find(struct mlxsw_sp_port *mlxsw_sp_port, u32 parent,
107 		    bool root_only)
108 {
109 	int tclass, child_index;
110 
111 	if (parent == TC_H_ROOT)
112 		return mlxsw_sp_port->root_qdisc;
113 
114 	if (root_only || !mlxsw_sp_port->root_qdisc ||
115 	    !mlxsw_sp_port->root_qdisc->ops ||
116 	    TC_H_MAJ(parent) != mlxsw_sp_port->root_qdisc->handle ||
117 	    TC_H_MIN(parent) > IEEE_8021QAZ_MAX_TCS)
118 		return NULL;
119 
120 	child_index = TC_H_MIN(parent);
121 	tclass = MLXSW_SP_PRIO_CHILD_TO_TCLASS(child_index);
122 	return &mlxsw_sp_port->tclass_qdiscs[tclass];
123 }
124 
125 static struct mlxsw_sp_qdisc *
126 mlxsw_sp_qdisc_find_by_handle(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle)
127 {
128 	int i;
129 
130 	if (mlxsw_sp_port->root_qdisc->handle == handle)
131 		return mlxsw_sp_port->root_qdisc;
132 
133 	if (mlxsw_sp_port->root_qdisc->handle == TC_H_UNSPEC)
134 		return NULL;
135 
136 	for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++)
137 		if (mlxsw_sp_port->tclass_qdiscs[i].handle == handle)
138 			return &mlxsw_sp_port->tclass_qdiscs[i];
139 
140 	return NULL;
141 }
142 
143 static int
144 mlxsw_sp_qdisc_destroy(struct mlxsw_sp_port *mlxsw_sp_port,
145 		       struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
146 {
147 	int err = 0;
148 
149 	if (!mlxsw_sp_qdisc)
150 		return 0;
151 
152 	if (mlxsw_sp_qdisc->ops && mlxsw_sp_qdisc->ops->destroy)
153 		err = mlxsw_sp_qdisc->ops->destroy(mlxsw_sp_port,
154 						   mlxsw_sp_qdisc);
155 
156 	mlxsw_sp_qdisc->handle = TC_H_UNSPEC;
157 	mlxsw_sp_qdisc->ops = NULL;
158 	return err;
159 }
160 
161 static int
162 mlxsw_sp_qdisc_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
163 		       struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
164 		       struct mlxsw_sp_qdisc_ops *ops, void *params)
165 {
166 	int err;
167 
168 	if (mlxsw_sp_qdisc->ops && mlxsw_sp_qdisc->ops->type != ops->type)
169 		/* In case this location contained a different qdisc of the
170 		 * same type we can override the old qdisc configuration.
171 		 * Otherwise, we need to remove the old qdisc before setting the
172 		 * new one.
173 		 */
174 		mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
175 	err = ops->check_params(mlxsw_sp_port, mlxsw_sp_qdisc, params);
176 	if (err)
177 		goto err_bad_param;
178 
179 	err = ops->replace(mlxsw_sp_port, mlxsw_sp_qdisc, params);
180 	if (err)
181 		goto err_config;
182 
183 	if (mlxsw_sp_qdisc->handle != handle) {
184 		mlxsw_sp_qdisc->ops = ops;
185 		if (ops->clean_stats)
186 			ops->clean_stats(mlxsw_sp_port, mlxsw_sp_qdisc);
187 	}
188 
189 	mlxsw_sp_qdisc->handle = handle;
190 	return 0;
191 
192 err_bad_param:
193 err_config:
194 	if (mlxsw_sp_qdisc->handle == handle && ops->unoffload)
195 		ops->unoffload(mlxsw_sp_port, mlxsw_sp_qdisc, params);
196 
197 	mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
198 	return err;
199 }
200 
201 static int
202 mlxsw_sp_qdisc_get_stats(struct mlxsw_sp_port *mlxsw_sp_port,
203 			 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
204 			 struct tc_qopt_offload_stats *stats_ptr)
205 {
206 	if (mlxsw_sp_qdisc && mlxsw_sp_qdisc->ops &&
207 	    mlxsw_sp_qdisc->ops->get_stats)
208 		return mlxsw_sp_qdisc->ops->get_stats(mlxsw_sp_port,
209 						      mlxsw_sp_qdisc,
210 						      stats_ptr);
211 
212 	return -EOPNOTSUPP;
213 }
214 
215 static int
216 mlxsw_sp_qdisc_get_xstats(struct mlxsw_sp_port *mlxsw_sp_port,
217 			  struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
218 			  void *xstats_ptr)
219 {
220 	if (mlxsw_sp_qdisc && mlxsw_sp_qdisc->ops &&
221 	    mlxsw_sp_qdisc->ops->get_xstats)
222 		return mlxsw_sp_qdisc->ops->get_xstats(mlxsw_sp_port,
223 						      mlxsw_sp_qdisc,
224 						      xstats_ptr);
225 
226 	return -EOPNOTSUPP;
227 }
228 
229 static void
230 mlxsw_sp_qdisc_bstats_per_priority_get(struct mlxsw_sp_port_xstats *xstats,
231 				       u8 prio_bitmap, u64 *tx_packets,
232 				       u64 *tx_bytes)
233 {
234 	int i;
235 
236 	*tx_packets = 0;
237 	*tx_bytes = 0;
238 	for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
239 		if (prio_bitmap & BIT(i)) {
240 			*tx_packets += xstats->tx_packets[i];
241 			*tx_bytes += xstats->tx_bytes[i];
242 		}
243 	}
244 }
245 
246 static int
247 mlxsw_sp_tclass_congestion_enable(struct mlxsw_sp_port *mlxsw_sp_port,
248 				  int tclass_num, u32 min, u32 max,
249 				  u32 probability, bool is_ecn)
250 {
251 	char cwtpm_cmd[MLXSW_REG_CWTPM_LEN];
252 	char cwtp_cmd[MLXSW_REG_CWTP_LEN];
253 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
254 	int err;
255 
256 	mlxsw_reg_cwtp_pack(cwtp_cmd, mlxsw_sp_port->local_port, tclass_num);
257 	mlxsw_reg_cwtp_profile_pack(cwtp_cmd, MLXSW_REG_CWTP_DEFAULT_PROFILE,
258 				    roundup(min, MLXSW_REG_CWTP_MIN_VALUE),
259 				    roundup(max, MLXSW_REG_CWTP_MIN_VALUE),
260 				    probability);
261 
262 	err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(cwtp), cwtp_cmd);
263 	if (err)
264 		return err;
265 
266 	mlxsw_reg_cwtpm_pack(cwtpm_cmd, mlxsw_sp_port->local_port, tclass_num,
267 			     MLXSW_REG_CWTP_DEFAULT_PROFILE, true, is_ecn);
268 
269 	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(cwtpm), cwtpm_cmd);
270 }
271 
272 static int
273 mlxsw_sp_tclass_congestion_disable(struct mlxsw_sp_port *mlxsw_sp_port,
274 				   int tclass_num)
275 {
276 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
277 	char cwtpm_cmd[MLXSW_REG_CWTPM_LEN];
278 
279 	mlxsw_reg_cwtpm_pack(cwtpm_cmd, mlxsw_sp_port->local_port, tclass_num,
280 			     MLXSW_REG_CWTPM_RESET_PROFILE, false, false);
281 	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(cwtpm), cwtpm_cmd);
282 }
283 
284 static void
285 mlxsw_sp_setup_tc_qdisc_red_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port,
286 					struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
287 {
288 	u8 tclass_num = mlxsw_sp_qdisc->tclass_num;
289 	struct mlxsw_sp_qdisc_stats *stats_base;
290 	struct mlxsw_sp_port_xstats *xstats;
291 	struct red_stats *red_base;
292 
293 	xstats = &mlxsw_sp_port->periodic_hw_stats.xstats;
294 	stats_base = &mlxsw_sp_qdisc->stats_base;
295 	red_base = &mlxsw_sp_qdisc->xstats_base.red;
296 
297 	mlxsw_sp_qdisc_bstats_per_priority_get(xstats,
298 					       mlxsw_sp_qdisc->prio_bitmap,
299 					       &stats_base->tx_packets,
300 					       &stats_base->tx_bytes);
301 	red_base->prob_mark = xstats->ecn;
302 	red_base->prob_drop = xstats->wred_drop[tclass_num];
303 	red_base->pdrop = xstats->tail_drop[tclass_num];
304 
305 	stats_base->overlimits = red_base->prob_drop + red_base->prob_mark;
306 	stats_base->drops = red_base->prob_drop + red_base->pdrop;
307 
308 	stats_base->backlog = 0;
309 }
310 
311 static int
312 mlxsw_sp_qdisc_red_destroy(struct mlxsw_sp_port *mlxsw_sp_port,
313 			   struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
314 {
315 	struct mlxsw_sp_qdisc *root_qdisc = mlxsw_sp_port->root_qdisc;
316 
317 	if (root_qdisc != mlxsw_sp_qdisc)
318 		root_qdisc->stats_base.backlog -=
319 					mlxsw_sp_qdisc->stats_base.backlog;
320 
321 	return mlxsw_sp_tclass_congestion_disable(mlxsw_sp_port,
322 						  mlxsw_sp_qdisc->tclass_num);
323 }
324 
325 static int
326 mlxsw_sp_qdisc_red_check_params(struct mlxsw_sp_port *mlxsw_sp_port,
327 				struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
328 				void *params)
329 {
330 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
331 	struct tc_red_qopt_offload_params *p = params;
332 
333 	if (p->min > p->max) {
334 		dev_err(mlxsw_sp->bus_info->dev,
335 			"spectrum: RED: min %u is bigger then max %u\n", p->min,
336 			p->max);
337 		return -EINVAL;
338 	}
339 	if (p->max > MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_BUFFER_SIZE)) {
340 		dev_err(mlxsw_sp->bus_info->dev,
341 			"spectrum: RED: max value %u is too big\n", p->max);
342 		return -EINVAL;
343 	}
344 	if (p->min == 0 || p->max == 0) {
345 		dev_err(mlxsw_sp->bus_info->dev,
346 			"spectrum: RED: 0 value is illegal for min and max\n");
347 		return -EINVAL;
348 	}
349 	return 0;
350 }
351 
352 static int
353 mlxsw_sp_qdisc_red_replace(struct mlxsw_sp_port *mlxsw_sp_port,
354 			   struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
355 			   void *params)
356 {
357 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
358 	struct tc_red_qopt_offload_params *p = params;
359 	u8 tclass_num = mlxsw_sp_qdisc->tclass_num;
360 	u32 min, max;
361 	u64 prob;
362 
363 	/* calculate probability in percentage */
364 	prob = p->probability;
365 	prob *= 100;
366 	prob = DIV_ROUND_UP(prob, 1 << 16);
367 	prob = DIV_ROUND_UP(prob, 1 << 16);
368 	min = mlxsw_sp_bytes_cells(mlxsw_sp, p->min);
369 	max = mlxsw_sp_bytes_cells(mlxsw_sp, p->max);
370 	return mlxsw_sp_tclass_congestion_enable(mlxsw_sp_port, tclass_num, min,
371 						 max, prob, p->is_ecn);
372 }
373 
374 static void
375 mlxsw_sp_qdisc_red_unoffload(struct mlxsw_sp_port *mlxsw_sp_port,
376 			     struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
377 			     void *params)
378 {
379 	struct tc_red_qopt_offload_params *p = params;
380 	u64 backlog;
381 
382 	backlog = mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp,
383 				       mlxsw_sp_qdisc->stats_base.backlog);
384 	p->qstats->backlog -= backlog;
385 	mlxsw_sp_qdisc->stats_base.backlog = 0;
386 }
387 
388 static int
389 mlxsw_sp_qdisc_get_red_xstats(struct mlxsw_sp_port *mlxsw_sp_port,
390 			      struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
391 			      void *xstats_ptr)
392 {
393 	struct red_stats *xstats_base = &mlxsw_sp_qdisc->xstats_base.red;
394 	u8 tclass_num = mlxsw_sp_qdisc->tclass_num;
395 	struct mlxsw_sp_port_xstats *xstats;
396 	struct red_stats *res = xstats_ptr;
397 	int early_drops, marks, pdrops;
398 
399 	xstats = &mlxsw_sp_port->periodic_hw_stats.xstats;
400 
401 	early_drops = xstats->wred_drop[tclass_num] - xstats_base->prob_drop;
402 	marks = xstats->ecn - xstats_base->prob_mark;
403 	pdrops = xstats->tail_drop[tclass_num] - xstats_base->pdrop;
404 
405 	res->pdrop += pdrops;
406 	res->prob_drop += early_drops;
407 	res->prob_mark += marks;
408 
409 	xstats_base->pdrop += pdrops;
410 	xstats_base->prob_drop += early_drops;
411 	xstats_base->prob_mark += marks;
412 	return 0;
413 }
414 
415 static int
416 mlxsw_sp_qdisc_get_red_stats(struct mlxsw_sp_port *mlxsw_sp_port,
417 			     struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
418 			     struct tc_qopt_offload_stats *stats_ptr)
419 {
420 	u64 tx_bytes, tx_packets, overlimits, drops, backlog;
421 	u8 tclass_num = mlxsw_sp_qdisc->tclass_num;
422 	struct mlxsw_sp_qdisc_stats *stats_base;
423 	struct mlxsw_sp_port_xstats *xstats;
424 
425 	xstats = &mlxsw_sp_port->periodic_hw_stats.xstats;
426 	stats_base = &mlxsw_sp_qdisc->stats_base;
427 
428 	mlxsw_sp_qdisc_bstats_per_priority_get(xstats,
429 					       mlxsw_sp_qdisc->prio_bitmap,
430 					       &tx_packets, &tx_bytes);
431 	tx_bytes = tx_bytes - stats_base->tx_bytes;
432 	tx_packets = tx_packets - stats_base->tx_packets;
433 
434 	overlimits = xstats->wred_drop[tclass_num] + xstats->ecn -
435 		     stats_base->overlimits;
436 	drops = xstats->wred_drop[tclass_num] + xstats->tail_drop[tclass_num] -
437 		stats_base->drops;
438 	backlog = xstats->backlog[tclass_num];
439 
440 	_bstats_update(stats_ptr->bstats, tx_bytes, tx_packets);
441 	stats_ptr->qstats->overlimits += overlimits;
442 	stats_ptr->qstats->drops += drops;
443 	stats_ptr->qstats->backlog +=
444 				mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp,
445 						     backlog) -
446 				mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp,
447 						     stats_base->backlog);
448 
449 	stats_base->backlog = backlog;
450 	stats_base->drops +=  drops;
451 	stats_base->overlimits += overlimits;
452 	stats_base->tx_bytes += tx_bytes;
453 	stats_base->tx_packets += tx_packets;
454 	return 0;
455 }
456 
457 #define MLXSW_SP_PORT_DEFAULT_TCLASS 0
458 
459 static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_red = {
460 	.type = MLXSW_SP_QDISC_RED,
461 	.check_params = mlxsw_sp_qdisc_red_check_params,
462 	.replace = mlxsw_sp_qdisc_red_replace,
463 	.unoffload = mlxsw_sp_qdisc_red_unoffload,
464 	.destroy = mlxsw_sp_qdisc_red_destroy,
465 	.get_stats = mlxsw_sp_qdisc_get_red_stats,
466 	.get_xstats = mlxsw_sp_qdisc_get_red_xstats,
467 	.clean_stats = mlxsw_sp_setup_tc_qdisc_red_clean_stats,
468 };
469 
470 int mlxsw_sp_setup_tc_red(struct mlxsw_sp_port *mlxsw_sp_port,
471 			  struct tc_red_qopt_offload *p)
472 {
473 	struct mlxsw_sp_qdisc *mlxsw_sp_qdisc;
474 
475 	mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent, false);
476 	if (!mlxsw_sp_qdisc)
477 		return -EOPNOTSUPP;
478 
479 	if (p->command == TC_RED_REPLACE)
480 		return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle,
481 					      mlxsw_sp_qdisc,
482 					      &mlxsw_sp_qdisc_ops_red,
483 					      &p->set);
484 
485 	if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle,
486 				    MLXSW_SP_QDISC_RED))
487 		return -EOPNOTSUPP;
488 
489 	switch (p->command) {
490 	case TC_RED_DESTROY:
491 		return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
492 	case TC_RED_XSTATS:
493 		return mlxsw_sp_qdisc_get_xstats(mlxsw_sp_port, mlxsw_sp_qdisc,
494 						 p->xstats);
495 	case TC_RED_STATS:
496 		return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc,
497 						&p->stats);
498 	default:
499 		return -EOPNOTSUPP;
500 	}
501 }
502 
503 static int
504 mlxsw_sp_qdisc_prio_destroy(struct mlxsw_sp_port *mlxsw_sp_port,
505 			    struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
506 {
507 	int i;
508 
509 	for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
510 		mlxsw_sp_port_prio_tc_set(mlxsw_sp_port, i,
511 					  MLXSW_SP_PORT_DEFAULT_TCLASS);
512 		mlxsw_sp_qdisc_destroy(mlxsw_sp_port,
513 				       &mlxsw_sp_port->tclass_qdiscs[i]);
514 		mlxsw_sp_port->tclass_qdiscs[i].prio_bitmap = 0;
515 	}
516 
517 	return 0;
518 }
519 
520 static int
521 mlxsw_sp_qdisc_prio_check_params(struct mlxsw_sp_port *mlxsw_sp_port,
522 				 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
523 				 void *params)
524 {
525 	struct tc_prio_qopt_offload_params *p = params;
526 
527 	if (p->bands > IEEE_8021QAZ_MAX_TCS)
528 		return -EOPNOTSUPP;
529 
530 	return 0;
531 }
532 
533 static int
534 mlxsw_sp_qdisc_prio_replace(struct mlxsw_sp_port *mlxsw_sp_port,
535 			    struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
536 			    void *params)
537 {
538 	struct tc_prio_qopt_offload_params *p = params;
539 	struct mlxsw_sp_qdisc *child_qdisc;
540 	int tclass, i, band, backlog;
541 	u8 old_priomap;
542 	int err;
543 
544 	for (band = 0; band < p->bands; band++) {
545 		tclass = MLXSW_SP_PRIO_BAND_TO_TCLASS(band);
546 		child_qdisc = &mlxsw_sp_port->tclass_qdiscs[tclass];
547 		old_priomap = child_qdisc->prio_bitmap;
548 		child_qdisc->prio_bitmap = 0;
549 		for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
550 			if (p->priomap[i] == band) {
551 				child_qdisc->prio_bitmap |= BIT(i);
552 				if (BIT(i) & old_priomap)
553 					continue;
554 				err = mlxsw_sp_port_prio_tc_set(mlxsw_sp_port,
555 								i, tclass);
556 				if (err)
557 					return err;
558 			}
559 		}
560 		if (old_priomap != child_qdisc->prio_bitmap &&
561 		    child_qdisc->ops && child_qdisc->ops->clean_stats) {
562 			backlog = child_qdisc->stats_base.backlog;
563 			child_qdisc->ops->clean_stats(mlxsw_sp_port,
564 						      child_qdisc);
565 			child_qdisc->stats_base.backlog = backlog;
566 		}
567 	}
568 	for (; band < IEEE_8021QAZ_MAX_TCS; band++) {
569 		tclass = MLXSW_SP_PRIO_BAND_TO_TCLASS(band);
570 		child_qdisc = &mlxsw_sp_port->tclass_qdiscs[tclass];
571 		child_qdisc->prio_bitmap = 0;
572 		mlxsw_sp_qdisc_destroy(mlxsw_sp_port, child_qdisc);
573 	}
574 	return 0;
575 }
576 
577 static void
578 mlxsw_sp_qdisc_prio_unoffload(struct mlxsw_sp_port *mlxsw_sp_port,
579 			      struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
580 			      void *params)
581 {
582 	struct tc_prio_qopt_offload_params *p = params;
583 	u64 backlog;
584 
585 	backlog = mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp,
586 				       mlxsw_sp_qdisc->stats_base.backlog);
587 	p->qstats->backlog -= backlog;
588 }
589 
590 static int
591 mlxsw_sp_qdisc_get_prio_stats(struct mlxsw_sp_port *mlxsw_sp_port,
592 			      struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
593 			      struct tc_qopt_offload_stats *stats_ptr)
594 {
595 	u64 tx_bytes, tx_packets, drops = 0, backlog = 0;
596 	struct mlxsw_sp_qdisc_stats *stats_base;
597 	struct mlxsw_sp_port_xstats *xstats;
598 	struct rtnl_link_stats64 *stats;
599 	int i;
600 
601 	xstats = &mlxsw_sp_port->periodic_hw_stats.xstats;
602 	stats = &mlxsw_sp_port->periodic_hw_stats.stats;
603 	stats_base = &mlxsw_sp_qdisc->stats_base;
604 
605 	tx_bytes = stats->tx_bytes - stats_base->tx_bytes;
606 	tx_packets = stats->tx_packets - stats_base->tx_packets;
607 
608 	for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
609 		drops += xstats->tail_drop[i];
610 		drops += xstats->wred_drop[i];
611 		backlog += xstats->backlog[i];
612 	}
613 	drops = drops - stats_base->drops;
614 
615 	_bstats_update(stats_ptr->bstats, tx_bytes, tx_packets);
616 	stats_ptr->qstats->drops += drops;
617 	stats_ptr->qstats->backlog +=
618 				mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp,
619 						     backlog) -
620 				mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp,
621 						     stats_base->backlog);
622 	stats_base->backlog = backlog;
623 	stats_base->drops += drops;
624 	stats_base->tx_bytes += tx_bytes;
625 	stats_base->tx_packets += tx_packets;
626 	return 0;
627 }
628 
629 static void
630 mlxsw_sp_setup_tc_qdisc_prio_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port,
631 					 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
632 {
633 	struct mlxsw_sp_qdisc_stats *stats_base;
634 	struct mlxsw_sp_port_xstats *xstats;
635 	struct rtnl_link_stats64 *stats;
636 	int i;
637 
638 	xstats = &mlxsw_sp_port->periodic_hw_stats.xstats;
639 	stats = &mlxsw_sp_port->periodic_hw_stats.stats;
640 	stats_base = &mlxsw_sp_qdisc->stats_base;
641 
642 	stats_base->tx_packets = stats->tx_packets;
643 	stats_base->tx_bytes = stats->tx_bytes;
644 
645 	stats_base->drops = 0;
646 	for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
647 		stats_base->drops += xstats->tail_drop[i];
648 		stats_base->drops += xstats->wred_drop[i];
649 	}
650 
651 	mlxsw_sp_qdisc->stats_base.backlog = 0;
652 }
653 
654 static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_prio = {
655 	.type = MLXSW_SP_QDISC_PRIO,
656 	.check_params = mlxsw_sp_qdisc_prio_check_params,
657 	.replace = mlxsw_sp_qdisc_prio_replace,
658 	.unoffload = mlxsw_sp_qdisc_prio_unoffload,
659 	.destroy = mlxsw_sp_qdisc_prio_destroy,
660 	.get_stats = mlxsw_sp_qdisc_get_prio_stats,
661 	.clean_stats = mlxsw_sp_setup_tc_qdisc_prio_clean_stats,
662 };
663 
664 /* Grafting is not supported in mlxsw. It will result in un-offloading of the
665  * grafted qdisc as well as the qdisc in the qdisc new location.
666  * (However, if the graft is to the location where the qdisc is already at, it
667  * will be ignored completely and won't cause un-offloading).
668  */
669 static int
670 mlxsw_sp_qdisc_prio_graft(struct mlxsw_sp_port *mlxsw_sp_port,
671 			  struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
672 			  struct tc_prio_qopt_offload_graft_params *p)
673 {
674 	int tclass_num = MLXSW_SP_PRIO_BAND_TO_TCLASS(p->band);
675 	struct mlxsw_sp_qdisc *old_qdisc;
676 
677 	/* Check if the grafted qdisc is already in its "new" location. If so -
678 	 * nothing needs to be done.
679 	 */
680 	if (p->band < IEEE_8021QAZ_MAX_TCS &&
681 	    mlxsw_sp_port->tclass_qdiscs[tclass_num].handle == p->child_handle)
682 		return 0;
683 
684 	/* See if the grafted qdisc is already offloaded on any tclass. If so,
685 	 * unoffload it.
686 	 */
687 	old_qdisc = mlxsw_sp_qdisc_find_by_handle(mlxsw_sp_port,
688 						  p->child_handle);
689 	if (old_qdisc)
690 		mlxsw_sp_qdisc_destroy(mlxsw_sp_port, old_qdisc);
691 
692 	mlxsw_sp_qdisc_destroy(mlxsw_sp_port,
693 			       &mlxsw_sp_port->tclass_qdiscs[tclass_num]);
694 	return -EOPNOTSUPP;
695 }
696 
697 int mlxsw_sp_setup_tc_prio(struct mlxsw_sp_port *mlxsw_sp_port,
698 			   struct tc_prio_qopt_offload *p)
699 {
700 	struct mlxsw_sp_qdisc *mlxsw_sp_qdisc;
701 
702 	mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent, true);
703 	if (!mlxsw_sp_qdisc)
704 		return -EOPNOTSUPP;
705 
706 	if (p->command == TC_PRIO_REPLACE)
707 		return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle,
708 					      mlxsw_sp_qdisc,
709 					      &mlxsw_sp_qdisc_ops_prio,
710 					      &p->replace_params);
711 
712 	if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle,
713 				    MLXSW_SP_QDISC_PRIO))
714 		return -EOPNOTSUPP;
715 
716 	switch (p->command) {
717 	case TC_PRIO_DESTROY:
718 		return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
719 	case TC_PRIO_STATS:
720 		return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc,
721 						&p->stats);
722 	case TC_PRIO_GRAFT:
723 		return mlxsw_sp_qdisc_prio_graft(mlxsw_sp_port, mlxsw_sp_qdisc,
724 						 &p->graft_params);
725 	default:
726 		return -EOPNOTSUPP;
727 	}
728 }
729 
730 int mlxsw_sp_tc_qdisc_init(struct mlxsw_sp_port *mlxsw_sp_port)
731 {
732 	struct mlxsw_sp_qdisc *mlxsw_sp_qdisc;
733 	int i;
734 
735 	mlxsw_sp_qdisc = kzalloc(sizeof(*mlxsw_sp_qdisc), GFP_KERNEL);
736 	if (!mlxsw_sp_qdisc)
737 		goto err_root_qdisc_init;
738 
739 	mlxsw_sp_port->root_qdisc = mlxsw_sp_qdisc;
740 	mlxsw_sp_port->root_qdisc->prio_bitmap = 0xff;
741 	mlxsw_sp_port->root_qdisc->tclass_num = MLXSW_SP_PORT_DEFAULT_TCLASS;
742 
743 	mlxsw_sp_qdisc = kcalloc(IEEE_8021QAZ_MAX_TCS,
744 				 sizeof(*mlxsw_sp_qdisc),
745 				 GFP_KERNEL);
746 	if (!mlxsw_sp_qdisc)
747 		goto err_tclass_qdiscs_init;
748 
749 	mlxsw_sp_port->tclass_qdiscs = mlxsw_sp_qdisc;
750 	for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++)
751 		mlxsw_sp_port->tclass_qdiscs[i].tclass_num = i;
752 
753 	return 0;
754 
755 err_tclass_qdiscs_init:
756 	kfree(mlxsw_sp_port->root_qdisc);
757 err_root_qdisc_init:
758 	return -ENOMEM;
759 }
760 
761 void mlxsw_sp_tc_qdisc_fini(struct mlxsw_sp_port *mlxsw_sp_port)
762 {
763 	kfree(mlxsw_sp_port->tclass_qdiscs);
764 	kfree(mlxsw_sp_port->root_qdisc);
765 }
766