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