19948a064SJiri Pirko // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
29948a064SJiri Pirko /* Copyright (c) 2017-2018 Mellanox Technologies. All rights reserved */
396f17e07SNogah Frankel
496f17e07SNogah Frankel #include <linux/kernel.h>
596f17e07SNogah Frankel #include <linux/errno.h>
696f17e07SNogah Frankel #include <linux/netdevice.h>
796f17e07SNogah Frankel #include <net/pkt_cls.h>
8861fb829SNogah Frankel #include <net/red.h>
996f17e07SNogah Frankel
1096f17e07SNogah Frankel #include "spectrum.h"
11f6668eacSPetr Machata #include "spectrum_span.h"
1296f17e07SNogah Frankel #include "reg.h"
1396f17e07SNogah Frankel
1446a3615bSNogah Frankel #define MLXSW_SP_PRIO_BAND_TO_TCLASS(band) (IEEE_8021QAZ_MAX_TCS - band - 1)
15eed4baebSNogah Frankel #define MLXSW_SP_PRIO_CHILD_TO_TCLASS(child) \
16eed4baebSNogah Frankel MLXSW_SP_PRIO_BAND_TO_TCLASS((child - 1))
1746a3615bSNogah Frankel
18371b437aSNogah Frankel enum mlxsw_sp_qdisc_type {
19371b437aSNogah Frankel MLXSW_SP_QDISC_NO_QDISC,
20371b437aSNogah Frankel MLXSW_SP_QDISC_RED,
2146a3615bSNogah Frankel MLXSW_SP_QDISC_PRIO,
2219f405b9SPetr Machata MLXSW_SP_QDISC_ETS,
23a44f58c4SPetr Machata MLXSW_SP_QDISC_TBF,
247bec1a45SPetr Machata MLXSW_SP_QDISC_FIFO,
25371b437aSNogah Frankel };
26371b437aSNogah Frankel
27ee88450dSPetr Machata struct mlxsw_sp_qdisc;
28ee88450dSPetr Machata
29562ffbc4SNogah Frankel struct mlxsw_sp_qdisc_ops {
309cf6c9c7SNogah Frankel enum mlxsw_sp_qdisc_type type;
319cf6c9c7SNogah Frankel int (*check_params)(struct mlxsw_sp_port *mlxsw_sp_port,
329cf6c9c7SNogah Frankel void *params);
33c4e372e2SPetr Machata int (*replace)(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
349cf6c9c7SNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, void *params);
359a37a59fSNogah Frankel int (*destroy)(struct mlxsw_sp_port *mlxsw_sp_port,
369a37a59fSNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc);
37562ffbc4SNogah Frankel int (*get_stats)(struct mlxsw_sp_port *mlxsw_sp_port,
38562ffbc4SNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
39562ffbc4SNogah Frankel struct tc_qopt_offload_stats *stats_ptr);
40562ffbc4SNogah Frankel int (*get_xstats)(struct mlxsw_sp_port *mlxsw_sp_port,
41562ffbc4SNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
42562ffbc4SNogah Frankel void *xstats_ptr);
439cf6c9c7SNogah Frankel void (*clean_stats)(struct mlxsw_sp_port *mlxsw_sp_port,
449cf6c9c7SNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc);
4593d8a4c1SNogah Frankel /* unoffload - to be used for a qdisc that stops being offloaded without
4693d8a4c1SNogah Frankel * being destroyed.
4793d8a4c1SNogah Frankel */
4893d8a4c1SNogah Frankel void (*unoffload)(struct mlxsw_sp_port *mlxsw_sp_port,
4993d8a4c1SNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, void *params);
5051d52ed9SPetr Machata struct mlxsw_sp_qdisc *(*find_class)(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
5151d52ed9SPetr Machata u32 parent);
525cbd9602SPetr Machata unsigned int num_classes;
5376ff72a7SPetr Machata
5476ff72a7SPetr Machata u8 (*get_prio_bitmap)(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
5576ff72a7SPetr Machata struct mlxsw_sp_qdisc *child);
5676ff72a7SPetr Machata int (*get_tclass_num)(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
5776ff72a7SPetr Machata struct mlxsw_sp_qdisc *child);
5876ff72a7SPetr Machata };
5976ff72a7SPetr Machata
6076ff72a7SPetr Machata struct mlxsw_sp_qdisc_ets_band {
6176ff72a7SPetr Machata u8 prio_bitmap;
6276ff72a7SPetr Machata int tclass_num;
6376ff72a7SPetr Machata };
6476ff72a7SPetr Machata
6576ff72a7SPetr Machata struct mlxsw_sp_qdisc_ets_data {
6676ff72a7SPetr Machata struct mlxsw_sp_qdisc_ets_band bands[IEEE_8021QAZ_MAX_TCS];
67562ffbc4SNogah Frankel };
68562ffbc4SNogah Frankel
69371b437aSNogah Frankel struct mlxsw_sp_qdisc {
70371b437aSNogah Frankel u32 handle;
71371b437aSNogah Frankel union {
724d1a4b84SNogah Frankel struct red_stats red;
734d1a4b84SNogah Frankel } xstats_base;
744d1a4b84SNogah Frankel struct mlxsw_sp_qdisc_stats {
75371b437aSNogah Frankel u64 tx_bytes;
76371b437aSNogah Frankel u64 tx_packets;
77371b437aSNogah Frankel u64 drops;
78371b437aSNogah Frankel u64 overlimits;
7993d8a4c1SNogah Frankel u64 backlog;
804d1a4b84SNogah Frankel } stats_base;
81562ffbc4SNogah Frankel
8276ff72a7SPetr Machata union {
8376ff72a7SPetr Machata struct mlxsw_sp_qdisc_ets_data *ets_data;
8476ff72a7SPetr Machata };
8576ff72a7SPetr Machata
86562ffbc4SNogah Frankel struct mlxsw_sp_qdisc_ops *ops;
87b21832b5SPetr Machata struct mlxsw_sp_qdisc *parent;
8851d52ed9SPetr Machata struct mlxsw_sp_qdisc *qdiscs;
8951d52ed9SPetr Machata unsigned int num_classes;
90371b437aSNogah Frankel };
91371b437aSNogah Frankel
92ee88450dSPetr Machata struct mlxsw_sp_qdisc_state {
93ee88450dSPetr Machata struct mlxsw_sp_qdisc root_qdisc;
947bec1a45SPetr Machata
957bec1a45SPetr Machata /* When a PRIO or ETS are added, the invisible FIFOs in their bands are
967bec1a45SPetr Machata * created first. When notifications for these FIFOs arrive, it is not
977bec1a45SPetr Machata * known what qdisc their parent handle refers to. It could be a
987bec1a45SPetr Machata * newly-created PRIO that will replace the currently-offloaded one, or
997bec1a45SPetr Machata * it could be e.g. a RED that will be attached below it.
1007bec1a45SPetr Machata *
1017bec1a45SPetr Machata * As the notifications start to arrive, use them to note what the
1027bec1a45SPetr Machata * future parent handle is, and keep track of which child FIFOs were
1037bec1a45SPetr Machata * seen. Then when the parent is known, retroactively offload those
1047bec1a45SPetr Machata * FIFOs.
1057bec1a45SPetr Machata */
1067bec1a45SPetr Machata u32 future_handle;
1077bec1a45SPetr Machata bool future_fifos[IEEE_8021QAZ_MAX_TCS];
108cff99e20SPetr Machata struct mutex lock; /* Protects qdisc state. */
109ee88450dSPetr Machata };
110ee88450dSPetr Machata
111cba7158fSNogah Frankel static bool
mlxsw_sp_qdisc_compare(struct mlxsw_sp_qdisc * mlxsw_sp_qdisc,u32 handle)112290fe2c5SPetr Machata mlxsw_sp_qdisc_compare(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, u32 handle)
113cba7158fSNogah Frankel {
114290fe2c5SPetr Machata return mlxsw_sp_qdisc->ops && mlxsw_sp_qdisc->handle == handle;
115cba7158fSNogah Frankel }
116cba7158fSNogah Frankel
117eed4baebSNogah Frankel 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)11851d52ed9SPetr Machata mlxsw_sp_qdisc_walk(struct mlxsw_sp_qdisc *qdisc,
11951d52ed9SPetr Machata struct mlxsw_sp_qdisc *(*pre)(struct mlxsw_sp_qdisc *,
12051d52ed9SPetr Machata void *),
12151d52ed9SPetr Machata void *data)
12251d52ed9SPetr Machata {
12351d52ed9SPetr Machata struct mlxsw_sp_qdisc *tmp;
12451d52ed9SPetr Machata unsigned int i;
12551d52ed9SPetr Machata
12651d52ed9SPetr Machata if (pre) {
12751d52ed9SPetr Machata tmp = pre(qdisc, data);
12851d52ed9SPetr Machata if (tmp)
12951d52ed9SPetr Machata return tmp;
13051d52ed9SPetr Machata }
13151d52ed9SPetr Machata
13251d52ed9SPetr Machata if (qdisc->ops) {
13351d52ed9SPetr Machata for (i = 0; i < qdisc->num_classes; i++) {
13451d52ed9SPetr Machata tmp = &qdisc->qdiscs[i];
13551d52ed9SPetr Machata if (qdisc->ops) {
13651d52ed9SPetr Machata tmp = mlxsw_sp_qdisc_walk(tmp, pre, data);
13751d52ed9SPetr Machata if (tmp)
13851d52ed9SPetr Machata return tmp;
13951d52ed9SPetr Machata }
14051d52ed9SPetr Machata }
14151d52ed9SPetr Machata }
14251d52ed9SPetr Machata
14351d52ed9SPetr Machata return NULL;
14451d52ed9SPetr Machata }
14551d52ed9SPetr Machata
14651d52ed9SPetr Machata static struct mlxsw_sp_qdisc *
mlxsw_sp_qdisc_walk_cb_find(struct mlxsw_sp_qdisc * qdisc,void * data)14751d52ed9SPetr Machata mlxsw_sp_qdisc_walk_cb_find(struct mlxsw_sp_qdisc *qdisc, void *data)
14851d52ed9SPetr Machata {
14951d52ed9SPetr Machata u32 parent = *(u32 *)data;
15051d52ed9SPetr Machata
15151d52ed9SPetr Machata if (qdisc->ops && TC_H_MAJ(qdisc->handle) == TC_H_MAJ(parent)) {
15251d52ed9SPetr Machata if (qdisc->ops->find_class)
15351d52ed9SPetr Machata return qdisc->ops->find_class(qdisc, parent);
15451d52ed9SPetr Machata }
15551d52ed9SPetr Machata
15651d52ed9SPetr Machata return NULL;
15751d52ed9SPetr Machata }
15851d52ed9SPetr Machata
15951d52ed9SPetr Machata static struct mlxsw_sp_qdisc *
mlxsw_sp_qdisc_find(struct mlxsw_sp_port * mlxsw_sp_port,u32 parent)160c2792f38SPetr Machata mlxsw_sp_qdisc_find(struct mlxsw_sp_port *mlxsw_sp_port, u32 parent)
161eed4baebSNogah Frankel {
162ee88450dSPetr Machata struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc;
163eed4baebSNogah Frankel
16451d52ed9SPetr Machata if (!qdisc_state)
16551d52ed9SPetr Machata return NULL;
166eed4baebSNogah Frankel if (parent == TC_H_ROOT)
167ee88450dSPetr Machata return &qdisc_state->root_qdisc;
16851d52ed9SPetr Machata return mlxsw_sp_qdisc_walk(&qdisc_state->root_qdisc,
16951d52ed9SPetr Machata mlxsw_sp_qdisc_walk_cb_find, &parent);
17051d52ed9SPetr Machata }
171eed4baebSNogah Frankel
17251d52ed9SPetr Machata static struct mlxsw_sp_qdisc *
mlxsw_sp_qdisc_walk_cb_find_by_handle(struct mlxsw_sp_qdisc * qdisc,void * data)17351d52ed9SPetr Machata mlxsw_sp_qdisc_walk_cb_find_by_handle(struct mlxsw_sp_qdisc *qdisc, void *data)
17451d52ed9SPetr Machata {
17551d52ed9SPetr Machata u32 handle = *(u32 *)data;
17651d52ed9SPetr Machata
17751d52ed9SPetr Machata if (qdisc->ops && qdisc->handle == handle)
17851d52ed9SPetr Machata return qdisc;
17951d52ed9SPetr Machata return NULL;
180eed4baebSNogah Frankel }
181eed4baebSNogah Frankel
18232dc5efcSNogah Frankel static struct mlxsw_sp_qdisc *
mlxsw_sp_qdisc_find_by_handle(struct mlxsw_sp_port * mlxsw_sp_port,u32 handle)18332dc5efcSNogah Frankel mlxsw_sp_qdisc_find_by_handle(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle)
18432dc5efcSNogah Frankel {
185ee88450dSPetr Machata struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc;
18632dc5efcSNogah Frankel
18751d52ed9SPetr Machata if (!qdisc_state)
18832dc5efcSNogah Frankel return NULL;
18951d52ed9SPetr Machata return mlxsw_sp_qdisc_walk(&qdisc_state->root_qdisc,
19051d52ed9SPetr Machata mlxsw_sp_qdisc_walk_cb_find_by_handle,
19151d52ed9SPetr Machata &handle);
19232dc5efcSNogah Frankel }
19332dc5efcSNogah Frankel
194b21832b5SPetr Machata static void
mlxsw_sp_qdisc_reduce_parent_backlog(struct mlxsw_sp_qdisc * mlxsw_sp_qdisc)195b21832b5SPetr Machata mlxsw_sp_qdisc_reduce_parent_backlog(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
196b21832b5SPetr Machata {
197b21832b5SPetr Machata struct mlxsw_sp_qdisc *tmp;
198b21832b5SPetr Machata
199b21832b5SPetr Machata for (tmp = mlxsw_sp_qdisc->parent; tmp; tmp = tmp->parent)
200b21832b5SPetr Machata tmp->stats_base.backlog -= mlxsw_sp_qdisc->stats_base.backlog;
201b21832b5SPetr Machata }
202b21832b5SPetr Machata
mlxsw_sp_qdisc_get_prio_bitmap(struct mlxsw_sp_port * mlxsw_sp_port,struct mlxsw_sp_qdisc * mlxsw_sp_qdisc)20376ff72a7SPetr Machata static u8 mlxsw_sp_qdisc_get_prio_bitmap(struct mlxsw_sp_port *mlxsw_sp_port,
20476ff72a7SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
20576ff72a7SPetr Machata {
20676ff72a7SPetr Machata struct mlxsw_sp_qdisc *parent = mlxsw_sp_qdisc->parent;
20776ff72a7SPetr Machata
20876ff72a7SPetr Machata if (!parent)
20976ff72a7SPetr Machata return 0xff;
2102a18c08dSPetr Machata if (!parent->ops->get_prio_bitmap)
2112a18c08dSPetr Machata return mlxsw_sp_qdisc_get_prio_bitmap(mlxsw_sp_port, parent);
21276ff72a7SPetr Machata return parent->ops->get_prio_bitmap(parent, mlxsw_sp_qdisc);
21376ff72a7SPetr Machata }
21476ff72a7SPetr Machata
21576ff72a7SPetr Machata #define MLXSW_SP_PORT_DEFAULT_TCLASS 0
21676ff72a7SPetr Machata
mlxsw_sp_qdisc_get_tclass_num(struct mlxsw_sp_port * mlxsw_sp_port,struct mlxsw_sp_qdisc * mlxsw_sp_qdisc)21776ff72a7SPetr Machata static int mlxsw_sp_qdisc_get_tclass_num(struct mlxsw_sp_port *mlxsw_sp_port,
21876ff72a7SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
21976ff72a7SPetr Machata {
22076ff72a7SPetr Machata struct mlxsw_sp_qdisc *parent = mlxsw_sp_qdisc->parent;
22176ff72a7SPetr Machata
22276ff72a7SPetr Machata if (!parent)
22376ff72a7SPetr Machata return MLXSW_SP_PORT_DEFAULT_TCLASS;
2242a18c08dSPetr Machata if (!parent->ops->get_tclass_num)
2252a18c08dSPetr Machata return mlxsw_sp_qdisc_get_tclass_num(mlxsw_sp_port, parent);
22676ff72a7SPetr Machata return parent->ops->get_tclass_num(parent, mlxsw_sp_qdisc);
22776ff72a7SPetr Machata }
22876ff72a7SPetr Machata
22996f17e07SNogah Frankel static int
mlxsw_sp_qdisc_destroy(struct mlxsw_sp_port * mlxsw_sp_port,struct mlxsw_sp_qdisc * mlxsw_sp_qdisc)2309a37a59fSNogah Frankel mlxsw_sp_qdisc_destroy(struct mlxsw_sp_port *mlxsw_sp_port,
2319a37a59fSNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
2329a37a59fSNogah Frankel {
233509f04caSPetr Machata struct mlxsw_sp_qdisc *root_qdisc = &mlxsw_sp_port->qdisc->root_qdisc;
234509f04caSPetr Machata int err_hdroom = 0;
2359a37a59fSNogah Frankel int err = 0;
23665626e07SPetr Machata int i;
2379a37a59fSNogah Frankel
2389a37a59fSNogah Frankel if (!mlxsw_sp_qdisc)
2399a37a59fSNogah Frankel return 0;
2409a37a59fSNogah Frankel
241509f04caSPetr Machata if (root_qdisc == mlxsw_sp_qdisc) {
242509f04caSPetr Machata struct mlxsw_sp_hdroom hdroom = *mlxsw_sp_port->hdroom;
243509f04caSPetr Machata
244509f04caSPetr Machata hdroom.mode = MLXSW_SP_HDROOM_MODE_DCB;
245509f04caSPetr Machata mlxsw_sp_hdroom_prios_reset_buf_idx(&hdroom);
246509f04caSPetr Machata mlxsw_sp_hdroom_bufs_reset_lossiness(&hdroom);
247509f04caSPetr Machata mlxsw_sp_hdroom_bufs_reset_sizes(mlxsw_sp_port, &hdroom);
248509f04caSPetr Machata err_hdroom = mlxsw_sp_hdroom_configure(mlxsw_sp_port, &hdroom);
249509f04caSPetr Machata }
250509f04caSPetr Machata
251b21832b5SPetr Machata if (!mlxsw_sp_qdisc->ops)
252b21832b5SPetr Machata return 0;
253b21832b5SPetr Machata
25465626e07SPetr Machata for (i = 0; i < mlxsw_sp_qdisc->num_classes; i++)
25565626e07SPetr Machata mlxsw_sp_qdisc_destroy(mlxsw_sp_port,
25665626e07SPetr Machata &mlxsw_sp_qdisc->qdiscs[i]);
257b21832b5SPetr Machata mlxsw_sp_qdisc_reduce_parent_backlog(mlxsw_sp_qdisc);
258b21832b5SPetr Machata if (mlxsw_sp_qdisc->ops->destroy)
2599a37a59fSNogah Frankel err = mlxsw_sp_qdisc->ops->destroy(mlxsw_sp_port,
2609a37a59fSNogah Frankel mlxsw_sp_qdisc);
2615cbd9602SPetr Machata if (mlxsw_sp_qdisc->ops->clean_stats)
2625cbd9602SPetr Machata mlxsw_sp_qdisc->ops->clean_stats(mlxsw_sp_port, mlxsw_sp_qdisc);
2639a37a59fSNogah Frankel
2649a37a59fSNogah Frankel mlxsw_sp_qdisc->handle = TC_H_UNSPEC;
2659a37a59fSNogah Frankel mlxsw_sp_qdisc->ops = NULL;
2665cbd9602SPetr Machata mlxsw_sp_qdisc->num_classes = 0;
2675cbd9602SPetr Machata kfree(mlxsw_sp_qdisc->qdiscs);
2685cbd9602SPetr Machata mlxsw_sp_qdisc->qdiscs = NULL;
269509f04caSPetr Machata return err_hdroom ?: err;
2709a37a59fSNogah Frankel }
2719a37a59fSNogah Frankel
272c2792f38SPetr Machata struct mlxsw_sp_qdisc_tree_validate {
273c2792f38SPetr Machata bool forbid_ets;
274*48e4d00bSPetr Machata bool forbid_root_tbf;
275c2792f38SPetr Machata bool forbid_tbf;
276c2792f38SPetr Machata bool forbid_red;
277c2792f38SPetr Machata };
278c2792f38SPetr Machata
279c2792f38SPetr Machata static int
280c2792f38SPetr Machata __mlxsw_sp_qdisc_tree_validate(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
281c2792f38SPetr Machata struct mlxsw_sp_qdisc_tree_validate validate);
282c2792f38SPetr Machata
283c2792f38SPetr Machata static int
mlxsw_sp_qdisc_tree_validate_children(struct mlxsw_sp_qdisc * mlxsw_sp_qdisc,struct mlxsw_sp_qdisc_tree_validate validate)284c2792f38SPetr Machata mlxsw_sp_qdisc_tree_validate_children(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
285c2792f38SPetr Machata struct mlxsw_sp_qdisc_tree_validate validate)
286c2792f38SPetr Machata {
287c2792f38SPetr Machata unsigned int i;
288c2792f38SPetr Machata int err;
289c2792f38SPetr Machata
290c2792f38SPetr Machata for (i = 0; i < mlxsw_sp_qdisc->num_classes; i++) {
291c2792f38SPetr Machata err = __mlxsw_sp_qdisc_tree_validate(&mlxsw_sp_qdisc->qdiscs[i],
292c2792f38SPetr Machata validate);
293c2792f38SPetr Machata if (err)
294c2792f38SPetr Machata return err;
295c2792f38SPetr Machata }
296c2792f38SPetr Machata
297c2792f38SPetr Machata return 0;
298c2792f38SPetr Machata }
299c2792f38SPetr Machata
300c2792f38SPetr Machata static int
__mlxsw_sp_qdisc_tree_validate(struct mlxsw_sp_qdisc * mlxsw_sp_qdisc,struct mlxsw_sp_qdisc_tree_validate validate)301c2792f38SPetr Machata __mlxsw_sp_qdisc_tree_validate(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
302c2792f38SPetr Machata struct mlxsw_sp_qdisc_tree_validate validate)
303c2792f38SPetr Machata {
304c2792f38SPetr Machata if (!mlxsw_sp_qdisc->ops)
305c2792f38SPetr Machata return 0;
306c2792f38SPetr Machata
307c2792f38SPetr Machata switch (mlxsw_sp_qdisc->ops->type) {
308c2792f38SPetr Machata case MLXSW_SP_QDISC_FIFO:
309c2792f38SPetr Machata break;
310c2792f38SPetr Machata case MLXSW_SP_QDISC_RED:
311c2792f38SPetr Machata if (validate.forbid_red)
312c2792f38SPetr Machata return -EINVAL;
313c2792f38SPetr Machata validate.forbid_red = true;
314*48e4d00bSPetr Machata validate.forbid_root_tbf = true;
315c2792f38SPetr Machata validate.forbid_ets = true;
316c2792f38SPetr Machata break;
317c2792f38SPetr Machata case MLXSW_SP_QDISC_TBF:
318*48e4d00bSPetr Machata if (validate.forbid_root_tbf) {
319c2792f38SPetr Machata if (validate.forbid_tbf)
320c2792f38SPetr Machata return -EINVAL;
321*48e4d00bSPetr Machata /* This is a TC TBF. */
322c2792f38SPetr Machata validate.forbid_tbf = true;
323c2792f38SPetr Machata validate.forbid_ets = true;
324*48e4d00bSPetr Machata } else {
325*48e4d00bSPetr Machata /* This is root TBF. */
326*48e4d00bSPetr Machata validate.forbid_root_tbf = true;
327*48e4d00bSPetr Machata }
328c2792f38SPetr Machata break;
329c2792f38SPetr Machata case MLXSW_SP_QDISC_PRIO:
330c2792f38SPetr Machata case MLXSW_SP_QDISC_ETS:
331c2792f38SPetr Machata if (validate.forbid_ets)
332c2792f38SPetr Machata return -EINVAL;
333*48e4d00bSPetr Machata validate.forbid_root_tbf = true;
334c2792f38SPetr Machata validate.forbid_ets = true;
335c2792f38SPetr Machata break;
336c2792f38SPetr Machata default:
337c2792f38SPetr Machata WARN_ON(1);
338c2792f38SPetr Machata return -EINVAL;
339c2792f38SPetr Machata }
340c2792f38SPetr Machata
341c2792f38SPetr Machata return mlxsw_sp_qdisc_tree_validate_children(mlxsw_sp_qdisc, validate);
342c2792f38SPetr Machata }
343c2792f38SPetr Machata
mlxsw_sp_qdisc_tree_validate(struct mlxsw_sp_port * mlxsw_sp_port)344c2792f38SPetr Machata static int mlxsw_sp_qdisc_tree_validate(struct mlxsw_sp_port *mlxsw_sp_port)
345c2792f38SPetr Machata {
346c2792f38SPetr Machata struct mlxsw_sp_qdisc_tree_validate validate = {};
347c2792f38SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc;
348c2792f38SPetr Machata
349c2792f38SPetr Machata mlxsw_sp_qdisc = &mlxsw_sp_port->qdisc->root_qdisc;
350c2792f38SPetr Machata return __mlxsw_sp_qdisc_tree_validate(mlxsw_sp_qdisc, validate);
351c2792f38SPetr Machata }
352c2792f38SPetr Machata
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)3535cbd9602SPetr Machata static int mlxsw_sp_qdisc_create(struct mlxsw_sp_port *mlxsw_sp_port,
3545cbd9602SPetr Machata u32 handle,
3559cf6c9c7SNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
3569cf6c9c7SNogah Frankel struct mlxsw_sp_qdisc_ops *ops, void *params)
3579cf6c9c7SNogah Frankel {
358509f04caSPetr Machata struct mlxsw_sp_qdisc *root_qdisc = &mlxsw_sp_port->qdisc->root_qdisc;
359509f04caSPetr Machata struct mlxsw_sp_hdroom orig_hdroom;
3605cbd9602SPetr Machata unsigned int i;
3619cf6c9c7SNogah Frankel int err;
3629cf6c9c7SNogah Frankel
3635cbd9602SPetr Machata err = ops->check_params(mlxsw_sp_port, params);
3645cbd9602SPetr Machata if (err)
3655cbd9602SPetr Machata return err;
3665cbd9602SPetr Machata
3675cbd9602SPetr Machata if (ops->num_classes) {
3685cbd9602SPetr Machata mlxsw_sp_qdisc->qdiscs = kcalloc(ops->num_classes,
3695cbd9602SPetr Machata sizeof(*mlxsw_sp_qdisc->qdiscs),
3705cbd9602SPetr Machata GFP_KERNEL);
3715cbd9602SPetr Machata if (!mlxsw_sp_qdisc->qdiscs)
3725cbd9602SPetr Machata return -ENOMEM;
3735cbd9602SPetr Machata
3745cbd9602SPetr Machata for (i = 0; i < ops->num_classes; i++)
3755cbd9602SPetr Machata mlxsw_sp_qdisc->qdiscs[i].parent = mlxsw_sp_qdisc;
3765cbd9602SPetr Machata }
377509f04caSPetr Machata
378509f04caSPetr Machata orig_hdroom = *mlxsw_sp_port->hdroom;
379509f04caSPetr Machata if (root_qdisc == mlxsw_sp_qdisc) {
380509f04caSPetr Machata struct mlxsw_sp_hdroom hdroom = orig_hdroom;
381509f04caSPetr Machata
382509f04caSPetr Machata hdroom.mode = MLXSW_SP_HDROOM_MODE_TC;
383509f04caSPetr Machata mlxsw_sp_hdroom_prios_reset_buf_idx(&hdroom);
384509f04caSPetr Machata mlxsw_sp_hdroom_bufs_reset_lossiness(&hdroom);
385509f04caSPetr Machata mlxsw_sp_hdroom_bufs_reset_sizes(mlxsw_sp_port, &hdroom);
386509f04caSPetr Machata
387509f04caSPetr Machata err = mlxsw_sp_hdroom_configure(mlxsw_sp_port, &hdroom);
388509f04caSPetr Machata if (err)
389509f04caSPetr Machata goto err_hdroom_configure;
390509f04caSPetr Machata }
391509f04caSPetr Machata
3925cbd9602SPetr Machata mlxsw_sp_qdisc->num_classes = ops->num_classes;
3935cbd9602SPetr Machata mlxsw_sp_qdisc->ops = ops;
3945cbd9602SPetr Machata mlxsw_sp_qdisc->handle = handle;
395c2792f38SPetr Machata err = mlxsw_sp_qdisc_tree_validate(mlxsw_sp_port);
396c2792f38SPetr Machata if (err)
397c2792f38SPetr Machata goto err_replace;
398c2792f38SPetr Machata
3995cbd9602SPetr Machata err = ops->replace(mlxsw_sp_port, handle, mlxsw_sp_qdisc, params);
4005cbd9602SPetr Machata if (err)
4015cbd9602SPetr Machata goto err_replace;
4025cbd9602SPetr Machata
4035cbd9602SPetr Machata return 0;
4045cbd9602SPetr Machata
4055cbd9602SPetr Machata err_replace:
4065cbd9602SPetr Machata mlxsw_sp_qdisc->handle = TC_H_UNSPEC;
4075cbd9602SPetr Machata mlxsw_sp_qdisc->ops = NULL;
4085cbd9602SPetr Machata mlxsw_sp_qdisc->num_classes = 0;
4095cbd9602SPetr Machata mlxsw_sp_hdroom_configure(mlxsw_sp_port, &orig_hdroom);
4105cbd9602SPetr Machata err_hdroom_configure:
4115cbd9602SPetr Machata kfree(mlxsw_sp_qdisc->qdiscs);
4125cbd9602SPetr Machata mlxsw_sp_qdisc->qdiscs = NULL;
4135cbd9602SPetr Machata return err;
4145cbd9602SPetr Machata }
4155cbd9602SPetr Machata
4165cbd9602SPetr Machata static int
mlxsw_sp_qdisc_change(struct mlxsw_sp_port * mlxsw_sp_port,u32 handle,struct mlxsw_sp_qdisc * mlxsw_sp_qdisc,void * params)4175cbd9602SPetr Machata mlxsw_sp_qdisc_change(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
4185cbd9602SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, void *params)
4195cbd9602SPetr Machata {
4205cbd9602SPetr Machata struct mlxsw_sp_qdisc_ops *ops = mlxsw_sp_qdisc->ops;
4215cbd9602SPetr Machata int err;
4225cbd9602SPetr Machata
42317c0e6d1SPetr Machata err = ops->check_params(mlxsw_sp_port, params);
4249cf6c9c7SNogah Frankel if (err)
4255cbd9602SPetr Machata goto unoffload;
4269cf6c9c7SNogah Frankel
427c4e372e2SPetr Machata err = ops->replace(mlxsw_sp_port, handle, mlxsw_sp_qdisc, params);
4289cf6c9c7SNogah Frankel if (err)
4295cbd9602SPetr Machata goto unoffload;
4309cf6c9c7SNogah Frankel
4317bec1a45SPetr Machata /* Check if the Qdisc changed. That includes a situation where an
4327bec1a45SPetr Machata * invisible Qdisc replaces another one, or is being added for the
4337bec1a45SPetr Machata * first time.
4347bec1a45SPetr Machata */
4355cbd9602SPetr Machata if (mlxsw_sp_qdisc->handle != handle) {
4369cf6c9c7SNogah Frankel if (ops->clean_stats)
4379cf6c9c7SNogah Frankel ops->clean_stats(mlxsw_sp_port, mlxsw_sp_qdisc);
4389cf6c9c7SNogah Frankel }
4399cf6c9c7SNogah Frankel
4409cf6c9c7SNogah Frankel mlxsw_sp_qdisc->handle = handle;
4419cf6c9c7SNogah Frankel return 0;
4429cf6c9c7SNogah Frankel
4435cbd9602SPetr Machata unoffload:
4445cbd9602SPetr Machata if (ops->unoffload)
44593d8a4c1SNogah Frankel ops->unoffload(mlxsw_sp_port, mlxsw_sp_qdisc, params);
44693d8a4c1SNogah Frankel
4479cf6c9c7SNogah Frankel mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
4489cf6c9c7SNogah Frankel return err;
4499cf6c9c7SNogah Frankel }
4509cf6c9c7SNogah Frankel
4519cf6c9c7SNogah Frankel 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)4525cbd9602SPetr Machata mlxsw_sp_qdisc_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
4535cbd9602SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
4545cbd9602SPetr Machata struct mlxsw_sp_qdisc_ops *ops, void *params)
4555cbd9602SPetr Machata {
4565cbd9602SPetr Machata if (mlxsw_sp_qdisc->ops && mlxsw_sp_qdisc->ops->type != ops->type)
4575cbd9602SPetr Machata /* In case this location contained a different qdisc of the
4585cbd9602SPetr Machata * same type we can override the old qdisc configuration.
4595cbd9602SPetr Machata * Otherwise, we need to remove the old qdisc before setting the
4605cbd9602SPetr Machata * new one.
4615cbd9602SPetr Machata */
4625cbd9602SPetr Machata mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
4635cbd9602SPetr Machata
4645cbd9602SPetr Machata if (!mlxsw_sp_qdisc->ops)
4655cbd9602SPetr Machata return mlxsw_sp_qdisc_create(mlxsw_sp_port, handle,
4665cbd9602SPetr Machata mlxsw_sp_qdisc, ops, params);
4675cbd9602SPetr Machata else
4685cbd9602SPetr Machata return mlxsw_sp_qdisc_change(mlxsw_sp_port, handle,
4695cbd9602SPetr Machata mlxsw_sp_qdisc, params);
4705cbd9602SPetr Machata }
4715cbd9602SPetr Machata
4725cbd9602SPetr Machata 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)473562ffbc4SNogah Frankel mlxsw_sp_qdisc_get_stats(struct mlxsw_sp_port *mlxsw_sp_port,
474562ffbc4SNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
475562ffbc4SNogah Frankel struct tc_qopt_offload_stats *stats_ptr)
476562ffbc4SNogah Frankel {
477562ffbc4SNogah Frankel if (mlxsw_sp_qdisc && mlxsw_sp_qdisc->ops &&
478562ffbc4SNogah Frankel mlxsw_sp_qdisc->ops->get_stats)
479562ffbc4SNogah Frankel return mlxsw_sp_qdisc->ops->get_stats(mlxsw_sp_port,
480562ffbc4SNogah Frankel mlxsw_sp_qdisc,
481562ffbc4SNogah Frankel stats_ptr);
482562ffbc4SNogah Frankel
483562ffbc4SNogah Frankel return -EOPNOTSUPP;
484562ffbc4SNogah Frankel }
485562ffbc4SNogah Frankel
486562ffbc4SNogah Frankel static int
mlxsw_sp_qdisc_get_xstats(struct mlxsw_sp_port * mlxsw_sp_port,struct mlxsw_sp_qdisc * mlxsw_sp_qdisc,void * xstats_ptr)487562ffbc4SNogah Frankel mlxsw_sp_qdisc_get_xstats(struct mlxsw_sp_port *mlxsw_sp_port,
488562ffbc4SNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
489562ffbc4SNogah Frankel void *xstats_ptr)
490562ffbc4SNogah Frankel {
491562ffbc4SNogah Frankel if (mlxsw_sp_qdisc && mlxsw_sp_qdisc->ops &&
492562ffbc4SNogah Frankel mlxsw_sp_qdisc->ops->get_xstats)
493562ffbc4SNogah Frankel return mlxsw_sp_qdisc->ops->get_xstats(mlxsw_sp_port,
494562ffbc4SNogah Frankel mlxsw_sp_qdisc,
495562ffbc4SNogah Frankel xstats_ptr);
496562ffbc4SNogah Frankel
497562ffbc4SNogah Frankel return -EOPNOTSUPP;
498562ffbc4SNogah Frankel }
499562ffbc4SNogah Frankel
50085005b82SPetr Machata static u64
mlxsw_sp_xstats_backlog(struct mlxsw_sp_port_xstats * xstats,int tclass_num)50185005b82SPetr Machata mlxsw_sp_xstats_backlog(struct mlxsw_sp_port_xstats *xstats, int tclass_num)
50285005b82SPetr Machata {
50385005b82SPetr Machata return xstats->backlog[tclass_num] +
50485005b82SPetr Machata xstats->backlog[tclass_num + 8];
50585005b82SPetr Machata }
50685005b82SPetr Machata
50785005b82SPetr Machata static u64
mlxsw_sp_xstats_tail_drop(struct mlxsw_sp_port_xstats * xstats,int tclass_num)50885005b82SPetr Machata mlxsw_sp_xstats_tail_drop(struct mlxsw_sp_port_xstats *xstats, int tclass_num)
50985005b82SPetr Machata {
51085005b82SPetr Machata return xstats->tail_drop[tclass_num] +
51185005b82SPetr Machata xstats->tail_drop[tclass_num + 8];
51285005b82SPetr Machata }
51385005b82SPetr Machata
51404cc0bf5SNogah Frankel static void
mlxsw_sp_qdisc_bstats_per_priority_get(struct mlxsw_sp_port_xstats * xstats,u8 prio_bitmap,u64 * tx_packets,u64 * tx_bytes)51504cc0bf5SNogah Frankel mlxsw_sp_qdisc_bstats_per_priority_get(struct mlxsw_sp_port_xstats *xstats,
51604cc0bf5SNogah Frankel u8 prio_bitmap, u64 *tx_packets,
51704cc0bf5SNogah Frankel u64 *tx_bytes)
51804cc0bf5SNogah Frankel {
51904cc0bf5SNogah Frankel int i;
52004cc0bf5SNogah Frankel
52104cc0bf5SNogah Frankel *tx_packets = 0;
52204cc0bf5SNogah Frankel *tx_bytes = 0;
52304cc0bf5SNogah Frankel for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
52404cc0bf5SNogah Frankel if (prio_bitmap & BIT(i)) {
52504cc0bf5SNogah Frankel *tx_packets += xstats->tx_packets[i];
52604cc0bf5SNogah Frankel *tx_bytes += xstats->tx_bytes[i];
52704cc0bf5SNogah Frankel }
52804cc0bf5SNogah Frankel }
52904cc0bf5SNogah Frankel }
53004cc0bf5SNogah Frankel
531cf9af379SPetr Machata 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)532cf9af379SPetr Machata mlxsw_sp_qdisc_collect_tc_stats(struct mlxsw_sp_port *mlxsw_sp_port,
533cf9af379SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
534cf9af379SPetr Machata u64 *p_tx_bytes, u64 *p_tx_packets,
535cf9af379SPetr Machata u64 *p_drops, u64 *p_backlog)
536cf9af379SPetr Machata {
537cf9af379SPetr Machata struct mlxsw_sp_port_xstats *xstats;
538cf9af379SPetr Machata u64 tx_bytes, tx_packets;
53976ff72a7SPetr Machata u8 prio_bitmap;
54076ff72a7SPetr Machata int tclass_num;
541cf9af379SPetr Machata
54276ff72a7SPetr Machata prio_bitmap = mlxsw_sp_qdisc_get_prio_bitmap(mlxsw_sp_port,
54376ff72a7SPetr Machata mlxsw_sp_qdisc);
54476ff72a7SPetr Machata tclass_num = mlxsw_sp_qdisc_get_tclass_num(mlxsw_sp_port,
54576ff72a7SPetr Machata mlxsw_sp_qdisc);
546cf9af379SPetr Machata xstats = &mlxsw_sp_port->periodic_hw_stats.xstats;
54776ff72a7SPetr Machata mlxsw_sp_qdisc_bstats_per_priority_get(xstats, prio_bitmap,
548cf9af379SPetr Machata &tx_packets, &tx_bytes);
549cf9af379SPetr Machata
550cf9af379SPetr Machata *p_tx_packets += tx_packets;
551cf9af379SPetr Machata *p_tx_bytes += tx_bytes;
552cf9af379SPetr Machata *p_drops += xstats->wred_drop[tclass_num] +
553cf9af379SPetr Machata mlxsw_sp_xstats_tail_drop(xstats, tclass_num);
554cf9af379SPetr Machata *p_backlog += mlxsw_sp_xstats_backlog(xstats, tclass_num);
555cf9af379SPetr Machata }
556cf9af379SPetr Machata
557cf9af379SPetr Machata 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)558cf9af379SPetr Machata mlxsw_sp_qdisc_update_stats(struct mlxsw_sp *mlxsw_sp,
559cf9af379SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
560cf9af379SPetr Machata u64 tx_bytes, u64 tx_packets,
561cf9af379SPetr Machata u64 drops, u64 backlog,
562cf9af379SPetr Machata struct tc_qopt_offload_stats *stats_ptr)
563cf9af379SPetr Machata {
564cf9af379SPetr Machata struct mlxsw_sp_qdisc_stats *stats_base = &mlxsw_sp_qdisc->stats_base;
565cf9af379SPetr Machata
566cf9af379SPetr Machata tx_bytes -= stats_base->tx_bytes;
567cf9af379SPetr Machata tx_packets -= stats_base->tx_packets;
568cf9af379SPetr Machata drops -= stats_base->drops;
569cf9af379SPetr Machata backlog -= stats_base->backlog;
570cf9af379SPetr Machata
571cf9af379SPetr Machata _bstats_update(stats_ptr->bstats, tx_bytes, tx_packets);
572cf9af379SPetr Machata stats_ptr->qstats->drops += drops;
573cf9af379SPetr Machata stats_ptr->qstats->backlog += mlxsw_sp_cells_bytes(mlxsw_sp, backlog);
574cf9af379SPetr Machata
575cf9af379SPetr Machata stats_base->backlog += backlog;
576cf9af379SPetr Machata stats_base->drops += drops;
577cf9af379SPetr Machata stats_base->tx_bytes += tx_bytes;
578cf9af379SPetr Machata stats_base->tx_packets += tx_packets;
579cf9af379SPetr Machata }
580cf9af379SPetr Machata
5813d0d5921SPetr Machata 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)5823d0d5921SPetr Machata mlxsw_sp_qdisc_get_tc_stats(struct mlxsw_sp_port *mlxsw_sp_port,
5833d0d5921SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
5843d0d5921SPetr Machata struct tc_qopt_offload_stats *stats_ptr)
5853d0d5921SPetr Machata {
5863d0d5921SPetr Machata u64 tx_packets = 0;
5873d0d5921SPetr Machata u64 tx_bytes = 0;
5883d0d5921SPetr Machata u64 backlog = 0;
5893d0d5921SPetr Machata u64 drops = 0;
5903d0d5921SPetr Machata
5913d0d5921SPetr Machata mlxsw_sp_qdisc_collect_tc_stats(mlxsw_sp_port, mlxsw_sp_qdisc,
5923d0d5921SPetr Machata &tx_bytes, &tx_packets,
5933d0d5921SPetr Machata &drops, &backlog);
5943d0d5921SPetr Machata mlxsw_sp_qdisc_update_stats(mlxsw_sp_port->mlxsw_sp, mlxsw_sp_qdisc,
5953d0d5921SPetr Machata tx_bytes, tx_packets, drops, backlog,
5963d0d5921SPetr Machata stats_ptr);
5973d0d5921SPetr Machata }
5983d0d5921SPetr Machata
599562ffbc4SNogah Frankel 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)60096f17e07SNogah Frankel mlxsw_sp_tclass_congestion_enable(struct mlxsw_sp_port *mlxsw_sp_port,
60196f17e07SNogah Frankel int tclass_num, u32 min, u32 max,
6028040c96bSPetr Machata u32 probability, bool is_wred, bool is_ecn)
60396f17e07SNogah Frankel {
604db84924cSJiri Pirko char cwtpm_cmd[MLXSW_REG_CWTPM_LEN];
605db84924cSJiri Pirko char cwtp_cmd[MLXSW_REG_CWTP_LEN];
60696f17e07SNogah Frankel struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
60796f17e07SNogah Frankel int err;
60896f17e07SNogah Frankel
60996f17e07SNogah Frankel mlxsw_reg_cwtp_pack(cwtp_cmd, mlxsw_sp_port->local_port, tclass_num);
61096f17e07SNogah Frankel mlxsw_reg_cwtp_profile_pack(cwtp_cmd, MLXSW_REG_CWTP_DEFAULT_PROFILE,
61196f17e07SNogah Frankel roundup(min, MLXSW_REG_CWTP_MIN_VALUE),
61296f17e07SNogah Frankel roundup(max, MLXSW_REG_CWTP_MIN_VALUE),
61396f17e07SNogah Frankel probability);
61496f17e07SNogah Frankel
61596f17e07SNogah Frankel err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(cwtp), cwtp_cmd);
61696f17e07SNogah Frankel if (err)
61796f17e07SNogah Frankel return err;
61896f17e07SNogah Frankel
619db84924cSJiri Pirko mlxsw_reg_cwtpm_pack(cwtpm_cmd, mlxsw_sp_port->local_port, tclass_num,
6208040c96bSPetr Machata MLXSW_REG_CWTP_DEFAULT_PROFILE, is_wred, is_ecn);
62196f17e07SNogah Frankel
622db84924cSJiri Pirko return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(cwtpm), cwtpm_cmd);
62396f17e07SNogah Frankel }
62496f17e07SNogah Frankel
62596f17e07SNogah Frankel static int
mlxsw_sp_tclass_congestion_disable(struct mlxsw_sp_port * mlxsw_sp_port,int tclass_num)62696f17e07SNogah Frankel mlxsw_sp_tclass_congestion_disable(struct mlxsw_sp_port *mlxsw_sp_port,
62796f17e07SNogah Frankel int tclass_num)
62896f17e07SNogah Frankel {
62996f17e07SNogah Frankel struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
63096f17e07SNogah Frankel char cwtpm_cmd[MLXSW_REG_CWTPM_LEN];
63196f17e07SNogah Frankel
63296f17e07SNogah Frankel mlxsw_reg_cwtpm_pack(cwtpm_cmd, mlxsw_sp_port->local_port, tclass_num,
63396f17e07SNogah Frankel MLXSW_REG_CWTPM_RESET_PROFILE, false, false);
63496f17e07SNogah Frankel return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(cwtpm), cwtpm_cmd);
63596f17e07SNogah Frankel }
63696f17e07SNogah Frankel
637861fb829SNogah Frankel static void
mlxsw_sp_setup_tc_qdisc_red_clean_stats(struct mlxsw_sp_port * mlxsw_sp_port,struct mlxsw_sp_qdisc * mlxsw_sp_qdisc)638c2ed6db7SNogah Frankel mlxsw_sp_setup_tc_qdisc_red_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port,
639d56c8955SNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
640861fb829SNogah Frankel {
6414d1a4b84SNogah Frankel struct mlxsw_sp_qdisc_stats *stats_base;
642861fb829SNogah Frankel struct mlxsw_sp_port_xstats *xstats;
6434d1a4b84SNogah Frankel struct red_stats *red_base;
64476ff72a7SPetr Machata u8 prio_bitmap;
64576ff72a7SPetr Machata int tclass_num;
646861fb829SNogah Frankel
64776ff72a7SPetr Machata prio_bitmap = mlxsw_sp_qdisc_get_prio_bitmap(mlxsw_sp_port,
64876ff72a7SPetr Machata mlxsw_sp_qdisc);
64976ff72a7SPetr Machata tclass_num = mlxsw_sp_qdisc_get_tclass_num(mlxsw_sp_port,
65076ff72a7SPetr Machata mlxsw_sp_qdisc);
651861fb829SNogah Frankel xstats = &mlxsw_sp_port->periodic_hw_stats.xstats;
6524d1a4b84SNogah Frankel stats_base = &mlxsw_sp_qdisc->stats_base;
653c2ed6db7SNogah Frankel red_base = &mlxsw_sp_qdisc->xstats_base.red;
6543670756fSNogah Frankel
65576ff72a7SPetr Machata mlxsw_sp_qdisc_bstats_per_priority_get(xstats, prio_bitmap,
65604cc0bf5SNogah Frankel &stats_base->tx_packets,
65704cc0bf5SNogah Frankel &stats_base->tx_bytes);
65815be36b8SPetr Machata red_base->prob_mark = xstats->tc_ecn[tclass_num];
6594d1a4b84SNogah Frankel red_base->prob_drop = xstats->wred_drop[tclass_num];
66085005b82SPetr Machata red_base->pdrop = mlxsw_sp_xstats_tail_drop(xstats, tclass_num);
6613670756fSNogah Frankel
662c2ed6db7SNogah Frankel stats_base->overlimits = red_base->prob_drop + red_base->prob_mark;
6634d1a4b84SNogah Frankel stats_base->drops = red_base->prob_drop + red_base->pdrop;
664416ef9b1SJakub Kicinski
665416ef9b1SJakub Kicinski stats_base->backlog = 0;
666861fb829SNogah Frankel }
667861fb829SNogah Frankel
66896f17e07SNogah Frankel static int
mlxsw_sp_qdisc_red_destroy(struct mlxsw_sp_port * mlxsw_sp_port,struct mlxsw_sp_qdisc * mlxsw_sp_qdisc)669cba7158fSNogah Frankel mlxsw_sp_qdisc_red_destroy(struct mlxsw_sp_port *mlxsw_sp_port,
670d56c8955SNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
67196f17e07SNogah Frankel {
67276ff72a7SPetr Machata int tclass_num = mlxsw_sp_qdisc_get_tclass_num(mlxsw_sp_port,
67376ff72a7SPetr Machata mlxsw_sp_qdisc);
67476ff72a7SPetr Machata
67576ff72a7SPetr Machata return mlxsw_sp_tclass_congestion_disable(mlxsw_sp_port, tclass_num);
67696f17e07SNogah Frankel }
67796f17e07SNogah Frankel
67896f17e07SNogah Frankel static int
mlxsw_sp_qdisc_red_check_params(struct mlxsw_sp_port * mlxsw_sp_port,void * params)6799cf6c9c7SNogah Frankel mlxsw_sp_qdisc_red_check_params(struct mlxsw_sp_port *mlxsw_sp_port,
6809cf6c9c7SNogah Frankel void *params)
68196f17e07SNogah Frankel {
68296f17e07SNogah Frankel struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
6839cf6c9c7SNogah Frankel struct tc_red_qopt_offload_params *p = params;
68496f17e07SNogah Frankel
68596f17e07SNogah Frankel if (p->min > p->max) {
68696f17e07SNogah Frankel dev_err(mlxsw_sp->bus_info->dev,
68796f17e07SNogah Frankel "spectrum: RED: min %u is bigger then max %u\n", p->min,
68896f17e07SNogah Frankel p->max);
6899cf6c9c7SNogah Frankel return -EINVAL;
69096f17e07SNogah Frankel }
691914c4fc1SPetr Machata if (p->max > MLXSW_CORE_RES_GET(mlxsw_sp->core,
692914c4fc1SPetr Machata GUARANTEED_SHARED_BUFFER)) {
69396f17e07SNogah Frankel dev_err(mlxsw_sp->bus_info->dev,
69496f17e07SNogah Frankel "spectrum: RED: max value %u is too big\n", p->max);
6959cf6c9c7SNogah Frankel return -EINVAL;
69696f17e07SNogah Frankel }
69796f17e07SNogah Frankel if (p->min == 0 || p->max == 0) {
69896f17e07SNogah Frankel dev_err(mlxsw_sp->bus_info->dev,
69996f17e07SNogah Frankel "spectrum: RED: 0 value is illegal for min and max\n");
7009cf6c9c7SNogah Frankel return -EINVAL;
70196f17e07SNogah Frankel }
7029cf6c9c7SNogah Frankel return 0;
7039cf6c9c7SNogah Frankel }
7049cf6c9c7SNogah Frankel
7059cf6c9c7SNogah Frankel static int
7062a18c08dSPetr Machata mlxsw_sp_qdisc_future_fifo_replace(struct mlxsw_sp_port *mlxsw_sp_port,
7072a18c08dSPetr Machata u32 handle, unsigned int band,
7082a18c08dSPetr Machata struct mlxsw_sp_qdisc *child_qdisc);
7092a18c08dSPetr Machata static void
7102a18c08dSPetr Machata mlxsw_sp_qdisc_future_fifos_init(struct mlxsw_sp_port *mlxsw_sp_port,
7112a18c08dSPetr Machata u32 handle);
7122a18c08dSPetr Machata
7132a18c08dSPetr Machata 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)714c4e372e2SPetr Machata mlxsw_sp_qdisc_red_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
7159cf6c9c7SNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
7169cf6c9c7SNogah Frankel void *params)
7179cf6c9c7SNogah Frankel {
7189cf6c9c7SNogah Frankel struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
7199cf6c9c7SNogah Frankel struct tc_red_qopt_offload_params *p = params;
72076ff72a7SPetr Machata int tclass_num;
7219cf6c9c7SNogah Frankel u32 min, max;
7229cf6c9c7SNogah Frankel u64 prob;
7232a18c08dSPetr Machata int err;
7242a18c08dSPetr Machata
7252a18c08dSPetr Machata err = mlxsw_sp_qdisc_future_fifo_replace(mlxsw_sp_port, handle, 0,
7262a18c08dSPetr Machata &mlxsw_sp_qdisc->qdiscs[0]);
7272a18c08dSPetr Machata if (err)
7282a18c08dSPetr Machata return err;
7292a18c08dSPetr Machata mlxsw_sp_qdisc_future_fifos_init(mlxsw_sp_port, TC_H_UNSPEC);
73096f17e07SNogah Frankel
73176ff72a7SPetr Machata tclass_num = mlxsw_sp_qdisc_get_tclass_num(mlxsw_sp_port,
73276ff72a7SPetr Machata mlxsw_sp_qdisc);
73376ff72a7SPetr Machata
73496f17e07SNogah Frankel /* calculate probability in percentage */
73596f17e07SNogah Frankel prob = p->probability;
73696f17e07SNogah Frankel prob *= 100;
73796f17e07SNogah Frankel prob = DIV_ROUND_UP(prob, 1 << 16);
73896f17e07SNogah Frankel prob = DIV_ROUND_UP(prob, 1 << 16);
73996f17e07SNogah Frankel min = mlxsw_sp_bytes_cells(mlxsw_sp, p->min);
74096f17e07SNogah Frankel max = mlxsw_sp_bytes_cells(mlxsw_sp, p->max);
7418040c96bSPetr Machata return mlxsw_sp_tclass_congestion_enable(mlxsw_sp_port, tclass_num,
7428040c96bSPetr Machata min, max, prob,
7438040c96bSPetr Machata !p->is_nodrop, p->is_ecn);
74496f17e07SNogah Frankel }
74596f17e07SNogah Frankel
746416ef9b1SJakub Kicinski 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)747be1d5a8aSPetr Machata mlxsw_sp_qdisc_leaf_unoffload(struct mlxsw_sp_port *mlxsw_sp_port,
748be1d5a8aSPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
749be1d5a8aSPetr Machata struct gnet_stats_queue *qstats)
750be1d5a8aSPetr Machata {
751be1d5a8aSPetr Machata u64 backlog;
752be1d5a8aSPetr Machata
753be1d5a8aSPetr Machata backlog = mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp,
754be1d5a8aSPetr Machata mlxsw_sp_qdisc->stats_base.backlog);
755be1d5a8aSPetr Machata qstats->backlog -= backlog;
756be1d5a8aSPetr Machata mlxsw_sp_qdisc->stats_base.backlog = 0;
757be1d5a8aSPetr Machata }
758be1d5a8aSPetr Machata
759be1d5a8aSPetr Machata static void
mlxsw_sp_qdisc_red_unoffload(struct mlxsw_sp_port * mlxsw_sp_port,struct mlxsw_sp_qdisc * mlxsw_sp_qdisc,void * params)760416ef9b1SJakub Kicinski mlxsw_sp_qdisc_red_unoffload(struct mlxsw_sp_port *mlxsw_sp_port,
761416ef9b1SJakub Kicinski struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
762416ef9b1SJakub Kicinski void *params)
763416ef9b1SJakub Kicinski {
764416ef9b1SJakub Kicinski struct tc_red_qopt_offload_params *p = params;
765416ef9b1SJakub Kicinski
766be1d5a8aSPetr Machata mlxsw_sp_qdisc_leaf_unoffload(mlxsw_sp_port, mlxsw_sp_qdisc, p->qstats);
767416ef9b1SJakub Kicinski }
768416ef9b1SJakub Kicinski
769861fb829SNogah Frankel 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)770cba7158fSNogah Frankel mlxsw_sp_qdisc_get_red_xstats(struct mlxsw_sp_port *mlxsw_sp_port,
771861fb829SNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
772562ffbc4SNogah Frankel void *xstats_ptr)
773861fb829SNogah Frankel {
7744d1a4b84SNogah Frankel struct red_stats *xstats_base = &mlxsw_sp_qdisc->xstats_base.red;
775861fb829SNogah Frankel struct mlxsw_sp_port_xstats *xstats;
776562ffbc4SNogah Frankel struct red_stats *res = xstats_ptr;
77715be36b8SPetr Machata int early_drops, marks, pdrops;
77876ff72a7SPetr Machata int tclass_num;
779861fb829SNogah Frankel
78076ff72a7SPetr Machata tclass_num = mlxsw_sp_qdisc_get_tclass_num(mlxsw_sp_port,
78176ff72a7SPetr Machata mlxsw_sp_qdisc);
782861fb829SNogah Frankel xstats = &mlxsw_sp_port->periodic_hw_stats.xstats;
783861fb829SNogah Frankel
784f8253df5SNogah Frankel early_drops = xstats->wred_drop[tclass_num] - xstats_base->prob_drop;
78515be36b8SPetr Machata marks = xstats->tc_ecn[tclass_num] - xstats_base->prob_mark;
78685005b82SPetr Machata pdrops = mlxsw_sp_xstats_tail_drop(xstats, tclass_num) -
78785005b82SPetr Machata xstats_base->pdrop;
788f8253df5SNogah Frankel
789f8253df5SNogah Frankel res->pdrop += pdrops;
790f8253df5SNogah Frankel res->prob_drop += early_drops;
79115be36b8SPetr Machata res->prob_mark += marks;
792f8253df5SNogah Frankel
793f8253df5SNogah Frankel xstats_base->pdrop += pdrops;
794f8253df5SNogah Frankel xstats_base->prob_drop += early_drops;
79515be36b8SPetr Machata xstats_base->prob_mark += marks;
796861fb829SNogah Frankel return 0;
797861fb829SNogah Frankel }
798861fb829SNogah Frankel
7993670756fSNogah Frankel 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)800cba7158fSNogah Frankel mlxsw_sp_qdisc_get_red_stats(struct mlxsw_sp_port *mlxsw_sp_port,
8013670756fSNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
802562ffbc4SNogah Frankel struct tc_qopt_offload_stats *stats_ptr)
8033670756fSNogah Frankel {
8044d1a4b84SNogah Frankel struct mlxsw_sp_qdisc_stats *stats_base;
8053670756fSNogah Frankel struct mlxsw_sp_port_xstats *xstats;
806cf9af379SPetr Machata u64 overlimits;
80776ff72a7SPetr Machata int tclass_num;
8083670756fSNogah Frankel
80976ff72a7SPetr Machata tclass_num = mlxsw_sp_qdisc_get_tclass_num(mlxsw_sp_port,
81076ff72a7SPetr Machata mlxsw_sp_qdisc);
8113670756fSNogah Frankel xstats = &mlxsw_sp_port->periodic_hw_stats.xstats;
8124d1a4b84SNogah Frankel stats_base = &mlxsw_sp_qdisc->stats_base;
8133670756fSNogah Frankel
8143d0d5921SPetr Machata mlxsw_sp_qdisc_get_tc_stats(mlxsw_sp_port, mlxsw_sp_qdisc, stats_ptr);
81515be36b8SPetr Machata overlimits = xstats->wred_drop[tclass_num] +
81615be36b8SPetr Machata xstats->tc_ecn[tclass_num] - stats_base->overlimits;
8173670756fSNogah Frankel
818562ffbc4SNogah Frankel stats_ptr->qstats->overlimits += overlimits;
8194d1a4b84SNogah Frankel stats_base->overlimits += overlimits;
820cf9af379SPetr Machata
8213670756fSNogah Frankel return 0;
8223670756fSNogah Frankel }
8233670756fSNogah Frankel
82451d52ed9SPetr Machata static struct mlxsw_sp_qdisc *
mlxsw_sp_qdisc_leaf_find_class(struct mlxsw_sp_qdisc * mlxsw_sp_qdisc,u32 parent)82551d52ed9SPetr Machata mlxsw_sp_qdisc_leaf_find_class(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
82651d52ed9SPetr Machata u32 parent)
82751d52ed9SPetr Machata {
8282a18c08dSPetr Machata /* RED and TBF are formally classful qdiscs, but all class references,
8292a18c08dSPetr Machata * including X:0, just refer to the same one class.
8302a18c08dSPetr Machata */
8312a18c08dSPetr Machata return &mlxsw_sp_qdisc->qdiscs[0];
83251d52ed9SPetr Machata }
83351d52ed9SPetr Machata
834562ffbc4SNogah Frankel static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_red = {
8359cf6c9c7SNogah Frankel .type = MLXSW_SP_QDISC_RED,
8369cf6c9c7SNogah Frankel .check_params = mlxsw_sp_qdisc_red_check_params,
8379cf6c9c7SNogah Frankel .replace = mlxsw_sp_qdisc_red_replace,
838416ef9b1SJakub Kicinski .unoffload = mlxsw_sp_qdisc_red_unoffload,
8399a37a59fSNogah Frankel .destroy = mlxsw_sp_qdisc_red_destroy,
840562ffbc4SNogah Frankel .get_stats = mlxsw_sp_qdisc_get_red_stats,
841562ffbc4SNogah Frankel .get_xstats = mlxsw_sp_qdisc_get_red_xstats,
8429cf6c9c7SNogah Frankel .clean_stats = mlxsw_sp_setup_tc_qdisc_red_clean_stats,
84351d52ed9SPetr Machata .find_class = mlxsw_sp_qdisc_leaf_find_class,
8442a18c08dSPetr Machata .num_classes = 1,
845562ffbc4SNogah Frankel };
846562ffbc4SNogah Frankel
847be7e2a5aSPetr Machata static int mlxsw_sp_qdisc_graft(struct mlxsw_sp_port *mlxsw_sp_port,
848be7e2a5aSPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
849be7e2a5aSPetr Machata u8 band, u32 child_handle);
850be7e2a5aSPetr Machata
__mlxsw_sp_setup_tc_red(struct mlxsw_sp_port * mlxsw_sp_port,struct tc_red_qopt_offload * p)851cff99e20SPetr Machata static int __mlxsw_sp_setup_tc_red(struct mlxsw_sp_port *mlxsw_sp_port,
85296f17e07SNogah Frankel struct tc_red_qopt_offload *p)
85396f17e07SNogah Frankel {
85496f17e07SNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc;
85596f17e07SNogah Frankel
856c2792f38SPetr Machata mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent);
857eed4baebSNogah Frankel if (!mlxsw_sp_qdisc)
85896f17e07SNogah Frankel return -EOPNOTSUPP;
85996f17e07SNogah Frankel
860cba7158fSNogah Frankel if (p->command == TC_RED_REPLACE)
8619cf6c9c7SNogah Frankel return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle,
862562ffbc4SNogah Frankel mlxsw_sp_qdisc,
863562ffbc4SNogah Frankel &mlxsw_sp_qdisc_ops_red,
864562ffbc4SNogah Frankel &p->set);
865cba7158fSNogah Frankel
866290fe2c5SPetr Machata if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle))
867cba7158fSNogah Frankel return -EOPNOTSUPP;
868cba7158fSNogah Frankel
869cba7158fSNogah Frankel switch (p->command) {
87096f17e07SNogah Frankel case TC_RED_DESTROY:
8719a37a59fSNogah Frankel return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
872861fb829SNogah Frankel case TC_RED_XSTATS:
873562ffbc4SNogah Frankel return mlxsw_sp_qdisc_get_xstats(mlxsw_sp_port, mlxsw_sp_qdisc,
874861fb829SNogah Frankel p->xstats);
8753670756fSNogah Frankel case TC_RED_STATS:
876562ffbc4SNogah Frankel return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc,
8773670756fSNogah Frankel &p->stats);
878be7e2a5aSPetr Machata case TC_RED_GRAFT:
879be7e2a5aSPetr Machata return mlxsw_sp_qdisc_graft(mlxsw_sp_port, mlxsw_sp_qdisc, 0,
880be7e2a5aSPetr Machata p->child_handle);
88196f17e07SNogah Frankel default:
88296f17e07SNogah Frankel return -EOPNOTSUPP;
88396f17e07SNogah Frankel }
88496f17e07SNogah Frankel }
885371b437aSNogah Frankel
mlxsw_sp_setup_tc_red(struct mlxsw_sp_port * mlxsw_sp_port,struct tc_red_qopt_offload * p)886cff99e20SPetr Machata int mlxsw_sp_setup_tc_red(struct mlxsw_sp_port *mlxsw_sp_port,
887cff99e20SPetr Machata struct tc_red_qopt_offload *p)
888cff99e20SPetr Machata {
889cff99e20SPetr Machata int err;
890cff99e20SPetr Machata
891cff99e20SPetr Machata mutex_lock(&mlxsw_sp_port->qdisc->lock);
892cff99e20SPetr Machata err = __mlxsw_sp_setup_tc_red(mlxsw_sp_port, p);
893cff99e20SPetr Machata mutex_unlock(&mlxsw_sp_port->qdisc->lock);
894cff99e20SPetr Machata
895cff99e20SPetr Machata return err;
896cff99e20SPetr Machata }
897cff99e20SPetr Machata
898a44f58c4SPetr Machata static void
mlxsw_sp_setup_tc_qdisc_leaf_clean_stats(struct mlxsw_sp_port * mlxsw_sp_port,struct mlxsw_sp_qdisc * mlxsw_sp_qdisc)899a44f58c4SPetr Machata mlxsw_sp_setup_tc_qdisc_leaf_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port,
900a44f58c4SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
901a44f58c4SPetr Machata {
902a44f58c4SPetr Machata u64 backlog_cells = 0;
903a44f58c4SPetr Machata u64 tx_packets = 0;
904a44f58c4SPetr Machata u64 tx_bytes = 0;
905a44f58c4SPetr Machata u64 drops = 0;
906a44f58c4SPetr Machata
907a44f58c4SPetr Machata mlxsw_sp_qdisc_collect_tc_stats(mlxsw_sp_port, mlxsw_sp_qdisc,
908a44f58c4SPetr Machata &tx_bytes, &tx_packets,
909a44f58c4SPetr Machata &drops, &backlog_cells);
910a44f58c4SPetr Machata
911a44f58c4SPetr Machata mlxsw_sp_qdisc->stats_base.tx_packets = tx_packets;
912a44f58c4SPetr Machata mlxsw_sp_qdisc->stats_base.tx_bytes = tx_bytes;
913a44f58c4SPetr Machata mlxsw_sp_qdisc->stats_base.drops = drops;
914a44f58c4SPetr Machata mlxsw_sp_qdisc->stats_base.backlog = 0;
915a44f58c4SPetr Machata }
916a44f58c4SPetr Machata
917*48e4d00bSPetr Machata 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*48e4d00bSPetr Machata mlxsw_sp_qdisc_tbf_hr(struct mlxsw_sp_port *mlxsw_sp_port,
919*48e4d00bSPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
920*48e4d00bSPetr Machata {
921*48e4d00bSPetr Machata if (mlxsw_sp_qdisc == &mlxsw_sp_port->qdisc->root_qdisc)
922*48e4d00bSPetr Machata return MLXSW_REG_QEEC_HR_PORT;
923*48e4d00bSPetr Machata
924*48e4d00bSPetr Machata /* Configure subgroup shaper, so that both UC and MC traffic is subject
925*48e4d00bSPetr Machata * to shaping. That is unlike RED, however UC queue lengths are going to
926*48e4d00bSPetr Machata * be different than MC ones due to different pool and quota
927*48e4d00bSPetr Machata * configurations, so the configuration is not applicable. For shaper on
928*48e4d00bSPetr Machata * the other hand, subjecting the overall stream to the configured
929*48e4d00bSPetr Machata * shaper makes sense. Also note that that is what we do for
930*48e4d00bSPetr Machata * ieee_setmaxrate().
931*48e4d00bSPetr Machata */
932*48e4d00bSPetr Machata return MLXSW_REG_QEEC_HR_SUBGROUP;
933*48e4d00bSPetr Machata }
934*48e4d00bSPetr Machata
935a44f58c4SPetr Machata static int
mlxsw_sp_qdisc_tbf_destroy(struct mlxsw_sp_port * mlxsw_sp_port,struct mlxsw_sp_qdisc * mlxsw_sp_qdisc)936a44f58c4SPetr Machata mlxsw_sp_qdisc_tbf_destroy(struct mlxsw_sp_port *mlxsw_sp_port,
937a44f58c4SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
938a44f58c4SPetr Machata {
939*48e4d00bSPetr Machata enum mlxsw_reg_qeec_hr hr = mlxsw_sp_qdisc_tbf_hr(mlxsw_sp_port,
940*48e4d00bSPetr Machata mlxsw_sp_qdisc);
94176ff72a7SPetr Machata int tclass_num = mlxsw_sp_qdisc_get_tclass_num(mlxsw_sp_port,
94276ff72a7SPetr Machata mlxsw_sp_qdisc);
94376ff72a7SPetr Machata
944*48e4d00bSPetr Machata return mlxsw_sp_port_ets_maxrate_set(mlxsw_sp_port, hr, tclass_num, 0,
945a44f58c4SPetr Machata MLXSW_REG_QEEC_MAS_DIS, 0);
946a44f58c4SPetr Machata }
947a44f58c4SPetr Machata
948a44f58c4SPetr Machata static int
mlxsw_sp_qdisc_tbf_bs(struct mlxsw_sp_port * mlxsw_sp_port,u32 max_size,u8 * p_burst_size)949a44f58c4SPetr Machata mlxsw_sp_qdisc_tbf_bs(struct mlxsw_sp_port *mlxsw_sp_port,
950a44f58c4SPetr Machata u32 max_size, u8 *p_burst_size)
951a44f58c4SPetr Machata {
952a44f58c4SPetr Machata /* TBF burst size is configured in bytes. The ASIC burst size value is
953a44f58c4SPetr Machata * ((2 ^ bs) * 512 bits. Convert the TBF bytes to 512-bit units.
954a44f58c4SPetr Machata */
955a44f58c4SPetr Machata u32 bs512 = max_size / 64;
956a44f58c4SPetr Machata u8 bs = fls(bs512);
957a44f58c4SPetr Machata
958a44f58c4SPetr Machata if (!bs)
959a44f58c4SPetr Machata return -EINVAL;
960a44f58c4SPetr Machata --bs;
961a44f58c4SPetr Machata
962a44f58c4SPetr Machata /* Demand a power of two. */
963a44f58c4SPetr Machata if ((1 << bs) != bs512)
964a44f58c4SPetr Machata return -EINVAL;
965a44f58c4SPetr Machata
966a44f58c4SPetr Machata if (bs < mlxsw_sp_port->mlxsw_sp->lowest_shaper_bs ||
967a44f58c4SPetr Machata bs > MLXSW_REG_QEEC_HIGHEST_SHAPER_BS)
968a44f58c4SPetr Machata return -EINVAL;
969a44f58c4SPetr Machata
970a44f58c4SPetr Machata *p_burst_size = bs;
971a44f58c4SPetr Machata return 0;
972a44f58c4SPetr Machata }
973a44f58c4SPetr Machata
974a44f58c4SPetr Machata static u32
mlxsw_sp_qdisc_tbf_max_size(u8 bs)975a44f58c4SPetr Machata mlxsw_sp_qdisc_tbf_max_size(u8 bs)
976a44f58c4SPetr Machata {
977a44f58c4SPetr Machata return (1U << bs) * 64;
978a44f58c4SPetr Machata }
979a44f58c4SPetr Machata
980a44f58c4SPetr Machata static u64
mlxsw_sp_qdisc_tbf_rate_kbps(struct tc_tbf_qopt_offload_replace_params * p)981a44f58c4SPetr Machata mlxsw_sp_qdisc_tbf_rate_kbps(struct tc_tbf_qopt_offload_replace_params *p)
982a44f58c4SPetr Machata {
983a44f58c4SPetr Machata /* TBF interface is in bytes/s, whereas Spectrum ASIC is configured in
984a44f58c4SPetr Machata * Kbits/s.
985a44f58c4SPetr Machata */
98691a7d4bfSNathan Chancellor return div_u64(p->rate.rate_bytes_ps, 1000) * 8;
987a44f58c4SPetr Machata }
988a44f58c4SPetr Machata
989a44f58c4SPetr Machata static int
mlxsw_sp_qdisc_tbf_check_params(struct mlxsw_sp_port * mlxsw_sp_port,void * params)990a44f58c4SPetr Machata mlxsw_sp_qdisc_tbf_check_params(struct mlxsw_sp_port *mlxsw_sp_port,
991a44f58c4SPetr Machata void *params)
992a44f58c4SPetr Machata {
993a44f58c4SPetr Machata struct tc_tbf_qopt_offload_replace_params *p = params;
994a44f58c4SPetr Machata struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
995a44f58c4SPetr Machata u64 rate_kbps = mlxsw_sp_qdisc_tbf_rate_kbps(p);
996a44f58c4SPetr Machata u8 burst_size;
997a44f58c4SPetr Machata int err;
998a44f58c4SPetr Machata
999a44f58c4SPetr Machata if (rate_kbps >= MLXSW_REG_QEEC_MAS_DIS) {
1000a44f58c4SPetr Machata dev_err(mlxsw_sp_port->mlxsw_sp->bus_info->dev,
1001a44f58c4SPetr Machata "spectrum: TBF: rate of %lluKbps must be below %u\n",
1002a44f58c4SPetr Machata rate_kbps, MLXSW_REG_QEEC_MAS_DIS);
1003a44f58c4SPetr Machata return -EINVAL;
1004a44f58c4SPetr Machata }
1005a44f58c4SPetr Machata
1006a44f58c4SPetr Machata err = mlxsw_sp_qdisc_tbf_bs(mlxsw_sp_port, p->max_size, &burst_size);
1007a44f58c4SPetr Machata if (err) {
1008a44f58c4SPetr Machata u8 highest_shaper_bs = MLXSW_REG_QEEC_HIGHEST_SHAPER_BS;
1009a44f58c4SPetr Machata
1010a44f58c4SPetr Machata dev_err(mlxsw_sp->bus_info->dev,
1011a44f58c4SPetr Machata "spectrum: TBF: invalid burst size of %u, must be a power of two between %u and %u",
1012a44f58c4SPetr Machata p->max_size,
1013a44f58c4SPetr Machata mlxsw_sp_qdisc_tbf_max_size(mlxsw_sp->lowest_shaper_bs),
1014a44f58c4SPetr Machata mlxsw_sp_qdisc_tbf_max_size(highest_shaper_bs));
1015a44f58c4SPetr Machata return -EINVAL;
1016a44f58c4SPetr Machata }
1017a44f58c4SPetr Machata
1018a44f58c4SPetr Machata return 0;
1019a44f58c4SPetr Machata }
1020a44f58c4SPetr Machata
1021a44f58c4SPetr Machata 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)1022c4e372e2SPetr Machata mlxsw_sp_qdisc_tbf_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
1023a44f58c4SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
1024a44f58c4SPetr Machata void *params)
1025a44f58c4SPetr Machata {
1026*48e4d00bSPetr Machata enum mlxsw_reg_qeec_hr hr = mlxsw_sp_qdisc_tbf_hr(mlxsw_sp_port,
1027*48e4d00bSPetr Machata mlxsw_sp_qdisc);
1028a44f58c4SPetr Machata struct tc_tbf_qopt_offload_replace_params *p = params;
1029a44f58c4SPetr Machata u64 rate_kbps = mlxsw_sp_qdisc_tbf_rate_kbps(p);
103076ff72a7SPetr Machata int tclass_num;
1031a44f58c4SPetr Machata u8 burst_size;
1032a44f58c4SPetr Machata int err;
1033a44f58c4SPetr Machata
10342a18c08dSPetr Machata err = mlxsw_sp_qdisc_future_fifo_replace(mlxsw_sp_port, handle, 0,
10352a18c08dSPetr Machata &mlxsw_sp_qdisc->qdiscs[0]);
10362a18c08dSPetr Machata if (err)
10372a18c08dSPetr Machata return err;
10382a18c08dSPetr Machata mlxsw_sp_qdisc_future_fifos_init(mlxsw_sp_port, TC_H_UNSPEC);
10392a18c08dSPetr Machata
104076ff72a7SPetr Machata tclass_num = mlxsw_sp_qdisc_get_tclass_num(mlxsw_sp_port,
104176ff72a7SPetr Machata mlxsw_sp_qdisc);
104276ff72a7SPetr Machata
1043a44f58c4SPetr Machata err = mlxsw_sp_qdisc_tbf_bs(mlxsw_sp_port, p->max_size, &burst_size);
1044a44f58c4SPetr Machata if (WARN_ON_ONCE(err))
1045a44f58c4SPetr Machata /* check_params above was supposed to reject this value. */
1046a44f58c4SPetr Machata return -EINVAL;
1047a44f58c4SPetr Machata
1048*48e4d00bSPetr Machata return mlxsw_sp_port_ets_maxrate_set(mlxsw_sp_port, hr, tclass_num, 0,
1049a44f58c4SPetr Machata rate_kbps, burst_size);
1050a44f58c4SPetr Machata }
1051a44f58c4SPetr Machata
1052a44f58c4SPetr Machata static void
mlxsw_sp_qdisc_tbf_unoffload(struct mlxsw_sp_port * mlxsw_sp_port,struct mlxsw_sp_qdisc * mlxsw_sp_qdisc,void * params)1053a44f58c4SPetr Machata mlxsw_sp_qdisc_tbf_unoffload(struct mlxsw_sp_port *mlxsw_sp_port,
1054a44f58c4SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
1055a44f58c4SPetr Machata void *params)
1056a44f58c4SPetr Machata {
1057a44f58c4SPetr Machata struct tc_tbf_qopt_offload_replace_params *p = params;
1058a44f58c4SPetr Machata
1059a44f58c4SPetr Machata mlxsw_sp_qdisc_leaf_unoffload(mlxsw_sp_port, mlxsw_sp_qdisc, p->qstats);
1060a44f58c4SPetr Machata }
1061a44f58c4SPetr Machata
1062a44f58c4SPetr Machata 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)1063a44f58c4SPetr Machata mlxsw_sp_qdisc_get_tbf_stats(struct mlxsw_sp_port *mlxsw_sp_port,
1064a44f58c4SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
1065a44f58c4SPetr Machata struct tc_qopt_offload_stats *stats_ptr)
1066a44f58c4SPetr Machata {
1067a44f58c4SPetr Machata mlxsw_sp_qdisc_get_tc_stats(mlxsw_sp_port, mlxsw_sp_qdisc,
1068a44f58c4SPetr Machata stats_ptr);
1069a44f58c4SPetr Machata return 0;
1070a44f58c4SPetr Machata }
1071a44f58c4SPetr Machata
1072a44f58c4SPetr Machata static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_tbf = {
1073a44f58c4SPetr Machata .type = MLXSW_SP_QDISC_TBF,
1074a44f58c4SPetr Machata .check_params = mlxsw_sp_qdisc_tbf_check_params,
1075a44f58c4SPetr Machata .replace = mlxsw_sp_qdisc_tbf_replace,
1076a44f58c4SPetr Machata .unoffload = mlxsw_sp_qdisc_tbf_unoffload,
1077a44f58c4SPetr Machata .destroy = mlxsw_sp_qdisc_tbf_destroy,
1078a44f58c4SPetr Machata .get_stats = mlxsw_sp_qdisc_get_tbf_stats,
1079a44f58c4SPetr Machata .clean_stats = mlxsw_sp_setup_tc_qdisc_leaf_clean_stats,
108051d52ed9SPetr Machata .find_class = mlxsw_sp_qdisc_leaf_find_class,
10812a18c08dSPetr Machata .num_classes = 1,
1082a44f58c4SPetr Machata };
1083a44f58c4SPetr Machata
__mlxsw_sp_setup_tc_tbf(struct mlxsw_sp_port * mlxsw_sp_port,struct tc_tbf_qopt_offload * p)1084cff99e20SPetr Machata static int __mlxsw_sp_setup_tc_tbf(struct mlxsw_sp_port *mlxsw_sp_port,
1085a44f58c4SPetr Machata struct tc_tbf_qopt_offload *p)
1086a44f58c4SPetr Machata {
1087a44f58c4SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc;
1088a44f58c4SPetr Machata
1089c2792f38SPetr Machata mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent);
1090a44f58c4SPetr Machata if (!mlxsw_sp_qdisc)
1091a44f58c4SPetr Machata return -EOPNOTSUPP;
1092a44f58c4SPetr Machata
1093a44f58c4SPetr Machata if (p->command == TC_TBF_REPLACE)
1094a44f58c4SPetr Machata return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle,
1095a44f58c4SPetr Machata mlxsw_sp_qdisc,
1096a44f58c4SPetr Machata &mlxsw_sp_qdisc_ops_tbf,
1097a44f58c4SPetr Machata &p->replace_params);
1098a44f58c4SPetr Machata
1099290fe2c5SPetr Machata if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle))
1100a44f58c4SPetr Machata return -EOPNOTSUPP;
1101a44f58c4SPetr Machata
1102a44f58c4SPetr Machata switch (p->command) {
1103a44f58c4SPetr Machata case TC_TBF_DESTROY:
1104a44f58c4SPetr Machata return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
1105a44f58c4SPetr Machata case TC_TBF_STATS:
1106a44f58c4SPetr Machata return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc,
1107a44f58c4SPetr Machata &p->stats);
1108be7e2a5aSPetr Machata case TC_TBF_GRAFT:
1109be7e2a5aSPetr Machata return mlxsw_sp_qdisc_graft(mlxsw_sp_port, mlxsw_sp_qdisc, 0,
1110be7e2a5aSPetr Machata p->child_handle);
1111a44f58c4SPetr Machata default:
1112a44f58c4SPetr Machata return -EOPNOTSUPP;
1113a44f58c4SPetr Machata }
1114a44f58c4SPetr Machata }
1115a44f58c4SPetr Machata
mlxsw_sp_setup_tc_tbf(struct mlxsw_sp_port * mlxsw_sp_port,struct tc_tbf_qopt_offload * p)1116cff99e20SPetr Machata int mlxsw_sp_setup_tc_tbf(struct mlxsw_sp_port *mlxsw_sp_port,
1117cff99e20SPetr Machata struct tc_tbf_qopt_offload *p)
1118cff99e20SPetr Machata {
1119cff99e20SPetr Machata int err;
1120cff99e20SPetr Machata
1121cff99e20SPetr Machata mutex_lock(&mlxsw_sp_port->qdisc->lock);
1122cff99e20SPetr Machata err = __mlxsw_sp_setup_tc_tbf(mlxsw_sp_port, p);
1123cff99e20SPetr Machata mutex_unlock(&mlxsw_sp_port->qdisc->lock);
1124cff99e20SPetr Machata
1125cff99e20SPetr Machata return err;
1126cff99e20SPetr Machata }
1127cff99e20SPetr Machata
112846a3615bSNogah Frankel static int
mlxsw_sp_qdisc_fifo_check_params(struct mlxsw_sp_port * mlxsw_sp_port,void * params)11297bec1a45SPetr Machata mlxsw_sp_qdisc_fifo_check_params(struct mlxsw_sp_port *mlxsw_sp_port,
11307bec1a45SPetr Machata void *params)
11317bec1a45SPetr Machata {
11327bec1a45SPetr Machata return 0;
11337bec1a45SPetr Machata }
11347bec1a45SPetr Machata
11357bec1a45SPetr Machata 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)11367bec1a45SPetr Machata mlxsw_sp_qdisc_fifo_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
11377bec1a45SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
11387bec1a45SPetr Machata void *params)
11397bec1a45SPetr Machata {
11407bec1a45SPetr Machata return 0;
11417bec1a45SPetr Machata }
11427bec1a45SPetr Machata
11437bec1a45SPetr Machata 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)11447bec1a45SPetr Machata mlxsw_sp_qdisc_get_fifo_stats(struct mlxsw_sp_port *mlxsw_sp_port,
11457bec1a45SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
11467bec1a45SPetr Machata struct tc_qopt_offload_stats *stats_ptr)
11477bec1a45SPetr Machata {
11487bec1a45SPetr Machata mlxsw_sp_qdisc_get_tc_stats(mlxsw_sp_port, mlxsw_sp_qdisc,
11497bec1a45SPetr Machata stats_ptr);
11507bec1a45SPetr Machata return 0;
11517bec1a45SPetr Machata }
11527bec1a45SPetr Machata
11537bec1a45SPetr Machata static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_fifo = {
11547bec1a45SPetr Machata .type = MLXSW_SP_QDISC_FIFO,
11557bec1a45SPetr Machata .check_params = mlxsw_sp_qdisc_fifo_check_params,
11567bec1a45SPetr Machata .replace = mlxsw_sp_qdisc_fifo_replace,
11577bec1a45SPetr Machata .get_stats = mlxsw_sp_qdisc_get_fifo_stats,
11587bec1a45SPetr Machata .clean_stats = mlxsw_sp_setup_tc_qdisc_leaf_clean_stats,
11597bec1a45SPetr Machata };
11607bec1a45SPetr Machata
116191796f50SPetr Machata 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)116291796f50SPetr Machata mlxsw_sp_qdisc_future_fifo_replace(struct mlxsw_sp_port *mlxsw_sp_port,
116391796f50SPetr Machata u32 handle, unsigned int band,
116491796f50SPetr Machata struct mlxsw_sp_qdisc *child_qdisc)
116591796f50SPetr Machata {
116691796f50SPetr Machata struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc;
116791796f50SPetr Machata
116891796f50SPetr Machata if (handle == qdisc_state->future_handle &&
116991796f50SPetr Machata qdisc_state->future_fifos[band])
117091796f50SPetr Machata return mlxsw_sp_qdisc_replace(mlxsw_sp_port, TC_H_UNSPEC,
117191796f50SPetr Machata child_qdisc,
117291796f50SPetr Machata &mlxsw_sp_qdisc_ops_fifo,
117391796f50SPetr Machata NULL);
117491796f50SPetr Machata return 0;
117591796f50SPetr Machata }
117691796f50SPetr Machata
117791796f50SPetr Machata static void
mlxsw_sp_qdisc_future_fifos_init(struct mlxsw_sp_port * mlxsw_sp_port,u32 handle)117891796f50SPetr Machata mlxsw_sp_qdisc_future_fifos_init(struct mlxsw_sp_port *mlxsw_sp_port,
117991796f50SPetr Machata u32 handle)
118091796f50SPetr Machata {
118191796f50SPetr Machata struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc;
118291796f50SPetr Machata
118391796f50SPetr Machata qdisc_state->future_handle = handle;
118491796f50SPetr Machata memset(qdisc_state->future_fifos, 0, sizeof(qdisc_state->future_fifos));
118591796f50SPetr Machata }
118691796f50SPetr Machata
__mlxsw_sp_setup_tc_fifo(struct mlxsw_sp_port * mlxsw_sp_port,struct tc_fifo_qopt_offload * p)1187cff99e20SPetr Machata static int __mlxsw_sp_setup_tc_fifo(struct mlxsw_sp_port *mlxsw_sp_port,
11887bec1a45SPetr Machata struct tc_fifo_qopt_offload *p)
11897bec1a45SPetr Machata {
11907bec1a45SPetr Machata struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc;
11917bec1a45SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc;
11927de85b04SPetr Machata unsigned int band;
11937bec1a45SPetr Machata u32 parent_handle;
11947bec1a45SPetr Machata
1195c2792f38SPetr Machata mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent);
11967bec1a45SPetr Machata if (!mlxsw_sp_qdisc && p->handle == TC_H_UNSPEC) {
11977bec1a45SPetr Machata parent_handle = TC_H_MAJ(p->parent);
11987bec1a45SPetr Machata if (parent_handle != qdisc_state->future_handle) {
11997bec1a45SPetr Machata /* This notifications is for a different Qdisc than
12007bec1a45SPetr Machata * previously. Wipe the future cache.
12017bec1a45SPetr Machata */
120291796f50SPetr Machata mlxsw_sp_qdisc_future_fifos_init(mlxsw_sp_port,
120391796f50SPetr Machata parent_handle);
12047bec1a45SPetr Machata }
12057bec1a45SPetr Machata
12067de85b04SPetr Machata band = TC_H_MIN(p->parent) - 1;
12077de85b04SPetr Machata if (band < IEEE_8021QAZ_MAX_TCS) {
12087bec1a45SPetr Machata if (p->command == TC_FIFO_REPLACE)
12097de85b04SPetr Machata qdisc_state->future_fifos[band] = true;
12107bec1a45SPetr Machata else if (p->command == TC_FIFO_DESTROY)
12117de85b04SPetr Machata qdisc_state->future_fifos[band] = false;
12127bec1a45SPetr Machata }
12137bec1a45SPetr Machata }
12147bec1a45SPetr Machata if (!mlxsw_sp_qdisc)
12157bec1a45SPetr Machata return -EOPNOTSUPP;
12167bec1a45SPetr Machata
12177bec1a45SPetr Machata if (p->command == TC_FIFO_REPLACE) {
12187bec1a45SPetr Machata return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle,
12197bec1a45SPetr Machata mlxsw_sp_qdisc,
12207bec1a45SPetr Machata &mlxsw_sp_qdisc_ops_fifo, NULL);
12217bec1a45SPetr Machata }
12227bec1a45SPetr Machata
1223290fe2c5SPetr Machata if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle))
12247bec1a45SPetr Machata return -EOPNOTSUPP;
12257bec1a45SPetr Machata
12267bec1a45SPetr Machata switch (p->command) {
12277bec1a45SPetr Machata case TC_FIFO_DESTROY:
1228549f2aaeSPetr Machata return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
12297bec1a45SPetr Machata case TC_FIFO_STATS:
12307bec1a45SPetr Machata return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc,
12317bec1a45SPetr Machata &p->stats);
12327bec1a45SPetr Machata case TC_FIFO_REPLACE: /* Handled above. */
12337bec1a45SPetr Machata break;
12347bec1a45SPetr Machata }
12357bec1a45SPetr Machata
12367bec1a45SPetr Machata return -EOPNOTSUPP;
12377bec1a45SPetr Machata }
12387bec1a45SPetr Machata
mlxsw_sp_setup_tc_fifo(struct mlxsw_sp_port * mlxsw_sp_port,struct tc_fifo_qopt_offload * p)1239cff99e20SPetr Machata int mlxsw_sp_setup_tc_fifo(struct mlxsw_sp_port *mlxsw_sp_port,
1240cff99e20SPetr Machata struct tc_fifo_qopt_offload *p)
1241cff99e20SPetr Machata {
1242cff99e20SPetr Machata int err;
1243cff99e20SPetr Machata
1244cff99e20SPetr Machata mutex_lock(&mlxsw_sp_port->qdisc->lock);
1245cff99e20SPetr Machata err = __mlxsw_sp_setup_tc_fifo(mlxsw_sp_port, p);
1246cff99e20SPetr Machata mutex_unlock(&mlxsw_sp_port->qdisc->lock);
1247cff99e20SPetr Machata
1248cff99e20SPetr Machata return err;
1249cff99e20SPetr Machata }
1250cff99e20SPetr Machata
__mlxsw_sp_qdisc_ets_destroy(struct mlxsw_sp_port * mlxsw_sp_port,struct mlxsw_sp_qdisc * mlxsw_sp_qdisc)125151d52ed9SPetr Machata static int __mlxsw_sp_qdisc_ets_destroy(struct mlxsw_sp_port *mlxsw_sp_port,
125251d52ed9SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
125346a3615bSNogah Frankel {
125446a3615bSNogah Frankel int i;
125546a3615bSNogah Frankel
125651d52ed9SPetr Machata for (i = 0; i < mlxsw_sp_qdisc->num_classes; i++) {
125746a3615bSNogah Frankel mlxsw_sp_port_prio_tc_set(mlxsw_sp_port, i,
125846a3615bSNogah Frankel MLXSW_SP_PORT_DEFAULT_TCLASS);
12597917f52aSPetr Machata mlxsw_sp_port_ets_set(mlxsw_sp_port,
12607917f52aSPetr Machata MLXSW_REG_QEEC_HR_SUBGROUP,
12617917f52aSPetr Machata i, 0, false, 0);
1262eed4baebSNogah Frankel }
126346a3615bSNogah Frankel
126476ff72a7SPetr Machata kfree(mlxsw_sp_qdisc->ets_data);
126576ff72a7SPetr Machata mlxsw_sp_qdisc->ets_data = NULL;
126646a3615bSNogah Frankel return 0;
126746a3615bSNogah Frankel }
126846a3615bSNogah Frankel
126946a3615bSNogah Frankel static int
mlxsw_sp_qdisc_prio_destroy(struct mlxsw_sp_port * mlxsw_sp_port,struct mlxsw_sp_qdisc * mlxsw_sp_qdisc)12707917f52aSPetr Machata mlxsw_sp_qdisc_prio_destroy(struct mlxsw_sp_port *mlxsw_sp_port,
12717917f52aSPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
12727917f52aSPetr Machata {
127351d52ed9SPetr Machata return __mlxsw_sp_qdisc_ets_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
12747917f52aSPetr Machata }
12757917f52aSPetr Machata
12767917f52aSPetr Machata static int
__mlxsw_sp_qdisc_ets_check_params(unsigned int nbands)12777917f52aSPetr Machata __mlxsw_sp_qdisc_ets_check_params(unsigned int nbands)
12787917f52aSPetr Machata {
12797917f52aSPetr Machata if (nbands > IEEE_8021QAZ_MAX_TCS)
12807917f52aSPetr Machata return -EOPNOTSUPP;
12817917f52aSPetr Machata
12827917f52aSPetr Machata return 0;
12837917f52aSPetr Machata }
12847917f52aSPetr Machata
12857917f52aSPetr Machata static int
mlxsw_sp_qdisc_prio_check_params(struct mlxsw_sp_port * mlxsw_sp_port,void * params)128646a3615bSNogah Frankel mlxsw_sp_qdisc_prio_check_params(struct mlxsw_sp_port *mlxsw_sp_port,
128746a3615bSNogah Frankel void *params)
128846a3615bSNogah Frankel {
128946a3615bSNogah Frankel struct tc_prio_qopt_offload_params *p = params;
129046a3615bSNogah Frankel
12917917f52aSPetr Machata return __mlxsw_sp_qdisc_ets_check_params(p->bands);
129246a3615bSNogah Frankel }
129346a3615bSNogah Frankel
129401164ddaSPetr Machata static struct mlxsw_sp_qdisc *
mlxsw_sp_qdisc_walk_cb_clean_stats(struct mlxsw_sp_qdisc * mlxsw_sp_qdisc,void * mlxsw_sp_port)129501164ddaSPetr Machata mlxsw_sp_qdisc_walk_cb_clean_stats(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
129601164ddaSPetr Machata void *mlxsw_sp_port)
129701164ddaSPetr Machata {
129801164ddaSPetr Machata u64 backlog;
129901164ddaSPetr Machata
130001164ddaSPetr Machata if (mlxsw_sp_qdisc->ops) {
130101164ddaSPetr Machata backlog = mlxsw_sp_qdisc->stats_base.backlog;
130201164ddaSPetr Machata if (mlxsw_sp_qdisc->ops->clean_stats)
130301164ddaSPetr Machata mlxsw_sp_qdisc->ops->clean_stats(mlxsw_sp_port,
130401164ddaSPetr Machata mlxsw_sp_qdisc);
130501164ddaSPetr Machata mlxsw_sp_qdisc->stats_base.backlog = backlog;
130601164ddaSPetr Machata }
130701164ddaSPetr Machata
130801164ddaSPetr Machata return NULL;
130901164ddaSPetr Machata }
131001164ddaSPetr Machata
131101164ddaSPetr Machata static void
mlxsw_sp_qdisc_tree_clean_stats(struct mlxsw_sp_port * mlxsw_sp_port,struct mlxsw_sp_qdisc * mlxsw_sp_qdisc)131201164ddaSPetr Machata mlxsw_sp_qdisc_tree_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port,
131301164ddaSPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
131401164ddaSPetr Machata {
131501164ddaSPetr Machata mlxsw_sp_qdisc_walk(mlxsw_sp_qdisc, mlxsw_sp_qdisc_walk_cb_clean_stats,
131601164ddaSPetr Machata mlxsw_sp_port);
131701164ddaSPetr Machata }
131801164ddaSPetr Machata
131946a3615bSNogah Frankel 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)132051d52ed9SPetr Machata __mlxsw_sp_qdisc_ets_replace(struct mlxsw_sp_port *mlxsw_sp_port,
132151d52ed9SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
132251d52ed9SPetr Machata u32 handle, unsigned int nbands,
13237917f52aSPetr Machata const unsigned int *quanta,
13247917f52aSPetr Machata const unsigned int *weights,
13257917f52aSPetr Machata const u8 *priomap)
132646a3615bSNogah Frankel {
132776ff72a7SPetr Machata struct mlxsw_sp_qdisc_ets_data *ets_data = mlxsw_sp_qdisc->ets_data;
132876ff72a7SPetr Machata struct mlxsw_sp_qdisc_ets_band *ets_band;
132904cc0bf5SNogah Frankel struct mlxsw_sp_qdisc *child_qdisc;
133076ff72a7SPetr Machata u8 old_priomap, new_priomap;
133101164ddaSPetr Machata int i, band;
133246a3615bSNogah Frankel int err;
133346a3615bSNogah Frankel
133476ff72a7SPetr Machata if (!ets_data) {
133576ff72a7SPetr Machata ets_data = kzalloc(sizeof(*ets_data), GFP_KERNEL);
133676ff72a7SPetr Machata if (!ets_data)
133776ff72a7SPetr Machata return -ENOMEM;
133876ff72a7SPetr Machata mlxsw_sp_qdisc->ets_data = ets_data;
133976ff72a7SPetr Machata
134076ff72a7SPetr Machata for (band = 0; band < mlxsw_sp_qdisc->num_classes; band++) {
134176ff72a7SPetr Machata int tclass_num = MLXSW_SP_PRIO_BAND_TO_TCLASS(band);
134276ff72a7SPetr Machata
134376ff72a7SPetr Machata ets_band = &ets_data->bands[band];
134476ff72a7SPetr Machata ets_band->tclass_num = tclass_num;
134576ff72a7SPetr Machata }
134676ff72a7SPetr Machata }
134776ff72a7SPetr Machata
13487917f52aSPetr Machata for (band = 0; band < nbands; band++) {
134976ff72a7SPetr Machata int tclass_num;
135076ff72a7SPetr Machata
135151d52ed9SPetr Machata child_qdisc = &mlxsw_sp_qdisc->qdiscs[band];
135276ff72a7SPetr Machata ets_band = &ets_data->bands[band];
135376ff72a7SPetr Machata
135476ff72a7SPetr Machata tclass_num = ets_band->tclass_num;
135576ff72a7SPetr Machata old_priomap = ets_band->prio_bitmap;
135676ff72a7SPetr Machata new_priomap = 0;
13577917f52aSPetr Machata
13587917f52aSPetr Machata err = mlxsw_sp_port_ets_set(mlxsw_sp_port,
13597917f52aSPetr Machata MLXSW_REG_QEEC_HR_SUBGROUP,
136076ff72a7SPetr Machata tclass_num, 0, !!quanta[band],
13617917f52aSPetr Machata weights[band]);
13627917f52aSPetr Machata if (err)
13637917f52aSPetr Machata return err;
13647917f52aSPetr Machata
136546a3615bSNogah Frankel for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
13667917f52aSPetr Machata if (priomap[i] == band) {
136776ff72a7SPetr Machata new_priomap |= BIT(i);
136804cc0bf5SNogah Frankel if (BIT(i) & old_priomap)
136904cc0bf5SNogah Frankel continue;
137004cc0bf5SNogah Frankel err = mlxsw_sp_port_prio_tc_set(mlxsw_sp_port,
137176ff72a7SPetr Machata i, tclass_num);
137246a3615bSNogah Frankel if (err)
137346a3615bSNogah Frankel return err;
137404cc0bf5SNogah Frankel }
137504cc0bf5SNogah Frankel }
13765cbd9602SPetr Machata
137776ff72a7SPetr Machata ets_band->prio_bitmap = new_priomap;
13785cbd9602SPetr Machata
137901164ddaSPetr Machata if (old_priomap != new_priomap)
138001164ddaSPetr Machata mlxsw_sp_qdisc_tree_clean_stats(mlxsw_sp_port,
138104cc0bf5SNogah Frankel child_qdisc);
13827bec1a45SPetr Machata
138391796f50SPetr Machata err = mlxsw_sp_qdisc_future_fifo_replace(mlxsw_sp_port, handle,
138491796f50SPetr Machata band, child_qdisc);
13857bec1a45SPetr Machata if (err)
13867bec1a45SPetr Machata return err;
13877bec1a45SPetr Machata }
138898ceb7b6SNogah Frankel for (; band < IEEE_8021QAZ_MAX_TCS; band++) {
138976ff72a7SPetr Machata ets_band = &ets_data->bands[band];
139076ff72a7SPetr Machata ets_band->prio_bitmap = 0;
139176ff72a7SPetr Machata
139251d52ed9SPetr Machata child_qdisc = &mlxsw_sp_qdisc->qdiscs[band];
139398ceb7b6SNogah Frankel mlxsw_sp_qdisc_destroy(mlxsw_sp_port, child_qdisc);
139476ff72a7SPetr Machata
13957917f52aSPetr Machata mlxsw_sp_port_ets_set(mlxsw_sp_port,
13967917f52aSPetr Machata MLXSW_REG_QEEC_HR_SUBGROUP,
139776ff72a7SPetr Machata ets_band->tclass_num, 0, false, 0);
139898ceb7b6SNogah Frankel }
13997bec1a45SPetr Machata
140091796f50SPetr Machata mlxsw_sp_qdisc_future_fifos_init(mlxsw_sp_port, TC_H_UNSPEC);
140146a3615bSNogah Frankel return 0;
140246a3615bSNogah Frankel }
140346a3615bSNogah Frankel
14047917f52aSPetr Machata 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)1405c4e372e2SPetr Machata mlxsw_sp_qdisc_prio_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
14067917f52aSPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
14077917f52aSPetr Machata void *params)
14087917f52aSPetr Machata {
14097917f52aSPetr Machata struct tc_prio_qopt_offload_params *p = params;
14107917f52aSPetr Machata unsigned int zeroes[TCQ_ETS_MAX_BANDS] = {0};
14117917f52aSPetr Machata
141251d52ed9SPetr Machata return __mlxsw_sp_qdisc_ets_replace(mlxsw_sp_port, mlxsw_sp_qdisc,
141351d52ed9SPetr Machata handle, p->bands, zeroes,
141451d52ed9SPetr Machata zeroes, p->priomap);
14157917f52aSPetr Machata }
14167917f52aSPetr Machata
14177917f52aSPetr Machata 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)14187917f52aSPetr Machata __mlxsw_sp_qdisc_ets_unoffload(struct mlxsw_sp_port *mlxsw_sp_port,
14197917f52aSPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
14207917f52aSPetr Machata struct gnet_stats_queue *qstats)
14217917f52aSPetr Machata {
14227917f52aSPetr Machata u64 backlog;
14237917f52aSPetr Machata
14247917f52aSPetr Machata backlog = mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp,
14257917f52aSPetr Machata mlxsw_sp_qdisc->stats_base.backlog);
14267917f52aSPetr Machata qstats->backlog -= backlog;
14277917f52aSPetr Machata }
14287917f52aSPetr Machata
1429e02f08a0SWei Yongjun static void
mlxsw_sp_qdisc_prio_unoffload(struct mlxsw_sp_port * mlxsw_sp_port,struct mlxsw_sp_qdisc * mlxsw_sp_qdisc,void * params)143093d8a4c1SNogah Frankel mlxsw_sp_qdisc_prio_unoffload(struct mlxsw_sp_port *mlxsw_sp_port,
143193d8a4c1SNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
143293d8a4c1SNogah Frankel void *params)
143393d8a4c1SNogah Frankel {
143493d8a4c1SNogah Frankel struct tc_prio_qopt_offload_params *p = params;
143593d8a4c1SNogah Frankel
14367917f52aSPetr Machata __mlxsw_sp_qdisc_ets_unoffload(mlxsw_sp_port, mlxsw_sp_qdisc,
14377917f52aSPetr Machata p->qstats);
143893d8a4c1SNogah Frankel }
143993d8a4c1SNogah Frankel
144093d8a4c1SNogah Frankel 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)144193d8a4c1SNogah Frankel mlxsw_sp_qdisc_get_prio_stats(struct mlxsw_sp_port *mlxsw_sp_port,
144293d8a4c1SNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
144393d8a4c1SNogah Frankel struct tc_qopt_offload_stats *stats_ptr)
144493d8a4c1SNogah Frankel {
1445cf9af379SPetr Machata struct mlxsw_sp_qdisc *tc_qdisc;
1446cf9af379SPetr Machata u64 tx_packets = 0;
1447cf9af379SPetr Machata u64 tx_bytes = 0;
1448cf9af379SPetr Machata u64 backlog = 0;
1449cf9af379SPetr Machata u64 drops = 0;
145093d8a4c1SNogah Frankel int i;
145193d8a4c1SNogah Frankel
145251d52ed9SPetr Machata for (i = 0; i < mlxsw_sp_qdisc->num_classes; i++) {
145351d52ed9SPetr Machata tc_qdisc = &mlxsw_sp_qdisc->qdiscs[i];
1454cf9af379SPetr Machata mlxsw_sp_qdisc_collect_tc_stats(mlxsw_sp_port, tc_qdisc,
1455cf9af379SPetr Machata &tx_bytes, &tx_packets,
1456cf9af379SPetr Machata &drops, &backlog);
145793d8a4c1SNogah Frankel }
145893d8a4c1SNogah Frankel
1459cf9af379SPetr Machata mlxsw_sp_qdisc_update_stats(mlxsw_sp_port->mlxsw_sp, mlxsw_sp_qdisc,
1460cf9af379SPetr Machata tx_bytes, tx_packets, drops, backlog,
1461cf9af379SPetr Machata stats_ptr);
146293d8a4c1SNogah Frankel return 0;
146393d8a4c1SNogah Frankel }
146493d8a4c1SNogah Frankel
146593d8a4c1SNogah Frankel static void
mlxsw_sp_setup_tc_qdisc_prio_clean_stats(struct mlxsw_sp_port * mlxsw_sp_port,struct mlxsw_sp_qdisc * mlxsw_sp_qdisc)146693d8a4c1SNogah Frankel mlxsw_sp_setup_tc_qdisc_prio_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port,
146793d8a4c1SNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
146893d8a4c1SNogah Frankel {
146993d8a4c1SNogah Frankel struct mlxsw_sp_qdisc_stats *stats_base;
147093d8a4c1SNogah Frankel struct mlxsw_sp_port_xstats *xstats;
147193d8a4c1SNogah Frankel struct rtnl_link_stats64 *stats;
147293d8a4c1SNogah Frankel int i;
147393d8a4c1SNogah Frankel
147493d8a4c1SNogah Frankel xstats = &mlxsw_sp_port->periodic_hw_stats.xstats;
147593d8a4c1SNogah Frankel stats = &mlxsw_sp_port->periodic_hw_stats.stats;
147693d8a4c1SNogah Frankel stats_base = &mlxsw_sp_qdisc->stats_base;
147793d8a4c1SNogah Frankel
147893d8a4c1SNogah Frankel stats_base->tx_packets = stats->tx_packets;
147993d8a4c1SNogah Frankel stats_base->tx_bytes = stats->tx_bytes;
148093d8a4c1SNogah Frankel
148193d8a4c1SNogah Frankel stats_base->drops = 0;
148223f2b404SNogah Frankel for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
148385005b82SPetr Machata stats_base->drops += mlxsw_sp_xstats_tail_drop(xstats, i);
148423f2b404SNogah Frankel stats_base->drops += xstats->wred_drop[i];
148523f2b404SNogah Frankel }
148693d8a4c1SNogah Frankel
148793d8a4c1SNogah Frankel mlxsw_sp_qdisc->stats_base.backlog = 0;
148893d8a4c1SNogah Frankel }
148993d8a4c1SNogah Frankel
149051d52ed9SPetr Machata static struct mlxsw_sp_qdisc *
mlxsw_sp_qdisc_prio_find_class(struct mlxsw_sp_qdisc * mlxsw_sp_qdisc,u32 parent)149151d52ed9SPetr Machata mlxsw_sp_qdisc_prio_find_class(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
149251d52ed9SPetr Machata u32 parent)
149351d52ed9SPetr Machata {
149451d52ed9SPetr Machata int child_index = TC_H_MIN(parent);
149551d52ed9SPetr Machata int band = child_index - 1;
149651d52ed9SPetr Machata
149751d52ed9SPetr Machata if (band < 0 || band >= mlxsw_sp_qdisc->num_classes)
149851d52ed9SPetr Machata return NULL;
149951d52ed9SPetr Machata return &mlxsw_sp_qdisc->qdiscs[band];
150051d52ed9SPetr Machata }
150151d52ed9SPetr Machata
150276ff72a7SPetr Machata 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)150376ff72a7SPetr Machata mlxsw_sp_qdisc_ets_get_band(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
150476ff72a7SPetr Machata struct mlxsw_sp_qdisc *child)
150576ff72a7SPetr Machata {
150676ff72a7SPetr Machata unsigned int band = child - mlxsw_sp_qdisc->qdiscs;
150776ff72a7SPetr Machata
150876ff72a7SPetr Machata if (WARN_ON(band >= IEEE_8021QAZ_MAX_TCS))
150976ff72a7SPetr Machata band = 0;
151076ff72a7SPetr Machata return &mlxsw_sp_qdisc->ets_data->bands[band];
151176ff72a7SPetr Machata }
151276ff72a7SPetr Machata
151376ff72a7SPetr Machata static u8
mlxsw_sp_qdisc_ets_get_prio_bitmap(struct mlxsw_sp_qdisc * mlxsw_sp_qdisc,struct mlxsw_sp_qdisc * child)151476ff72a7SPetr Machata mlxsw_sp_qdisc_ets_get_prio_bitmap(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
151576ff72a7SPetr Machata struct mlxsw_sp_qdisc *child)
151676ff72a7SPetr Machata {
151776ff72a7SPetr Machata return mlxsw_sp_qdisc_ets_get_band(mlxsw_sp_qdisc, child)->prio_bitmap;
151876ff72a7SPetr Machata }
151976ff72a7SPetr Machata
152076ff72a7SPetr Machata static int
mlxsw_sp_qdisc_ets_get_tclass_num(struct mlxsw_sp_qdisc * mlxsw_sp_qdisc,struct mlxsw_sp_qdisc * child)152176ff72a7SPetr Machata mlxsw_sp_qdisc_ets_get_tclass_num(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
152276ff72a7SPetr Machata struct mlxsw_sp_qdisc *child)
152376ff72a7SPetr Machata {
152476ff72a7SPetr Machata return mlxsw_sp_qdisc_ets_get_band(mlxsw_sp_qdisc, child)->tclass_num;
152576ff72a7SPetr Machata }
152676ff72a7SPetr Machata
152746a3615bSNogah Frankel static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_prio = {
152846a3615bSNogah Frankel .type = MLXSW_SP_QDISC_PRIO,
152946a3615bSNogah Frankel .check_params = mlxsw_sp_qdisc_prio_check_params,
153046a3615bSNogah Frankel .replace = mlxsw_sp_qdisc_prio_replace,
153193d8a4c1SNogah Frankel .unoffload = mlxsw_sp_qdisc_prio_unoffload,
153246a3615bSNogah Frankel .destroy = mlxsw_sp_qdisc_prio_destroy,
153393d8a4c1SNogah Frankel .get_stats = mlxsw_sp_qdisc_get_prio_stats,
153493d8a4c1SNogah Frankel .clean_stats = mlxsw_sp_setup_tc_qdisc_prio_clean_stats,
153551d52ed9SPetr Machata .find_class = mlxsw_sp_qdisc_prio_find_class,
15365cbd9602SPetr Machata .num_classes = IEEE_8021QAZ_MAX_TCS,
153776ff72a7SPetr Machata .get_prio_bitmap = mlxsw_sp_qdisc_ets_get_prio_bitmap,
153876ff72a7SPetr Machata .get_tclass_num = mlxsw_sp_qdisc_ets_get_tclass_num,
153946a3615bSNogah Frankel };
154046a3615bSNogah Frankel
154119f405b9SPetr Machata static int
mlxsw_sp_qdisc_ets_check_params(struct mlxsw_sp_port * mlxsw_sp_port,void * params)154219f405b9SPetr Machata mlxsw_sp_qdisc_ets_check_params(struct mlxsw_sp_port *mlxsw_sp_port,
154319f405b9SPetr Machata void *params)
154419f405b9SPetr Machata {
154519f405b9SPetr Machata struct tc_ets_qopt_offload_replace_params *p = params;
154619f405b9SPetr Machata
154719f405b9SPetr Machata return __mlxsw_sp_qdisc_ets_check_params(p->bands);
154819f405b9SPetr Machata }
154919f405b9SPetr Machata
155019f405b9SPetr Machata 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)1551c4e372e2SPetr Machata mlxsw_sp_qdisc_ets_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
155219f405b9SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
155319f405b9SPetr Machata void *params)
155419f405b9SPetr Machata {
155519f405b9SPetr Machata struct tc_ets_qopt_offload_replace_params *p = params;
155619f405b9SPetr Machata
155751d52ed9SPetr Machata return __mlxsw_sp_qdisc_ets_replace(mlxsw_sp_port, mlxsw_sp_qdisc,
155851d52ed9SPetr Machata handle, p->bands, p->quanta,
155951d52ed9SPetr Machata p->weights, p->priomap);
156019f405b9SPetr Machata }
156119f405b9SPetr Machata
156219f405b9SPetr Machata static void
mlxsw_sp_qdisc_ets_unoffload(struct mlxsw_sp_port * mlxsw_sp_port,struct mlxsw_sp_qdisc * mlxsw_sp_qdisc,void * params)156319f405b9SPetr Machata mlxsw_sp_qdisc_ets_unoffload(struct mlxsw_sp_port *mlxsw_sp_port,
156419f405b9SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
156519f405b9SPetr Machata void *params)
156619f405b9SPetr Machata {
156719f405b9SPetr Machata struct tc_ets_qopt_offload_replace_params *p = params;
156819f405b9SPetr Machata
156919f405b9SPetr Machata __mlxsw_sp_qdisc_ets_unoffload(mlxsw_sp_port, mlxsw_sp_qdisc,
157019f405b9SPetr Machata p->qstats);
157119f405b9SPetr Machata }
157219f405b9SPetr Machata
157319f405b9SPetr Machata static int
mlxsw_sp_qdisc_ets_destroy(struct mlxsw_sp_port * mlxsw_sp_port,struct mlxsw_sp_qdisc * mlxsw_sp_qdisc)157419f405b9SPetr Machata mlxsw_sp_qdisc_ets_destroy(struct mlxsw_sp_port *mlxsw_sp_port,
157519f405b9SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
157619f405b9SPetr Machata {
157751d52ed9SPetr Machata return __mlxsw_sp_qdisc_ets_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
157819f405b9SPetr Machata }
157919f405b9SPetr Machata
158019f405b9SPetr Machata static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_ets = {
158119f405b9SPetr Machata .type = MLXSW_SP_QDISC_ETS,
158219f405b9SPetr Machata .check_params = mlxsw_sp_qdisc_ets_check_params,
158319f405b9SPetr Machata .replace = mlxsw_sp_qdisc_ets_replace,
158419f405b9SPetr Machata .unoffload = mlxsw_sp_qdisc_ets_unoffload,
158519f405b9SPetr Machata .destroy = mlxsw_sp_qdisc_ets_destroy,
158619f405b9SPetr Machata .get_stats = mlxsw_sp_qdisc_get_prio_stats,
158719f405b9SPetr Machata .clean_stats = mlxsw_sp_setup_tc_qdisc_prio_clean_stats,
158851d52ed9SPetr Machata .find_class = mlxsw_sp_qdisc_prio_find_class,
15895cbd9602SPetr Machata .num_classes = IEEE_8021QAZ_MAX_TCS,
159076ff72a7SPetr Machata .get_prio_bitmap = mlxsw_sp_qdisc_ets_get_prio_bitmap,
159176ff72a7SPetr Machata .get_tclass_num = mlxsw_sp_qdisc_ets_get_tclass_num,
159219f405b9SPetr Machata };
159319f405b9SPetr Machata
15945bc146c9SPetr Machata /* Linux allows linking of Qdiscs to arbitrary classes (so long as the resulting
15955bc146c9SPetr Machata * graph is free of cycles). These operations do not change the parent handle
15965bc146c9SPetr Machata * though, which means it can be incomplete (if there is more than one class
15975bc146c9SPetr Machata * where the Qdisc in question is grafted) or outright wrong (if the Qdisc was
15985bc146c9SPetr Machata * linked to a different class and then removed from the original class).
15995bc146c9SPetr Machata *
16005bc146c9SPetr Machata * E.g. consider this sequence of operations:
16015bc146c9SPetr Machata *
16025bc146c9SPetr Machata * # tc qdisc add dev swp1 root handle 1: prio
16035bc146c9SPetr Machata * # tc qdisc add dev swp1 parent 1:3 handle 13: red limit 1000000 avpkt 10000
16045bc146c9SPetr Machata * RED: set bandwidth to 10Mbit
16055bc146c9SPetr Machata * # tc qdisc link dev swp1 handle 13: parent 1:2
16065bc146c9SPetr Machata *
16075bc146c9SPetr Machata * At this point, both 1:2 and 1:3 have the same RED Qdisc instance as their
16085bc146c9SPetr Machata * child. But RED will still only claim that 1:3 is its parent. If it's removed
16095bc146c9SPetr Machata * from that band, its only parent will be 1:2, but it will continue to claim
16105bc146c9SPetr Machata * that it is in fact 1:3.
16115bc146c9SPetr Machata *
16125bc146c9SPetr Machata * The notification for child Qdisc replace (e.g. TC_RED_REPLACE) comes before
16135bc146c9SPetr Machata * the notification for parent graft (e.g. TC_PRIO_GRAFT). We take the replace
16145bc146c9SPetr Machata * notification to offload the child Qdisc, based on its parent handle, and use
16155bc146c9SPetr Machata * the graft operation to validate that the class where the child is actually
16165bc146c9SPetr Machata * grafted corresponds to the parent handle. If the two don't match, we
16175bc146c9SPetr Machata * unoffload the child.
161832dc5efcSNogah Frankel */
mlxsw_sp_qdisc_graft(struct mlxsw_sp_port * mlxsw_sp_port,struct mlxsw_sp_qdisc * mlxsw_sp_qdisc,u8 band,u32 child_handle)1619be7e2a5aSPetr Machata static int mlxsw_sp_qdisc_graft(struct mlxsw_sp_port *mlxsw_sp_port,
162032dc5efcSNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
16217917f52aSPetr Machata u8 band, u32 child_handle)
162232dc5efcSNogah Frankel {
162332dc5efcSNogah Frankel struct mlxsw_sp_qdisc *old_qdisc;
1624d566ed04SPetr Machata u32 parent;
162532dc5efcSNogah Frankel
162651d52ed9SPetr Machata if (band < mlxsw_sp_qdisc->num_classes &&
162751d52ed9SPetr Machata mlxsw_sp_qdisc->qdiscs[band].handle == child_handle)
162832dc5efcSNogah Frankel return 0;
162932dc5efcSNogah Frankel
1630a2d6d7aeSDavid S. Miller if (!child_handle) {
16313971a535SPetr Machata /* This is an invisible FIFO replacing the original Qdisc.
16323971a535SPetr Machata * Ignore it--the original Qdisc's destroy will follow.
16333971a535SPetr Machata */
16343971a535SPetr Machata return 0;
16353971a535SPetr Machata }
16363971a535SPetr Machata
163732dc5efcSNogah Frankel /* See if the grafted qdisc is already offloaded on any tclass. If so,
163832dc5efcSNogah Frankel * unoffload it.
163932dc5efcSNogah Frankel */
164032dc5efcSNogah Frankel old_qdisc = mlxsw_sp_qdisc_find_by_handle(mlxsw_sp_port,
16417917f52aSPetr Machata child_handle);
164232dc5efcSNogah Frankel if (old_qdisc)
164332dc5efcSNogah Frankel mlxsw_sp_qdisc_destroy(mlxsw_sp_port, old_qdisc);
164432dc5efcSNogah Frankel
1645d566ed04SPetr Machata parent = TC_H_MAKE(mlxsw_sp_qdisc->handle, band + 1);
1646d566ed04SPetr Machata mlxsw_sp_qdisc = mlxsw_sp_qdisc->ops->find_class(mlxsw_sp_qdisc,
1647d566ed04SPetr Machata parent);
164851d52ed9SPetr Machata if (!WARN_ON(!mlxsw_sp_qdisc))
164951d52ed9SPetr Machata mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
165051d52ed9SPetr Machata
165132dc5efcSNogah Frankel return -EOPNOTSUPP;
165232dc5efcSNogah Frankel }
165332dc5efcSNogah Frankel
__mlxsw_sp_setup_tc_prio(struct mlxsw_sp_port * mlxsw_sp_port,struct tc_prio_qopt_offload * p)1654cff99e20SPetr Machata static int __mlxsw_sp_setup_tc_prio(struct mlxsw_sp_port *mlxsw_sp_port,
165546a3615bSNogah Frankel struct tc_prio_qopt_offload *p)
165646a3615bSNogah Frankel {
165746a3615bSNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc;
165846a3615bSNogah Frankel
1659c2792f38SPetr Machata mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent);
1660eed4baebSNogah Frankel if (!mlxsw_sp_qdisc)
166146a3615bSNogah Frankel return -EOPNOTSUPP;
166246a3615bSNogah Frankel
166346a3615bSNogah Frankel if (p->command == TC_PRIO_REPLACE)
166446a3615bSNogah Frankel return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle,
166546a3615bSNogah Frankel mlxsw_sp_qdisc,
166646a3615bSNogah Frankel &mlxsw_sp_qdisc_ops_prio,
166746a3615bSNogah Frankel &p->replace_params);
166846a3615bSNogah Frankel
1669290fe2c5SPetr Machata if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle))
167046a3615bSNogah Frankel return -EOPNOTSUPP;
167146a3615bSNogah Frankel
167246a3615bSNogah Frankel switch (p->command) {
167346a3615bSNogah Frankel case TC_PRIO_DESTROY:
167446a3615bSNogah Frankel return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
167593d8a4c1SNogah Frankel case TC_PRIO_STATS:
167693d8a4c1SNogah Frankel return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc,
167793d8a4c1SNogah Frankel &p->stats);
167832dc5efcSNogah Frankel case TC_PRIO_GRAFT:
1679be7e2a5aSPetr Machata return mlxsw_sp_qdisc_graft(mlxsw_sp_port, mlxsw_sp_qdisc,
1680be7e2a5aSPetr Machata p->graft_params.band,
1681be7e2a5aSPetr Machata p->graft_params.child_handle);
168246a3615bSNogah Frankel default:
168346a3615bSNogah Frankel return -EOPNOTSUPP;
168446a3615bSNogah Frankel }
168546a3615bSNogah Frankel }
168646a3615bSNogah Frankel
mlxsw_sp_setup_tc_prio(struct mlxsw_sp_port * mlxsw_sp_port,struct tc_prio_qopt_offload * p)1687cff99e20SPetr Machata int mlxsw_sp_setup_tc_prio(struct mlxsw_sp_port *mlxsw_sp_port,
1688cff99e20SPetr Machata struct tc_prio_qopt_offload *p)
1689cff99e20SPetr Machata {
1690cff99e20SPetr Machata int err;
1691cff99e20SPetr Machata
1692cff99e20SPetr Machata mutex_lock(&mlxsw_sp_port->qdisc->lock);
1693cff99e20SPetr Machata err = __mlxsw_sp_setup_tc_prio(mlxsw_sp_port, p);
1694cff99e20SPetr Machata mutex_unlock(&mlxsw_sp_port->qdisc->lock);
1695cff99e20SPetr Machata
1696cff99e20SPetr Machata return err;
1697cff99e20SPetr Machata }
1698cff99e20SPetr Machata
__mlxsw_sp_setup_tc_ets(struct mlxsw_sp_port * mlxsw_sp_port,struct tc_ets_qopt_offload * p)1699cff99e20SPetr Machata static int __mlxsw_sp_setup_tc_ets(struct mlxsw_sp_port *mlxsw_sp_port,
170019f405b9SPetr Machata struct tc_ets_qopt_offload *p)
170119f405b9SPetr Machata {
170219f405b9SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc;
170319f405b9SPetr Machata
1704c2792f38SPetr Machata mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent);
170519f405b9SPetr Machata if (!mlxsw_sp_qdisc)
170619f405b9SPetr Machata return -EOPNOTSUPP;
170719f405b9SPetr Machata
170819f405b9SPetr Machata if (p->command == TC_ETS_REPLACE)
170919f405b9SPetr Machata return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle,
171019f405b9SPetr Machata mlxsw_sp_qdisc,
171119f405b9SPetr Machata &mlxsw_sp_qdisc_ops_ets,
171219f405b9SPetr Machata &p->replace_params);
171319f405b9SPetr Machata
1714290fe2c5SPetr Machata if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle))
171519f405b9SPetr Machata return -EOPNOTSUPP;
171619f405b9SPetr Machata
171719f405b9SPetr Machata switch (p->command) {
171819f405b9SPetr Machata case TC_ETS_DESTROY:
171919f405b9SPetr Machata return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
172019f405b9SPetr Machata case TC_ETS_STATS:
172119f405b9SPetr Machata return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc,
172219f405b9SPetr Machata &p->stats);
172319f405b9SPetr Machata case TC_ETS_GRAFT:
1724be7e2a5aSPetr Machata return mlxsw_sp_qdisc_graft(mlxsw_sp_port, mlxsw_sp_qdisc,
172519f405b9SPetr Machata p->graft_params.band,
172619f405b9SPetr Machata p->graft_params.child_handle);
172719f405b9SPetr Machata default:
172819f405b9SPetr Machata return -EOPNOTSUPP;
172919f405b9SPetr Machata }
173019f405b9SPetr Machata }
173119f405b9SPetr Machata
mlxsw_sp_setup_tc_ets(struct mlxsw_sp_port * mlxsw_sp_port,struct tc_ets_qopt_offload * p)1732cff99e20SPetr Machata int mlxsw_sp_setup_tc_ets(struct mlxsw_sp_port *mlxsw_sp_port,
1733cff99e20SPetr Machata struct tc_ets_qopt_offload *p)
1734cff99e20SPetr Machata {
1735cff99e20SPetr Machata int err;
1736cff99e20SPetr Machata
1737cff99e20SPetr Machata mutex_lock(&mlxsw_sp_port->qdisc->lock);
1738cff99e20SPetr Machata err = __mlxsw_sp_setup_tc_ets(mlxsw_sp_port, p);
1739cff99e20SPetr Machata mutex_unlock(&mlxsw_sp_port->qdisc->lock);
1740cff99e20SPetr Machata
1741cff99e20SPetr Machata return err;
1742cff99e20SPetr Machata }
1743cff99e20SPetr Machata
1744f6668eacSPetr Machata struct mlxsw_sp_qevent_block {
1745f6668eacSPetr Machata struct list_head binding_list;
1746f6668eacSPetr Machata struct list_head mall_entry_list;
1747f6668eacSPetr Machata struct mlxsw_sp *mlxsw_sp;
1748f6668eacSPetr Machata };
1749f6668eacSPetr Machata
1750f6668eacSPetr Machata struct mlxsw_sp_qevent_binding {
1751f6668eacSPetr Machata struct list_head list;
1752f6668eacSPetr Machata struct mlxsw_sp_port *mlxsw_sp_port;
1753f6668eacSPetr Machata u32 handle;
1754f6668eacSPetr Machata int tclass_num;
1755f6668eacSPetr Machata enum mlxsw_sp_span_trigger span_trigger;
1756099bf89dSPetr Machata unsigned int action_mask;
1757f6668eacSPetr Machata };
1758f6668eacSPetr Machata
1759f6668eacSPetr Machata static LIST_HEAD(mlxsw_sp_qevent_block_cb_list);
1760f6668eacSPetr Machata
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)176154a92385SPetr Machata static int mlxsw_sp_qevent_span_configure(struct mlxsw_sp *mlxsw_sp,
1762f6668eacSPetr Machata struct mlxsw_sp_mall_entry *mall_entry,
176354a92385SPetr Machata struct mlxsw_sp_qevent_binding *qevent_binding,
176454a92385SPetr Machata const struct mlxsw_sp_span_agent_parms *agent_parms,
176554a92385SPetr Machata int *p_span_id)
1766f6668eacSPetr Machata {
17670908e42aSPetr Machata enum mlxsw_sp_span_trigger span_trigger = qevent_binding->span_trigger;
1768f6668eacSPetr Machata struct mlxsw_sp_port *mlxsw_sp_port = qevent_binding->mlxsw_sp_port;
1769f6668eacSPetr Machata struct mlxsw_sp_span_trigger_parms trigger_parms = {};
17700908e42aSPetr Machata bool ingress;
1771f6668eacSPetr Machata int span_id;
1772f6668eacSPetr Machata int err;
1773f6668eacSPetr Machata
177454a92385SPetr Machata err = mlxsw_sp_span_agent_get(mlxsw_sp, &span_id, agent_parms);
1775f6668eacSPetr Machata if (err)
1776f6668eacSPetr Machata return err;
1777f6668eacSPetr Machata
17780908e42aSPetr Machata ingress = mlxsw_sp_span_trigger_is_ingress(span_trigger);
17790908e42aSPetr Machata err = mlxsw_sp_span_analyzed_port_get(mlxsw_sp_port, ingress);
1780f6668eacSPetr Machata if (err)
1781f6668eacSPetr Machata goto err_analyzed_port_get;
1782f6668eacSPetr Machata
1783f6668eacSPetr Machata trigger_parms.span_id = span_id;
17842dcbd920SIdo Schimmel trigger_parms.probability_rate = 1;
17850908e42aSPetr Machata err = mlxsw_sp_span_agent_bind(mlxsw_sp, span_trigger, mlxsw_sp_port,
1786f6668eacSPetr Machata &trigger_parms);
1787f6668eacSPetr Machata if (err)
1788f6668eacSPetr Machata goto err_agent_bind;
1789f6668eacSPetr Machata
17900908e42aSPetr Machata err = mlxsw_sp_span_trigger_enable(mlxsw_sp_port, span_trigger,
1791f6668eacSPetr Machata qevent_binding->tclass_num);
1792f6668eacSPetr Machata if (err)
1793f6668eacSPetr Machata goto err_trigger_enable;
1794f6668eacSPetr Machata
179554a92385SPetr Machata *p_span_id = span_id;
1796f6668eacSPetr Machata return 0;
1797f6668eacSPetr Machata
1798f6668eacSPetr Machata err_trigger_enable:
17990908e42aSPetr Machata mlxsw_sp_span_agent_unbind(mlxsw_sp, span_trigger, mlxsw_sp_port,
1800f6668eacSPetr Machata &trigger_parms);
1801f6668eacSPetr Machata err_agent_bind:
18020908e42aSPetr Machata mlxsw_sp_span_analyzed_port_put(mlxsw_sp_port, ingress);
1803f6668eacSPetr Machata err_analyzed_port_get:
1804f6668eacSPetr Machata mlxsw_sp_span_agent_put(mlxsw_sp, span_id);
1805f6668eacSPetr Machata return err;
1806f6668eacSPetr Machata }
1807f6668eacSPetr Machata
mlxsw_sp_qevent_span_deconfigure(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_qevent_binding * qevent_binding,int span_id)180854a92385SPetr Machata static void mlxsw_sp_qevent_span_deconfigure(struct mlxsw_sp *mlxsw_sp,
180954a92385SPetr Machata struct mlxsw_sp_qevent_binding *qevent_binding,
181054a92385SPetr Machata int span_id)
1811f6668eacSPetr Machata {
18120908e42aSPetr Machata enum mlxsw_sp_span_trigger span_trigger = qevent_binding->span_trigger;
1813f6668eacSPetr Machata struct mlxsw_sp_port *mlxsw_sp_port = qevent_binding->mlxsw_sp_port;
1814f6668eacSPetr Machata struct mlxsw_sp_span_trigger_parms trigger_parms = {
181554a92385SPetr Machata .span_id = span_id,
1816f6668eacSPetr Machata };
18170908e42aSPetr Machata bool ingress;
1818f6668eacSPetr Machata
18190908e42aSPetr Machata ingress = mlxsw_sp_span_trigger_is_ingress(span_trigger);
18200908e42aSPetr Machata
18210908e42aSPetr Machata mlxsw_sp_span_trigger_disable(mlxsw_sp_port, span_trigger,
1822f6668eacSPetr Machata qevent_binding->tclass_num);
18230908e42aSPetr Machata mlxsw_sp_span_agent_unbind(mlxsw_sp, span_trigger, mlxsw_sp_port,
1824f6668eacSPetr Machata &trigger_parms);
18250908e42aSPetr Machata mlxsw_sp_span_analyzed_port_put(mlxsw_sp_port, ingress);
182654a92385SPetr Machata mlxsw_sp_span_agent_put(mlxsw_sp, span_id);
182754a92385SPetr Machata }
182854a92385SPetr Machata
mlxsw_sp_qevent_mirror_configure(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_mall_entry * mall_entry,struct mlxsw_sp_qevent_binding * qevent_binding)182954a92385SPetr Machata static int mlxsw_sp_qevent_mirror_configure(struct mlxsw_sp *mlxsw_sp,
183054a92385SPetr Machata struct mlxsw_sp_mall_entry *mall_entry,
183154a92385SPetr Machata struct mlxsw_sp_qevent_binding *qevent_binding)
183254a92385SPetr Machata {
183354a92385SPetr Machata struct mlxsw_sp_span_agent_parms agent_parms = {
183454a92385SPetr Machata .to_dev = mall_entry->mirror.to_dev,
183554a92385SPetr Machata };
183654a92385SPetr Machata
183754a92385SPetr Machata return mlxsw_sp_qevent_span_configure(mlxsw_sp, mall_entry, qevent_binding,
183854a92385SPetr Machata &agent_parms, &mall_entry->mirror.span_id);
183954a92385SPetr Machata }
184054a92385SPetr Machata
mlxsw_sp_qevent_mirror_deconfigure(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_mall_entry * mall_entry,struct mlxsw_sp_qevent_binding * qevent_binding)184154a92385SPetr Machata static void mlxsw_sp_qevent_mirror_deconfigure(struct mlxsw_sp *mlxsw_sp,
184254a92385SPetr Machata struct mlxsw_sp_mall_entry *mall_entry,
184354a92385SPetr Machata struct mlxsw_sp_qevent_binding *qevent_binding)
184454a92385SPetr Machata {
184554a92385SPetr Machata mlxsw_sp_qevent_span_deconfigure(mlxsw_sp, qevent_binding, mall_entry->mirror.span_id);
184654a92385SPetr Machata }
184754a92385SPetr Machata
mlxsw_sp_qevent_trap_configure(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_mall_entry * mall_entry,struct mlxsw_sp_qevent_binding * qevent_binding)184854a92385SPetr Machata static int mlxsw_sp_qevent_trap_configure(struct mlxsw_sp *mlxsw_sp,
184954a92385SPetr Machata struct mlxsw_sp_mall_entry *mall_entry,
185054a92385SPetr Machata struct mlxsw_sp_qevent_binding *qevent_binding)
185154a92385SPetr Machata {
18525c7659ebSIdo Schimmel struct mlxsw_sp_span_agent_parms agent_parms = {
18535c7659ebSIdo Schimmel .session_id = MLXSW_SP_SPAN_SESSION_ID_BUFFER,
18545c7659ebSIdo Schimmel };
185554a92385SPetr Machata int err;
185654a92385SPetr Machata
185754a92385SPetr Machata err = mlxsw_sp_trap_group_policer_hw_id_get(mlxsw_sp,
185854a92385SPetr Machata DEVLINK_TRAP_GROUP_GENERIC_ID_BUFFER_DROPS,
185954a92385SPetr Machata &agent_parms.policer_enable,
186054a92385SPetr Machata &agent_parms.policer_id);
186154a92385SPetr Machata if (err)
186254a92385SPetr Machata return err;
186354a92385SPetr Machata
186454a92385SPetr Machata return mlxsw_sp_qevent_span_configure(mlxsw_sp, mall_entry, qevent_binding,
186554a92385SPetr Machata &agent_parms, &mall_entry->trap.span_id);
186654a92385SPetr Machata }
186754a92385SPetr Machata
mlxsw_sp_qevent_trap_deconfigure(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_mall_entry * mall_entry,struct mlxsw_sp_qevent_binding * qevent_binding)186854a92385SPetr Machata static void mlxsw_sp_qevent_trap_deconfigure(struct mlxsw_sp *mlxsw_sp,
186954a92385SPetr Machata struct mlxsw_sp_mall_entry *mall_entry,
187054a92385SPetr Machata struct mlxsw_sp_qevent_binding *qevent_binding)
187154a92385SPetr Machata {
187254a92385SPetr Machata mlxsw_sp_qevent_span_deconfigure(mlxsw_sp, qevent_binding, mall_entry->trap.span_id);
1873f6668eacSPetr Machata }
1874f6668eacSPetr Machata
1875a34dda72SPetr Machata 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)1876a34dda72SPetr Machata mlxsw_sp_qevent_entry_configure(struct mlxsw_sp *mlxsw_sp,
1877f6668eacSPetr Machata struct mlxsw_sp_mall_entry *mall_entry,
1878a34dda72SPetr Machata struct mlxsw_sp_qevent_binding *qevent_binding,
1879a34dda72SPetr Machata struct netlink_ext_ack *extack)
1880f6668eacSPetr Machata {
1881099bf89dSPetr Machata if (!(BIT(mall_entry->type) & qevent_binding->action_mask)) {
1882099bf89dSPetr Machata NL_SET_ERR_MSG(extack, "Action not supported at this qevent");
1883099bf89dSPetr Machata return -EOPNOTSUPP;
1884099bf89dSPetr Machata }
1885099bf89dSPetr Machata
1886f6668eacSPetr Machata switch (mall_entry->type) {
1887f6668eacSPetr Machata case MLXSW_SP_MALL_ACTION_TYPE_MIRROR:
1888f6668eacSPetr Machata return mlxsw_sp_qevent_mirror_configure(mlxsw_sp, mall_entry, qevent_binding);
188954a92385SPetr Machata case MLXSW_SP_MALL_ACTION_TYPE_TRAP:
189054a92385SPetr Machata return mlxsw_sp_qevent_trap_configure(mlxsw_sp, mall_entry, qevent_binding);
1891f6668eacSPetr Machata default:
1892f6668eacSPetr Machata /* This should have been validated away. */
1893f6668eacSPetr Machata WARN_ON(1);
1894f6668eacSPetr Machata return -EOPNOTSUPP;
1895f6668eacSPetr Machata }
1896f6668eacSPetr Machata }
1897f6668eacSPetr Machata
mlxsw_sp_qevent_entry_deconfigure(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_mall_entry * mall_entry,struct mlxsw_sp_qevent_binding * qevent_binding)1898f6668eacSPetr Machata static void mlxsw_sp_qevent_entry_deconfigure(struct mlxsw_sp *mlxsw_sp,
1899f6668eacSPetr Machata struct mlxsw_sp_mall_entry *mall_entry,
1900f6668eacSPetr Machata struct mlxsw_sp_qevent_binding *qevent_binding)
1901f6668eacSPetr Machata {
1902f6668eacSPetr Machata switch (mall_entry->type) {
1903f6668eacSPetr Machata case MLXSW_SP_MALL_ACTION_TYPE_MIRROR:
1904f6668eacSPetr Machata return mlxsw_sp_qevent_mirror_deconfigure(mlxsw_sp, mall_entry, qevent_binding);
190554a92385SPetr Machata case MLXSW_SP_MALL_ACTION_TYPE_TRAP:
190654a92385SPetr Machata return mlxsw_sp_qevent_trap_deconfigure(mlxsw_sp, mall_entry, qevent_binding);
1907f6668eacSPetr Machata default:
1908f6668eacSPetr Machata WARN_ON(1);
1909f6668eacSPetr Machata return;
1910f6668eacSPetr Machata }
1911f6668eacSPetr Machata }
1912f6668eacSPetr Machata
1913a34dda72SPetr Machata 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)1914a34dda72SPetr Machata mlxsw_sp_qevent_binding_configure(struct mlxsw_sp_qevent_block *qevent_block,
1915a34dda72SPetr Machata struct mlxsw_sp_qevent_binding *qevent_binding,
1916a34dda72SPetr Machata struct netlink_ext_ack *extack)
1917f6668eacSPetr Machata {
1918f6668eacSPetr Machata struct mlxsw_sp_mall_entry *mall_entry;
1919f6668eacSPetr Machata int err;
1920f6668eacSPetr Machata
1921f6668eacSPetr Machata list_for_each_entry(mall_entry, &qevent_block->mall_entry_list, list) {
1922f6668eacSPetr Machata err = mlxsw_sp_qevent_entry_configure(qevent_block->mlxsw_sp, mall_entry,
1923a34dda72SPetr Machata qevent_binding, extack);
1924f6668eacSPetr Machata if (err)
1925f6668eacSPetr Machata goto err_entry_configure;
1926f6668eacSPetr Machata }
1927f6668eacSPetr Machata
1928f6668eacSPetr Machata return 0;
1929f6668eacSPetr Machata
1930f6668eacSPetr Machata err_entry_configure:
1931f6668eacSPetr Machata list_for_each_entry_continue_reverse(mall_entry, &qevent_block->mall_entry_list, list)
1932f6668eacSPetr Machata mlxsw_sp_qevent_entry_deconfigure(qevent_block->mlxsw_sp, mall_entry,
1933f6668eacSPetr Machata qevent_binding);
1934f6668eacSPetr Machata return err;
1935f6668eacSPetr Machata }
1936f6668eacSPetr Machata
mlxsw_sp_qevent_binding_deconfigure(struct mlxsw_sp_qevent_block * qevent_block,struct mlxsw_sp_qevent_binding * qevent_binding)1937f6668eacSPetr Machata static void mlxsw_sp_qevent_binding_deconfigure(struct mlxsw_sp_qevent_block *qevent_block,
1938f6668eacSPetr Machata struct mlxsw_sp_qevent_binding *qevent_binding)
1939f6668eacSPetr Machata {
1940f6668eacSPetr Machata struct mlxsw_sp_mall_entry *mall_entry;
1941f6668eacSPetr Machata
1942f6668eacSPetr Machata list_for_each_entry(mall_entry, &qevent_block->mall_entry_list, list)
1943f6668eacSPetr Machata mlxsw_sp_qevent_entry_deconfigure(qevent_block->mlxsw_sp, mall_entry,
1944f6668eacSPetr Machata qevent_binding);
1945f6668eacSPetr Machata }
1946f6668eacSPetr Machata
1947a34dda72SPetr Machata static int
mlxsw_sp_qevent_block_configure(struct mlxsw_sp_qevent_block * qevent_block,struct netlink_ext_ack * extack)1948a34dda72SPetr Machata mlxsw_sp_qevent_block_configure(struct mlxsw_sp_qevent_block *qevent_block,
1949a34dda72SPetr Machata struct netlink_ext_ack *extack)
1950f6668eacSPetr Machata {
1951f6668eacSPetr Machata struct mlxsw_sp_qevent_binding *qevent_binding;
1952f6668eacSPetr Machata int err;
1953f6668eacSPetr Machata
1954f6668eacSPetr Machata list_for_each_entry(qevent_binding, &qevent_block->binding_list, list) {
1955a34dda72SPetr Machata err = mlxsw_sp_qevent_binding_configure(qevent_block,
1956a34dda72SPetr Machata qevent_binding,
1957a34dda72SPetr Machata extack);
1958f6668eacSPetr Machata if (err)
1959f6668eacSPetr Machata goto err_binding_configure;
1960f6668eacSPetr Machata }
1961f6668eacSPetr Machata
1962f6668eacSPetr Machata return 0;
1963f6668eacSPetr Machata
1964f6668eacSPetr Machata err_binding_configure:
1965f6668eacSPetr Machata list_for_each_entry_continue_reverse(qevent_binding, &qevent_block->binding_list, list)
1966f6668eacSPetr Machata mlxsw_sp_qevent_binding_deconfigure(qevent_block, qevent_binding);
1967f6668eacSPetr Machata return err;
1968f6668eacSPetr Machata }
1969f6668eacSPetr Machata
mlxsw_sp_qevent_block_deconfigure(struct mlxsw_sp_qevent_block * qevent_block)1970f6668eacSPetr Machata static void mlxsw_sp_qevent_block_deconfigure(struct mlxsw_sp_qevent_block *qevent_block)
1971f6668eacSPetr Machata {
1972f6668eacSPetr Machata struct mlxsw_sp_qevent_binding *qevent_binding;
1973f6668eacSPetr Machata
1974f6668eacSPetr Machata list_for_each_entry(qevent_binding, &qevent_block->binding_list, list)
1975f6668eacSPetr Machata mlxsw_sp_qevent_binding_deconfigure(qevent_block, qevent_binding);
1976f6668eacSPetr Machata }
1977f6668eacSPetr Machata
1978f6668eacSPetr Machata static struct mlxsw_sp_mall_entry *
mlxsw_sp_qevent_mall_entry_find(struct mlxsw_sp_qevent_block * block,unsigned long cookie)1979f6668eacSPetr Machata mlxsw_sp_qevent_mall_entry_find(struct mlxsw_sp_qevent_block *block, unsigned long cookie)
1980f6668eacSPetr Machata {
1981f6668eacSPetr Machata struct mlxsw_sp_mall_entry *mall_entry;
1982f6668eacSPetr Machata
1983f6668eacSPetr Machata list_for_each_entry(mall_entry, &block->mall_entry_list, list)
1984f6668eacSPetr Machata if (mall_entry->cookie == cookie)
1985f6668eacSPetr Machata return mall_entry;
1986f6668eacSPetr Machata
1987f6668eacSPetr Machata return NULL;
1988f6668eacSPetr Machata }
1989f6668eacSPetr Machata
mlxsw_sp_qevent_mall_replace(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_qevent_block * qevent_block,struct tc_cls_matchall_offload * f)1990f6668eacSPetr Machata static int mlxsw_sp_qevent_mall_replace(struct mlxsw_sp *mlxsw_sp,
1991f6668eacSPetr Machata struct mlxsw_sp_qevent_block *qevent_block,
1992f6668eacSPetr Machata struct tc_cls_matchall_offload *f)
1993f6668eacSPetr Machata {
1994f6668eacSPetr Machata struct mlxsw_sp_mall_entry *mall_entry;
1995f6668eacSPetr Machata struct flow_action_entry *act;
1996f6668eacSPetr Machata int err;
1997f6668eacSPetr Machata
1998f6668eacSPetr Machata /* It should not currently be possible to replace a matchall rule. So
1999f6668eacSPetr Machata * this must be a new rule.
2000f6668eacSPetr Machata */
2001f6668eacSPetr Machata if (!list_empty(&qevent_block->mall_entry_list)) {
2002f6668eacSPetr Machata NL_SET_ERR_MSG(f->common.extack, "At most one filter supported");
2003f6668eacSPetr Machata return -EOPNOTSUPP;
2004f6668eacSPetr Machata }
2005f6668eacSPetr Machata if (f->rule->action.num_entries != 1) {
2006f6668eacSPetr Machata NL_SET_ERR_MSG(f->common.extack, "Only singular actions supported");
2007f6668eacSPetr Machata return -EOPNOTSUPP;
2008f6668eacSPetr Machata }
2009f6668eacSPetr Machata if (f->common.chain_index) {
2010f6668eacSPetr Machata NL_SET_ERR_MSG(f->common.extack, "Only chain 0 is supported");
2011f6668eacSPetr Machata return -EOPNOTSUPP;
2012f6668eacSPetr Machata }
2013f6668eacSPetr Machata if (f->common.protocol != htons(ETH_P_ALL)) {
2014f6668eacSPetr Machata NL_SET_ERR_MSG(f->common.extack, "Protocol matching not supported");
2015f6668eacSPetr Machata return -EOPNOTSUPP;
2016f6668eacSPetr Machata }
2017f6668eacSPetr Machata
2018f6668eacSPetr Machata act = &f->rule->action.entries[0];
2019f6668eacSPetr Machata if (!(act->hw_stats & FLOW_ACTION_HW_STATS_DISABLED)) {
2020f6668eacSPetr Machata NL_SET_ERR_MSG(f->common.extack, "HW counters not supported on qevents");
2021f6668eacSPetr Machata return -EOPNOTSUPP;
2022f6668eacSPetr Machata }
2023f6668eacSPetr Machata
2024f6668eacSPetr Machata mall_entry = kzalloc(sizeof(*mall_entry), GFP_KERNEL);
2025f6668eacSPetr Machata if (!mall_entry)
2026f6668eacSPetr Machata return -ENOMEM;
2027f6668eacSPetr Machata mall_entry->cookie = f->cookie;
2028f6668eacSPetr Machata
2029f6668eacSPetr Machata if (act->id == FLOW_ACTION_MIRRED) {
2030f6668eacSPetr Machata mall_entry->type = MLXSW_SP_MALL_ACTION_TYPE_MIRROR;
2031f6668eacSPetr Machata mall_entry->mirror.to_dev = act->dev;
203254a92385SPetr Machata } else if (act->id == FLOW_ACTION_TRAP) {
203354a92385SPetr Machata mall_entry->type = MLXSW_SP_MALL_ACTION_TYPE_TRAP;
2034f6668eacSPetr Machata } else {
2035f6668eacSPetr Machata NL_SET_ERR_MSG(f->common.extack, "Unsupported action");
2036f6668eacSPetr Machata err = -EOPNOTSUPP;
2037f6668eacSPetr Machata goto err_unsupported_action;
2038f6668eacSPetr Machata }
2039f6668eacSPetr Machata
2040f6668eacSPetr Machata list_add_tail(&mall_entry->list, &qevent_block->mall_entry_list);
2041f6668eacSPetr Machata
2042a34dda72SPetr Machata err = mlxsw_sp_qevent_block_configure(qevent_block, f->common.extack);
2043f6668eacSPetr Machata if (err)
2044f6668eacSPetr Machata goto err_block_configure;
2045f6668eacSPetr Machata
2046f6668eacSPetr Machata return 0;
2047f6668eacSPetr Machata
2048f6668eacSPetr Machata err_block_configure:
2049f6668eacSPetr Machata list_del(&mall_entry->list);
2050f6668eacSPetr Machata err_unsupported_action:
2051f6668eacSPetr Machata kfree(mall_entry);
2052f6668eacSPetr Machata return err;
2053f6668eacSPetr Machata }
2054f6668eacSPetr Machata
mlxsw_sp_qevent_mall_destroy(struct mlxsw_sp_qevent_block * qevent_block,struct tc_cls_matchall_offload * f)2055f6668eacSPetr Machata static void mlxsw_sp_qevent_mall_destroy(struct mlxsw_sp_qevent_block *qevent_block,
2056f6668eacSPetr Machata struct tc_cls_matchall_offload *f)
2057f6668eacSPetr Machata {
2058f6668eacSPetr Machata struct mlxsw_sp_mall_entry *mall_entry;
2059f6668eacSPetr Machata
2060f6668eacSPetr Machata mall_entry = mlxsw_sp_qevent_mall_entry_find(qevent_block, f->cookie);
2061f6668eacSPetr Machata if (!mall_entry)
2062f6668eacSPetr Machata return;
2063f6668eacSPetr Machata
2064f6668eacSPetr Machata mlxsw_sp_qevent_block_deconfigure(qevent_block);
2065f6668eacSPetr Machata
2066f6668eacSPetr Machata list_del(&mall_entry->list);
2067f6668eacSPetr Machata kfree(mall_entry);
2068f6668eacSPetr Machata }
2069f6668eacSPetr Machata
mlxsw_sp_qevent_block_mall_cb(struct mlxsw_sp_qevent_block * qevent_block,struct tc_cls_matchall_offload * f)2070f6668eacSPetr Machata static int mlxsw_sp_qevent_block_mall_cb(struct mlxsw_sp_qevent_block *qevent_block,
2071f6668eacSPetr Machata struct tc_cls_matchall_offload *f)
2072f6668eacSPetr Machata {
2073f6668eacSPetr Machata struct mlxsw_sp *mlxsw_sp = qevent_block->mlxsw_sp;
2074f6668eacSPetr Machata
2075f6668eacSPetr Machata switch (f->command) {
2076f6668eacSPetr Machata case TC_CLSMATCHALL_REPLACE:
2077f6668eacSPetr Machata return mlxsw_sp_qevent_mall_replace(mlxsw_sp, qevent_block, f);
2078f6668eacSPetr Machata case TC_CLSMATCHALL_DESTROY:
2079f6668eacSPetr Machata mlxsw_sp_qevent_mall_destroy(qevent_block, f);
2080f6668eacSPetr Machata return 0;
2081f6668eacSPetr Machata default:
2082f6668eacSPetr Machata return -EOPNOTSUPP;
2083f6668eacSPetr Machata }
2084f6668eacSPetr Machata }
2085f6668eacSPetr Machata
mlxsw_sp_qevent_block_cb(enum tc_setup_type type,void * type_data,void * cb_priv)2086f6668eacSPetr Machata static int mlxsw_sp_qevent_block_cb(enum tc_setup_type type, void *type_data, void *cb_priv)
2087f6668eacSPetr Machata {
2088f6668eacSPetr Machata struct mlxsw_sp_qevent_block *qevent_block = cb_priv;
2089f6668eacSPetr Machata
2090f6668eacSPetr Machata switch (type) {
2091f6668eacSPetr Machata case TC_SETUP_CLSMATCHALL:
2092f6668eacSPetr Machata return mlxsw_sp_qevent_block_mall_cb(qevent_block, type_data);
2093f6668eacSPetr Machata default:
2094f6668eacSPetr Machata return -EOPNOTSUPP;
2095f6668eacSPetr Machata }
2096f6668eacSPetr Machata }
2097f6668eacSPetr Machata
mlxsw_sp_qevent_block_create(struct mlxsw_sp * mlxsw_sp,struct net * net)2098f6668eacSPetr Machata static struct mlxsw_sp_qevent_block *mlxsw_sp_qevent_block_create(struct mlxsw_sp *mlxsw_sp,
2099f6668eacSPetr Machata struct net *net)
2100f6668eacSPetr Machata {
2101f6668eacSPetr Machata struct mlxsw_sp_qevent_block *qevent_block;
2102f6668eacSPetr Machata
2103f6668eacSPetr Machata qevent_block = kzalloc(sizeof(*qevent_block), GFP_KERNEL);
2104f6668eacSPetr Machata if (!qevent_block)
2105f6668eacSPetr Machata return NULL;
2106f6668eacSPetr Machata
2107f6668eacSPetr Machata INIT_LIST_HEAD(&qevent_block->binding_list);
2108f6668eacSPetr Machata INIT_LIST_HEAD(&qevent_block->mall_entry_list);
2109f6668eacSPetr Machata qevent_block->mlxsw_sp = mlxsw_sp;
2110f6668eacSPetr Machata return qevent_block;
2111f6668eacSPetr Machata }
2112f6668eacSPetr Machata
2113f6668eacSPetr Machata static void
mlxsw_sp_qevent_block_destroy(struct mlxsw_sp_qevent_block * qevent_block)2114f6668eacSPetr Machata mlxsw_sp_qevent_block_destroy(struct mlxsw_sp_qevent_block *qevent_block)
2115f6668eacSPetr Machata {
2116f6668eacSPetr Machata WARN_ON(!list_empty(&qevent_block->binding_list));
2117f6668eacSPetr Machata WARN_ON(!list_empty(&qevent_block->mall_entry_list));
2118f6668eacSPetr Machata kfree(qevent_block);
2119f6668eacSPetr Machata }
2120f6668eacSPetr Machata
mlxsw_sp_qevent_block_release(void * cb_priv)2121f6668eacSPetr Machata static void mlxsw_sp_qevent_block_release(void *cb_priv)
2122f6668eacSPetr Machata {
2123f6668eacSPetr Machata struct mlxsw_sp_qevent_block *qevent_block = cb_priv;
2124f6668eacSPetr Machata
2125f6668eacSPetr Machata mlxsw_sp_qevent_block_destroy(qevent_block);
2126f6668eacSPetr Machata }
2127f6668eacSPetr Machata
2128f6668eacSPetr Machata 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)2129f6668eacSPetr Machata mlxsw_sp_qevent_binding_create(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, int tclass_num,
2130099bf89dSPetr Machata enum mlxsw_sp_span_trigger span_trigger,
2131099bf89dSPetr Machata unsigned int action_mask)
2132f6668eacSPetr Machata {
2133f6668eacSPetr Machata struct mlxsw_sp_qevent_binding *binding;
2134f6668eacSPetr Machata
2135f6668eacSPetr Machata binding = kzalloc(sizeof(*binding), GFP_KERNEL);
2136f6668eacSPetr Machata if (!binding)
2137f6668eacSPetr Machata return ERR_PTR(-ENOMEM);
2138f6668eacSPetr Machata
2139f6668eacSPetr Machata binding->mlxsw_sp_port = mlxsw_sp_port;
2140f6668eacSPetr Machata binding->handle = handle;
2141f6668eacSPetr Machata binding->tclass_num = tclass_num;
2142f6668eacSPetr Machata binding->span_trigger = span_trigger;
2143099bf89dSPetr Machata binding->action_mask = action_mask;
2144f6668eacSPetr Machata return binding;
2145f6668eacSPetr Machata }
2146f6668eacSPetr Machata
2147f6668eacSPetr Machata static void
mlxsw_sp_qevent_binding_destroy(struct mlxsw_sp_qevent_binding * binding)2148f6668eacSPetr Machata mlxsw_sp_qevent_binding_destroy(struct mlxsw_sp_qevent_binding *binding)
2149f6668eacSPetr Machata {
2150f6668eacSPetr Machata kfree(binding);
2151f6668eacSPetr Machata }
2152f6668eacSPetr Machata
2153f6668eacSPetr Machata 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)2154f6668eacSPetr Machata mlxsw_sp_qevent_binding_lookup(struct mlxsw_sp_qevent_block *block,
2155f6668eacSPetr Machata struct mlxsw_sp_port *mlxsw_sp_port,
2156f6668eacSPetr Machata u32 handle,
2157f6668eacSPetr Machata enum mlxsw_sp_span_trigger span_trigger)
2158f6668eacSPetr Machata {
2159f6668eacSPetr Machata struct mlxsw_sp_qevent_binding *qevent_binding;
2160f6668eacSPetr Machata
2161f6668eacSPetr Machata list_for_each_entry(qevent_binding, &block->binding_list, list)
2162f6668eacSPetr Machata if (qevent_binding->mlxsw_sp_port == mlxsw_sp_port &&
2163f6668eacSPetr Machata qevent_binding->handle == handle &&
2164f6668eacSPetr Machata qevent_binding->span_trigger == span_trigger)
2165f6668eacSPetr Machata return qevent_binding;
2166f6668eacSPetr Machata return NULL;
2167f6668eacSPetr Machata }
2168f6668eacSPetr Machata
2169099bf89dSPetr Machata 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)2170099bf89dSPetr Machata mlxsw_sp_setup_tc_block_qevent_bind(struct mlxsw_sp_port *mlxsw_sp_port,
2171f6668eacSPetr Machata struct flow_block_offload *f,
2172099bf89dSPetr Machata enum mlxsw_sp_span_trigger span_trigger,
2173099bf89dSPetr Machata unsigned int action_mask)
2174f6668eacSPetr Machata {
2175f6668eacSPetr Machata struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
2176f6668eacSPetr Machata struct mlxsw_sp_qevent_binding *qevent_binding;
2177f6668eacSPetr Machata struct mlxsw_sp_qevent_block *qevent_block;
2178f6668eacSPetr Machata struct flow_block_cb *block_cb;
2179f6668eacSPetr Machata struct mlxsw_sp_qdisc *qdisc;
2180f6668eacSPetr Machata bool register_block = false;
218176ff72a7SPetr Machata int tclass_num;
2182f6668eacSPetr Machata int err;
2183f6668eacSPetr Machata
2184f6668eacSPetr Machata block_cb = flow_block_cb_lookup(f->block, mlxsw_sp_qevent_block_cb, mlxsw_sp);
2185f6668eacSPetr Machata if (!block_cb) {
2186f6668eacSPetr Machata qevent_block = mlxsw_sp_qevent_block_create(mlxsw_sp, f->net);
2187f6668eacSPetr Machata if (!qevent_block)
2188f6668eacSPetr Machata return -ENOMEM;
2189f6668eacSPetr Machata block_cb = flow_block_cb_alloc(mlxsw_sp_qevent_block_cb, mlxsw_sp, qevent_block,
2190f6668eacSPetr Machata mlxsw_sp_qevent_block_release);
2191f6668eacSPetr Machata if (IS_ERR(block_cb)) {
2192f6668eacSPetr Machata mlxsw_sp_qevent_block_destroy(qevent_block);
2193f6668eacSPetr Machata return PTR_ERR(block_cb);
2194f6668eacSPetr Machata }
2195f6668eacSPetr Machata register_block = true;
2196f6668eacSPetr Machata } else {
2197f6668eacSPetr Machata qevent_block = flow_block_cb_priv(block_cb);
2198f6668eacSPetr Machata }
2199f6668eacSPetr Machata flow_block_cb_incref(block_cb);
2200f6668eacSPetr Machata
2201f6668eacSPetr Machata qdisc = mlxsw_sp_qdisc_find_by_handle(mlxsw_sp_port, f->sch->handle);
2202f6668eacSPetr Machata if (!qdisc) {
2203f6668eacSPetr Machata NL_SET_ERR_MSG(f->extack, "Qdisc not offloaded");
2204f6668eacSPetr Machata err = -ENOENT;
2205f6668eacSPetr Machata goto err_find_qdisc;
2206f6668eacSPetr Machata }
2207f6668eacSPetr Machata
2208f6668eacSPetr Machata if (WARN_ON(mlxsw_sp_qevent_binding_lookup(qevent_block, mlxsw_sp_port, f->sch->handle,
2209f6668eacSPetr Machata span_trigger))) {
2210f6668eacSPetr Machata err = -EEXIST;
2211f6668eacSPetr Machata goto err_binding_exists;
2212f6668eacSPetr Machata }
2213f6668eacSPetr Machata
221476ff72a7SPetr Machata tclass_num = mlxsw_sp_qdisc_get_tclass_num(mlxsw_sp_port, qdisc);
2215099bf89dSPetr Machata qevent_binding = mlxsw_sp_qevent_binding_create(mlxsw_sp_port,
2216099bf89dSPetr Machata f->sch->handle,
221776ff72a7SPetr Machata tclass_num,
2218099bf89dSPetr Machata span_trigger,
2219099bf89dSPetr Machata action_mask);
2220f6668eacSPetr Machata if (IS_ERR(qevent_binding)) {
2221f6668eacSPetr Machata err = PTR_ERR(qevent_binding);
2222f6668eacSPetr Machata goto err_binding_create;
2223f6668eacSPetr Machata }
2224f6668eacSPetr Machata
2225a34dda72SPetr Machata err = mlxsw_sp_qevent_binding_configure(qevent_block, qevent_binding,
2226a34dda72SPetr Machata f->extack);
2227f6668eacSPetr Machata if (err)
2228f6668eacSPetr Machata goto err_binding_configure;
2229f6668eacSPetr Machata
2230f6668eacSPetr Machata list_add(&qevent_binding->list, &qevent_block->binding_list);
2231f6668eacSPetr Machata
2232f6668eacSPetr Machata if (register_block) {
2233f6668eacSPetr Machata flow_block_cb_add(block_cb, f);
2234f6668eacSPetr Machata list_add_tail(&block_cb->driver_list, &mlxsw_sp_qevent_block_cb_list);
2235f6668eacSPetr Machata }
2236f6668eacSPetr Machata
2237f6668eacSPetr Machata return 0;
2238f6668eacSPetr Machata
2239f6668eacSPetr Machata err_binding_configure:
2240f6668eacSPetr Machata mlxsw_sp_qevent_binding_destroy(qevent_binding);
2241f6668eacSPetr Machata err_binding_create:
2242f6668eacSPetr Machata err_binding_exists:
2243f6668eacSPetr Machata err_find_qdisc:
2244f6668eacSPetr Machata if (!flow_block_cb_decref(block_cb))
2245f6668eacSPetr Machata flow_block_cb_free(block_cb);
2246f6668eacSPetr Machata return err;
2247f6668eacSPetr Machata }
2248f6668eacSPetr Machata
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)2249f6668eacSPetr Machata static void mlxsw_sp_setup_tc_block_qevent_unbind(struct mlxsw_sp_port *mlxsw_sp_port,
2250f6668eacSPetr Machata struct flow_block_offload *f,
2251f6668eacSPetr Machata enum mlxsw_sp_span_trigger span_trigger)
2252f6668eacSPetr Machata {
2253f6668eacSPetr Machata struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
2254f6668eacSPetr Machata struct mlxsw_sp_qevent_binding *qevent_binding;
2255f6668eacSPetr Machata struct mlxsw_sp_qevent_block *qevent_block;
2256f6668eacSPetr Machata struct flow_block_cb *block_cb;
2257f6668eacSPetr Machata
2258f6668eacSPetr Machata block_cb = flow_block_cb_lookup(f->block, mlxsw_sp_qevent_block_cb, mlxsw_sp);
2259f6668eacSPetr Machata if (!block_cb)
2260f6668eacSPetr Machata return;
2261f6668eacSPetr Machata qevent_block = flow_block_cb_priv(block_cb);
2262f6668eacSPetr Machata
2263f6668eacSPetr Machata qevent_binding = mlxsw_sp_qevent_binding_lookup(qevent_block, mlxsw_sp_port, f->sch->handle,
2264f6668eacSPetr Machata span_trigger);
2265f6668eacSPetr Machata if (!qevent_binding)
2266f6668eacSPetr Machata return;
2267f6668eacSPetr Machata
2268f6668eacSPetr Machata list_del(&qevent_binding->list);
2269f6668eacSPetr Machata mlxsw_sp_qevent_binding_deconfigure(qevent_block, qevent_binding);
2270f6668eacSPetr Machata mlxsw_sp_qevent_binding_destroy(qevent_binding);
2271f6668eacSPetr Machata
2272f6668eacSPetr Machata if (!flow_block_cb_decref(block_cb)) {
2273f6668eacSPetr Machata flow_block_cb_remove(block_cb, f);
2274f6668eacSPetr Machata list_del(&block_cb->driver_list);
2275f6668eacSPetr Machata }
2276f6668eacSPetr Machata }
2277f6668eacSPetr Machata
2278099bf89dSPetr Machata 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)2279099bf89dSPetr Machata mlxsw_sp_setup_tc_block_qevent(struct mlxsw_sp_port *mlxsw_sp_port,
2280f6668eacSPetr Machata struct flow_block_offload *f,
2281099bf89dSPetr Machata enum mlxsw_sp_span_trigger span_trigger,
2282099bf89dSPetr Machata unsigned int action_mask)
2283f6668eacSPetr Machata {
2284f6668eacSPetr Machata f->driver_block_list = &mlxsw_sp_qevent_block_cb_list;
2285f6668eacSPetr Machata
2286f6668eacSPetr Machata switch (f->command) {
2287f6668eacSPetr Machata case FLOW_BLOCK_BIND:
2288099bf89dSPetr Machata return mlxsw_sp_setup_tc_block_qevent_bind(mlxsw_sp_port, f,
2289099bf89dSPetr Machata span_trigger,
2290099bf89dSPetr Machata action_mask);
2291f6668eacSPetr Machata case FLOW_BLOCK_UNBIND:
2292f6668eacSPetr Machata mlxsw_sp_setup_tc_block_qevent_unbind(mlxsw_sp_port, f, span_trigger);
2293f6668eacSPetr Machata return 0;
2294f6668eacSPetr Machata default:
2295f6668eacSPetr Machata return -EOPNOTSUPP;
2296f6668eacSPetr Machata }
2297f6668eacSPetr Machata }
2298f6668eacSPetr Machata
mlxsw_sp_setup_tc_block_qevent_early_drop(struct mlxsw_sp_port * mlxsw_sp_port,struct flow_block_offload * f)2299f6668eacSPetr Machata int mlxsw_sp_setup_tc_block_qevent_early_drop(struct mlxsw_sp_port *mlxsw_sp_port,
2300f6668eacSPetr Machata struct flow_block_offload *f)
2301f6668eacSPetr Machata {
2302099bf89dSPetr Machata unsigned int action_mask = BIT(MLXSW_SP_MALL_ACTION_TYPE_MIRROR) |
2303099bf89dSPetr Machata BIT(MLXSW_SP_MALL_ACTION_TYPE_TRAP);
2304099bf89dSPetr Machata
2305099bf89dSPetr Machata return mlxsw_sp_setup_tc_block_qevent(mlxsw_sp_port, f,
2306099bf89dSPetr Machata MLXSW_SP_SPAN_TRIGGER_EARLY_DROP,
2307099bf89dSPetr Machata action_mask);
2308f6668eacSPetr Machata }
2309f6668eacSPetr Machata
mlxsw_sp_setup_tc_block_qevent_mark(struct mlxsw_sp_port * mlxsw_sp_port,struct flow_block_offload * f)23109c18eaf2SPetr Machata int mlxsw_sp_setup_tc_block_qevent_mark(struct mlxsw_sp_port *mlxsw_sp_port,
23119c18eaf2SPetr Machata struct flow_block_offload *f)
23129c18eaf2SPetr Machata {
23139c18eaf2SPetr Machata unsigned int action_mask = BIT(MLXSW_SP_MALL_ACTION_TYPE_MIRROR);
23149c18eaf2SPetr Machata
23159c18eaf2SPetr Machata return mlxsw_sp_setup_tc_block_qevent(mlxsw_sp_port, f,
23169c18eaf2SPetr Machata MLXSW_SP_SPAN_TRIGGER_ECN,
23179c18eaf2SPetr Machata action_mask);
23189c18eaf2SPetr Machata }
23199c18eaf2SPetr Machata
mlxsw_sp_tc_qdisc_init(struct mlxsw_sp_port * mlxsw_sp_port)2320371b437aSNogah Frankel int mlxsw_sp_tc_qdisc_init(struct mlxsw_sp_port *mlxsw_sp_port)
2321371b437aSNogah Frankel {
2322ee88450dSPetr Machata struct mlxsw_sp_qdisc_state *qdisc_state;
2323371b437aSNogah Frankel
2324ee88450dSPetr Machata qdisc_state = kzalloc(sizeof(*qdisc_state), GFP_KERNEL);
2325ee88450dSPetr Machata if (!qdisc_state)
2326eed4baebSNogah Frankel return -ENOMEM;
2327ee88450dSPetr Machata
2328cff99e20SPetr Machata mutex_init(&qdisc_state->lock);
2329ee88450dSPetr Machata mlxsw_sp_port->qdisc = qdisc_state;
2330ee88450dSPetr Machata return 0;
2331371b437aSNogah Frankel }
2332371b437aSNogah Frankel
mlxsw_sp_tc_qdisc_fini(struct mlxsw_sp_port * mlxsw_sp_port)2333371b437aSNogah Frankel void mlxsw_sp_tc_qdisc_fini(struct mlxsw_sp_port *mlxsw_sp_port)
2334371b437aSNogah Frankel {
2335cff99e20SPetr Machata mutex_destroy(&mlxsw_sp_port->qdisc->lock);
2336ee88450dSPetr Machata kfree(mlxsw_sp_port->qdisc);
2337371b437aSNogah Frankel }
2338