blob: cad603c35271bac2de3303713f9a1f181931f214 [file] [log] [blame]
Nogah Frankel96f17e02017-11-06 07:23:45 +01001/*
2 * drivers/net/ethernet/mellanox/mlxsw/spectrum_qdisc.c
3 * Copyright (c) 2017 Mellanox Technologies. All rights reserved.
4 * Copyright (c) 2017 Nogah Frankel <nogahf@mellanox.com>
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the names of the copyright holders nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * Alternatively, this software may be distributed under the terms of the
19 * GNU General Public License ("GPL") version 2 as published by the Free
20 * Software Foundation.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 * POSSIBILITY OF SUCH DAMAGE.
33 */
34
35#include <linux/kernel.h>
36#include <linux/errno.h>
37#include <linux/netdevice.h>
38#include <net/pkt_cls.h>
Nogah Frankel861fb822017-11-06 07:23:48 +010039#include <net/red.h>
Nogah Frankel96f17e02017-11-06 07:23:45 +010040
41#include "spectrum.h"
42#include "reg.h"
43
Nogah Frankel46a36152018-01-14 12:33:16 +010044#define MLXSW_SP_PRIO_BAND_TO_TCLASS(band) (IEEE_8021QAZ_MAX_TCS - band - 1)
Nogah Frankeleed4bae2018-02-28 10:44:58 +010045#define MLXSW_SP_PRIO_CHILD_TO_TCLASS(child) \
46 MLXSW_SP_PRIO_BAND_TO_TCLASS((child - 1))
Nogah Frankel46a36152018-01-14 12:33:16 +010047
Nogah Frankel371b4372018-01-10 14:59:57 +010048enum mlxsw_sp_qdisc_type {
49 MLXSW_SP_QDISC_NO_QDISC,
50 MLXSW_SP_QDISC_RED,
Nogah Frankel46a36152018-01-14 12:33:16 +010051 MLXSW_SP_QDISC_PRIO,
Nogah Frankel371b4372018-01-10 14:59:57 +010052};
53
Nogah Frankel562ffbc2018-01-10 15:00:04 +010054struct mlxsw_sp_qdisc_ops {
Nogah Frankel9cf6c9c2018-01-10 15:00:06 +010055 enum mlxsw_sp_qdisc_type type;
56 int (*check_params)(struct mlxsw_sp_port *mlxsw_sp_port,
57 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
58 void *params);
59 int (*replace)(struct mlxsw_sp_port *mlxsw_sp_port,
60 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, void *params);
Nogah Frankel9a37a592018-01-10 15:00:05 +010061 int (*destroy)(struct mlxsw_sp_port *mlxsw_sp_port,
62 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc);
Nogah Frankel562ffbc2018-01-10 15:00:04 +010063 int (*get_stats)(struct mlxsw_sp_port *mlxsw_sp_port,
64 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
65 struct tc_qopt_offload_stats *stats_ptr);
66 int (*get_xstats)(struct mlxsw_sp_port *mlxsw_sp_port,
67 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
68 void *xstats_ptr);
Nogah Frankel9cf6c9c2018-01-10 15:00:06 +010069 void (*clean_stats)(struct mlxsw_sp_port *mlxsw_sp_port,
70 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc);
Nogah Frankel93d8a4c2018-01-14 12:33:17 +010071 /* unoffload - to be used for a qdisc that stops being offloaded without
72 * being destroyed.
73 */
74 void (*unoffload)(struct mlxsw_sp_port *mlxsw_sp_port,
75 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, void *params);
Nogah Frankel562ffbc2018-01-10 15:00:04 +010076};
77
Nogah Frankel371b4372018-01-10 14:59:57 +010078struct mlxsw_sp_qdisc {
79 u32 handle;
Nogah Frankeld56c89552018-01-10 15:00:02 +010080 u8 tclass_num;
Nogah Frankel1631ab22018-02-28 10:45:00 +010081 u8 prio_bitmap;
Nogah Frankel371b4372018-01-10 14:59:57 +010082 union {
Nogah Frankel4d1a4b82018-01-10 15:00:00 +010083 struct red_stats red;
84 } xstats_base;
85 struct mlxsw_sp_qdisc_stats {
86 u64 tx_bytes;
87 u64 tx_packets;
88 u64 drops;
89 u64 overlimits;
Nogah Frankel93d8a4c2018-01-14 12:33:17 +010090 u64 backlog;
Nogah Frankel4d1a4b82018-01-10 15:00:00 +010091 } stats_base;
Nogah Frankel562ffbc2018-01-10 15:00:04 +010092
93 struct mlxsw_sp_qdisc_ops *ops;
Nogah Frankel371b4372018-01-10 14:59:57 +010094};
95
Nogah Frankelcba7158f2018-01-10 15:00:03 +010096static bool
97mlxsw_sp_qdisc_compare(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, u32 handle,
98 enum mlxsw_sp_qdisc_type type)
99{
Nogah Frankel9cf6c9c2018-01-10 15:00:06 +0100100 return mlxsw_sp_qdisc && mlxsw_sp_qdisc->ops &&
101 mlxsw_sp_qdisc->ops->type == type &&
102 mlxsw_sp_qdisc->handle == handle;
Nogah Frankelcba7158f2018-01-10 15:00:03 +0100103}
104
Nogah Frankeleed4bae2018-02-28 10:44:58 +0100105static struct mlxsw_sp_qdisc *
106mlxsw_sp_qdisc_find(struct mlxsw_sp_port *mlxsw_sp_port, u32 parent,
107 bool root_only)
108{
109 int tclass, child_index;
110
111 if (parent == TC_H_ROOT)
112 return mlxsw_sp_port->root_qdisc;
113
114 if (root_only || !mlxsw_sp_port->root_qdisc ||
115 !mlxsw_sp_port->root_qdisc->ops ||
116 TC_H_MAJ(parent) != mlxsw_sp_port->root_qdisc->handle ||
117 TC_H_MIN(parent) > IEEE_8021QAZ_MAX_TCS)
118 return NULL;
119
120 child_index = TC_H_MIN(parent);
121 tclass = MLXSW_SP_PRIO_CHILD_TO_TCLASS(child_index);
122 return &mlxsw_sp_port->tclass_qdiscs[tclass];
123}
124
Nogah Frankel32dc5ef2018-02-28 10:45:07 +0100125static struct mlxsw_sp_qdisc *
126mlxsw_sp_qdisc_find_by_handle(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle)
127{
128 int i;
129
130 if (mlxsw_sp_port->root_qdisc->handle == handle)
131 return mlxsw_sp_port->root_qdisc;
132
133 if (mlxsw_sp_port->root_qdisc->handle == TC_H_UNSPEC)
134 return NULL;
135
136 for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++)
137 if (mlxsw_sp_port->tclass_qdiscs[i].handle == handle)
138 return &mlxsw_sp_port->tclass_qdiscs[i];
139
140 return NULL;
141}
142
Nogah Frankel96f17e02017-11-06 07:23:45 +0100143static int
Nogah Frankel9a37a592018-01-10 15:00:05 +0100144mlxsw_sp_qdisc_destroy(struct mlxsw_sp_port *mlxsw_sp_port,
145 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
146{
147 int err = 0;
148
149 if (!mlxsw_sp_qdisc)
150 return 0;
151
152 if (mlxsw_sp_qdisc->ops && mlxsw_sp_qdisc->ops->destroy)
153 err = mlxsw_sp_qdisc->ops->destroy(mlxsw_sp_port,
154 mlxsw_sp_qdisc);
155
156 mlxsw_sp_qdisc->handle = TC_H_UNSPEC;
Nogah Frankel9a37a592018-01-10 15:00:05 +0100157 mlxsw_sp_qdisc->ops = NULL;
158 return err;
159}
160
161static int
Nogah Frankel9cf6c9c2018-01-10 15:00:06 +0100162mlxsw_sp_qdisc_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
163 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
164 struct mlxsw_sp_qdisc_ops *ops, void *params)
165{
166 int err;
167
Nogah Frankel56202ca2018-01-10 15:00:07 +0100168 if (mlxsw_sp_qdisc->ops && mlxsw_sp_qdisc->ops->type != ops->type)
169 /* In case this location contained a different qdisc of the
170 * same type we can override the old qdisc configuration.
171 * Otherwise, we need to remove the old qdisc before setting the
172 * new one.
173 */
174 mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
Nogah Frankel9cf6c9c2018-01-10 15:00:06 +0100175 err = ops->check_params(mlxsw_sp_port, mlxsw_sp_qdisc, params);
176 if (err)
177 goto err_bad_param;
178
179 err = ops->replace(mlxsw_sp_port, mlxsw_sp_qdisc, params);
180 if (err)
181 goto err_config;
182
183 if (mlxsw_sp_qdisc->handle != handle) {
184 mlxsw_sp_qdisc->ops = ops;
185 if (ops->clean_stats)
186 ops->clean_stats(mlxsw_sp_port, mlxsw_sp_qdisc);
187 }
188
189 mlxsw_sp_qdisc->handle = handle;
190 return 0;
191
192err_bad_param:
193err_config:
Nogah Frankel93d8a4c2018-01-14 12:33:17 +0100194 if (mlxsw_sp_qdisc->handle == handle && ops->unoffload)
195 ops->unoffload(mlxsw_sp_port, mlxsw_sp_qdisc, params);
196
Nogah Frankel9cf6c9c2018-01-10 15:00:06 +0100197 mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
198 return err;
199}
200
201static int
Nogah Frankel562ffbc2018-01-10 15:00:04 +0100202mlxsw_sp_qdisc_get_stats(struct mlxsw_sp_port *mlxsw_sp_port,
203 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
204 struct tc_qopt_offload_stats *stats_ptr)
205{
206 if (mlxsw_sp_qdisc && mlxsw_sp_qdisc->ops &&
207 mlxsw_sp_qdisc->ops->get_stats)
208 return mlxsw_sp_qdisc->ops->get_stats(mlxsw_sp_port,
209 mlxsw_sp_qdisc,
210 stats_ptr);
211
212 return -EOPNOTSUPP;
213}
214
215static int
216mlxsw_sp_qdisc_get_xstats(struct mlxsw_sp_port *mlxsw_sp_port,
217 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
218 void *xstats_ptr)
219{
220 if (mlxsw_sp_qdisc && mlxsw_sp_qdisc->ops &&
221 mlxsw_sp_qdisc->ops->get_xstats)
222 return mlxsw_sp_qdisc->ops->get_xstats(mlxsw_sp_port,
223 mlxsw_sp_qdisc,
224 xstats_ptr);
225
226 return -EOPNOTSUPP;
227}
228
Nogah Frankel04cc0bf2018-02-28 10:45:01 +0100229static void
230mlxsw_sp_qdisc_bstats_per_priority_get(struct mlxsw_sp_port_xstats *xstats,
231 u8 prio_bitmap, u64 *tx_packets,
232 u64 *tx_bytes)
233{
234 int i;
235
236 *tx_packets = 0;
237 *tx_bytes = 0;
238 for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
239 if (prio_bitmap & BIT(i)) {
240 *tx_packets += xstats->tx_packets[i];
241 *tx_bytes += xstats->tx_bytes[i];
242 }
243 }
244}
245
Nogah Frankel562ffbc2018-01-10 15:00:04 +0100246static int
Nogah Frankel96f17e02017-11-06 07:23:45 +0100247mlxsw_sp_tclass_congestion_enable(struct mlxsw_sp_port *mlxsw_sp_port,
248 int tclass_num, u32 min, u32 max,
249 u32 probability, bool is_ecn)
250{
Jiri Pirkodb849242018-01-10 11:42:44 +0100251 char cwtpm_cmd[MLXSW_REG_CWTPM_LEN];
252 char cwtp_cmd[MLXSW_REG_CWTP_LEN];
Nogah Frankel96f17e02017-11-06 07:23:45 +0100253 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
254 int err;
255
256 mlxsw_reg_cwtp_pack(cwtp_cmd, mlxsw_sp_port->local_port, tclass_num);
257 mlxsw_reg_cwtp_profile_pack(cwtp_cmd, MLXSW_REG_CWTP_DEFAULT_PROFILE,
258 roundup(min, MLXSW_REG_CWTP_MIN_VALUE),
259 roundup(max, MLXSW_REG_CWTP_MIN_VALUE),
260 probability);
261
262 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(cwtp), cwtp_cmd);
263 if (err)
264 return err;
265
Jiri Pirkodb849242018-01-10 11:42:44 +0100266 mlxsw_reg_cwtpm_pack(cwtpm_cmd, mlxsw_sp_port->local_port, tclass_num,
Nogah Frankel96f17e02017-11-06 07:23:45 +0100267 MLXSW_REG_CWTP_DEFAULT_PROFILE, true, is_ecn);
268
Jiri Pirkodb849242018-01-10 11:42:44 +0100269 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(cwtpm), cwtpm_cmd);
Nogah Frankel96f17e02017-11-06 07:23:45 +0100270}
271
272static int
273mlxsw_sp_tclass_congestion_disable(struct mlxsw_sp_port *mlxsw_sp_port,
274 int tclass_num)
275{
276 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
277 char cwtpm_cmd[MLXSW_REG_CWTPM_LEN];
278
279 mlxsw_reg_cwtpm_pack(cwtpm_cmd, mlxsw_sp_port->local_port, tclass_num,
280 MLXSW_REG_CWTPM_RESET_PROFILE, false, false);
281 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(cwtpm), cwtpm_cmd);
282}
283
Nogah Frankel861fb822017-11-06 07:23:48 +0100284static void
Nogah Frankelc2ed6db2018-01-10 15:00:01 +0100285mlxsw_sp_setup_tc_qdisc_red_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port,
Nogah Frankeld56c89552018-01-10 15:00:02 +0100286 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
Nogah Frankel861fb822017-11-06 07:23:48 +0100287{
Nogah Frankeld56c89552018-01-10 15:00:02 +0100288 u8 tclass_num = mlxsw_sp_qdisc->tclass_num;
Nogah Frankel4d1a4b82018-01-10 15:00:00 +0100289 struct mlxsw_sp_qdisc_stats *stats_base;
Nogah Frankel861fb822017-11-06 07:23:48 +0100290 struct mlxsw_sp_port_xstats *xstats;
Nogah Frankel4d1a4b82018-01-10 15:00:00 +0100291 struct red_stats *red_base;
Nogah Frankel861fb822017-11-06 07:23:48 +0100292
293 xstats = &mlxsw_sp_port->periodic_hw_stats.xstats;
Nogah Frankel4d1a4b82018-01-10 15:00:00 +0100294 stats_base = &mlxsw_sp_qdisc->stats_base;
Nogah Frankelc2ed6db2018-01-10 15:00:01 +0100295 red_base = &mlxsw_sp_qdisc->xstats_base.red;
Nogah Frankel36707562017-11-06 07:23:49 +0100296
Nogah Frankel04cc0bf2018-02-28 10:45:01 +0100297 mlxsw_sp_qdisc_bstats_per_priority_get(xstats,
298 mlxsw_sp_qdisc->prio_bitmap,
299 &stats_base->tx_packets,
300 &stats_base->tx_bytes);
Nogah Frankelc2ed6db2018-01-10 15:00:01 +0100301 red_base->prob_mark = xstats->ecn;
302 red_base->prob_drop = xstats->wred_drop[tclass_num];
303 red_base->pdrop = xstats->tail_drop[tclass_num];
Nogah Frankel36707562017-11-06 07:23:49 +0100304
Nogah Frankelc2ed6db2018-01-10 15:00:01 +0100305 stats_base->overlimits = red_base->prob_drop + red_base->prob_mark;
306 stats_base->drops = red_base->prob_drop + red_base->pdrop;
Jakub Kicinski416ef9b2018-01-14 20:01:26 -0800307
308 stats_base->backlog = 0;
Nogah Frankel861fb822017-11-06 07:23:48 +0100309}
310
Nogah Frankel96f17e02017-11-06 07:23:45 +0100311static int
Nogah Frankelcba7158f2018-01-10 15:00:03 +0100312mlxsw_sp_qdisc_red_destroy(struct mlxsw_sp_port *mlxsw_sp_port,
Nogah Frankeld56c89552018-01-10 15:00:02 +0100313 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
Nogah Frankel96f17e02017-11-06 07:23:45 +0100314{
Nogah Frankelcc6e5c12018-02-28 10:45:02 +0100315 struct mlxsw_sp_qdisc *root_qdisc = mlxsw_sp_port->root_qdisc;
316
317 if (root_qdisc != mlxsw_sp_qdisc)
318 root_qdisc->stats_base.backlog -=
319 mlxsw_sp_qdisc->stats_base.backlog;
320
Nogah Frankel9a37a592018-01-10 15:00:05 +0100321 return mlxsw_sp_tclass_congestion_disable(mlxsw_sp_port,
322 mlxsw_sp_qdisc->tclass_num);
Nogah Frankel96f17e02017-11-06 07:23:45 +0100323}
324
325static int
Nogah Frankel9cf6c9c2018-01-10 15:00:06 +0100326mlxsw_sp_qdisc_red_check_params(struct mlxsw_sp_port *mlxsw_sp_port,
327 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
328 void *params)
Nogah Frankel96f17e02017-11-06 07:23:45 +0100329{
330 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
Nogah Frankel9cf6c9c2018-01-10 15:00:06 +0100331 struct tc_red_qopt_offload_params *p = params;
Nogah Frankel96f17e02017-11-06 07:23:45 +0100332
333 if (p->min > p->max) {
334 dev_err(mlxsw_sp->bus_info->dev,
335 "spectrum: RED: min %u is bigger then max %u\n", p->min,
336 p->max);
Nogah Frankel9cf6c9c2018-01-10 15:00:06 +0100337 return -EINVAL;
Nogah Frankel96f17e02017-11-06 07:23:45 +0100338 }
339 if (p->max > MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_BUFFER_SIZE)) {
340 dev_err(mlxsw_sp->bus_info->dev,
341 "spectrum: RED: max value %u is too big\n", p->max);
Nogah Frankel9cf6c9c2018-01-10 15:00:06 +0100342 return -EINVAL;
Nogah Frankel96f17e02017-11-06 07:23:45 +0100343 }
344 if (p->min == 0 || p->max == 0) {
345 dev_err(mlxsw_sp->bus_info->dev,
346 "spectrum: RED: 0 value is illegal for min and max\n");
Nogah Frankel9cf6c9c2018-01-10 15:00:06 +0100347 return -EINVAL;
Nogah Frankel96f17e02017-11-06 07:23:45 +0100348 }
Nogah Frankel9cf6c9c2018-01-10 15:00:06 +0100349 return 0;
350}
351
352static int
353mlxsw_sp_qdisc_red_replace(struct mlxsw_sp_port *mlxsw_sp_port,
354 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
355 void *params)
356{
357 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
358 struct tc_red_qopt_offload_params *p = params;
359 u8 tclass_num = mlxsw_sp_qdisc->tclass_num;
360 u32 min, max;
361 u64 prob;
Nogah Frankel96f17e02017-11-06 07:23:45 +0100362
363 /* calculate probability in percentage */
364 prob = p->probability;
365 prob *= 100;
366 prob = DIV_ROUND_UP(prob, 1 << 16);
367 prob = DIV_ROUND_UP(prob, 1 << 16);
368 min = mlxsw_sp_bytes_cells(mlxsw_sp, p->min);
369 max = mlxsw_sp_bytes_cells(mlxsw_sp, p->max);
Nogah Frankel9cf6c9c2018-01-10 15:00:06 +0100370 return mlxsw_sp_tclass_congestion_enable(mlxsw_sp_port, tclass_num, min,
371 max, prob, p->is_ecn);
Nogah Frankel96f17e02017-11-06 07:23:45 +0100372}
373
Jakub Kicinski416ef9b2018-01-14 20:01:26 -0800374static void
375mlxsw_sp_qdisc_red_unoffload(struct mlxsw_sp_port *mlxsw_sp_port,
376 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
377 void *params)
378{
379 struct tc_red_qopt_offload_params *p = params;
380 u64 backlog;
381
382 backlog = mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp,
383 mlxsw_sp_qdisc->stats_base.backlog);
384 p->qstats->backlog -= backlog;
Nogah Frankelcc6e5c12018-02-28 10:45:02 +0100385 mlxsw_sp_qdisc->stats_base.backlog = 0;
Jakub Kicinski416ef9b2018-01-14 20:01:26 -0800386}
387
Nogah Frankel861fb822017-11-06 07:23:48 +0100388static int
Nogah Frankelcba7158f2018-01-10 15:00:03 +0100389mlxsw_sp_qdisc_get_red_xstats(struct mlxsw_sp_port *mlxsw_sp_port,
Nogah Frankel861fb822017-11-06 07:23:48 +0100390 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
Nogah Frankel562ffbc2018-01-10 15:00:04 +0100391 void *xstats_ptr)
Nogah Frankel861fb822017-11-06 07:23:48 +0100392{
Nogah Frankel4d1a4b82018-01-10 15:00:00 +0100393 struct red_stats *xstats_base = &mlxsw_sp_qdisc->xstats_base.red;
Nogah Frankeld56c89552018-01-10 15:00:02 +0100394 u8 tclass_num = mlxsw_sp_qdisc->tclass_num;
Nogah Frankel861fb822017-11-06 07:23:48 +0100395 struct mlxsw_sp_port_xstats *xstats;
Nogah Frankel562ffbc2018-01-10 15:00:04 +0100396 struct red_stats *res = xstats_ptr;
Nogah Frankelf8253df2018-01-10 14:59:59 +0100397 int early_drops, marks, pdrops;
Nogah Frankel861fb822017-11-06 07:23:48 +0100398
Nogah Frankel861fb822017-11-06 07:23:48 +0100399 xstats = &mlxsw_sp_port->periodic_hw_stats.xstats;
400
Nogah Frankelf8253df2018-01-10 14:59:59 +0100401 early_drops = xstats->wred_drop[tclass_num] - xstats_base->prob_drop;
402 marks = xstats->ecn - xstats_base->prob_mark;
403 pdrops = xstats->tail_drop[tclass_num] - xstats_base->pdrop;
404
405 res->pdrop += pdrops;
406 res->prob_drop += early_drops;
407 res->prob_mark += marks;
408
409 xstats_base->pdrop += pdrops;
410 xstats_base->prob_drop += early_drops;
411 xstats_base->prob_mark += marks;
Nogah Frankel861fb822017-11-06 07:23:48 +0100412 return 0;
413}
414
Nogah Frankel36707562017-11-06 07:23:49 +0100415static int
Nogah Frankelcba7158f2018-01-10 15:00:03 +0100416mlxsw_sp_qdisc_get_red_stats(struct mlxsw_sp_port *mlxsw_sp_port,
Nogah Frankel36707562017-11-06 07:23:49 +0100417 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
Nogah Frankel562ffbc2018-01-10 15:00:04 +0100418 struct tc_qopt_offload_stats *stats_ptr)
Nogah Frankel36707562017-11-06 07:23:49 +0100419{
Jakub Kicinski416ef9b2018-01-14 20:01:26 -0800420 u64 tx_bytes, tx_packets, overlimits, drops, backlog;
Nogah Frankeld56c89552018-01-10 15:00:02 +0100421 u8 tclass_num = mlxsw_sp_qdisc->tclass_num;
Nogah Frankel4d1a4b82018-01-10 15:00:00 +0100422 struct mlxsw_sp_qdisc_stats *stats_base;
Nogah Frankel36707562017-11-06 07:23:49 +0100423 struct mlxsw_sp_port_xstats *xstats;
Nogah Frankel36707562017-11-06 07:23:49 +0100424
Nogah Frankel36707562017-11-06 07:23:49 +0100425 xstats = &mlxsw_sp_port->periodic_hw_stats.xstats;
Nogah Frankel4d1a4b82018-01-10 15:00:00 +0100426 stats_base = &mlxsw_sp_qdisc->stats_base;
Nogah Frankel36707562017-11-06 07:23:49 +0100427
Nogah Frankel04cc0bf2018-02-28 10:45:01 +0100428 mlxsw_sp_qdisc_bstats_per_priority_get(xstats,
429 mlxsw_sp_qdisc->prio_bitmap,
430 &tx_packets, &tx_bytes);
431 tx_bytes = tx_bytes - stats_base->tx_bytes;
432 tx_packets = tx_packets - stats_base->tx_packets;
433
Nogah Frankel36707562017-11-06 07:23:49 +0100434 overlimits = xstats->wred_drop[tclass_num] + xstats->ecn -
Nogah Frankel4d1a4b82018-01-10 15:00:00 +0100435 stats_base->overlimits;
Nogah Frankel36707562017-11-06 07:23:49 +0100436 drops = xstats->wred_drop[tclass_num] + xstats->tail_drop[tclass_num] -
Nogah Frankel4d1a4b82018-01-10 15:00:00 +0100437 stats_base->drops;
Jakub Kicinski416ef9b2018-01-14 20:01:26 -0800438 backlog = xstats->backlog[tclass_num];
Nogah Frankel36707562017-11-06 07:23:49 +0100439
Nogah Frankel562ffbc2018-01-10 15:00:04 +0100440 _bstats_update(stats_ptr->bstats, tx_bytes, tx_packets);
441 stats_ptr->qstats->overlimits += overlimits;
442 stats_ptr->qstats->drops += drops;
443 stats_ptr->qstats->backlog +=
Jakub Kicinski416ef9b2018-01-14 20:01:26 -0800444 mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp,
445 backlog) -
446 mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp,
447 stats_base->backlog);
Nogah Frankel36707562017-11-06 07:23:49 +0100448
Jakub Kicinski416ef9b2018-01-14 20:01:26 -0800449 stats_base->backlog = backlog;
Nogah Frankel4d1a4b82018-01-10 15:00:00 +0100450 stats_base->drops += drops;
451 stats_base->overlimits += overlimits;
452 stats_base->tx_bytes += tx_bytes;
453 stats_base->tx_packets += tx_packets;
Nogah Frankel36707562017-11-06 07:23:49 +0100454 return 0;
455}
456
Nogah Frankel96f17e02017-11-06 07:23:45 +0100457#define MLXSW_SP_PORT_DEFAULT_TCLASS 0
458
Nogah Frankel562ffbc2018-01-10 15:00:04 +0100459static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_red = {
Nogah Frankel9cf6c9c2018-01-10 15:00:06 +0100460 .type = MLXSW_SP_QDISC_RED,
461 .check_params = mlxsw_sp_qdisc_red_check_params,
462 .replace = mlxsw_sp_qdisc_red_replace,
Jakub Kicinski416ef9b2018-01-14 20:01:26 -0800463 .unoffload = mlxsw_sp_qdisc_red_unoffload,
Nogah Frankel9a37a592018-01-10 15:00:05 +0100464 .destroy = mlxsw_sp_qdisc_red_destroy,
Nogah Frankel562ffbc2018-01-10 15:00:04 +0100465 .get_stats = mlxsw_sp_qdisc_get_red_stats,
466 .get_xstats = mlxsw_sp_qdisc_get_red_xstats,
Nogah Frankel9cf6c9c2018-01-10 15:00:06 +0100467 .clean_stats = mlxsw_sp_setup_tc_qdisc_red_clean_stats,
Nogah Frankel562ffbc2018-01-10 15:00:04 +0100468};
469
Nogah Frankel96f17e02017-11-06 07:23:45 +0100470int mlxsw_sp_setup_tc_red(struct mlxsw_sp_port *mlxsw_sp_port,
471 struct tc_red_qopt_offload *p)
472{
473 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc;
Nogah Frankel96f17e02017-11-06 07:23:45 +0100474
Nogah Frankeleed4bae2018-02-28 10:44:58 +0100475 mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent, false);
476 if (!mlxsw_sp_qdisc)
Nogah Frankel96f17e02017-11-06 07:23:45 +0100477 return -EOPNOTSUPP;
478
Nogah Frankelcba7158f2018-01-10 15:00:03 +0100479 if (p->command == TC_RED_REPLACE)
Nogah Frankel9cf6c9c2018-01-10 15:00:06 +0100480 return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle,
481 mlxsw_sp_qdisc,
482 &mlxsw_sp_qdisc_ops_red,
483 &p->set);
Nogah Frankelcba7158f2018-01-10 15:00:03 +0100484
485 if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle,
486 MLXSW_SP_QDISC_RED))
487 return -EOPNOTSUPP;
488
489 switch (p->command) {
Nogah Frankel96f17e02017-11-06 07:23:45 +0100490 case TC_RED_DESTROY:
Nogah Frankel9a37a592018-01-10 15:00:05 +0100491 return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
Nogah Frankel861fb822017-11-06 07:23:48 +0100492 case TC_RED_XSTATS:
Nogah Frankel562ffbc2018-01-10 15:00:04 +0100493 return mlxsw_sp_qdisc_get_xstats(mlxsw_sp_port, mlxsw_sp_qdisc,
494 p->xstats);
Nogah Frankel36707562017-11-06 07:23:49 +0100495 case TC_RED_STATS:
Nogah Frankel562ffbc2018-01-10 15:00:04 +0100496 return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc,
497 &p->stats);
Nogah Frankel96f17e02017-11-06 07:23:45 +0100498 default:
499 return -EOPNOTSUPP;
500 }
501}
Nogah Frankel371b4372018-01-10 14:59:57 +0100502
Nogah Frankel46a36152018-01-14 12:33:16 +0100503static int
504mlxsw_sp_qdisc_prio_destroy(struct mlxsw_sp_port *mlxsw_sp_port,
505 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
506{
507 int i;
508
Nogah Frankeleed4bae2018-02-28 10:44:58 +0100509 for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
Nogah Frankel46a36152018-01-14 12:33:16 +0100510 mlxsw_sp_port_prio_tc_set(mlxsw_sp_port, i,
511 MLXSW_SP_PORT_DEFAULT_TCLASS);
Nogah Frankeleed4bae2018-02-28 10:44:58 +0100512 mlxsw_sp_qdisc_destroy(mlxsw_sp_port,
513 &mlxsw_sp_port->tclass_qdiscs[i]);
Nogah Frankel1631ab22018-02-28 10:45:00 +0100514 mlxsw_sp_port->tclass_qdiscs[i].prio_bitmap = 0;
Nogah Frankeleed4bae2018-02-28 10:44:58 +0100515 }
Nogah Frankel46a36152018-01-14 12:33:16 +0100516
517 return 0;
518}
519
520static int
521mlxsw_sp_qdisc_prio_check_params(struct mlxsw_sp_port *mlxsw_sp_port,
522 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
523 void *params)
524{
525 struct tc_prio_qopt_offload_params *p = params;
526
527 if (p->bands > IEEE_8021QAZ_MAX_TCS)
528 return -EOPNOTSUPP;
529
530 return 0;
531}
532
533static int
534mlxsw_sp_qdisc_prio_replace(struct mlxsw_sp_port *mlxsw_sp_port,
535 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
536 void *params)
537{
538 struct tc_prio_qopt_offload_params *p = params;
Nogah Frankel04cc0bf2018-02-28 10:45:01 +0100539 struct mlxsw_sp_qdisc *child_qdisc;
Nogah Frankelcc6e5c12018-02-28 10:45:02 +0100540 int tclass, i, band, backlog;
Nogah Frankel04cc0bf2018-02-28 10:45:01 +0100541 u8 old_priomap;
Nogah Frankel46a36152018-01-14 12:33:16 +0100542 int err;
543
Nogah Frankel04cc0bf2018-02-28 10:45:01 +0100544 for (band = 0; band < p->bands; band++) {
545 tclass = MLXSW_SP_PRIO_BAND_TO_TCLASS(band);
546 child_qdisc = &mlxsw_sp_port->tclass_qdiscs[tclass];
547 old_priomap = child_qdisc->prio_bitmap;
548 child_qdisc->prio_bitmap = 0;
549 for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
550 if (p->priomap[i] == band) {
551 child_qdisc->prio_bitmap |= BIT(i);
552 if (BIT(i) & old_priomap)
553 continue;
554 err = mlxsw_sp_port_prio_tc_set(mlxsw_sp_port,
555 i, tclass);
556 if (err)
557 return err;
558 }
559 }
560 if (old_priomap != child_qdisc->prio_bitmap &&
Nogah Frankelcc6e5c12018-02-28 10:45:02 +0100561 child_qdisc->ops && child_qdisc->ops->clean_stats) {
562 backlog = child_qdisc->stats_base.backlog;
Nogah Frankel04cc0bf2018-02-28 10:45:01 +0100563 child_qdisc->ops->clean_stats(mlxsw_sp_port,
564 child_qdisc);
Nogah Frankelcc6e5c12018-02-28 10:45:02 +0100565 child_qdisc->stats_base.backlog = backlog;
566 }
Nogah Frankel46a36152018-01-14 12:33:16 +0100567 }
Nogah Frankel98ceb7b2018-02-28 10:45:05 +0100568 for (; band < IEEE_8021QAZ_MAX_TCS; band++) {
569 tclass = MLXSW_SP_PRIO_BAND_TO_TCLASS(band);
570 child_qdisc = &mlxsw_sp_port->tclass_qdiscs[tclass];
571 child_qdisc->prio_bitmap = 0;
572 mlxsw_sp_qdisc_destroy(mlxsw_sp_port, child_qdisc);
573 }
Nogah Frankel46a36152018-01-14 12:33:16 +0100574 return 0;
575}
576
Wei Yongjune02f08a2018-01-15 10:43:03 +0000577static void
Nogah Frankel93d8a4c2018-01-14 12:33:17 +0100578mlxsw_sp_qdisc_prio_unoffload(struct mlxsw_sp_port *mlxsw_sp_port,
579 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
580 void *params)
581{
582 struct tc_prio_qopt_offload_params *p = params;
583 u64 backlog;
584
585 backlog = mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp,
586 mlxsw_sp_qdisc->stats_base.backlog);
587 p->qstats->backlog -= backlog;
588}
589
590static int
591mlxsw_sp_qdisc_get_prio_stats(struct mlxsw_sp_port *mlxsw_sp_port,
592 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
593 struct tc_qopt_offload_stats *stats_ptr)
594{
595 u64 tx_bytes, tx_packets, drops = 0, backlog = 0;
596 struct mlxsw_sp_qdisc_stats *stats_base;
597 struct mlxsw_sp_port_xstats *xstats;
598 struct rtnl_link_stats64 *stats;
599 int i;
600
601 xstats = &mlxsw_sp_port->periodic_hw_stats.xstats;
602 stats = &mlxsw_sp_port->periodic_hw_stats.stats;
603 stats_base = &mlxsw_sp_qdisc->stats_base;
604
605 tx_bytes = stats->tx_bytes - stats_base->tx_bytes;
606 tx_packets = stats->tx_packets - stats_base->tx_packets;
607
608 for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
609 drops += xstats->tail_drop[i];
Nogah Frankel23f2b402018-02-28 10:45:04 +0100610 drops += xstats->wred_drop[i];
Nogah Frankel93d8a4c2018-01-14 12:33:17 +0100611 backlog += xstats->backlog[i];
612 }
613 drops = drops - stats_base->drops;
614
615 _bstats_update(stats_ptr->bstats, tx_bytes, tx_packets);
616 stats_ptr->qstats->drops += drops;
617 stats_ptr->qstats->backlog +=
618 mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp,
619 backlog) -
620 mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp,
621 stats_base->backlog);
622 stats_base->backlog = backlog;
623 stats_base->drops += drops;
624 stats_base->tx_bytes += tx_bytes;
625 stats_base->tx_packets += tx_packets;
626 return 0;
627}
628
629static void
630mlxsw_sp_setup_tc_qdisc_prio_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port,
631 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
632{
633 struct mlxsw_sp_qdisc_stats *stats_base;
634 struct mlxsw_sp_port_xstats *xstats;
635 struct rtnl_link_stats64 *stats;
636 int i;
637
638 xstats = &mlxsw_sp_port->periodic_hw_stats.xstats;
639 stats = &mlxsw_sp_port->periodic_hw_stats.stats;
640 stats_base = &mlxsw_sp_qdisc->stats_base;
641
642 stats_base->tx_packets = stats->tx_packets;
643 stats_base->tx_bytes = stats->tx_bytes;
644
645 stats_base->drops = 0;
Nogah Frankel23f2b402018-02-28 10:45:04 +0100646 for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
Nogah Frankel93d8a4c2018-01-14 12:33:17 +0100647 stats_base->drops += xstats->tail_drop[i];
Nogah Frankel23f2b402018-02-28 10:45:04 +0100648 stats_base->drops += xstats->wred_drop[i];
649 }
Nogah Frankel93d8a4c2018-01-14 12:33:17 +0100650
651 mlxsw_sp_qdisc->stats_base.backlog = 0;
652}
653
Nogah Frankel46a36152018-01-14 12:33:16 +0100654static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_prio = {
655 .type = MLXSW_SP_QDISC_PRIO,
656 .check_params = mlxsw_sp_qdisc_prio_check_params,
657 .replace = mlxsw_sp_qdisc_prio_replace,
Nogah Frankel93d8a4c2018-01-14 12:33:17 +0100658 .unoffload = mlxsw_sp_qdisc_prio_unoffload,
Nogah Frankel46a36152018-01-14 12:33:16 +0100659 .destroy = mlxsw_sp_qdisc_prio_destroy,
Nogah Frankel93d8a4c2018-01-14 12:33:17 +0100660 .get_stats = mlxsw_sp_qdisc_get_prio_stats,
661 .clean_stats = mlxsw_sp_setup_tc_qdisc_prio_clean_stats,
Nogah Frankel46a36152018-01-14 12:33:16 +0100662};
663
Nogah Frankel32dc5ef2018-02-28 10:45:07 +0100664/* Grafting is not supported in mlxsw. It will result in un-offloading of the
665 * grafted qdisc as well as the qdisc in the qdisc new location.
666 * (However, if the graft is to the location where the qdisc is already at, it
667 * will be ignored completely and won't cause un-offloading).
668 */
669static int
670mlxsw_sp_qdisc_prio_graft(struct mlxsw_sp_port *mlxsw_sp_port,
671 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
672 struct tc_prio_qopt_offload_graft_params *p)
673{
674 int tclass_num = MLXSW_SP_PRIO_BAND_TO_TCLASS(p->band);
675 struct mlxsw_sp_qdisc *old_qdisc;
676
677 /* Check if the grafted qdisc is already in its "new" location. If so -
678 * nothing needs to be done.
679 */
680 if (p->band < IEEE_8021QAZ_MAX_TCS &&
681 mlxsw_sp_port->tclass_qdiscs[tclass_num].handle == p->child_handle)
682 return 0;
683
684 /* See if the grafted qdisc is already offloaded on any tclass. If so,
685 * unoffload it.
686 */
687 old_qdisc = mlxsw_sp_qdisc_find_by_handle(mlxsw_sp_port,
688 p->child_handle);
689 if (old_qdisc)
690 mlxsw_sp_qdisc_destroy(mlxsw_sp_port, old_qdisc);
691
692 mlxsw_sp_qdisc_destroy(mlxsw_sp_port,
693 &mlxsw_sp_port->tclass_qdiscs[tclass_num]);
694 return -EOPNOTSUPP;
695}
696
Nogah Frankel46a36152018-01-14 12:33:16 +0100697int mlxsw_sp_setup_tc_prio(struct mlxsw_sp_port *mlxsw_sp_port,
698 struct tc_prio_qopt_offload *p)
699{
700 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc;
701
Nogah Frankeleed4bae2018-02-28 10:44:58 +0100702 mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent, true);
703 if (!mlxsw_sp_qdisc)
Nogah Frankel46a36152018-01-14 12:33:16 +0100704 return -EOPNOTSUPP;
705
Nogah Frankel46a36152018-01-14 12:33:16 +0100706 if (p->command == TC_PRIO_REPLACE)
707 return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle,
708 mlxsw_sp_qdisc,
709 &mlxsw_sp_qdisc_ops_prio,
710 &p->replace_params);
711
712 if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle,
713 MLXSW_SP_QDISC_PRIO))
714 return -EOPNOTSUPP;
715
716 switch (p->command) {
717 case TC_PRIO_DESTROY:
718 return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
Nogah Frankel93d8a4c2018-01-14 12:33:17 +0100719 case TC_PRIO_STATS:
720 return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc,
721 &p->stats);
Nogah Frankel32dc5ef2018-02-28 10:45:07 +0100722 case TC_PRIO_GRAFT:
723 return mlxsw_sp_qdisc_prio_graft(mlxsw_sp_port, mlxsw_sp_qdisc,
724 &p->graft_params);
Nogah Frankel46a36152018-01-14 12:33:16 +0100725 default:
726 return -EOPNOTSUPP;
727 }
728}
729
Nogah Frankel371b4372018-01-10 14:59:57 +0100730int mlxsw_sp_tc_qdisc_init(struct mlxsw_sp_port *mlxsw_sp_port)
731{
Nogah Frankeleed4bae2018-02-28 10:44:58 +0100732 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc;
733 int i;
Nogah Frankel371b4372018-01-10 14:59:57 +0100734
Nogah Frankeleed4bae2018-02-28 10:44:58 +0100735 mlxsw_sp_qdisc = kzalloc(sizeof(*mlxsw_sp_qdisc), GFP_KERNEL);
736 if (!mlxsw_sp_qdisc)
737 goto err_root_qdisc_init;
738
739 mlxsw_sp_port->root_qdisc = mlxsw_sp_qdisc;
Nogah Frankel1631ab22018-02-28 10:45:00 +0100740 mlxsw_sp_port->root_qdisc->prio_bitmap = 0xff;
Nogah Frankeld56c89552018-01-10 15:00:02 +0100741 mlxsw_sp_port->root_qdisc->tclass_num = MLXSW_SP_PORT_DEFAULT_TCLASS;
742
Kees Cook6396bb22018-06-12 14:03:40 -0700743 mlxsw_sp_qdisc = kcalloc(IEEE_8021QAZ_MAX_TCS,
744 sizeof(*mlxsw_sp_qdisc),
Nogah Frankeleed4bae2018-02-28 10:44:58 +0100745 GFP_KERNEL);
746 if (!mlxsw_sp_qdisc)
747 goto err_tclass_qdiscs_init;
748
749 mlxsw_sp_port->tclass_qdiscs = mlxsw_sp_qdisc;
750 for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++)
751 mlxsw_sp_port->tclass_qdiscs[i].tclass_num = i;
752
Nogah Frankel371b4372018-01-10 14:59:57 +0100753 return 0;
Nogah Frankeleed4bae2018-02-28 10:44:58 +0100754
755err_tclass_qdiscs_init:
756 kfree(mlxsw_sp_port->root_qdisc);
757err_root_qdisc_init:
758 return -ENOMEM;
Nogah Frankel371b4372018-01-10 14:59:57 +0100759}
760
761void mlxsw_sp_tc_qdisc_fini(struct mlxsw_sp_port *mlxsw_sp_port)
762{
Nogah Frankeleed4bae2018-02-28 10:44:58 +0100763 kfree(mlxsw_sp_port->tclass_qdiscs);
Nogah Frankel371b4372018-01-10 14:59:57 +0100764 kfree(mlxsw_sp_port->root_qdisc);
765}