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