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
mlxsw_sp_qdisc_compare(struct mlxsw_sp_qdisc * mlxsw_sp_qdisc,u32 handle,enum mlxsw_sp_qdisc_type type)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 *
mlxsw_sp_qdisc_find(struct mlxsw_sp_port * mlxsw_sp_port,u32 parent,bool root_only)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 *
mlxsw_sp_qdisc_find_by_handle(struct mlxsw_sp_port * mlxsw_sp_port,u32 handle)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
mlxsw_sp_qdisc_destroy(struct mlxsw_sp_port * mlxsw_sp_port,struct mlxsw_sp_qdisc * mlxsw_sp_qdisc)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
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)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
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)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
mlxsw_sp_qdisc_get_xstats(struct mlxsw_sp_port * mlxsw_sp_port,struct mlxsw_sp_qdisc * mlxsw_sp_qdisc,void * xstats_ptr)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 void
mlxsw_sp_qdisc_bstats_per_priority_get(struct mlxsw_sp_port_xstats * xstats,u8 prio_bitmap,u64 * tx_packets,u64 * tx_bytes)199 mlxsw_sp_qdisc_bstats_per_priority_get(struct mlxsw_sp_port_xstats *xstats,
200 u8 prio_bitmap, u64 *tx_packets,
201 u64 *tx_bytes)
202 {
203 int i;
204
205 *tx_packets = 0;
206 *tx_bytes = 0;
207 for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
208 if (prio_bitmap & BIT(i)) {
209 *tx_packets += xstats->tx_packets[i];
210 *tx_bytes += xstats->tx_bytes[i];
211 }
212 }
213 }
214
215 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_ecn)216 mlxsw_sp_tclass_congestion_enable(struct mlxsw_sp_port *mlxsw_sp_port,
217 int tclass_num, u32 min, u32 max,
218 u32 probability, bool is_ecn)
219 {
220 char cwtpm_cmd[MLXSW_REG_CWTPM_LEN];
221 char cwtp_cmd[MLXSW_REG_CWTP_LEN];
222 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
223 int err;
224
225 mlxsw_reg_cwtp_pack(cwtp_cmd, mlxsw_sp_port->local_port, tclass_num);
226 mlxsw_reg_cwtp_profile_pack(cwtp_cmd, MLXSW_REG_CWTP_DEFAULT_PROFILE,
227 roundup(min, MLXSW_REG_CWTP_MIN_VALUE),
228 roundup(max, MLXSW_REG_CWTP_MIN_VALUE),
229 probability);
230
231 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(cwtp), cwtp_cmd);
232 if (err)
233 return err;
234
235 mlxsw_reg_cwtpm_pack(cwtpm_cmd, mlxsw_sp_port->local_port, tclass_num,
236 MLXSW_REG_CWTP_DEFAULT_PROFILE, true, is_ecn);
237
238 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(cwtpm), cwtpm_cmd);
239 }
240
241 static int
mlxsw_sp_tclass_congestion_disable(struct mlxsw_sp_port * mlxsw_sp_port,int tclass_num)242 mlxsw_sp_tclass_congestion_disable(struct mlxsw_sp_port *mlxsw_sp_port,
243 int tclass_num)
244 {
245 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
246 char cwtpm_cmd[MLXSW_REG_CWTPM_LEN];
247
248 mlxsw_reg_cwtpm_pack(cwtpm_cmd, mlxsw_sp_port->local_port, tclass_num,
249 MLXSW_REG_CWTPM_RESET_PROFILE, false, false);
250 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(cwtpm), cwtpm_cmd);
251 }
252
253 static void
mlxsw_sp_setup_tc_qdisc_red_clean_stats(struct mlxsw_sp_port * mlxsw_sp_port,struct mlxsw_sp_qdisc * mlxsw_sp_qdisc)254 mlxsw_sp_setup_tc_qdisc_red_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port,
255 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
256 {
257 u8 tclass_num = mlxsw_sp_qdisc->tclass_num;
258 struct mlxsw_sp_qdisc_stats *stats_base;
259 struct mlxsw_sp_port_xstats *xstats;
260 struct red_stats *red_base;
261
262 xstats = &mlxsw_sp_port->periodic_hw_stats.xstats;
263 stats_base = &mlxsw_sp_qdisc->stats_base;
264 red_base = &mlxsw_sp_qdisc->xstats_base.red;
265
266 mlxsw_sp_qdisc_bstats_per_priority_get(xstats,
267 mlxsw_sp_qdisc->prio_bitmap,
268 &stats_base->tx_packets,
269 &stats_base->tx_bytes);
270 red_base->prob_mark = xstats->ecn;
271 red_base->prob_drop = xstats->wred_drop[tclass_num];
272 red_base->pdrop = xstats->tail_drop[tclass_num];
273
274 stats_base->overlimits = red_base->prob_drop + red_base->prob_mark;
275 stats_base->drops = red_base->prob_drop + red_base->pdrop;
276
277 stats_base->backlog = 0;
278 }
279
280 static int
mlxsw_sp_qdisc_red_destroy(struct mlxsw_sp_port * mlxsw_sp_port,struct mlxsw_sp_qdisc * mlxsw_sp_qdisc)281 mlxsw_sp_qdisc_red_destroy(struct mlxsw_sp_port *mlxsw_sp_port,
282 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
283 {
284 struct mlxsw_sp_qdisc *root_qdisc = mlxsw_sp_port->root_qdisc;
285
286 if (root_qdisc != mlxsw_sp_qdisc)
287 root_qdisc->stats_base.backlog -=
288 mlxsw_sp_qdisc->stats_base.backlog;
289
290 return mlxsw_sp_tclass_congestion_disable(mlxsw_sp_port,
291 mlxsw_sp_qdisc->tclass_num);
292 }
293
294 static int
mlxsw_sp_qdisc_red_check_params(struct mlxsw_sp_port * mlxsw_sp_port,struct mlxsw_sp_qdisc * mlxsw_sp_qdisc,void * params)295 mlxsw_sp_qdisc_red_check_params(struct mlxsw_sp_port *mlxsw_sp_port,
296 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
297 void *params)
298 {
299 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
300 struct tc_red_qopt_offload_params *p = params;
301
302 if (p->min > p->max) {
303 dev_err(mlxsw_sp->bus_info->dev,
304 "spectrum: RED: min %u is bigger then max %u\n", p->min,
305 p->max);
306 return -EINVAL;
307 }
308 if (p->max > MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_BUFFER_SIZE)) {
309 dev_err(mlxsw_sp->bus_info->dev,
310 "spectrum: RED: max value %u is too big\n", p->max);
311 return -EINVAL;
312 }
313 if (p->min == 0 || p->max == 0) {
314 dev_err(mlxsw_sp->bus_info->dev,
315 "spectrum: RED: 0 value is illegal for min and max\n");
316 return -EINVAL;
317 }
318 return 0;
319 }
320
321 static int
mlxsw_sp_qdisc_red_replace(struct mlxsw_sp_port * mlxsw_sp_port,struct mlxsw_sp_qdisc * mlxsw_sp_qdisc,void * params)322 mlxsw_sp_qdisc_red_replace(struct mlxsw_sp_port *mlxsw_sp_port,
323 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
324 void *params)
325 {
326 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
327 struct tc_red_qopt_offload_params *p = params;
328 u8 tclass_num = mlxsw_sp_qdisc->tclass_num;
329 u32 min, max;
330 u64 prob;
331
332 /* calculate probability in percentage */
333 prob = p->probability;
334 prob *= 100;
335 prob = DIV_ROUND_UP(prob, 1 << 16);
336 prob = DIV_ROUND_UP(prob, 1 << 16);
337 min = mlxsw_sp_bytes_cells(mlxsw_sp, p->min);
338 max = mlxsw_sp_bytes_cells(mlxsw_sp, p->max);
339 return mlxsw_sp_tclass_congestion_enable(mlxsw_sp_port, tclass_num, min,
340 max, prob, p->is_ecn);
341 }
342
343 static void
mlxsw_sp_qdisc_red_unoffload(struct mlxsw_sp_port * mlxsw_sp_port,struct mlxsw_sp_qdisc * mlxsw_sp_qdisc,void * params)344 mlxsw_sp_qdisc_red_unoffload(struct mlxsw_sp_port *mlxsw_sp_port,
345 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
346 void *params)
347 {
348 struct tc_red_qopt_offload_params *p = params;
349 u64 backlog;
350
351 backlog = mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp,
352 mlxsw_sp_qdisc->stats_base.backlog);
353 p->qstats->backlog -= backlog;
354 mlxsw_sp_qdisc->stats_base.backlog = 0;
355 }
356
357 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)358 mlxsw_sp_qdisc_get_red_xstats(struct mlxsw_sp_port *mlxsw_sp_port,
359 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
360 void *xstats_ptr)
361 {
362 struct red_stats *xstats_base = &mlxsw_sp_qdisc->xstats_base.red;
363 u8 tclass_num = mlxsw_sp_qdisc->tclass_num;
364 struct mlxsw_sp_port_xstats *xstats;
365 struct red_stats *res = xstats_ptr;
366 int early_drops, marks, pdrops;
367
368 xstats = &mlxsw_sp_port->periodic_hw_stats.xstats;
369
370 early_drops = xstats->wred_drop[tclass_num] - xstats_base->prob_drop;
371 marks = xstats->ecn - xstats_base->prob_mark;
372 pdrops = xstats->tail_drop[tclass_num] - xstats_base->pdrop;
373
374 res->pdrop += pdrops;
375 res->prob_drop += early_drops;
376 res->prob_mark += marks;
377
378 xstats_base->pdrop += pdrops;
379 xstats_base->prob_drop += early_drops;
380 xstats_base->prob_mark += marks;
381 return 0;
382 }
383
384 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)385 mlxsw_sp_qdisc_get_red_stats(struct mlxsw_sp_port *mlxsw_sp_port,
386 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
387 struct tc_qopt_offload_stats *stats_ptr)
388 {
389 u64 tx_bytes, tx_packets, overlimits, drops, backlog;
390 u8 tclass_num = mlxsw_sp_qdisc->tclass_num;
391 struct mlxsw_sp_qdisc_stats *stats_base;
392 struct mlxsw_sp_port_xstats *xstats;
393
394 xstats = &mlxsw_sp_port->periodic_hw_stats.xstats;
395 stats_base = &mlxsw_sp_qdisc->stats_base;
396
397 mlxsw_sp_qdisc_bstats_per_priority_get(xstats,
398 mlxsw_sp_qdisc->prio_bitmap,
399 &tx_packets, &tx_bytes);
400 tx_bytes = tx_bytes - stats_base->tx_bytes;
401 tx_packets = tx_packets - stats_base->tx_packets;
402
403 overlimits = xstats->wred_drop[tclass_num] + xstats->ecn -
404 stats_base->overlimits;
405 drops = xstats->wred_drop[tclass_num] + xstats->tail_drop[tclass_num] -
406 stats_base->drops;
407 backlog = xstats->backlog[tclass_num];
408
409 _bstats_update(stats_ptr->bstats, tx_bytes, tx_packets);
410 stats_ptr->qstats->overlimits += overlimits;
411 stats_ptr->qstats->drops += drops;
412 stats_ptr->qstats->backlog +=
413 mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp,
414 backlog) -
415 mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp,
416 stats_base->backlog);
417
418 stats_base->backlog = backlog;
419 stats_base->drops += drops;
420 stats_base->overlimits += overlimits;
421 stats_base->tx_bytes += tx_bytes;
422 stats_base->tx_packets += tx_packets;
423 return 0;
424 }
425
426 #define MLXSW_SP_PORT_DEFAULT_TCLASS 0
427
428 static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_red = {
429 .type = MLXSW_SP_QDISC_RED,
430 .check_params = mlxsw_sp_qdisc_red_check_params,
431 .replace = mlxsw_sp_qdisc_red_replace,
432 .unoffload = mlxsw_sp_qdisc_red_unoffload,
433 .destroy = mlxsw_sp_qdisc_red_destroy,
434 .get_stats = mlxsw_sp_qdisc_get_red_stats,
435 .get_xstats = mlxsw_sp_qdisc_get_red_xstats,
436 .clean_stats = mlxsw_sp_setup_tc_qdisc_red_clean_stats,
437 };
438
mlxsw_sp_setup_tc_red(struct mlxsw_sp_port * mlxsw_sp_port,struct tc_red_qopt_offload * p)439 int mlxsw_sp_setup_tc_red(struct mlxsw_sp_port *mlxsw_sp_port,
440 struct tc_red_qopt_offload *p)
441 {
442 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc;
443
444 mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent, false);
445 if (!mlxsw_sp_qdisc)
446 return -EOPNOTSUPP;
447
448 if (p->command == TC_RED_REPLACE)
449 return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle,
450 mlxsw_sp_qdisc,
451 &mlxsw_sp_qdisc_ops_red,
452 &p->set);
453
454 if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle,
455 MLXSW_SP_QDISC_RED))
456 return -EOPNOTSUPP;
457
458 switch (p->command) {
459 case TC_RED_DESTROY:
460 return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
461 case TC_RED_XSTATS:
462 return mlxsw_sp_qdisc_get_xstats(mlxsw_sp_port, mlxsw_sp_qdisc,
463 p->xstats);
464 case TC_RED_STATS:
465 return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc,
466 &p->stats);
467 default:
468 return -EOPNOTSUPP;
469 }
470 }
471
472 static int
mlxsw_sp_qdisc_prio_destroy(struct mlxsw_sp_port * mlxsw_sp_port,struct mlxsw_sp_qdisc * mlxsw_sp_qdisc)473 mlxsw_sp_qdisc_prio_destroy(struct mlxsw_sp_port *mlxsw_sp_port,
474 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
475 {
476 int i;
477
478 for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
479 mlxsw_sp_port_prio_tc_set(mlxsw_sp_port, i,
480 MLXSW_SP_PORT_DEFAULT_TCLASS);
481 mlxsw_sp_qdisc_destroy(mlxsw_sp_port,
482 &mlxsw_sp_port->tclass_qdiscs[i]);
483 mlxsw_sp_port->tclass_qdiscs[i].prio_bitmap = 0;
484 }
485
486 return 0;
487 }
488
489 static int
mlxsw_sp_qdisc_prio_check_params(struct mlxsw_sp_port * mlxsw_sp_port,struct mlxsw_sp_qdisc * mlxsw_sp_qdisc,void * params)490 mlxsw_sp_qdisc_prio_check_params(struct mlxsw_sp_port *mlxsw_sp_port,
491 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
492 void *params)
493 {
494 struct tc_prio_qopt_offload_params *p = params;
495
496 if (p->bands > IEEE_8021QAZ_MAX_TCS)
497 return -EOPNOTSUPP;
498
499 return 0;
500 }
501
502 static int
mlxsw_sp_qdisc_prio_replace(struct mlxsw_sp_port * mlxsw_sp_port,struct mlxsw_sp_qdisc * mlxsw_sp_qdisc,void * params)503 mlxsw_sp_qdisc_prio_replace(struct mlxsw_sp_port *mlxsw_sp_port,
504 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
505 void *params)
506 {
507 struct tc_prio_qopt_offload_params *p = params;
508 struct mlxsw_sp_qdisc *child_qdisc;
509 int tclass, i, band, backlog;
510 u8 old_priomap;
511 int err;
512
513 for (band = 0; band < p->bands; band++) {
514 tclass = MLXSW_SP_PRIO_BAND_TO_TCLASS(band);
515 child_qdisc = &mlxsw_sp_port->tclass_qdiscs[tclass];
516 old_priomap = child_qdisc->prio_bitmap;
517 child_qdisc->prio_bitmap = 0;
518 for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
519 if (p->priomap[i] == band) {
520 child_qdisc->prio_bitmap |= BIT(i);
521 if (BIT(i) & old_priomap)
522 continue;
523 err = mlxsw_sp_port_prio_tc_set(mlxsw_sp_port,
524 i, tclass);
525 if (err)
526 return err;
527 }
528 }
529 if (old_priomap != child_qdisc->prio_bitmap &&
530 child_qdisc->ops && child_qdisc->ops->clean_stats) {
531 backlog = child_qdisc->stats_base.backlog;
532 child_qdisc->ops->clean_stats(mlxsw_sp_port,
533 child_qdisc);
534 child_qdisc->stats_base.backlog = backlog;
535 }
536 }
537 for (; band < IEEE_8021QAZ_MAX_TCS; band++) {
538 tclass = MLXSW_SP_PRIO_BAND_TO_TCLASS(band);
539 child_qdisc = &mlxsw_sp_port->tclass_qdiscs[tclass];
540 child_qdisc->prio_bitmap = 0;
541 mlxsw_sp_qdisc_destroy(mlxsw_sp_port, child_qdisc);
542 }
543 return 0;
544 }
545
546 static void
mlxsw_sp_qdisc_prio_unoffload(struct mlxsw_sp_port * mlxsw_sp_port,struct mlxsw_sp_qdisc * mlxsw_sp_qdisc,void * params)547 mlxsw_sp_qdisc_prio_unoffload(struct mlxsw_sp_port *mlxsw_sp_port,
548 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
549 void *params)
550 {
551 struct tc_prio_qopt_offload_params *p = params;
552 u64 backlog;
553
554 backlog = mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp,
555 mlxsw_sp_qdisc->stats_base.backlog);
556 p->qstats->backlog -= backlog;
557 }
558
559 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)560 mlxsw_sp_qdisc_get_prio_stats(struct mlxsw_sp_port *mlxsw_sp_port,
561 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
562 struct tc_qopt_offload_stats *stats_ptr)
563 {
564 u64 tx_bytes, tx_packets, drops = 0, backlog = 0;
565 struct mlxsw_sp_qdisc_stats *stats_base;
566 struct mlxsw_sp_port_xstats *xstats;
567 struct rtnl_link_stats64 *stats;
568 int i;
569
570 xstats = &mlxsw_sp_port->periodic_hw_stats.xstats;
571 stats = &mlxsw_sp_port->periodic_hw_stats.stats;
572 stats_base = &mlxsw_sp_qdisc->stats_base;
573
574 tx_bytes = stats->tx_bytes - stats_base->tx_bytes;
575 tx_packets = stats->tx_packets - stats_base->tx_packets;
576
577 for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
578 drops += xstats->tail_drop[i];
579 drops += xstats->wred_drop[i];
580 backlog += xstats->backlog[i];
581 }
582 drops = drops - stats_base->drops;
583
584 _bstats_update(stats_ptr->bstats, tx_bytes, tx_packets);
585 stats_ptr->qstats->drops += drops;
586 stats_ptr->qstats->backlog +=
587 mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp,
588 backlog) -
589 mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp,
590 stats_base->backlog);
591 stats_base->backlog = backlog;
592 stats_base->drops += drops;
593 stats_base->tx_bytes += tx_bytes;
594 stats_base->tx_packets += tx_packets;
595 return 0;
596 }
597
598 static void
mlxsw_sp_setup_tc_qdisc_prio_clean_stats(struct mlxsw_sp_port * mlxsw_sp_port,struct mlxsw_sp_qdisc * mlxsw_sp_qdisc)599 mlxsw_sp_setup_tc_qdisc_prio_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port,
600 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
601 {
602 struct mlxsw_sp_qdisc_stats *stats_base;
603 struct mlxsw_sp_port_xstats *xstats;
604 struct rtnl_link_stats64 *stats;
605 int i;
606
607 xstats = &mlxsw_sp_port->periodic_hw_stats.xstats;
608 stats = &mlxsw_sp_port->periodic_hw_stats.stats;
609 stats_base = &mlxsw_sp_qdisc->stats_base;
610
611 stats_base->tx_packets = stats->tx_packets;
612 stats_base->tx_bytes = stats->tx_bytes;
613
614 stats_base->drops = 0;
615 for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
616 stats_base->drops += xstats->tail_drop[i];
617 stats_base->drops += xstats->wred_drop[i];
618 }
619
620 mlxsw_sp_qdisc->stats_base.backlog = 0;
621 }
622
623 static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_prio = {
624 .type = MLXSW_SP_QDISC_PRIO,
625 .check_params = mlxsw_sp_qdisc_prio_check_params,
626 .replace = mlxsw_sp_qdisc_prio_replace,
627 .unoffload = mlxsw_sp_qdisc_prio_unoffload,
628 .destroy = mlxsw_sp_qdisc_prio_destroy,
629 .get_stats = mlxsw_sp_qdisc_get_prio_stats,
630 .clean_stats = mlxsw_sp_setup_tc_qdisc_prio_clean_stats,
631 };
632
633 /* Grafting is not supported in mlxsw. It will result in un-offloading of the
634 * grafted qdisc as well as the qdisc in the qdisc new location.
635 * (However, if the graft is to the location where the qdisc is already at, it
636 * will be ignored completely and won't cause un-offloading).
637 */
638 static int
mlxsw_sp_qdisc_prio_graft(struct mlxsw_sp_port * mlxsw_sp_port,struct mlxsw_sp_qdisc * mlxsw_sp_qdisc,struct tc_prio_qopt_offload_graft_params * p)639 mlxsw_sp_qdisc_prio_graft(struct mlxsw_sp_port *mlxsw_sp_port,
640 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
641 struct tc_prio_qopt_offload_graft_params *p)
642 {
643 int tclass_num = MLXSW_SP_PRIO_BAND_TO_TCLASS(p->band);
644 struct mlxsw_sp_qdisc *old_qdisc;
645
646 /* Check if the grafted qdisc is already in its "new" location. If so -
647 * nothing needs to be done.
648 */
649 if (p->band < IEEE_8021QAZ_MAX_TCS &&
650 mlxsw_sp_port->tclass_qdiscs[tclass_num].handle == p->child_handle)
651 return 0;
652
653 /* See if the grafted qdisc is already offloaded on any tclass. If so,
654 * unoffload it.
655 */
656 old_qdisc = mlxsw_sp_qdisc_find_by_handle(mlxsw_sp_port,
657 p->child_handle);
658 if (old_qdisc)
659 mlxsw_sp_qdisc_destroy(mlxsw_sp_port, old_qdisc);
660
661 mlxsw_sp_qdisc_destroy(mlxsw_sp_port,
662 &mlxsw_sp_port->tclass_qdiscs[tclass_num]);
663 return -EOPNOTSUPP;
664 }
665
mlxsw_sp_setup_tc_prio(struct mlxsw_sp_port * mlxsw_sp_port,struct tc_prio_qopt_offload * p)666 int mlxsw_sp_setup_tc_prio(struct mlxsw_sp_port *mlxsw_sp_port,
667 struct tc_prio_qopt_offload *p)
668 {
669 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc;
670
671 mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent, true);
672 if (!mlxsw_sp_qdisc)
673 return -EOPNOTSUPP;
674
675 if (p->command == TC_PRIO_REPLACE)
676 return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle,
677 mlxsw_sp_qdisc,
678 &mlxsw_sp_qdisc_ops_prio,
679 &p->replace_params);
680
681 if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle,
682 MLXSW_SP_QDISC_PRIO))
683 return -EOPNOTSUPP;
684
685 switch (p->command) {
686 case TC_PRIO_DESTROY:
687 return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
688 case TC_PRIO_STATS:
689 return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc,
690 &p->stats);
691 case TC_PRIO_GRAFT:
692 return mlxsw_sp_qdisc_prio_graft(mlxsw_sp_port, mlxsw_sp_qdisc,
693 &p->graft_params);
694 default:
695 return -EOPNOTSUPP;
696 }
697 }
698
mlxsw_sp_tc_qdisc_init(struct mlxsw_sp_port * mlxsw_sp_port)699 int mlxsw_sp_tc_qdisc_init(struct mlxsw_sp_port *mlxsw_sp_port)
700 {
701 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc;
702 int i;
703
704 mlxsw_sp_qdisc = kzalloc(sizeof(*mlxsw_sp_qdisc), GFP_KERNEL);
705 if (!mlxsw_sp_qdisc)
706 goto err_root_qdisc_init;
707
708 mlxsw_sp_port->root_qdisc = mlxsw_sp_qdisc;
709 mlxsw_sp_port->root_qdisc->prio_bitmap = 0xff;
710 mlxsw_sp_port->root_qdisc->tclass_num = MLXSW_SP_PORT_DEFAULT_TCLASS;
711
712 mlxsw_sp_qdisc = kcalloc(IEEE_8021QAZ_MAX_TCS,
713 sizeof(*mlxsw_sp_qdisc),
714 GFP_KERNEL);
715 if (!mlxsw_sp_qdisc)
716 goto err_tclass_qdiscs_init;
717
718 mlxsw_sp_port->tclass_qdiscs = mlxsw_sp_qdisc;
719 for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++)
720 mlxsw_sp_port->tclass_qdiscs[i].tclass_num = i;
721
722 return 0;
723
724 err_tclass_qdiscs_init:
725 kfree(mlxsw_sp_port->root_qdisc);
726 err_root_qdisc_init:
727 return -ENOMEM;
728 }
729
mlxsw_sp_tc_qdisc_fini(struct mlxsw_sp_port * mlxsw_sp_port)730 void mlxsw_sp_tc_qdisc_fini(struct mlxsw_sp_port *mlxsw_sp_port)
731 {
732 kfree(mlxsw_sp_port->tclass_qdiscs);
733 kfree(mlxsw_sp_port->root_qdisc);
734 }
735