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 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 * 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 * 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 * 160*c2792f38SPetr 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 * 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 * 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 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 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; 21076ff72a7SPetr Machata return parent->ops->get_prio_bitmap(parent, mlxsw_sp_qdisc); 21176ff72a7SPetr Machata } 21276ff72a7SPetr Machata 21376ff72a7SPetr Machata #define MLXSW_SP_PORT_DEFAULT_TCLASS 0 21476ff72a7SPetr Machata 21576ff72a7SPetr Machata static int mlxsw_sp_qdisc_get_tclass_num(struct mlxsw_sp_port *mlxsw_sp_port, 21676ff72a7SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 21776ff72a7SPetr Machata { 21876ff72a7SPetr Machata struct mlxsw_sp_qdisc *parent = mlxsw_sp_qdisc->parent; 21976ff72a7SPetr Machata 22076ff72a7SPetr Machata if (!parent) 22176ff72a7SPetr Machata return MLXSW_SP_PORT_DEFAULT_TCLASS; 22276ff72a7SPetr Machata return parent->ops->get_tclass_num(parent, mlxsw_sp_qdisc); 22376ff72a7SPetr Machata } 22476ff72a7SPetr Machata 22596f17e07SNogah Frankel static int 2269a37a59fSNogah Frankel mlxsw_sp_qdisc_destroy(struct mlxsw_sp_port *mlxsw_sp_port, 2279a37a59fSNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 2289a37a59fSNogah Frankel { 229509f04caSPetr Machata struct mlxsw_sp_qdisc *root_qdisc = &mlxsw_sp_port->qdisc->root_qdisc; 230509f04caSPetr Machata int err_hdroom = 0; 2319a37a59fSNogah Frankel int err = 0; 23265626e07SPetr Machata int i; 2339a37a59fSNogah Frankel 2349a37a59fSNogah Frankel if (!mlxsw_sp_qdisc) 2359a37a59fSNogah Frankel return 0; 2369a37a59fSNogah Frankel 237509f04caSPetr Machata if (root_qdisc == mlxsw_sp_qdisc) { 238509f04caSPetr Machata struct mlxsw_sp_hdroom hdroom = *mlxsw_sp_port->hdroom; 239509f04caSPetr Machata 240509f04caSPetr Machata hdroom.mode = MLXSW_SP_HDROOM_MODE_DCB; 241509f04caSPetr Machata mlxsw_sp_hdroom_prios_reset_buf_idx(&hdroom); 242509f04caSPetr Machata mlxsw_sp_hdroom_bufs_reset_lossiness(&hdroom); 243509f04caSPetr Machata mlxsw_sp_hdroom_bufs_reset_sizes(mlxsw_sp_port, &hdroom); 244509f04caSPetr Machata err_hdroom = mlxsw_sp_hdroom_configure(mlxsw_sp_port, &hdroom); 245509f04caSPetr Machata } 246509f04caSPetr Machata 247b21832b5SPetr Machata if (!mlxsw_sp_qdisc->ops) 248b21832b5SPetr Machata return 0; 249b21832b5SPetr Machata 25065626e07SPetr Machata for (i = 0; i < mlxsw_sp_qdisc->num_classes; i++) 25165626e07SPetr Machata mlxsw_sp_qdisc_destroy(mlxsw_sp_port, 25265626e07SPetr Machata &mlxsw_sp_qdisc->qdiscs[i]); 253b21832b5SPetr Machata mlxsw_sp_qdisc_reduce_parent_backlog(mlxsw_sp_qdisc); 254b21832b5SPetr Machata if (mlxsw_sp_qdisc->ops->destroy) 2559a37a59fSNogah Frankel err = mlxsw_sp_qdisc->ops->destroy(mlxsw_sp_port, 2569a37a59fSNogah Frankel mlxsw_sp_qdisc); 2575cbd9602SPetr Machata if (mlxsw_sp_qdisc->ops->clean_stats) 2585cbd9602SPetr Machata mlxsw_sp_qdisc->ops->clean_stats(mlxsw_sp_port, mlxsw_sp_qdisc); 2599a37a59fSNogah Frankel 2609a37a59fSNogah Frankel mlxsw_sp_qdisc->handle = TC_H_UNSPEC; 2619a37a59fSNogah Frankel mlxsw_sp_qdisc->ops = NULL; 2625cbd9602SPetr Machata mlxsw_sp_qdisc->num_classes = 0; 2635cbd9602SPetr Machata kfree(mlxsw_sp_qdisc->qdiscs); 2645cbd9602SPetr Machata mlxsw_sp_qdisc->qdiscs = NULL; 265509f04caSPetr Machata return err_hdroom ?: err; 2669a37a59fSNogah Frankel } 2679a37a59fSNogah Frankel 268*c2792f38SPetr Machata struct mlxsw_sp_qdisc_tree_validate { 269*c2792f38SPetr Machata bool forbid_ets; 270*c2792f38SPetr Machata bool forbid_tbf; 271*c2792f38SPetr Machata bool forbid_red; 272*c2792f38SPetr Machata }; 273*c2792f38SPetr Machata 274*c2792f38SPetr Machata static int 275*c2792f38SPetr Machata __mlxsw_sp_qdisc_tree_validate(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 276*c2792f38SPetr Machata struct mlxsw_sp_qdisc_tree_validate validate); 277*c2792f38SPetr Machata 278*c2792f38SPetr Machata static int 279*c2792f38SPetr Machata mlxsw_sp_qdisc_tree_validate_children(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 280*c2792f38SPetr Machata struct mlxsw_sp_qdisc_tree_validate validate) 281*c2792f38SPetr Machata { 282*c2792f38SPetr Machata unsigned int i; 283*c2792f38SPetr Machata int err; 284*c2792f38SPetr Machata 285*c2792f38SPetr Machata for (i = 0; i < mlxsw_sp_qdisc->num_classes; i++) { 286*c2792f38SPetr Machata err = __mlxsw_sp_qdisc_tree_validate(&mlxsw_sp_qdisc->qdiscs[i], 287*c2792f38SPetr Machata validate); 288*c2792f38SPetr Machata if (err) 289*c2792f38SPetr Machata return err; 290*c2792f38SPetr Machata } 291*c2792f38SPetr Machata 292*c2792f38SPetr Machata return 0; 293*c2792f38SPetr Machata } 294*c2792f38SPetr Machata 295*c2792f38SPetr Machata static int 296*c2792f38SPetr Machata __mlxsw_sp_qdisc_tree_validate(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 297*c2792f38SPetr Machata struct mlxsw_sp_qdisc_tree_validate validate) 298*c2792f38SPetr Machata { 299*c2792f38SPetr Machata if (!mlxsw_sp_qdisc->ops) 300*c2792f38SPetr Machata return 0; 301*c2792f38SPetr Machata 302*c2792f38SPetr Machata switch (mlxsw_sp_qdisc->ops->type) { 303*c2792f38SPetr Machata case MLXSW_SP_QDISC_FIFO: 304*c2792f38SPetr Machata break; 305*c2792f38SPetr Machata case MLXSW_SP_QDISC_RED: 306*c2792f38SPetr Machata if (validate.forbid_red) 307*c2792f38SPetr Machata return -EINVAL; 308*c2792f38SPetr Machata validate.forbid_red = true; 309*c2792f38SPetr Machata validate.forbid_ets = true; 310*c2792f38SPetr Machata break; 311*c2792f38SPetr Machata case MLXSW_SP_QDISC_TBF: 312*c2792f38SPetr Machata if (validate.forbid_tbf) 313*c2792f38SPetr Machata return -EINVAL; 314*c2792f38SPetr Machata validate.forbid_tbf = true; 315*c2792f38SPetr Machata validate.forbid_ets = true; 316*c2792f38SPetr Machata break; 317*c2792f38SPetr Machata case MLXSW_SP_QDISC_PRIO: 318*c2792f38SPetr Machata case MLXSW_SP_QDISC_ETS: 319*c2792f38SPetr Machata if (validate.forbid_ets) 320*c2792f38SPetr Machata return -EINVAL; 321*c2792f38SPetr Machata validate.forbid_ets = true; 322*c2792f38SPetr Machata break; 323*c2792f38SPetr Machata default: 324*c2792f38SPetr Machata WARN_ON(1); 325*c2792f38SPetr Machata return -EINVAL; 326*c2792f38SPetr Machata } 327*c2792f38SPetr Machata 328*c2792f38SPetr Machata return mlxsw_sp_qdisc_tree_validate_children(mlxsw_sp_qdisc, validate); 329*c2792f38SPetr Machata } 330*c2792f38SPetr Machata 331*c2792f38SPetr Machata static int mlxsw_sp_qdisc_tree_validate(struct mlxsw_sp_port *mlxsw_sp_port) 332*c2792f38SPetr Machata { 333*c2792f38SPetr Machata struct mlxsw_sp_qdisc_tree_validate validate = {}; 334*c2792f38SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc; 335*c2792f38SPetr Machata 336*c2792f38SPetr Machata mlxsw_sp_qdisc = &mlxsw_sp_port->qdisc->root_qdisc; 337*c2792f38SPetr Machata return __mlxsw_sp_qdisc_tree_validate(mlxsw_sp_qdisc, validate); 338*c2792f38SPetr Machata } 339*c2792f38SPetr Machata 3405cbd9602SPetr Machata static int mlxsw_sp_qdisc_create(struct mlxsw_sp_port *mlxsw_sp_port, 3415cbd9602SPetr Machata u32 handle, 3429cf6c9c7SNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 3439cf6c9c7SNogah Frankel struct mlxsw_sp_qdisc_ops *ops, void *params) 3449cf6c9c7SNogah Frankel { 345509f04caSPetr Machata struct mlxsw_sp_qdisc *root_qdisc = &mlxsw_sp_port->qdisc->root_qdisc; 346509f04caSPetr Machata struct mlxsw_sp_hdroom orig_hdroom; 3475cbd9602SPetr Machata unsigned int i; 3489cf6c9c7SNogah Frankel int err; 3499cf6c9c7SNogah Frankel 3505cbd9602SPetr Machata err = ops->check_params(mlxsw_sp_port, params); 3515cbd9602SPetr Machata if (err) 3525cbd9602SPetr Machata return err; 3535cbd9602SPetr Machata 3545cbd9602SPetr Machata if (ops->num_classes) { 3555cbd9602SPetr Machata mlxsw_sp_qdisc->qdiscs = kcalloc(ops->num_classes, 3565cbd9602SPetr Machata sizeof(*mlxsw_sp_qdisc->qdiscs), 3575cbd9602SPetr Machata GFP_KERNEL); 3585cbd9602SPetr Machata if (!mlxsw_sp_qdisc->qdiscs) 3595cbd9602SPetr Machata return -ENOMEM; 3605cbd9602SPetr Machata 3615cbd9602SPetr Machata for (i = 0; i < ops->num_classes; i++) 3625cbd9602SPetr Machata mlxsw_sp_qdisc->qdiscs[i].parent = mlxsw_sp_qdisc; 3635cbd9602SPetr Machata } 364509f04caSPetr Machata 365509f04caSPetr Machata orig_hdroom = *mlxsw_sp_port->hdroom; 366509f04caSPetr Machata if (root_qdisc == mlxsw_sp_qdisc) { 367509f04caSPetr Machata struct mlxsw_sp_hdroom hdroom = orig_hdroom; 368509f04caSPetr Machata 369509f04caSPetr Machata hdroom.mode = MLXSW_SP_HDROOM_MODE_TC; 370509f04caSPetr Machata mlxsw_sp_hdroom_prios_reset_buf_idx(&hdroom); 371509f04caSPetr Machata mlxsw_sp_hdroom_bufs_reset_lossiness(&hdroom); 372509f04caSPetr Machata mlxsw_sp_hdroom_bufs_reset_sizes(mlxsw_sp_port, &hdroom); 373509f04caSPetr Machata 374509f04caSPetr Machata err = mlxsw_sp_hdroom_configure(mlxsw_sp_port, &hdroom); 375509f04caSPetr Machata if (err) 376509f04caSPetr Machata goto err_hdroom_configure; 377509f04caSPetr Machata } 378509f04caSPetr Machata 3795cbd9602SPetr Machata mlxsw_sp_qdisc->num_classes = ops->num_classes; 3805cbd9602SPetr Machata mlxsw_sp_qdisc->ops = ops; 3815cbd9602SPetr Machata mlxsw_sp_qdisc->handle = handle; 382*c2792f38SPetr Machata err = mlxsw_sp_qdisc_tree_validate(mlxsw_sp_port); 383*c2792f38SPetr Machata if (err) 384*c2792f38SPetr Machata goto err_replace; 385*c2792f38SPetr Machata 3865cbd9602SPetr Machata err = ops->replace(mlxsw_sp_port, handle, mlxsw_sp_qdisc, params); 3875cbd9602SPetr Machata if (err) 3885cbd9602SPetr Machata goto err_replace; 3895cbd9602SPetr Machata 3905cbd9602SPetr Machata return 0; 3915cbd9602SPetr Machata 3925cbd9602SPetr Machata err_replace: 3935cbd9602SPetr Machata mlxsw_sp_qdisc->handle = TC_H_UNSPEC; 3945cbd9602SPetr Machata mlxsw_sp_qdisc->ops = NULL; 3955cbd9602SPetr Machata mlxsw_sp_qdisc->num_classes = 0; 3965cbd9602SPetr Machata mlxsw_sp_hdroom_configure(mlxsw_sp_port, &orig_hdroom); 3975cbd9602SPetr Machata err_hdroom_configure: 3985cbd9602SPetr Machata kfree(mlxsw_sp_qdisc->qdiscs); 3995cbd9602SPetr Machata mlxsw_sp_qdisc->qdiscs = NULL; 4005cbd9602SPetr Machata return err; 4015cbd9602SPetr Machata } 4025cbd9602SPetr Machata 4035cbd9602SPetr Machata static int 4045cbd9602SPetr Machata mlxsw_sp_qdisc_change(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, 4055cbd9602SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, void *params) 4065cbd9602SPetr Machata { 4075cbd9602SPetr Machata struct mlxsw_sp_qdisc_ops *ops = mlxsw_sp_qdisc->ops; 4085cbd9602SPetr Machata int err; 4095cbd9602SPetr Machata 41017c0e6d1SPetr Machata err = ops->check_params(mlxsw_sp_port, params); 4119cf6c9c7SNogah Frankel if (err) 4125cbd9602SPetr Machata goto unoffload; 4139cf6c9c7SNogah Frankel 414c4e372e2SPetr Machata err = ops->replace(mlxsw_sp_port, handle, mlxsw_sp_qdisc, params); 4159cf6c9c7SNogah Frankel if (err) 4165cbd9602SPetr Machata goto unoffload; 4179cf6c9c7SNogah Frankel 4187bec1a45SPetr Machata /* Check if the Qdisc changed. That includes a situation where an 4197bec1a45SPetr Machata * invisible Qdisc replaces another one, or is being added for the 4207bec1a45SPetr Machata * first time. 4217bec1a45SPetr Machata */ 4225cbd9602SPetr Machata if (mlxsw_sp_qdisc->handle != handle) { 4239cf6c9c7SNogah Frankel if (ops->clean_stats) 4249cf6c9c7SNogah Frankel ops->clean_stats(mlxsw_sp_port, mlxsw_sp_qdisc); 4259cf6c9c7SNogah Frankel } 4269cf6c9c7SNogah Frankel 4279cf6c9c7SNogah Frankel mlxsw_sp_qdisc->handle = handle; 4289cf6c9c7SNogah Frankel return 0; 4299cf6c9c7SNogah Frankel 4305cbd9602SPetr Machata unoffload: 4315cbd9602SPetr Machata if (ops->unoffload) 43293d8a4c1SNogah Frankel ops->unoffload(mlxsw_sp_port, mlxsw_sp_qdisc, params); 43393d8a4c1SNogah Frankel 4349cf6c9c7SNogah Frankel mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc); 4359cf6c9c7SNogah Frankel return err; 4369cf6c9c7SNogah Frankel } 4379cf6c9c7SNogah Frankel 4389cf6c9c7SNogah Frankel static int 4395cbd9602SPetr Machata mlxsw_sp_qdisc_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, 4405cbd9602SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 4415cbd9602SPetr Machata struct mlxsw_sp_qdisc_ops *ops, void *params) 4425cbd9602SPetr Machata { 4435cbd9602SPetr Machata if (mlxsw_sp_qdisc->ops && mlxsw_sp_qdisc->ops->type != ops->type) 4445cbd9602SPetr Machata /* In case this location contained a different qdisc of the 4455cbd9602SPetr Machata * same type we can override the old qdisc configuration. 4465cbd9602SPetr Machata * Otherwise, we need to remove the old qdisc before setting the 4475cbd9602SPetr Machata * new one. 4485cbd9602SPetr Machata */ 4495cbd9602SPetr Machata mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc); 4505cbd9602SPetr Machata 4515cbd9602SPetr Machata if (!mlxsw_sp_qdisc->ops) 4525cbd9602SPetr Machata return mlxsw_sp_qdisc_create(mlxsw_sp_port, handle, 4535cbd9602SPetr Machata mlxsw_sp_qdisc, ops, params); 4545cbd9602SPetr Machata else 4555cbd9602SPetr Machata return mlxsw_sp_qdisc_change(mlxsw_sp_port, handle, 4565cbd9602SPetr Machata mlxsw_sp_qdisc, params); 4575cbd9602SPetr Machata } 4585cbd9602SPetr Machata 4595cbd9602SPetr Machata static int 460562ffbc4SNogah Frankel mlxsw_sp_qdisc_get_stats(struct mlxsw_sp_port *mlxsw_sp_port, 461562ffbc4SNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 462562ffbc4SNogah Frankel struct tc_qopt_offload_stats *stats_ptr) 463562ffbc4SNogah Frankel { 464562ffbc4SNogah Frankel if (mlxsw_sp_qdisc && mlxsw_sp_qdisc->ops && 465562ffbc4SNogah Frankel mlxsw_sp_qdisc->ops->get_stats) 466562ffbc4SNogah Frankel return mlxsw_sp_qdisc->ops->get_stats(mlxsw_sp_port, 467562ffbc4SNogah Frankel mlxsw_sp_qdisc, 468562ffbc4SNogah Frankel stats_ptr); 469562ffbc4SNogah Frankel 470562ffbc4SNogah Frankel return -EOPNOTSUPP; 471562ffbc4SNogah Frankel } 472562ffbc4SNogah Frankel 473562ffbc4SNogah Frankel static int 474562ffbc4SNogah Frankel mlxsw_sp_qdisc_get_xstats(struct mlxsw_sp_port *mlxsw_sp_port, 475562ffbc4SNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 476562ffbc4SNogah Frankel void *xstats_ptr) 477562ffbc4SNogah Frankel { 478562ffbc4SNogah Frankel if (mlxsw_sp_qdisc && mlxsw_sp_qdisc->ops && 479562ffbc4SNogah Frankel mlxsw_sp_qdisc->ops->get_xstats) 480562ffbc4SNogah Frankel return mlxsw_sp_qdisc->ops->get_xstats(mlxsw_sp_port, 481562ffbc4SNogah Frankel mlxsw_sp_qdisc, 482562ffbc4SNogah Frankel xstats_ptr); 483562ffbc4SNogah Frankel 484562ffbc4SNogah Frankel return -EOPNOTSUPP; 485562ffbc4SNogah Frankel } 486562ffbc4SNogah Frankel 48785005b82SPetr Machata static u64 48885005b82SPetr Machata mlxsw_sp_xstats_backlog(struct mlxsw_sp_port_xstats *xstats, int tclass_num) 48985005b82SPetr Machata { 49085005b82SPetr Machata return xstats->backlog[tclass_num] + 49185005b82SPetr Machata xstats->backlog[tclass_num + 8]; 49285005b82SPetr Machata } 49385005b82SPetr Machata 49485005b82SPetr Machata static u64 49585005b82SPetr Machata mlxsw_sp_xstats_tail_drop(struct mlxsw_sp_port_xstats *xstats, int tclass_num) 49685005b82SPetr Machata { 49785005b82SPetr Machata return xstats->tail_drop[tclass_num] + 49885005b82SPetr Machata xstats->tail_drop[tclass_num + 8]; 49985005b82SPetr Machata } 50085005b82SPetr Machata 50104cc0bf5SNogah Frankel static void 50204cc0bf5SNogah Frankel mlxsw_sp_qdisc_bstats_per_priority_get(struct mlxsw_sp_port_xstats *xstats, 50304cc0bf5SNogah Frankel u8 prio_bitmap, u64 *tx_packets, 50404cc0bf5SNogah Frankel u64 *tx_bytes) 50504cc0bf5SNogah Frankel { 50604cc0bf5SNogah Frankel int i; 50704cc0bf5SNogah Frankel 50804cc0bf5SNogah Frankel *tx_packets = 0; 50904cc0bf5SNogah Frankel *tx_bytes = 0; 51004cc0bf5SNogah Frankel for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { 51104cc0bf5SNogah Frankel if (prio_bitmap & BIT(i)) { 51204cc0bf5SNogah Frankel *tx_packets += xstats->tx_packets[i]; 51304cc0bf5SNogah Frankel *tx_bytes += xstats->tx_bytes[i]; 51404cc0bf5SNogah Frankel } 51504cc0bf5SNogah Frankel } 51604cc0bf5SNogah Frankel } 51704cc0bf5SNogah Frankel 518cf9af379SPetr Machata static void 519cf9af379SPetr Machata mlxsw_sp_qdisc_collect_tc_stats(struct mlxsw_sp_port *mlxsw_sp_port, 520cf9af379SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 521cf9af379SPetr Machata u64 *p_tx_bytes, u64 *p_tx_packets, 522cf9af379SPetr Machata u64 *p_drops, u64 *p_backlog) 523cf9af379SPetr Machata { 524cf9af379SPetr Machata struct mlxsw_sp_port_xstats *xstats; 525cf9af379SPetr Machata u64 tx_bytes, tx_packets; 52676ff72a7SPetr Machata u8 prio_bitmap; 52776ff72a7SPetr Machata int tclass_num; 528cf9af379SPetr Machata 52976ff72a7SPetr Machata prio_bitmap = mlxsw_sp_qdisc_get_prio_bitmap(mlxsw_sp_port, 53076ff72a7SPetr Machata mlxsw_sp_qdisc); 53176ff72a7SPetr Machata tclass_num = mlxsw_sp_qdisc_get_tclass_num(mlxsw_sp_port, 53276ff72a7SPetr Machata mlxsw_sp_qdisc); 533cf9af379SPetr Machata xstats = &mlxsw_sp_port->periodic_hw_stats.xstats; 53476ff72a7SPetr Machata mlxsw_sp_qdisc_bstats_per_priority_get(xstats, prio_bitmap, 535cf9af379SPetr Machata &tx_packets, &tx_bytes); 536cf9af379SPetr Machata 537cf9af379SPetr Machata *p_tx_packets += tx_packets; 538cf9af379SPetr Machata *p_tx_bytes += tx_bytes; 539cf9af379SPetr Machata *p_drops += xstats->wred_drop[tclass_num] + 540cf9af379SPetr Machata mlxsw_sp_xstats_tail_drop(xstats, tclass_num); 541cf9af379SPetr Machata *p_backlog += mlxsw_sp_xstats_backlog(xstats, tclass_num); 542cf9af379SPetr Machata } 543cf9af379SPetr Machata 544cf9af379SPetr Machata static void 545cf9af379SPetr Machata mlxsw_sp_qdisc_update_stats(struct mlxsw_sp *mlxsw_sp, 546cf9af379SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 547cf9af379SPetr Machata u64 tx_bytes, u64 tx_packets, 548cf9af379SPetr Machata u64 drops, u64 backlog, 549cf9af379SPetr Machata struct tc_qopt_offload_stats *stats_ptr) 550cf9af379SPetr Machata { 551cf9af379SPetr Machata struct mlxsw_sp_qdisc_stats *stats_base = &mlxsw_sp_qdisc->stats_base; 552cf9af379SPetr Machata 553cf9af379SPetr Machata tx_bytes -= stats_base->tx_bytes; 554cf9af379SPetr Machata tx_packets -= stats_base->tx_packets; 555cf9af379SPetr Machata drops -= stats_base->drops; 556cf9af379SPetr Machata backlog -= stats_base->backlog; 557cf9af379SPetr Machata 558cf9af379SPetr Machata _bstats_update(stats_ptr->bstats, tx_bytes, tx_packets); 559cf9af379SPetr Machata stats_ptr->qstats->drops += drops; 560cf9af379SPetr Machata stats_ptr->qstats->backlog += mlxsw_sp_cells_bytes(mlxsw_sp, backlog); 561cf9af379SPetr Machata 562cf9af379SPetr Machata stats_base->backlog += backlog; 563cf9af379SPetr Machata stats_base->drops += drops; 564cf9af379SPetr Machata stats_base->tx_bytes += tx_bytes; 565cf9af379SPetr Machata stats_base->tx_packets += tx_packets; 566cf9af379SPetr Machata } 567cf9af379SPetr Machata 5683d0d5921SPetr Machata static void 5693d0d5921SPetr Machata mlxsw_sp_qdisc_get_tc_stats(struct mlxsw_sp_port *mlxsw_sp_port, 5703d0d5921SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 5713d0d5921SPetr Machata struct tc_qopt_offload_stats *stats_ptr) 5723d0d5921SPetr Machata { 5733d0d5921SPetr Machata u64 tx_packets = 0; 5743d0d5921SPetr Machata u64 tx_bytes = 0; 5753d0d5921SPetr Machata u64 backlog = 0; 5763d0d5921SPetr Machata u64 drops = 0; 5773d0d5921SPetr Machata 5783d0d5921SPetr Machata mlxsw_sp_qdisc_collect_tc_stats(mlxsw_sp_port, mlxsw_sp_qdisc, 5793d0d5921SPetr Machata &tx_bytes, &tx_packets, 5803d0d5921SPetr Machata &drops, &backlog); 5813d0d5921SPetr Machata mlxsw_sp_qdisc_update_stats(mlxsw_sp_port->mlxsw_sp, mlxsw_sp_qdisc, 5823d0d5921SPetr Machata tx_bytes, tx_packets, drops, backlog, 5833d0d5921SPetr Machata stats_ptr); 5843d0d5921SPetr Machata } 5853d0d5921SPetr Machata 586562ffbc4SNogah Frankel static int 58796f17e07SNogah Frankel mlxsw_sp_tclass_congestion_enable(struct mlxsw_sp_port *mlxsw_sp_port, 58896f17e07SNogah Frankel int tclass_num, u32 min, u32 max, 5898040c96bSPetr Machata u32 probability, bool is_wred, bool is_ecn) 59096f17e07SNogah Frankel { 591db84924cSJiri Pirko char cwtpm_cmd[MLXSW_REG_CWTPM_LEN]; 592db84924cSJiri Pirko char cwtp_cmd[MLXSW_REG_CWTP_LEN]; 59396f17e07SNogah Frankel struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 59496f17e07SNogah Frankel int err; 59596f17e07SNogah Frankel 59696f17e07SNogah Frankel mlxsw_reg_cwtp_pack(cwtp_cmd, mlxsw_sp_port->local_port, tclass_num); 59796f17e07SNogah Frankel mlxsw_reg_cwtp_profile_pack(cwtp_cmd, MLXSW_REG_CWTP_DEFAULT_PROFILE, 59896f17e07SNogah Frankel roundup(min, MLXSW_REG_CWTP_MIN_VALUE), 59996f17e07SNogah Frankel roundup(max, MLXSW_REG_CWTP_MIN_VALUE), 60096f17e07SNogah Frankel probability); 60196f17e07SNogah Frankel 60296f17e07SNogah Frankel err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(cwtp), cwtp_cmd); 60396f17e07SNogah Frankel if (err) 60496f17e07SNogah Frankel return err; 60596f17e07SNogah Frankel 606db84924cSJiri Pirko mlxsw_reg_cwtpm_pack(cwtpm_cmd, mlxsw_sp_port->local_port, tclass_num, 6078040c96bSPetr Machata MLXSW_REG_CWTP_DEFAULT_PROFILE, is_wred, is_ecn); 60896f17e07SNogah Frankel 609db84924cSJiri Pirko return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(cwtpm), cwtpm_cmd); 61096f17e07SNogah Frankel } 61196f17e07SNogah Frankel 61296f17e07SNogah Frankel static int 61396f17e07SNogah Frankel mlxsw_sp_tclass_congestion_disable(struct mlxsw_sp_port *mlxsw_sp_port, 61496f17e07SNogah Frankel int tclass_num) 61596f17e07SNogah Frankel { 61696f17e07SNogah Frankel struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 61796f17e07SNogah Frankel char cwtpm_cmd[MLXSW_REG_CWTPM_LEN]; 61896f17e07SNogah Frankel 61996f17e07SNogah Frankel mlxsw_reg_cwtpm_pack(cwtpm_cmd, mlxsw_sp_port->local_port, tclass_num, 62096f17e07SNogah Frankel MLXSW_REG_CWTPM_RESET_PROFILE, false, false); 62196f17e07SNogah Frankel return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(cwtpm), cwtpm_cmd); 62296f17e07SNogah Frankel } 62396f17e07SNogah Frankel 624861fb829SNogah Frankel static void 625c2ed6db7SNogah Frankel mlxsw_sp_setup_tc_qdisc_red_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port, 626d56c8955SNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 627861fb829SNogah Frankel { 6284d1a4b84SNogah Frankel struct mlxsw_sp_qdisc_stats *stats_base; 629861fb829SNogah Frankel struct mlxsw_sp_port_xstats *xstats; 6304d1a4b84SNogah Frankel struct red_stats *red_base; 63176ff72a7SPetr Machata u8 prio_bitmap; 63276ff72a7SPetr Machata int tclass_num; 633861fb829SNogah Frankel 63476ff72a7SPetr Machata prio_bitmap = mlxsw_sp_qdisc_get_prio_bitmap(mlxsw_sp_port, 63576ff72a7SPetr Machata mlxsw_sp_qdisc); 63676ff72a7SPetr Machata tclass_num = mlxsw_sp_qdisc_get_tclass_num(mlxsw_sp_port, 63776ff72a7SPetr Machata mlxsw_sp_qdisc); 638861fb829SNogah Frankel xstats = &mlxsw_sp_port->periodic_hw_stats.xstats; 6394d1a4b84SNogah Frankel stats_base = &mlxsw_sp_qdisc->stats_base; 640c2ed6db7SNogah Frankel red_base = &mlxsw_sp_qdisc->xstats_base.red; 6413670756fSNogah Frankel 64276ff72a7SPetr Machata mlxsw_sp_qdisc_bstats_per_priority_get(xstats, prio_bitmap, 64304cc0bf5SNogah Frankel &stats_base->tx_packets, 64404cc0bf5SNogah Frankel &stats_base->tx_bytes); 64515be36b8SPetr Machata red_base->prob_mark = xstats->tc_ecn[tclass_num]; 6464d1a4b84SNogah Frankel red_base->prob_drop = xstats->wred_drop[tclass_num]; 64785005b82SPetr Machata red_base->pdrop = mlxsw_sp_xstats_tail_drop(xstats, tclass_num); 6483670756fSNogah Frankel 649c2ed6db7SNogah Frankel stats_base->overlimits = red_base->prob_drop + red_base->prob_mark; 6504d1a4b84SNogah Frankel stats_base->drops = red_base->prob_drop + red_base->pdrop; 651416ef9b1SJakub Kicinski 652416ef9b1SJakub Kicinski stats_base->backlog = 0; 653861fb829SNogah Frankel } 654861fb829SNogah Frankel 65596f17e07SNogah Frankel static int 656cba7158fSNogah Frankel mlxsw_sp_qdisc_red_destroy(struct mlxsw_sp_port *mlxsw_sp_port, 657d56c8955SNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 65896f17e07SNogah Frankel { 65976ff72a7SPetr Machata int tclass_num = mlxsw_sp_qdisc_get_tclass_num(mlxsw_sp_port, 66076ff72a7SPetr Machata mlxsw_sp_qdisc); 66176ff72a7SPetr Machata 66276ff72a7SPetr Machata return mlxsw_sp_tclass_congestion_disable(mlxsw_sp_port, tclass_num); 66396f17e07SNogah Frankel } 66496f17e07SNogah Frankel 66596f17e07SNogah Frankel static int 6669cf6c9c7SNogah Frankel mlxsw_sp_qdisc_red_check_params(struct mlxsw_sp_port *mlxsw_sp_port, 6679cf6c9c7SNogah Frankel void *params) 66896f17e07SNogah Frankel { 66996f17e07SNogah Frankel struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 6709cf6c9c7SNogah Frankel struct tc_red_qopt_offload_params *p = params; 67196f17e07SNogah Frankel 67296f17e07SNogah Frankel if (p->min > p->max) { 67396f17e07SNogah Frankel dev_err(mlxsw_sp->bus_info->dev, 67496f17e07SNogah Frankel "spectrum: RED: min %u is bigger then max %u\n", p->min, 67596f17e07SNogah Frankel p->max); 6769cf6c9c7SNogah Frankel return -EINVAL; 67796f17e07SNogah Frankel } 678914c4fc1SPetr Machata if (p->max > MLXSW_CORE_RES_GET(mlxsw_sp->core, 679914c4fc1SPetr Machata GUARANTEED_SHARED_BUFFER)) { 68096f17e07SNogah Frankel dev_err(mlxsw_sp->bus_info->dev, 68196f17e07SNogah Frankel "spectrum: RED: max value %u is too big\n", p->max); 6829cf6c9c7SNogah Frankel return -EINVAL; 68396f17e07SNogah Frankel } 68496f17e07SNogah Frankel if (p->min == 0 || p->max == 0) { 68596f17e07SNogah Frankel dev_err(mlxsw_sp->bus_info->dev, 68696f17e07SNogah Frankel "spectrum: RED: 0 value is illegal for min and max\n"); 6879cf6c9c7SNogah Frankel return -EINVAL; 68896f17e07SNogah Frankel } 6899cf6c9c7SNogah Frankel return 0; 6909cf6c9c7SNogah Frankel } 6919cf6c9c7SNogah Frankel 6929cf6c9c7SNogah Frankel static int 693c4e372e2SPetr Machata mlxsw_sp_qdisc_red_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, 6949cf6c9c7SNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 6959cf6c9c7SNogah Frankel void *params) 6969cf6c9c7SNogah Frankel { 6979cf6c9c7SNogah Frankel struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 6989cf6c9c7SNogah Frankel struct tc_red_qopt_offload_params *p = params; 69976ff72a7SPetr Machata int tclass_num; 7009cf6c9c7SNogah Frankel u32 min, max; 7019cf6c9c7SNogah Frankel u64 prob; 70296f17e07SNogah Frankel 70376ff72a7SPetr Machata tclass_num = mlxsw_sp_qdisc_get_tclass_num(mlxsw_sp_port, 70476ff72a7SPetr Machata mlxsw_sp_qdisc); 70576ff72a7SPetr Machata 70696f17e07SNogah Frankel /* calculate probability in percentage */ 70796f17e07SNogah Frankel prob = p->probability; 70896f17e07SNogah Frankel prob *= 100; 70996f17e07SNogah Frankel prob = DIV_ROUND_UP(prob, 1 << 16); 71096f17e07SNogah Frankel prob = DIV_ROUND_UP(prob, 1 << 16); 71196f17e07SNogah Frankel min = mlxsw_sp_bytes_cells(mlxsw_sp, p->min); 71296f17e07SNogah Frankel max = mlxsw_sp_bytes_cells(mlxsw_sp, p->max); 7138040c96bSPetr Machata return mlxsw_sp_tclass_congestion_enable(mlxsw_sp_port, tclass_num, 7148040c96bSPetr Machata min, max, prob, 7158040c96bSPetr Machata !p->is_nodrop, p->is_ecn); 71696f17e07SNogah Frankel } 71796f17e07SNogah Frankel 718416ef9b1SJakub Kicinski static void 719be1d5a8aSPetr Machata mlxsw_sp_qdisc_leaf_unoffload(struct mlxsw_sp_port *mlxsw_sp_port, 720be1d5a8aSPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 721be1d5a8aSPetr Machata struct gnet_stats_queue *qstats) 722be1d5a8aSPetr Machata { 723be1d5a8aSPetr Machata u64 backlog; 724be1d5a8aSPetr Machata 725be1d5a8aSPetr Machata backlog = mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp, 726be1d5a8aSPetr Machata mlxsw_sp_qdisc->stats_base.backlog); 727be1d5a8aSPetr Machata qstats->backlog -= backlog; 728be1d5a8aSPetr Machata mlxsw_sp_qdisc->stats_base.backlog = 0; 729be1d5a8aSPetr Machata } 730be1d5a8aSPetr Machata 731be1d5a8aSPetr Machata static void 732416ef9b1SJakub Kicinski mlxsw_sp_qdisc_red_unoffload(struct mlxsw_sp_port *mlxsw_sp_port, 733416ef9b1SJakub Kicinski struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 734416ef9b1SJakub Kicinski void *params) 735416ef9b1SJakub Kicinski { 736416ef9b1SJakub Kicinski struct tc_red_qopt_offload_params *p = params; 737416ef9b1SJakub Kicinski 738be1d5a8aSPetr Machata mlxsw_sp_qdisc_leaf_unoffload(mlxsw_sp_port, mlxsw_sp_qdisc, p->qstats); 739416ef9b1SJakub Kicinski } 740416ef9b1SJakub Kicinski 741861fb829SNogah Frankel static int 742cba7158fSNogah Frankel mlxsw_sp_qdisc_get_red_xstats(struct mlxsw_sp_port *mlxsw_sp_port, 743861fb829SNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 744562ffbc4SNogah Frankel void *xstats_ptr) 745861fb829SNogah Frankel { 7464d1a4b84SNogah Frankel struct red_stats *xstats_base = &mlxsw_sp_qdisc->xstats_base.red; 747861fb829SNogah Frankel struct mlxsw_sp_port_xstats *xstats; 748562ffbc4SNogah Frankel struct red_stats *res = xstats_ptr; 74915be36b8SPetr Machata int early_drops, marks, pdrops; 75076ff72a7SPetr Machata int tclass_num; 751861fb829SNogah Frankel 75276ff72a7SPetr Machata tclass_num = mlxsw_sp_qdisc_get_tclass_num(mlxsw_sp_port, 75376ff72a7SPetr Machata mlxsw_sp_qdisc); 754861fb829SNogah Frankel xstats = &mlxsw_sp_port->periodic_hw_stats.xstats; 755861fb829SNogah Frankel 756f8253df5SNogah Frankel early_drops = xstats->wred_drop[tclass_num] - xstats_base->prob_drop; 75715be36b8SPetr Machata marks = xstats->tc_ecn[tclass_num] - xstats_base->prob_mark; 75885005b82SPetr Machata pdrops = mlxsw_sp_xstats_tail_drop(xstats, tclass_num) - 75985005b82SPetr Machata xstats_base->pdrop; 760f8253df5SNogah Frankel 761f8253df5SNogah Frankel res->pdrop += pdrops; 762f8253df5SNogah Frankel res->prob_drop += early_drops; 76315be36b8SPetr Machata res->prob_mark += marks; 764f8253df5SNogah Frankel 765f8253df5SNogah Frankel xstats_base->pdrop += pdrops; 766f8253df5SNogah Frankel xstats_base->prob_drop += early_drops; 76715be36b8SPetr Machata xstats_base->prob_mark += marks; 768861fb829SNogah Frankel return 0; 769861fb829SNogah Frankel } 770861fb829SNogah Frankel 7713670756fSNogah Frankel static int 772cba7158fSNogah Frankel mlxsw_sp_qdisc_get_red_stats(struct mlxsw_sp_port *mlxsw_sp_port, 7733670756fSNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 774562ffbc4SNogah Frankel struct tc_qopt_offload_stats *stats_ptr) 7753670756fSNogah Frankel { 7764d1a4b84SNogah Frankel struct mlxsw_sp_qdisc_stats *stats_base; 7773670756fSNogah Frankel struct mlxsw_sp_port_xstats *xstats; 778cf9af379SPetr Machata u64 overlimits; 77976ff72a7SPetr Machata int tclass_num; 7803670756fSNogah Frankel 78176ff72a7SPetr Machata tclass_num = mlxsw_sp_qdisc_get_tclass_num(mlxsw_sp_port, 78276ff72a7SPetr Machata mlxsw_sp_qdisc); 7833670756fSNogah Frankel xstats = &mlxsw_sp_port->periodic_hw_stats.xstats; 7844d1a4b84SNogah Frankel stats_base = &mlxsw_sp_qdisc->stats_base; 7853670756fSNogah Frankel 7863d0d5921SPetr Machata mlxsw_sp_qdisc_get_tc_stats(mlxsw_sp_port, mlxsw_sp_qdisc, stats_ptr); 78715be36b8SPetr Machata overlimits = xstats->wred_drop[tclass_num] + 78815be36b8SPetr Machata xstats->tc_ecn[tclass_num] - stats_base->overlimits; 7893670756fSNogah Frankel 790562ffbc4SNogah Frankel stats_ptr->qstats->overlimits += overlimits; 7914d1a4b84SNogah Frankel stats_base->overlimits += overlimits; 792cf9af379SPetr Machata 7933670756fSNogah Frankel return 0; 7943670756fSNogah Frankel } 7953670756fSNogah Frankel 79651d52ed9SPetr Machata static struct mlxsw_sp_qdisc * 79751d52ed9SPetr Machata mlxsw_sp_qdisc_leaf_find_class(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 79851d52ed9SPetr Machata u32 parent) 79951d52ed9SPetr Machata { 80051d52ed9SPetr Machata return NULL; 80151d52ed9SPetr Machata } 80251d52ed9SPetr Machata 803562ffbc4SNogah Frankel static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_red = { 8049cf6c9c7SNogah Frankel .type = MLXSW_SP_QDISC_RED, 8059cf6c9c7SNogah Frankel .check_params = mlxsw_sp_qdisc_red_check_params, 8069cf6c9c7SNogah Frankel .replace = mlxsw_sp_qdisc_red_replace, 807416ef9b1SJakub Kicinski .unoffload = mlxsw_sp_qdisc_red_unoffload, 8089a37a59fSNogah Frankel .destroy = mlxsw_sp_qdisc_red_destroy, 809562ffbc4SNogah Frankel .get_stats = mlxsw_sp_qdisc_get_red_stats, 810562ffbc4SNogah Frankel .get_xstats = mlxsw_sp_qdisc_get_red_xstats, 8119cf6c9c7SNogah Frankel .clean_stats = mlxsw_sp_setup_tc_qdisc_red_clean_stats, 81251d52ed9SPetr Machata .find_class = mlxsw_sp_qdisc_leaf_find_class, 813562ffbc4SNogah Frankel }; 814562ffbc4SNogah Frankel 815be7e2a5aSPetr Machata static int mlxsw_sp_qdisc_graft(struct mlxsw_sp_port *mlxsw_sp_port, 816be7e2a5aSPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 817be7e2a5aSPetr Machata u8 band, u32 child_handle); 818be7e2a5aSPetr Machata 819cff99e20SPetr Machata static int __mlxsw_sp_setup_tc_red(struct mlxsw_sp_port *mlxsw_sp_port, 82096f17e07SNogah Frankel struct tc_red_qopt_offload *p) 82196f17e07SNogah Frankel { 82296f17e07SNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc; 82396f17e07SNogah Frankel 824*c2792f38SPetr Machata mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent); 825eed4baebSNogah Frankel if (!mlxsw_sp_qdisc) 82696f17e07SNogah Frankel return -EOPNOTSUPP; 82796f17e07SNogah Frankel 828cba7158fSNogah Frankel if (p->command == TC_RED_REPLACE) 8299cf6c9c7SNogah Frankel return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle, 830562ffbc4SNogah Frankel mlxsw_sp_qdisc, 831562ffbc4SNogah Frankel &mlxsw_sp_qdisc_ops_red, 832562ffbc4SNogah Frankel &p->set); 833cba7158fSNogah Frankel 834290fe2c5SPetr Machata if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle)) 835cba7158fSNogah Frankel return -EOPNOTSUPP; 836cba7158fSNogah Frankel 837cba7158fSNogah Frankel switch (p->command) { 83896f17e07SNogah Frankel case TC_RED_DESTROY: 8399a37a59fSNogah Frankel return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc); 840861fb829SNogah Frankel case TC_RED_XSTATS: 841562ffbc4SNogah Frankel return mlxsw_sp_qdisc_get_xstats(mlxsw_sp_port, mlxsw_sp_qdisc, 842861fb829SNogah Frankel p->xstats); 8433670756fSNogah Frankel case TC_RED_STATS: 844562ffbc4SNogah Frankel return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc, 8453670756fSNogah Frankel &p->stats); 846be7e2a5aSPetr Machata case TC_RED_GRAFT: 847be7e2a5aSPetr Machata return mlxsw_sp_qdisc_graft(mlxsw_sp_port, mlxsw_sp_qdisc, 0, 848be7e2a5aSPetr Machata p->child_handle); 84996f17e07SNogah Frankel default: 85096f17e07SNogah Frankel return -EOPNOTSUPP; 85196f17e07SNogah Frankel } 85296f17e07SNogah Frankel } 853371b437aSNogah Frankel 854cff99e20SPetr Machata int mlxsw_sp_setup_tc_red(struct mlxsw_sp_port *mlxsw_sp_port, 855cff99e20SPetr Machata struct tc_red_qopt_offload *p) 856cff99e20SPetr Machata { 857cff99e20SPetr Machata int err; 858cff99e20SPetr Machata 859cff99e20SPetr Machata mutex_lock(&mlxsw_sp_port->qdisc->lock); 860cff99e20SPetr Machata err = __mlxsw_sp_setup_tc_red(mlxsw_sp_port, p); 861cff99e20SPetr Machata mutex_unlock(&mlxsw_sp_port->qdisc->lock); 862cff99e20SPetr Machata 863cff99e20SPetr Machata return err; 864cff99e20SPetr Machata } 865cff99e20SPetr Machata 866a44f58c4SPetr Machata static void 867a44f58c4SPetr Machata mlxsw_sp_setup_tc_qdisc_leaf_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port, 868a44f58c4SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 869a44f58c4SPetr Machata { 870a44f58c4SPetr Machata u64 backlog_cells = 0; 871a44f58c4SPetr Machata u64 tx_packets = 0; 872a44f58c4SPetr Machata u64 tx_bytes = 0; 873a44f58c4SPetr Machata u64 drops = 0; 874a44f58c4SPetr Machata 875a44f58c4SPetr Machata mlxsw_sp_qdisc_collect_tc_stats(mlxsw_sp_port, mlxsw_sp_qdisc, 876a44f58c4SPetr Machata &tx_bytes, &tx_packets, 877a44f58c4SPetr Machata &drops, &backlog_cells); 878a44f58c4SPetr Machata 879a44f58c4SPetr Machata mlxsw_sp_qdisc->stats_base.tx_packets = tx_packets; 880a44f58c4SPetr Machata mlxsw_sp_qdisc->stats_base.tx_bytes = tx_bytes; 881a44f58c4SPetr Machata mlxsw_sp_qdisc->stats_base.drops = drops; 882a44f58c4SPetr Machata mlxsw_sp_qdisc->stats_base.backlog = 0; 883a44f58c4SPetr Machata } 884a44f58c4SPetr Machata 885a44f58c4SPetr Machata static int 886a44f58c4SPetr Machata mlxsw_sp_qdisc_tbf_destroy(struct mlxsw_sp_port *mlxsw_sp_port, 887a44f58c4SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 888a44f58c4SPetr Machata { 88976ff72a7SPetr Machata int tclass_num = mlxsw_sp_qdisc_get_tclass_num(mlxsw_sp_port, 89076ff72a7SPetr Machata mlxsw_sp_qdisc); 89176ff72a7SPetr Machata 892a44f58c4SPetr Machata return mlxsw_sp_port_ets_maxrate_set(mlxsw_sp_port, 893a44f58c4SPetr Machata MLXSW_REG_QEEC_HR_SUBGROUP, 89476ff72a7SPetr Machata tclass_num, 0, 895a44f58c4SPetr Machata MLXSW_REG_QEEC_MAS_DIS, 0); 896a44f58c4SPetr Machata } 897a44f58c4SPetr Machata 898a44f58c4SPetr Machata static int 899a44f58c4SPetr Machata mlxsw_sp_qdisc_tbf_bs(struct mlxsw_sp_port *mlxsw_sp_port, 900a44f58c4SPetr Machata u32 max_size, u8 *p_burst_size) 901a44f58c4SPetr Machata { 902a44f58c4SPetr Machata /* TBF burst size is configured in bytes. The ASIC burst size value is 903a44f58c4SPetr Machata * ((2 ^ bs) * 512 bits. Convert the TBF bytes to 512-bit units. 904a44f58c4SPetr Machata */ 905a44f58c4SPetr Machata u32 bs512 = max_size / 64; 906a44f58c4SPetr Machata u8 bs = fls(bs512); 907a44f58c4SPetr Machata 908a44f58c4SPetr Machata if (!bs) 909a44f58c4SPetr Machata return -EINVAL; 910a44f58c4SPetr Machata --bs; 911a44f58c4SPetr Machata 912a44f58c4SPetr Machata /* Demand a power of two. */ 913a44f58c4SPetr Machata if ((1 << bs) != bs512) 914a44f58c4SPetr Machata return -EINVAL; 915a44f58c4SPetr Machata 916a44f58c4SPetr Machata if (bs < mlxsw_sp_port->mlxsw_sp->lowest_shaper_bs || 917a44f58c4SPetr Machata bs > MLXSW_REG_QEEC_HIGHEST_SHAPER_BS) 918a44f58c4SPetr Machata return -EINVAL; 919a44f58c4SPetr Machata 920a44f58c4SPetr Machata *p_burst_size = bs; 921a44f58c4SPetr Machata return 0; 922a44f58c4SPetr Machata } 923a44f58c4SPetr Machata 924a44f58c4SPetr Machata static u32 925a44f58c4SPetr Machata mlxsw_sp_qdisc_tbf_max_size(u8 bs) 926a44f58c4SPetr Machata { 927a44f58c4SPetr Machata return (1U << bs) * 64; 928a44f58c4SPetr Machata } 929a44f58c4SPetr Machata 930a44f58c4SPetr Machata static u64 931a44f58c4SPetr Machata mlxsw_sp_qdisc_tbf_rate_kbps(struct tc_tbf_qopt_offload_replace_params *p) 932a44f58c4SPetr Machata { 933a44f58c4SPetr Machata /* TBF interface is in bytes/s, whereas Spectrum ASIC is configured in 934a44f58c4SPetr Machata * Kbits/s. 935a44f58c4SPetr Machata */ 93691a7d4bfSNathan Chancellor return div_u64(p->rate.rate_bytes_ps, 1000) * 8; 937a44f58c4SPetr Machata } 938a44f58c4SPetr Machata 939a44f58c4SPetr Machata static int 940a44f58c4SPetr Machata mlxsw_sp_qdisc_tbf_check_params(struct mlxsw_sp_port *mlxsw_sp_port, 941a44f58c4SPetr Machata void *params) 942a44f58c4SPetr Machata { 943a44f58c4SPetr Machata struct tc_tbf_qopt_offload_replace_params *p = params; 944a44f58c4SPetr Machata struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 945a44f58c4SPetr Machata u64 rate_kbps = mlxsw_sp_qdisc_tbf_rate_kbps(p); 946a44f58c4SPetr Machata u8 burst_size; 947a44f58c4SPetr Machata int err; 948a44f58c4SPetr Machata 949a44f58c4SPetr Machata if (rate_kbps >= MLXSW_REG_QEEC_MAS_DIS) { 950a44f58c4SPetr Machata dev_err(mlxsw_sp_port->mlxsw_sp->bus_info->dev, 951a44f58c4SPetr Machata "spectrum: TBF: rate of %lluKbps must be below %u\n", 952a44f58c4SPetr Machata rate_kbps, MLXSW_REG_QEEC_MAS_DIS); 953a44f58c4SPetr Machata return -EINVAL; 954a44f58c4SPetr Machata } 955a44f58c4SPetr Machata 956a44f58c4SPetr Machata err = mlxsw_sp_qdisc_tbf_bs(mlxsw_sp_port, p->max_size, &burst_size); 957a44f58c4SPetr Machata if (err) { 958a44f58c4SPetr Machata u8 highest_shaper_bs = MLXSW_REG_QEEC_HIGHEST_SHAPER_BS; 959a44f58c4SPetr Machata 960a44f58c4SPetr Machata dev_err(mlxsw_sp->bus_info->dev, 961a44f58c4SPetr Machata "spectrum: TBF: invalid burst size of %u, must be a power of two between %u and %u", 962a44f58c4SPetr Machata p->max_size, 963a44f58c4SPetr Machata mlxsw_sp_qdisc_tbf_max_size(mlxsw_sp->lowest_shaper_bs), 964a44f58c4SPetr Machata mlxsw_sp_qdisc_tbf_max_size(highest_shaper_bs)); 965a44f58c4SPetr Machata return -EINVAL; 966a44f58c4SPetr Machata } 967a44f58c4SPetr Machata 968a44f58c4SPetr Machata return 0; 969a44f58c4SPetr Machata } 970a44f58c4SPetr Machata 971a44f58c4SPetr Machata static int 972c4e372e2SPetr Machata mlxsw_sp_qdisc_tbf_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, 973a44f58c4SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 974a44f58c4SPetr Machata void *params) 975a44f58c4SPetr Machata { 976a44f58c4SPetr Machata struct tc_tbf_qopt_offload_replace_params *p = params; 977a44f58c4SPetr Machata u64 rate_kbps = mlxsw_sp_qdisc_tbf_rate_kbps(p); 97876ff72a7SPetr Machata int tclass_num; 979a44f58c4SPetr Machata u8 burst_size; 980a44f58c4SPetr Machata int err; 981a44f58c4SPetr Machata 98276ff72a7SPetr Machata tclass_num = mlxsw_sp_qdisc_get_tclass_num(mlxsw_sp_port, 98376ff72a7SPetr Machata mlxsw_sp_qdisc); 98476ff72a7SPetr Machata 985a44f58c4SPetr Machata err = mlxsw_sp_qdisc_tbf_bs(mlxsw_sp_port, p->max_size, &burst_size); 986a44f58c4SPetr Machata if (WARN_ON_ONCE(err)) 987a44f58c4SPetr Machata /* check_params above was supposed to reject this value. */ 988a44f58c4SPetr Machata return -EINVAL; 989a44f58c4SPetr Machata 990a44f58c4SPetr Machata /* Configure subgroup shaper, so that both UC and MC traffic is subject 991a44f58c4SPetr Machata * to shaping. That is unlike RED, however UC queue lengths are going to 992a44f58c4SPetr Machata * be different than MC ones due to different pool and quota 993a44f58c4SPetr Machata * configurations, so the configuration is not applicable. For shaper on 994a44f58c4SPetr Machata * the other hand, subjecting the overall stream to the configured 995a44f58c4SPetr Machata * shaper makes sense. Also note that that is what we do for 996a44f58c4SPetr Machata * ieee_setmaxrate(). 997a44f58c4SPetr Machata */ 998a44f58c4SPetr Machata return mlxsw_sp_port_ets_maxrate_set(mlxsw_sp_port, 999a44f58c4SPetr Machata MLXSW_REG_QEEC_HR_SUBGROUP, 100076ff72a7SPetr Machata tclass_num, 0, 1001a44f58c4SPetr Machata rate_kbps, burst_size); 1002a44f58c4SPetr Machata } 1003a44f58c4SPetr Machata 1004a44f58c4SPetr Machata static void 1005a44f58c4SPetr Machata mlxsw_sp_qdisc_tbf_unoffload(struct mlxsw_sp_port *mlxsw_sp_port, 1006a44f58c4SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 1007a44f58c4SPetr Machata void *params) 1008a44f58c4SPetr Machata { 1009a44f58c4SPetr Machata struct tc_tbf_qopt_offload_replace_params *p = params; 1010a44f58c4SPetr Machata 1011a44f58c4SPetr Machata mlxsw_sp_qdisc_leaf_unoffload(mlxsw_sp_port, mlxsw_sp_qdisc, p->qstats); 1012a44f58c4SPetr Machata } 1013a44f58c4SPetr Machata 1014a44f58c4SPetr Machata static int 1015a44f58c4SPetr Machata mlxsw_sp_qdisc_get_tbf_stats(struct mlxsw_sp_port *mlxsw_sp_port, 1016a44f58c4SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 1017a44f58c4SPetr Machata struct tc_qopt_offload_stats *stats_ptr) 1018a44f58c4SPetr Machata { 1019a44f58c4SPetr Machata mlxsw_sp_qdisc_get_tc_stats(mlxsw_sp_port, mlxsw_sp_qdisc, 1020a44f58c4SPetr Machata stats_ptr); 1021a44f58c4SPetr Machata return 0; 1022a44f58c4SPetr Machata } 1023a44f58c4SPetr Machata 1024a44f58c4SPetr Machata static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_tbf = { 1025a44f58c4SPetr Machata .type = MLXSW_SP_QDISC_TBF, 1026a44f58c4SPetr Machata .check_params = mlxsw_sp_qdisc_tbf_check_params, 1027a44f58c4SPetr Machata .replace = mlxsw_sp_qdisc_tbf_replace, 1028a44f58c4SPetr Machata .unoffload = mlxsw_sp_qdisc_tbf_unoffload, 1029a44f58c4SPetr Machata .destroy = mlxsw_sp_qdisc_tbf_destroy, 1030a44f58c4SPetr Machata .get_stats = mlxsw_sp_qdisc_get_tbf_stats, 1031a44f58c4SPetr Machata .clean_stats = mlxsw_sp_setup_tc_qdisc_leaf_clean_stats, 103251d52ed9SPetr Machata .find_class = mlxsw_sp_qdisc_leaf_find_class, 1033a44f58c4SPetr Machata }; 1034a44f58c4SPetr Machata 1035cff99e20SPetr Machata static int __mlxsw_sp_setup_tc_tbf(struct mlxsw_sp_port *mlxsw_sp_port, 1036a44f58c4SPetr Machata struct tc_tbf_qopt_offload *p) 1037a44f58c4SPetr Machata { 1038a44f58c4SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc; 1039a44f58c4SPetr Machata 1040*c2792f38SPetr Machata mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent); 1041a44f58c4SPetr Machata if (!mlxsw_sp_qdisc) 1042a44f58c4SPetr Machata return -EOPNOTSUPP; 1043a44f58c4SPetr Machata 1044a44f58c4SPetr Machata if (p->command == TC_TBF_REPLACE) 1045a44f58c4SPetr Machata return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle, 1046a44f58c4SPetr Machata mlxsw_sp_qdisc, 1047a44f58c4SPetr Machata &mlxsw_sp_qdisc_ops_tbf, 1048a44f58c4SPetr Machata &p->replace_params); 1049a44f58c4SPetr Machata 1050290fe2c5SPetr Machata if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle)) 1051a44f58c4SPetr Machata return -EOPNOTSUPP; 1052a44f58c4SPetr Machata 1053a44f58c4SPetr Machata switch (p->command) { 1054a44f58c4SPetr Machata case TC_TBF_DESTROY: 1055a44f58c4SPetr Machata return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc); 1056a44f58c4SPetr Machata case TC_TBF_STATS: 1057a44f58c4SPetr Machata return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc, 1058a44f58c4SPetr Machata &p->stats); 1059be7e2a5aSPetr Machata case TC_TBF_GRAFT: 1060be7e2a5aSPetr Machata return mlxsw_sp_qdisc_graft(mlxsw_sp_port, mlxsw_sp_qdisc, 0, 1061be7e2a5aSPetr Machata p->child_handle); 1062a44f58c4SPetr Machata default: 1063a44f58c4SPetr Machata return -EOPNOTSUPP; 1064a44f58c4SPetr Machata } 1065a44f58c4SPetr Machata } 1066a44f58c4SPetr Machata 1067cff99e20SPetr Machata int mlxsw_sp_setup_tc_tbf(struct mlxsw_sp_port *mlxsw_sp_port, 1068cff99e20SPetr Machata struct tc_tbf_qopt_offload *p) 1069cff99e20SPetr Machata { 1070cff99e20SPetr Machata int err; 1071cff99e20SPetr Machata 1072cff99e20SPetr Machata mutex_lock(&mlxsw_sp_port->qdisc->lock); 1073cff99e20SPetr Machata err = __mlxsw_sp_setup_tc_tbf(mlxsw_sp_port, p); 1074cff99e20SPetr Machata mutex_unlock(&mlxsw_sp_port->qdisc->lock); 1075cff99e20SPetr Machata 1076cff99e20SPetr Machata return err; 1077cff99e20SPetr Machata } 1078cff99e20SPetr Machata 107946a3615bSNogah Frankel static int 10807bec1a45SPetr Machata mlxsw_sp_qdisc_fifo_check_params(struct mlxsw_sp_port *mlxsw_sp_port, 10817bec1a45SPetr Machata void *params) 10827bec1a45SPetr Machata { 10837bec1a45SPetr Machata return 0; 10847bec1a45SPetr Machata } 10857bec1a45SPetr Machata 10867bec1a45SPetr Machata static int 10877bec1a45SPetr Machata mlxsw_sp_qdisc_fifo_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, 10887bec1a45SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 10897bec1a45SPetr Machata void *params) 10907bec1a45SPetr Machata { 10917bec1a45SPetr Machata return 0; 10927bec1a45SPetr Machata } 10937bec1a45SPetr Machata 10947bec1a45SPetr Machata static int 10957bec1a45SPetr Machata mlxsw_sp_qdisc_get_fifo_stats(struct mlxsw_sp_port *mlxsw_sp_port, 10967bec1a45SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 10977bec1a45SPetr Machata struct tc_qopt_offload_stats *stats_ptr) 10987bec1a45SPetr Machata { 10997bec1a45SPetr Machata mlxsw_sp_qdisc_get_tc_stats(mlxsw_sp_port, mlxsw_sp_qdisc, 11007bec1a45SPetr Machata stats_ptr); 11017bec1a45SPetr Machata return 0; 11027bec1a45SPetr Machata } 11037bec1a45SPetr Machata 11047bec1a45SPetr Machata static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_fifo = { 11057bec1a45SPetr Machata .type = MLXSW_SP_QDISC_FIFO, 11067bec1a45SPetr Machata .check_params = mlxsw_sp_qdisc_fifo_check_params, 11077bec1a45SPetr Machata .replace = mlxsw_sp_qdisc_fifo_replace, 11087bec1a45SPetr Machata .get_stats = mlxsw_sp_qdisc_get_fifo_stats, 11097bec1a45SPetr Machata .clean_stats = mlxsw_sp_setup_tc_qdisc_leaf_clean_stats, 11107bec1a45SPetr Machata }; 11117bec1a45SPetr Machata 111291796f50SPetr Machata static int 111391796f50SPetr Machata mlxsw_sp_qdisc_future_fifo_replace(struct mlxsw_sp_port *mlxsw_sp_port, 111491796f50SPetr Machata u32 handle, unsigned int band, 111591796f50SPetr Machata struct mlxsw_sp_qdisc *child_qdisc) 111691796f50SPetr Machata { 111791796f50SPetr Machata struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc; 111891796f50SPetr Machata 111991796f50SPetr Machata if (handle == qdisc_state->future_handle && 112091796f50SPetr Machata qdisc_state->future_fifos[band]) 112191796f50SPetr Machata return mlxsw_sp_qdisc_replace(mlxsw_sp_port, TC_H_UNSPEC, 112291796f50SPetr Machata child_qdisc, 112391796f50SPetr Machata &mlxsw_sp_qdisc_ops_fifo, 112491796f50SPetr Machata NULL); 112591796f50SPetr Machata return 0; 112691796f50SPetr Machata } 112791796f50SPetr Machata 112891796f50SPetr Machata static void 112991796f50SPetr Machata mlxsw_sp_qdisc_future_fifos_init(struct mlxsw_sp_port *mlxsw_sp_port, 113091796f50SPetr Machata u32 handle) 113191796f50SPetr Machata { 113291796f50SPetr Machata struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc; 113391796f50SPetr Machata 113491796f50SPetr Machata qdisc_state->future_handle = handle; 113591796f50SPetr Machata memset(qdisc_state->future_fifos, 0, sizeof(qdisc_state->future_fifos)); 113691796f50SPetr Machata } 113791796f50SPetr Machata 1138cff99e20SPetr Machata static int __mlxsw_sp_setup_tc_fifo(struct mlxsw_sp_port *mlxsw_sp_port, 11397bec1a45SPetr Machata struct tc_fifo_qopt_offload *p) 11407bec1a45SPetr Machata { 11417bec1a45SPetr Machata struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc; 11427bec1a45SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc; 11437de85b04SPetr Machata unsigned int band; 11447bec1a45SPetr Machata u32 parent_handle; 11457bec1a45SPetr Machata 1146*c2792f38SPetr Machata mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent); 11477bec1a45SPetr Machata if (!mlxsw_sp_qdisc && p->handle == TC_H_UNSPEC) { 11487bec1a45SPetr Machata parent_handle = TC_H_MAJ(p->parent); 11497bec1a45SPetr Machata if (parent_handle != qdisc_state->future_handle) { 11507bec1a45SPetr Machata /* This notifications is for a different Qdisc than 11517bec1a45SPetr Machata * previously. Wipe the future cache. 11527bec1a45SPetr Machata */ 115391796f50SPetr Machata mlxsw_sp_qdisc_future_fifos_init(mlxsw_sp_port, 115491796f50SPetr Machata parent_handle); 11557bec1a45SPetr Machata } 11567bec1a45SPetr Machata 11577de85b04SPetr Machata band = TC_H_MIN(p->parent) - 1; 11587de85b04SPetr Machata if (band < IEEE_8021QAZ_MAX_TCS) { 11597bec1a45SPetr Machata if (p->command == TC_FIFO_REPLACE) 11607de85b04SPetr Machata qdisc_state->future_fifos[band] = true; 11617bec1a45SPetr Machata else if (p->command == TC_FIFO_DESTROY) 11627de85b04SPetr Machata qdisc_state->future_fifos[band] = false; 11637bec1a45SPetr Machata } 11647bec1a45SPetr Machata } 11657bec1a45SPetr Machata if (!mlxsw_sp_qdisc) 11667bec1a45SPetr Machata return -EOPNOTSUPP; 11677bec1a45SPetr Machata 11687bec1a45SPetr Machata if (p->command == TC_FIFO_REPLACE) { 11697bec1a45SPetr Machata return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle, 11707bec1a45SPetr Machata mlxsw_sp_qdisc, 11717bec1a45SPetr Machata &mlxsw_sp_qdisc_ops_fifo, NULL); 11727bec1a45SPetr Machata } 11737bec1a45SPetr Machata 1174290fe2c5SPetr Machata if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle)) 11757bec1a45SPetr Machata return -EOPNOTSUPP; 11767bec1a45SPetr Machata 11777bec1a45SPetr Machata switch (p->command) { 11787bec1a45SPetr Machata case TC_FIFO_DESTROY: 1179549f2aaeSPetr Machata return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc); 11807bec1a45SPetr Machata case TC_FIFO_STATS: 11817bec1a45SPetr Machata return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc, 11827bec1a45SPetr Machata &p->stats); 11837bec1a45SPetr Machata case TC_FIFO_REPLACE: /* Handled above. */ 11847bec1a45SPetr Machata break; 11857bec1a45SPetr Machata } 11867bec1a45SPetr Machata 11877bec1a45SPetr Machata return -EOPNOTSUPP; 11887bec1a45SPetr Machata } 11897bec1a45SPetr Machata 1190cff99e20SPetr Machata int mlxsw_sp_setup_tc_fifo(struct mlxsw_sp_port *mlxsw_sp_port, 1191cff99e20SPetr Machata struct tc_fifo_qopt_offload *p) 1192cff99e20SPetr Machata { 1193cff99e20SPetr Machata int err; 1194cff99e20SPetr Machata 1195cff99e20SPetr Machata mutex_lock(&mlxsw_sp_port->qdisc->lock); 1196cff99e20SPetr Machata err = __mlxsw_sp_setup_tc_fifo(mlxsw_sp_port, p); 1197cff99e20SPetr Machata mutex_unlock(&mlxsw_sp_port->qdisc->lock); 1198cff99e20SPetr Machata 1199cff99e20SPetr Machata return err; 1200cff99e20SPetr Machata } 1201cff99e20SPetr Machata 120251d52ed9SPetr Machata static int __mlxsw_sp_qdisc_ets_destroy(struct mlxsw_sp_port *mlxsw_sp_port, 120351d52ed9SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 120446a3615bSNogah Frankel { 120546a3615bSNogah Frankel int i; 120646a3615bSNogah Frankel 120751d52ed9SPetr Machata for (i = 0; i < mlxsw_sp_qdisc->num_classes; i++) { 120846a3615bSNogah Frankel mlxsw_sp_port_prio_tc_set(mlxsw_sp_port, i, 120946a3615bSNogah Frankel MLXSW_SP_PORT_DEFAULT_TCLASS); 12107917f52aSPetr Machata mlxsw_sp_port_ets_set(mlxsw_sp_port, 12117917f52aSPetr Machata MLXSW_REG_QEEC_HR_SUBGROUP, 12127917f52aSPetr Machata i, 0, false, 0); 1213eed4baebSNogah Frankel } 121446a3615bSNogah Frankel 121576ff72a7SPetr Machata kfree(mlxsw_sp_qdisc->ets_data); 121676ff72a7SPetr Machata mlxsw_sp_qdisc->ets_data = NULL; 121746a3615bSNogah Frankel return 0; 121846a3615bSNogah Frankel } 121946a3615bSNogah Frankel 122046a3615bSNogah Frankel static int 12217917f52aSPetr Machata mlxsw_sp_qdisc_prio_destroy(struct mlxsw_sp_port *mlxsw_sp_port, 12227917f52aSPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 12237917f52aSPetr Machata { 122451d52ed9SPetr Machata return __mlxsw_sp_qdisc_ets_destroy(mlxsw_sp_port, mlxsw_sp_qdisc); 12257917f52aSPetr Machata } 12267917f52aSPetr Machata 12277917f52aSPetr Machata static int 12287917f52aSPetr Machata __mlxsw_sp_qdisc_ets_check_params(unsigned int nbands) 12297917f52aSPetr Machata { 12307917f52aSPetr Machata if (nbands > IEEE_8021QAZ_MAX_TCS) 12317917f52aSPetr Machata return -EOPNOTSUPP; 12327917f52aSPetr Machata 12337917f52aSPetr Machata return 0; 12347917f52aSPetr Machata } 12357917f52aSPetr Machata 12367917f52aSPetr Machata static int 123746a3615bSNogah Frankel mlxsw_sp_qdisc_prio_check_params(struct mlxsw_sp_port *mlxsw_sp_port, 123846a3615bSNogah Frankel void *params) 123946a3615bSNogah Frankel { 124046a3615bSNogah Frankel struct tc_prio_qopt_offload_params *p = params; 124146a3615bSNogah Frankel 12427917f52aSPetr Machata return __mlxsw_sp_qdisc_ets_check_params(p->bands); 124346a3615bSNogah Frankel } 124446a3615bSNogah Frankel 124501164ddaSPetr Machata static struct mlxsw_sp_qdisc * 124601164ddaSPetr Machata mlxsw_sp_qdisc_walk_cb_clean_stats(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 124701164ddaSPetr Machata void *mlxsw_sp_port) 124801164ddaSPetr Machata { 124901164ddaSPetr Machata u64 backlog; 125001164ddaSPetr Machata 125101164ddaSPetr Machata if (mlxsw_sp_qdisc->ops) { 125201164ddaSPetr Machata backlog = mlxsw_sp_qdisc->stats_base.backlog; 125301164ddaSPetr Machata if (mlxsw_sp_qdisc->ops->clean_stats) 125401164ddaSPetr Machata mlxsw_sp_qdisc->ops->clean_stats(mlxsw_sp_port, 125501164ddaSPetr Machata mlxsw_sp_qdisc); 125601164ddaSPetr Machata mlxsw_sp_qdisc->stats_base.backlog = backlog; 125701164ddaSPetr Machata } 125801164ddaSPetr Machata 125901164ddaSPetr Machata return NULL; 126001164ddaSPetr Machata } 126101164ddaSPetr Machata 126201164ddaSPetr Machata static void 126301164ddaSPetr Machata mlxsw_sp_qdisc_tree_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port, 126401164ddaSPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 126501164ddaSPetr Machata { 126601164ddaSPetr Machata mlxsw_sp_qdisc_walk(mlxsw_sp_qdisc, mlxsw_sp_qdisc_walk_cb_clean_stats, 126701164ddaSPetr Machata mlxsw_sp_port); 126801164ddaSPetr Machata } 126901164ddaSPetr Machata 127046a3615bSNogah Frankel static int 127151d52ed9SPetr Machata __mlxsw_sp_qdisc_ets_replace(struct mlxsw_sp_port *mlxsw_sp_port, 127251d52ed9SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 127351d52ed9SPetr Machata u32 handle, unsigned int nbands, 12747917f52aSPetr Machata const unsigned int *quanta, 12757917f52aSPetr Machata const unsigned int *weights, 12767917f52aSPetr Machata const u8 *priomap) 127746a3615bSNogah Frankel { 127876ff72a7SPetr Machata struct mlxsw_sp_qdisc_ets_data *ets_data = mlxsw_sp_qdisc->ets_data; 127976ff72a7SPetr Machata struct mlxsw_sp_qdisc_ets_band *ets_band; 128004cc0bf5SNogah Frankel struct mlxsw_sp_qdisc *child_qdisc; 128176ff72a7SPetr Machata u8 old_priomap, new_priomap; 128201164ddaSPetr Machata int i, band; 128346a3615bSNogah Frankel int err; 128446a3615bSNogah Frankel 128576ff72a7SPetr Machata if (!ets_data) { 128676ff72a7SPetr Machata ets_data = kzalloc(sizeof(*ets_data), GFP_KERNEL); 128776ff72a7SPetr Machata if (!ets_data) 128876ff72a7SPetr Machata return -ENOMEM; 128976ff72a7SPetr Machata mlxsw_sp_qdisc->ets_data = ets_data; 129076ff72a7SPetr Machata 129176ff72a7SPetr Machata for (band = 0; band < mlxsw_sp_qdisc->num_classes; band++) { 129276ff72a7SPetr Machata int tclass_num = MLXSW_SP_PRIO_BAND_TO_TCLASS(band); 129376ff72a7SPetr Machata 129476ff72a7SPetr Machata ets_band = &ets_data->bands[band]; 129576ff72a7SPetr Machata ets_band->tclass_num = tclass_num; 129676ff72a7SPetr Machata } 129776ff72a7SPetr Machata } 129876ff72a7SPetr Machata 12997917f52aSPetr Machata for (band = 0; band < nbands; band++) { 130076ff72a7SPetr Machata int tclass_num; 130176ff72a7SPetr Machata 130251d52ed9SPetr Machata child_qdisc = &mlxsw_sp_qdisc->qdiscs[band]; 130376ff72a7SPetr Machata ets_band = &ets_data->bands[band]; 130476ff72a7SPetr Machata 130576ff72a7SPetr Machata tclass_num = ets_band->tclass_num; 130676ff72a7SPetr Machata old_priomap = ets_band->prio_bitmap; 130776ff72a7SPetr Machata new_priomap = 0; 13087917f52aSPetr Machata 13097917f52aSPetr Machata err = mlxsw_sp_port_ets_set(mlxsw_sp_port, 13107917f52aSPetr Machata MLXSW_REG_QEEC_HR_SUBGROUP, 131176ff72a7SPetr Machata tclass_num, 0, !!quanta[band], 13127917f52aSPetr Machata weights[band]); 13137917f52aSPetr Machata if (err) 13147917f52aSPetr Machata return err; 13157917f52aSPetr Machata 131646a3615bSNogah Frankel for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { 13177917f52aSPetr Machata if (priomap[i] == band) { 131876ff72a7SPetr Machata new_priomap |= BIT(i); 131904cc0bf5SNogah Frankel if (BIT(i) & old_priomap) 132004cc0bf5SNogah Frankel continue; 132104cc0bf5SNogah Frankel err = mlxsw_sp_port_prio_tc_set(mlxsw_sp_port, 132276ff72a7SPetr Machata i, tclass_num); 132346a3615bSNogah Frankel if (err) 132446a3615bSNogah Frankel return err; 132504cc0bf5SNogah Frankel } 132604cc0bf5SNogah Frankel } 13275cbd9602SPetr Machata 132876ff72a7SPetr Machata ets_band->prio_bitmap = new_priomap; 13295cbd9602SPetr Machata 133001164ddaSPetr Machata if (old_priomap != new_priomap) 133101164ddaSPetr Machata mlxsw_sp_qdisc_tree_clean_stats(mlxsw_sp_port, 133204cc0bf5SNogah Frankel child_qdisc); 13337bec1a45SPetr Machata 133491796f50SPetr Machata err = mlxsw_sp_qdisc_future_fifo_replace(mlxsw_sp_port, handle, 133591796f50SPetr Machata band, child_qdisc); 13367bec1a45SPetr Machata if (err) 13377bec1a45SPetr Machata return err; 13387bec1a45SPetr Machata } 133998ceb7b6SNogah Frankel for (; band < IEEE_8021QAZ_MAX_TCS; band++) { 134076ff72a7SPetr Machata ets_band = &ets_data->bands[band]; 134176ff72a7SPetr Machata ets_band->prio_bitmap = 0; 134276ff72a7SPetr Machata 134351d52ed9SPetr Machata child_qdisc = &mlxsw_sp_qdisc->qdiscs[band]; 134498ceb7b6SNogah Frankel mlxsw_sp_qdisc_destroy(mlxsw_sp_port, child_qdisc); 134576ff72a7SPetr Machata 13467917f52aSPetr Machata mlxsw_sp_port_ets_set(mlxsw_sp_port, 13477917f52aSPetr Machata MLXSW_REG_QEEC_HR_SUBGROUP, 134876ff72a7SPetr Machata ets_band->tclass_num, 0, false, 0); 134998ceb7b6SNogah Frankel } 13507bec1a45SPetr Machata 135191796f50SPetr Machata mlxsw_sp_qdisc_future_fifos_init(mlxsw_sp_port, TC_H_UNSPEC); 135246a3615bSNogah Frankel return 0; 135346a3615bSNogah Frankel } 135446a3615bSNogah Frankel 13557917f52aSPetr Machata static int 1356c4e372e2SPetr Machata mlxsw_sp_qdisc_prio_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, 13577917f52aSPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 13587917f52aSPetr Machata void *params) 13597917f52aSPetr Machata { 13607917f52aSPetr Machata struct tc_prio_qopt_offload_params *p = params; 13617917f52aSPetr Machata unsigned int zeroes[TCQ_ETS_MAX_BANDS] = {0}; 13627917f52aSPetr Machata 136351d52ed9SPetr Machata return __mlxsw_sp_qdisc_ets_replace(mlxsw_sp_port, mlxsw_sp_qdisc, 136451d52ed9SPetr Machata handle, p->bands, zeroes, 136551d52ed9SPetr Machata zeroes, p->priomap); 13667917f52aSPetr Machata } 13677917f52aSPetr Machata 13687917f52aSPetr Machata static void 13697917f52aSPetr Machata __mlxsw_sp_qdisc_ets_unoffload(struct mlxsw_sp_port *mlxsw_sp_port, 13707917f52aSPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 13717917f52aSPetr Machata struct gnet_stats_queue *qstats) 13727917f52aSPetr Machata { 13737917f52aSPetr Machata u64 backlog; 13747917f52aSPetr Machata 13757917f52aSPetr Machata backlog = mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp, 13767917f52aSPetr Machata mlxsw_sp_qdisc->stats_base.backlog); 13777917f52aSPetr Machata qstats->backlog -= backlog; 13787917f52aSPetr Machata } 13797917f52aSPetr Machata 1380e02f08a0SWei Yongjun static void 138193d8a4c1SNogah Frankel mlxsw_sp_qdisc_prio_unoffload(struct mlxsw_sp_port *mlxsw_sp_port, 138293d8a4c1SNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 138393d8a4c1SNogah Frankel void *params) 138493d8a4c1SNogah Frankel { 138593d8a4c1SNogah Frankel struct tc_prio_qopt_offload_params *p = params; 138693d8a4c1SNogah Frankel 13877917f52aSPetr Machata __mlxsw_sp_qdisc_ets_unoffload(mlxsw_sp_port, mlxsw_sp_qdisc, 13887917f52aSPetr Machata p->qstats); 138993d8a4c1SNogah Frankel } 139093d8a4c1SNogah Frankel 139193d8a4c1SNogah Frankel static int 139293d8a4c1SNogah Frankel mlxsw_sp_qdisc_get_prio_stats(struct mlxsw_sp_port *mlxsw_sp_port, 139393d8a4c1SNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 139493d8a4c1SNogah Frankel struct tc_qopt_offload_stats *stats_ptr) 139593d8a4c1SNogah Frankel { 1396cf9af379SPetr Machata struct mlxsw_sp_qdisc *tc_qdisc; 1397cf9af379SPetr Machata u64 tx_packets = 0; 1398cf9af379SPetr Machata u64 tx_bytes = 0; 1399cf9af379SPetr Machata u64 backlog = 0; 1400cf9af379SPetr Machata u64 drops = 0; 140193d8a4c1SNogah Frankel int i; 140293d8a4c1SNogah Frankel 140351d52ed9SPetr Machata for (i = 0; i < mlxsw_sp_qdisc->num_classes; i++) { 140451d52ed9SPetr Machata tc_qdisc = &mlxsw_sp_qdisc->qdiscs[i]; 1405cf9af379SPetr Machata mlxsw_sp_qdisc_collect_tc_stats(mlxsw_sp_port, tc_qdisc, 1406cf9af379SPetr Machata &tx_bytes, &tx_packets, 1407cf9af379SPetr Machata &drops, &backlog); 140893d8a4c1SNogah Frankel } 140993d8a4c1SNogah Frankel 1410cf9af379SPetr Machata mlxsw_sp_qdisc_update_stats(mlxsw_sp_port->mlxsw_sp, mlxsw_sp_qdisc, 1411cf9af379SPetr Machata tx_bytes, tx_packets, drops, backlog, 1412cf9af379SPetr Machata stats_ptr); 141393d8a4c1SNogah Frankel return 0; 141493d8a4c1SNogah Frankel } 141593d8a4c1SNogah Frankel 141693d8a4c1SNogah Frankel static void 141793d8a4c1SNogah Frankel mlxsw_sp_setup_tc_qdisc_prio_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port, 141893d8a4c1SNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 141993d8a4c1SNogah Frankel { 142093d8a4c1SNogah Frankel struct mlxsw_sp_qdisc_stats *stats_base; 142193d8a4c1SNogah Frankel struct mlxsw_sp_port_xstats *xstats; 142293d8a4c1SNogah Frankel struct rtnl_link_stats64 *stats; 142393d8a4c1SNogah Frankel int i; 142493d8a4c1SNogah Frankel 142593d8a4c1SNogah Frankel xstats = &mlxsw_sp_port->periodic_hw_stats.xstats; 142693d8a4c1SNogah Frankel stats = &mlxsw_sp_port->periodic_hw_stats.stats; 142793d8a4c1SNogah Frankel stats_base = &mlxsw_sp_qdisc->stats_base; 142893d8a4c1SNogah Frankel 142993d8a4c1SNogah Frankel stats_base->tx_packets = stats->tx_packets; 143093d8a4c1SNogah Frankel stats_base->tx_bytes = stats->tx_bytes; 143193d8a4c1SNogah Frankel 143293d8a4c1SNogah Frankel stats_base->drops = 0; 143323f2b404SNogah Frankel for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { 143485005b82SPetr Machata stats_base->drops += mlxsw_sp_xstats_tail_drop(xstats, i); 143523f2b404SNogah Frankel stats_base->drops += xstats->wred_drop[i]; 143623f2b404SNogah Frankel } 143793d8a4c1SNogah Frankel 143893d8a4c1SNogah Frankel mlxsw_sp_qdisc->stats_base.backlog = 0; 143993d8a4c1SNogah Frankel } 144093d8a4c1SNogah Frankel 144151d52ed9SPetr Machata static struct mlxsw_sp_qdisc * 144251d52ed9SPetr Machata mlxsw_sp_qdisc_prio_find_class(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 144351d52ed9SPetr Machata u32 parent) 144451d52ed9SPetr Machata { 144551d52ed9SPetr Machata int child_index = TC_H_MIN(parent); 144651d52ed9SPetr Machata int band = child_index - 1; 144751d52ed9SPetr Machata 144851d52ed9SPetr Machata if (band < 0 || band >= mlxsw_sp_qdisc->num_classes) 144951d52ed9SPetr Machata return NULL; 145051d52ed9SPetr Machata return &mlxsw_sp_qdisc->qdiscs[band]; 145151d52ed9SPetr Machata } 145251d52ed9SPetr Machata 145376ff72a7SPetr Machata static struct mlxsw_sp_qdisc_ets_band * 145476ff72a7SPetr Machata mlxsw_sp_qdisc_ets_get_band(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 145576ff72a7SPetr Machata struct mlxsw_sp_qdisc *child) 145676ff72a7SPetr Machata { 145776ff72a7SPetr Machata unsigned int band = child - mlxsw_sp_qdisc->qdiscs; 145876ff72a7SPetr Machata 145976ff72a7SPetr Machata if (WARN_ON(band >= IEEE_8021QAZ_MAX_TCS)) 146076ff72a7SPetr Machata band = 0; 146176ff72a7SPetr Machata return &mlxsw_sp_qdisc->ets_data->bands[band]; 146276ff72a7SPetr Machata } 146376ff72a7SPetr Machata 146476ff72a7SPetr Machata static u8 146576ff72a7SPetr Machata mlxsw_sp_qdisc_ets_get_prio_bitmap(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 146676ff72a7SPetr Machata struct mlxsw_sp_qdisc *child) 146776ff72a7SPetr Machata { 146876ff72a7SPetr Machata return mlxsw_sp_qdisc_ets_get_band(mlxsw_sp_qdisc, child)->prio_bitmap; 146976ff72a7SPetr Machata } 147076ff72a7SPetr Machata 147176ff72a7SPetr Machata static int 147276ff72a7SPetr Machata mlxsw_sp_qdisc_ets_get_tclass_num(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 147376ff72a7SPetr Machata struct mlxsw_sp_qdisc *child) 147476ff72a7SPetr Machata { 147576ff72a7SPetr Machata return mlxsw_sp_qdisc_ets_get_band(mlxsw_sp_qdisc, child)->tclass_num; 147676ff72a7SPetr Machata } 147776ff72a7SPetr Machata 147846a3615bSNogah Frankel static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_prio = { 147946a3615bSNogah Frankel .type = MLXSW_SP_QDISC_PRIO, 148046a3615bSNogah Frankel .check_params = mlxsw_sp_qdisc_prio_check_params, 148146a3615bSNogah Frankel .replace = mlxsw_sp_qdisc_prio_replace, 148293d8a4c1SNogah Frankel .unoffload = mlxsw_sp_qdisc_prio_unoffload, 148346a3615bSNogah Frankel .destroy = mlxsw_sp_qdisc_prio_destroy, 148493d8a4c1SNogah Frankel .get_stats = mlxsw_sp_qdisc_get_prio_stats, 148593d8a4c1SNogah Frankel .clean_stats = mlxsw_sp_setup_tc_qdisc_prio_clean_stats, 148651d52ed9SPetr Machata .find_class = mlxsw_sp_qdisc_prio_find_class, 14875cbd9602SPetr Machata .num_classes = IEEE_8021QAZ_MAX_TCS, 148876ff72a7SPetr Machata .get_prio_bitmap = mlxsw_sp_qdisc_ets_get_prio_bitmap, 148976ff72a7SPetr Machata .get_tclass_num = mlxsw_sp_qdisc_ets_get_tclass_num, 149046a3615bSNogah Frankel }; 149146a3615bSNogah Frankel 149219f405b9SPetr Machata static int 149319f405b9SPetr Machata mlxsw_sp_qdisc_ets_check_params(struct mlxsw_sp_port *mlxsw_sp_port, 149419f405b9SPetr Machata void *params) 149519f405b9SPetr Machata { 149619f405b9SPetr Machata struct tc_ets_qopt_offload_replace_params *p = params; 149719f405b9SPetr Machata 149819f405b9SPetr Machata return __mlxsw_sp_qdisc_ets_check_params(p->bands); 149919f405b9SPetr Machata } 150019f405b9SPetr Machata 150119f405b9SPetr Machata static int 1502c4e372e2SPetr Machata mlxsw_sp_qdisc_ets_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, 150319f405b9SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 150419f405b9SPetr Machata void *params) 150519f405b9SPetr Machata { 150619f405b9SPetr Machata struct tc_ets_qopt_offload_replace_params *p = params; 150719f405b9SPetr Machata 150851d52ed9SPetr Machata return __mlxsw_sp_qdisc_ets_replace(mlxsw_sp_port, mlxsw_sp_qdisc, 150951d52ed9SPetr Machata handle, p->bands, p->quanta, 151051d52ed9SPetr Machata p->weights, p->priomap); 151119f405b9SPetr Machata } 151219f405b9SPetr Machata 151319f405b9SPetr Machata static void 151419f405b9SPetr Machata mlxsw_sp_qdisc_ets_unoffload(struct mlxsw_sp_port *mlxsw_sp_port, 151519f405b9SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 151619f405b9SPetr Machata void *params) 151719f405b9SPetr Machata { 151819f405b9SPetr Machata struct tc_ets_qopt_offload_replace_params *p = params; 151919f405b9SPetr Machata 152019f405b9SPetr Machata __mlxsw_sp_qdisc_ets_unoffload(mlxsw_sp_port, mlxsw_sp_qdisc, 152119f405b9SPetr Machata p->qstats); 152219f405b9SPetr Machata } 152319f405b9SPetr Machata 152419f405b9SPetr Machata static int 152519f405b9SPetr Machata mlxsw_sp_qdisc_ets_destroy(struct mlxsw_sp_port *mlxsw_sp_port, 152619f405b9SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 152719f405b9SPetr Machata { 152851d52ed9SPetr Machata return __mlxsw_sp_qdisc_ets_destroy(mlxsw_sp_port, mlxsw_sp_qdisc); 152919f405b9SPetr Machata } 153019f405b9SPetr Machata 153119f405b9SPetr Machata static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_ets = { 153219f405b9SPetr Machata .type = MLXSW_SP_QDISC_ETS, 153319f405b9SPetr Machata .check_params = mlxsw_sp_qdisc_ets_check_params, 153419f405b9SPetr Machata .replace = mlxsw_sp_qdisc_ets_replace, 153519f405b9SPetr Machata .unoffload = mlxsw_sp_qdisc_ets_unoffload, 153619f405b9SPetr Machata .destroy = mlxsw_sp_qdisc_ets_destroy, 153719f405b9SPetr Machata .get_stats = mlxsw_sp_qdisc_get_prio_stats, 153819f405b9SPetr Machata .clean_stats = mlxsw_sp_setup_tc_qdisc_prio_clean_stats, 153951d52ed9SPetr Machata .find_class = mlxsw_sp_qdisc_prio_find_class, 15405cbd9602SPetr Machata .num_classes = IEEE_8021QAZ_MAX_TCS, 154176ff72a7SPetr Machata .get_prio_bitmap = mlxsw_sp_qdisc_ets_get_prio_bitmap, 154276ff72a7SPetr Machata .get_tclass_num = mlxsw_sp_qdisc_ets_get_tclass_num, 154319f405b9SPetr Machata }; 154419f405b9SPetr Machata 15455bc146c9SPetr Machata /* Linux allows linking of Qdiscs to arbitrary classes (so long as the resulting 15465bc146c9SPetr Machata * graph is free of cycles). These operations do not change the parent handle 15475bc146c9SPetr Machata * though, which means it can be incomplete (if there is more than one class 15485bc146c9SPetr Machata * where the Qdisc in question is grafted) or outright wrong (if the Qdisc was 15495bc146c9SPetr Machata * linked to a different class and then removed from the original class). 15505bc146c9SPetr Machata * 15515bc146c9SPetr Machata * E.g. consider this sequence of operations: 15525bc146c9SPetr Machata * 15535bc146c9SPetr Machata * # tc qdisc add dev swp1 root handle 1: prio 15545bc146c9SPetr Machata * # tc qdisc add dev swp1 parent 1:3 handle 13: red limit 1000000 avpkt 10000 15555bc146c9SPetr Machata * RED: set bandwidth to 10Mbit 15565bc146c9SPetr Machata * # tc qdisc link dev swp1 handle 13: parent 1:2 15575bc146c9SPetr Machata * 15585bc146c9SPetr Machata * At this point, both 1:2 and 1:3 have the same RED Qdisc instance as their 15595bc146c9SPetr Machata * child. But RED will still only claim that 1:3 is its parent. If it's removed 15605bc146c9SPetr Machata * from that band, its only parent will be 1:2, but it will continue to claim 15615bc146c9SPetr Machata * that it is in fact 1:3. 15625bc146c9SPetr Machata * 15635bc146c9SPetr Machata * The notification for child Qdisc replace (e.g. TC_RED_REPLACE) comes before 15645bc146c9SPetr Machata * the notification for parent graft (e.g. TC_PRIO_GRAFT). We take the replace 15655bc146c9SPetr Machata * notification to offload the child Qdisc, based on its parent handle, and use 15665bc146c9SPetr Machata * the graft operation to validate that the class where the child is actually 15675bc146c9SPetr Machata * grafted corresponds to the parent handle. If the two don't match, we 15685bc146c9SPetr Machata * unoffload the child. 156932dc5efcSNogah Frankel */ 1570be7e2a5aSPetr Machata static int mlxsw_sp_qdisc_graft(struct mlxsw_sp_port *mlxsw_sp_port, 157132dc5efcSNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 15727917f52aSPetr Machata u8 band, u32 child_handle) 157332dc5efcSNogah Frankel { 157432dc5efcSNogah Frankel struct mlxsw_sp_qdisc *old_qdisc; 1575d566ed04SPetr Machata u32 parent; 157632dc5efcSNogah Frankel 157751d52ed9SPetr Machata if (band < mlxsw_sp_qdisc->num_classes && 157851d52ed9SPetr Machata mlxsw_sp_qdisc->qdiscs[band].handle == child_handle) 157932dc5efcSNogah Frankel return 0; 158032dc5efcSNogah Frankel 1581a2d6d7aeSDavid S. Miller if (!child_handle) { 15823971a535SPetr Machata /* This is an invisible FIFO replacing the original Qdisc. 15833971a535SPetr Machata * Ignore it--the original Qdisc's destroy will follow. 15843971a535SPetr Machata */ 15853971a535SPetr Machata return 0; 15863971a535SPetr Machata } 15873971a535SPetr Machata 158832dc5efcSNogah Frankel /* See if the grafted qdisc is already offloaded on any tclass. If so, 158932dc5efcSNogah Frankel * unoffload it. 159032dc5efcSNogah Frankel */ 159132dc5efcSNogah Frankel old_qdisc = mlxsw_sp_qdisc_find_by_handle(mlxsw_sp_port, 15927917f52aSPetr Machata child_handle); 159332dc5efcSNogah Frankel if (old_qdisc) 159432dc5efcSNogah Frankel mlxsw_sp_qdisc_destroy(mlxsw_sp_port, old_qdisc); 159532dc5efcSNogah Frankel 1596d566ed04SPetr Machata parent = TC_H_MAKE(mlxsw_sp_qdisc->handle, band + 1); 1597d566ed04SPetr Machata mlxsw_sp_qdisc = mlxsw_sp_qdisc->ops->find_class(mlxsw_sp_qdisc, 1598d566ed04SPetr Machata parent); 159951d52ed9SPetr Machata if (!WARN_ON(!mlxsw_sp_qdisc)) 160051d52ed9SPetr Machata mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc); 160151d52ed9SPetr Machata 160232dc5efcSNogah Frankel return -EOPNOTSUPP; 160332dc5efcSNogah Frankel } 160432dc5efcSNogah Frankel 1605cff99e20SPetr Machata static int __mlxsw_sp_setup_tc_prio(struct mlxsw_sp_port *mlxsw_sp_port, 160646a3615bSNogah Frankel struct tc_prio_qopt_offload *p) 160746a3615bSNogah Frankel { 160846a3615bSNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc; 160946a3615bSNogah Frankel 1610*c2792f38SPetr Machata mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent); 1611eed4baebSNogah Frankel if (!mlxsw_sp_qdisc) 161246a3615bSNogah Frankel return -EOPNOTSUPP; 161346a3615bSNogah Frankel 161446a3615bSNogah Frankel if (p->command == TC_PRIO_REPLACE) 161546a3615bSNogah Frankel return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle, 161646a3615bSNogah Frankel mlxsw_sp_qdisc, 161746a3615bSNogah Frankel &mlxsw_sp_qdisc_ops_prio, 161846a3615bSNogah Frankel &p->replace_params); 161946a3615bSNogah Frankel 1620290fe2c5SPetr Machata if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle)) 162146a3615bSNogah Frankel return -EOPNOTSUPP; 162246a3615bSNogah Frankel 162346a3615bSNogah Frankel switch (p->command) { 162446a3615bSNogah Frankel case TC_PRIO_DESTROY: 162546a3615bSNogah Frankel return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc); 162693d8a4c1SNogah Frankel case TC_PRIO_STATS: 162793d8a4c1SNogah Frankel return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc, 162893d8a4c1SNogah Frankel &p->stats); 162932dc5efcSNogah Frankel case TC_PRIO_GRAFT: 1630be7e2a5aSPetr Machata return mlxsw_sp_qdisc_graft(mlxsw_sp_port, mlxsw_sp_qdisc, 1631be7e2a5aSPetr Machata p->graft_params.band, 1632be7e2a5aSPetr Machata p->graft_params.child_handle); 163346a3615bSNogah Frankel default: 163446a3615bSNogah Frankel return -EOPNOTSUPP; 163546a3615bSNogah Frankel } 163646a3615bSNogah Frankel } 163746a3615bSNogah Frankel 1638cff99e20SPetr Machata int mlxsw_sp_setup_tc_prio(struct mlxsw_sp_port *mlxsw_sp_port, 1639cff99e20SPetr Machata struct tc_prio_qopt_offload *p) 1640cff99e20SPetr Machata { 1641cff99e20SPetr Machata int err; 1642cff99e20SPetr Machata 1643cff99e20SPetr Machata mutex_lock(&mlxsw_sp_port->qdisc->lock); 1644cff99e20SPetr Machata err = __mlxsw_sp_setup_tc_prio(mlxsw_sp_port, p); 1645cff99e20SPetr Machata mutex_unlock(&mlxsw_sp_port->qdisc->lock); 1646cff99e20SPetr Machata 1647cff99e20SPetr Machata return err; 1648cff99e20SPetr Machata } 1649cff99e20SPetr Machata 1650cff99e20SPetr Machata static int __mlxsw_sp_setup_tc_ets(struct mlxsw_sp_port *mlxsw_sp_port, 165119f405b9SPetr Machata struct tc_ets_qopt_offload *p) 165219f405b9SPetr Machata { 165319f405b9SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc; 165419f405b9SPetr Machata 1655*c2792f38SPetr Machata mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent); 165619f405b9SPetr Machata if (!mlxsw_sp_qdisc) 165719f405b9SPetr Machata return -EOPNOTSUPP; 165819f405b9SPetr Machata 165919f405b9SPetr Machata if (p->command == TC_ETS_REPLACE) 166019f405b9SPetr Machata return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle, 166119f405b9SPetr Machata mlxsw_sp_qdisc, 166219f405b9SPetr Machata &mlxsw_sp_qdisc_ops_ets, 166319f405b9SPetr Machata &p->replace_params); 166419f405b9SPetr Machata 1665290fe2c5SPetr Machata if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle)) 166619f405b9SPetr Machata return -EOPNOTSUPP; 166719f405b9SPetr Machata 166819f405b9SPetr Machata switch (p->command) { 166919f405b9SPetr Machata case TC_ETS_DESTROY: 167019f405b9SPetr Machata return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc); 167119f405b9SPetr Machata case TC_ETS_STATS: 167219f405b9SPetr Machata return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc, 167319f405b9SPetr Machata &p->stats); 167419f405b9SPetr Machata case TC_ETS_GRAFT: 1675be7e2a5aSPetr Machata return mlxsw_sp_qdisc_graft(mlxsw_sp_port, mlxsw_sp_qdisc, 167619f405b9SPetr Machata p->graft_params.band, 167719f405b9SPetr Machata p->graft_params.child_handle); 167819f405b9SPetr Machata default: 167919f405b9SPetr Machata return -EOPNOTSUPP; 168019f405b9SPetr Machata } 168119f405b9SPetr Machata } 168219f405b9SPetr Machata 1683cff99e20SPetr Machata int mlxsw_sp_setup_tc_ets(struct mlxsw_sp_port *mlxsw_sp_port, 1684cff99e20SPetr Machata struct tc_ets_qopt_offload *p) 1685cff99e20SPetr Machata { 1686cff99e20SPetr Machata int err; 1687cff99e20SPetr Machata 1688cff99e20SPetr Machata mutex_lock(&mlxsw_sp_port->qdisc->lock); 1689cff99e20SPetr Machata err = __mlxsw_sp_setup_tc_ets(mlxsw_sp_port, p); 1690cff99e20SPetr Machata mutex_unlock(&mlxsw_sp_port->qdisc->lock); 1691cff99e20SPetr Machata 1692cff99e20SPetr Machata return err; 1693cff99e20SPetr Machata } 1694cff99e20SPetr Machata 1695f6668eacSPetr Machata struct mlxsw_sp_qevent_block { 1696f6668eacSPetr Machata struct list_head binding_list; 1697f6668eacSPetr Machata struct list_head mall_entry_list; 1698f6668eacSPetr Machata struct mlxsw_sp *mlxsw_sp; 1699f6668eacSPetr Machata }; 1700f6668eacSPetr Machata 1701f6668eacSPetr Machata struct mlxsw_sp_qevent_binding { 1702f6668eacSPetr Machata struct list_head list; 1703f6668eacSPetr Machata struct mlxsw_sp_port *mlxsw_sp_port; 1704f6668eacSPetr Machata u32 handle; 1705f6668eacSPetr Machata int tclass_num; 1706f6668eacSPetr Machata enum mlxsw_sp_span_trigger span_trigger; 1707099bf89dSPetr Machata unsigned int action_mask; 1708f6668eacSPetr Machata }; 1709f6668eacSPetr Machata 1710f6668eacSPetr Machata static LIST_HEAD(mlxsw_sp_qevent_block_cb_list); 1711f6668eacSPetr Machata 171254a92385SPetr Machata static int mlxsw_sp_qevent_span_configure(struct mlxsw_sp *mlxsw_sp, 1713f6668eacSPetr Machata struct mlxsw_sp_mall_entry *mall_entry, 171454a92385SPetr Machata struct mlxsw_sp_qevent_binding *qevent_binding, 171554a92385SPetr Machata const struct mlxsw_sp_span_agent_parms *agent_parms, 171654a92385SPetr Machata int *p_span_id) 1717f6668eacSPetr Machata { 17180908e42aSPetr Machata enum mlxsw_sp_span_trigger span_trigger = qevent_binding->span_trigger; 1719f6668eacSPetr Machata struct mlxsw_sp_port *mlxsw_sp_port = qevent_binding->mlxsw_sp_port; 1720f6668eacSPetr Machata struct mlxsw_sp_span_trigger_parms trigger_parms = {}; 17210908e42aSPetr Machata bool ingress; 1722f6668eacSPetr Machata int span_id; 1723f6668eacSPetr Machata int err; 1724f6668eacSPetr Machata 172554a92385SPetr Machata err = mlxsw_sp_span_agent_get(mlxsw_sp, &span_id, agent_parms); 1726f6668eacSPetr Machata if (err) 1727f6668eacSPetr Machata return err; 1728f6668eacSPetr Machata 17290908e42aSPetr Machata ingress = mlxsw_sp_span_trigger_is_ingress(span_trigger); 17300908e42aSPetr Machata err = mlxsw_sp_span_analyzed_port_get(mlxsw_sp_port, ingress); 1731f6668eacSPetr Machata if (err) 1732f6668eacSPetr Machata goto err_analyzed_port_get; 1733f6668eacSPetr Machata 1734f6668eacSPetr Machata trigger_parms.span_id = span_id; 17352dcbd920SIdo Schimmel trigger_parms.probability_rate = 1; 17360908e42aSPetr Machata err = mlxsw_sp_span_agent_bind(mlxsw_sp, span_trigger, mlxsw_sp_port, 1737f6668eacSPetr Machata &trigger_parms); 1738f6668eacSPetr Machata if (err) 1739f6668eacSPetr Machata goto err_agent_bind; 1740f6668eacSPetr Machata 17410908e42aSPetr Machata err = mlxsw_sp_span_trigger_enable(mlxsw_sp_port, span_trigger, 1742f6668eacSPetr Machata qevent_binding->tclass_num); 1743f6668eacSPetr Machata if (err) 1744f6668eacSPetr Machata goto err_trigger_enable; 1745f6668eacSPetr Machata 174654a92385SPetr Machata *p_span_id = span_id; 1747f6668eacSPetr Machata return 0; 1748f6668eacSPetr Machata 1749f6668eacSPetr Machata err_trigger_enable: 17500908e42aSPetr Machata mlxsw_sp_span_agent_unbind(mlxsw_sp, span_trigger, mlxsw_sp_port, 1751f6668eacSPetr Machata &trigger_parms); 1752f6668eacSPetr Machata err_agent_bind: 17530908e42aSPetr Machata mlxsw_sp_span_analyzed_port_put(mlxsw_sp_port, ingress); 1754f6668eacSPetr Machata err_analyzed_port_get: 1755f6668eacSPetr Machata mlxsw_sp_span_agent_put(mlxsw_sp, span_id); 1756f6668eacSPetr Machata return err; 1757f6668eacSPetr Machata } 1758f6668eacSPetr Machata 175954a92385SPetr Machata static void mlxsw_sp_qevent_span_deconfigure(struct mlxsw_sp *mlxsw_sp, 176054a92385SPetr Machata struct mlxsw_sp_qevent_binding *qevent_binding, 176154a92385SPetr Machata int span_id) 1762f6668eacSPetr Machata { 17630908e42aSPetr Machata enum mlxsw_sp_span_trigger span_trigger = qevent_binding->span_trigger; 1764f6668eacSPetr Machata struct mlxsw_sp_port *mlxsw_sp_port = qevent_binding->mlxsw_sp_port; 1765f6668eacSPetr Machata struct mlxsw_sp_span_trigger_parms trigger_parms = { 176654a92385SPetr Machata .span_id = span_id, 1767f6668eacSPetr Machata }; 17680908e42aSPetr Machata bool ingress; 1769f6668eacSPetr Machata 17700908e42aSPetr Machata ingress = mlxsw_sp_span_trigger_is_ingress(span_trigger); 17710908e42aSPetr Machata 17720908e42aSPetr Machata mlxsw_sp_span_trigger_disable(mlxsw_sp_port, span_trigger, 1773f6668eacSPetr Machata qevent_binding->tclass_num); 17740908e42aSPetr Machata mlxsw_sp_span_agent_unbind(mlxsw_sp, span_trigger, mlxsw_sp_port, 1775f6668eacSPetr Machata &trigger_parms); 17760908e42aSPetr Machata mlxsw_sp_span_analyzed_port_put(mlxsw_sp_port, ingress); 177754a92385SPetr Machata mlxsw_sp_span_agent_put(mlxsw_sp, span_id); 177854a92385SPetr Machata } 177954a92385SPetr Machata 178054a92385SPetr Machata static int mlxsw_sp_qevent_mirror_configure(struct mlxsw_sp *mlxsw_sp, 178154a92385SPetr Machata struct mlxsw_sp_mall_entry *mall_entry, 178254a92385SPetr Machata struct mlxsw_sp_qevent_binding *qevent_binding) 178354a92385SPetr Machata { 178454a92385SPetr Machata struct mlxsw_sp_span_agent_parms agent_parms = { 178554a92385SPetr Machata .to_dev = mall_entry->mirror.to_dev, 178654a92385SPetr Machata }; 178754a92385SPetr Machata 178854a92385SPetr Machata return mlxsw_sp_qevent_span_configure(mlxsw_sp, mall_entry, qevent_binding, 178954a92385SPetr Machata &agent_parms, &mall_entry->mirror.span_id); 179054a92385SPetr Machata } 179154a92385SPetr Machata 179254a92385SPetr Machata static void mlxsw_sp_qevent_mirror_deconfigure(struct mlxsw_sp *mlxsw_sp, 179354a92385SPetr Machata struct mlxsw_sp_mall_entry *mall_entry, 179454a92385SPetr Machata struct mlxsw_sp_qevent_binding *qevent_binding) 179554a92385SPetr Machata { 179654a92385SPetr Machata mlxsw_sp_qevent_span_deconfigure(mlxsw_sp, qevent_binding, mall_entry->mirror.span_id); 179754a92385SPetr Machata } 179854a92385SPetr Machata 179954a92385SPetr Machata static int mlxsw_sp_qevent_trap_configure(struct mlxsw_sp *mlxsw_sp, 180054a92385SPetr Machata struct mlxsw_sp_mall_entry *mall_entry, 180154a92385SPetr Machata struct mlxsw_sp_qevent_binding *qevent_binding) 180254a92385SPetr Machata { 18035c7659ebSIdo Schimmel struct mlxsw_sp_span_agent_parms agent_parms = { 18045c7659ebSIdo Schimmel .session_id = MLXSW_SP_SPAN_SESSION_ID_BUFFER, 18055c7659ebSIdo Schimmel }; 180654a92385SPetr Machata int err; 180754a92385SPetr Machata 180854a92385SPetr Machata err = mlxsw_sp_trap_group_policer_hw_id_get(mlxsw_sp, 180954a92385SPetr Machata DEVLINK_TRAP_GROUP_GENERIC_ID_BUFFER_DROPS, 181054a92385SPetr Machata &agent_parms.policer_enable, 181154a92385SPetr Machata &agent_parms.policer_id); 181254a92385SPetr Machata if (err) 181354a92385SPetr Machata return err; 181454a92385SPetr Machata 181554a92385SPetr Machata return mlxsw_sp_qevent_span_configure(mlxsw_sp, mall_entry, qevent_binding, 181654a92385SPetr Machata &agent_parms, &mall_entry->trap.span_id); 181754a92385SPetr Machata } 181854a92385SPetr Machata 181954a92385SPetr Machata static void mlxsw_sp_qevent_trap_deconfigure(struct mlxsw_sp *mlxsw_sp, 182054a92385SPetr Machata struct mlxsw_sp_mall_entry *mall_entry, 182154a92385SPetr Machata struct mlxsw_sp_qevent_binding *qevent_binding) 182254a92385SPetr Machata { 182354a92385SPetr Machata mlxsw_sp_qevent_span_deconfigure(mlxsw_sp, qevent_binding, mall_entry->trap.span_id); 1824f6668eacSPetr Machata } 1825f6668eacSPetr Machata 1826a34dda72SPetr Machata static int 1827a34dda72SPetr Machata mlxsw_sp_qevent_entry_configure(struct mlxsw_sp *mlxsw_sp, 1828f6668eacSPetr Machata struct mlxsw_sp_mall_entry *mall_entry, 1829a34dda72SPetr Machata struct mlxsw_sp_qevent_binding *qevent_binding, 1830a34dda72SPetr Machata struct netlink_ext_ack *extack) 1831f6668eacSPetr Machata { 1832099bf89dSPetr Machata if (!(BIT(mall_entry->type) & qevent_binding->action_mask)) { 1833099bf89dSPetr Machata NL_SET_ERR_MSG(extack, "Action not supported at this qevent"); 1834099bf89dSPetr Machata return -EOPNOTSUPP; 1835099bf89dSPetr Machata } 1836099bf89dSPetr Machata 1837f6668eacSPetr Machata switch (mall_entry->type) { 1838f6668eacSPetr Machata case MLXSW_SP_MALL_ACTION_TYPE_MIRROR: 1839f6668eacSPetr Machata return mlxsw_sp_qevent_mirror_configure(mlxsw_sp, mall_entry, qevent_binding); 184054a92385SPetr Machata case MLXSW_SP_MALL_ACTION_TYPE_TRAP: 184154a92385SPetr Machata return mlxsw_sp_qevent_trap_configure(mlxsw_sp, mall_entry, qevent_binding); 1842f6668eacSPetr Machata default: 1843f6668eacSPetr Machata /* This should have been validated away. */ 1844f6668eacSPetr Machata WARN_ON(1); 1845f6668eacSPetr Machata return -EOPNOTSUPP; 1846f6668eacSPetr Machata } 1847f6668eacSPetr Machata } 1848f6668eacSPetr Machata 1849f6668eacSPetr Machata static void mlxsw_sp_qevent_entry_deconfigure(struct mlxsw_sp *mlxsw_sp, 1850f6668eacSPetr Machata struct mlxsw_sp_mall_entry *mall_entry, 1851f6668eacSPetr Machata struct mlxsw_sp_qevent_binding *qevent_binding) 1852f6668eacSPetr Machata { 1853f6668eacSPetr Machata switch (mall_entry->type) { 1854f6668eacSPetr Machata case MLXSW_SP_MALL_ACTION_TYPE_MIRROR: 1855f6668eacSPetr Machata return mlxsw_sp_qevent_mirror_deconfigure(mlxsw_sp, mall_entry, qevent_binding); 185654a92385SPetr Machata case MLXSW_SP_MALL_ACTION_TYPE_TRAP: 185754a92385SPetr Machata return mlxsw_sp_qevent_trap_deconfigure(mlxsw_sp, mall_entry, qevent_binding); 1858f6668eacSPetr Machata default: 1859f6668eacSPetr Machata WARN_ON(1); 1860f6668eacSPetr Machata return; 1861f6668eacSPetr Machata } 1862f6668eacSPetr Machata } 1863f6668eacSPetr Machata 1864a34dda72SPetr Machata static int 1865a34dda72SPetr Machata mlxsw_sp_qevent_binding_configure(struct mlxsw_sp_qevent_block *qevent_block, 1866a34dda72SPetr Machata struct mlxsw_sp_qevent_binding *qevent_binding, 1867a34dda72SPetr Machata struct netlink_ext_ack *extack) 1868f6668eacSPetr Machata { 1869f6668eacSPetr Machata struct mlxsw_sp_mall_entry *mall_entry; 1870f6668eacSPetr Machata int err; 1871f6668eacSPetr Machata 1872f6668eacSPetr Machata list_for_each_entry(mall_entry, &qevent_block->mall_entry_list, list) { 1873f6668eacSPetr Machata err = mlxsw_sp_qevent_entry_configure(qevent_block->mlxsw_sp, mall_entry, 1874a34dda72SPetr Machata qevent_binding, extack); 1875f6668eacSPetr Machata if (err) 1876f6668eacSPetr Machata goto err_entry_configure; 1877f6668eacSPetr Machata } 1878f6668eacSPetr Machata 1879f6668eacSPetr Machata return 0; 1880f6668eacSPetr Machata 1881f6668eacSPetr Machata err_entry_configure: 1882f6668eacSPetr Machata list_for_each_entry_continue_reverse(mall_entry, &qevent_block->mall_entry_list, list) 1883f6668eacSPetr Machata mlxsw_sp_qevent_entry_deconfigure(qevent_block->mlxsw_sp, mall_entry, 1884f6668eacSPetr Machata qevent_binding); 1885f6668eacSPetr Machata return err; 1886f6668eacSPetr Machata } 1887f6668eacSPetr Machata 1888f6668eacSPetr Machata static void mlxsw_sp_qevent_binding_deconfigure(struct mlxsw_sp_qevent_block *qevent_block, 1889f6668eacSPetr Machata struct mlxsw_sp_qevent_binding *qevent_binding) 1890f6668eacSPetr Machata { 1891f6668eacSPetr Machata struct mlxsw_sp_mall_entry *mall_entry; 1892f6668eacSPetr Machata 1893f6668eacSPetr Machata list_for_each_entry(mall_entry, &qevent_block->mall_entry_list, list) 1894f6668eacSPetr Machata mlxsw_sp_qevent_entry_deconfigure(qevent_block->mlxsw_sp, mall_entry, 1895f6668eacSPetr Machata qevent_binding); 1896f6668eacSPetr Machata } 1897f6668eacSPetr Machata 1898a34dda72SPetr Machata static int 1899a34dda72SPetr Machata mlxsw_sp_qevent_block_configure(struct mlxsw_sp_qevent_block *qevent_block, 1900a34dda72SPetr Machata struct netlink_ext_ack *extack) 1901f6668eacSPetr Machata { 1902f6668eacSPetr Machata struct mlxsw_sp_qevent_binding *qevent_binding; 1903f6668eacSPetr Machata int err; 1904f6668eacSPetr Machata 1905f6668eacSPetr Machata list_for_each_entry(qevent_binding, &qevent_block->binding_list, list) { 1906a34dda72SPetr Machata err = mlxsw_sp_qevent_binding_configure(qevent_block, 1907a34dda72SPetr Machata qevent_binding, 1908a34dda72SPetr Machata extack); 1909f6668eacSPetr Machata if (err) 1910f6668eacSPetr Machata goto err_binding_configure; 1911f6668eacSPetr Machata } 1912f6668eacSPetr Machata 1913f6668eacSPetr Machata return 0; 1914f6668eacSPetr Machata 1915f6668eacSPetr Machata err_binding_configure: 1916f6668eacSPetr Machata list_for_each_entry_continue_reverse(qevent_binding, &qevent_block->binding_list, list) 1917f6668eacSPetr Machata mlxsw_sp_qevent_binding_deconfigure(qevent_block, qevent_binding); 1918f6668eacSPetr Machata return err; 1919f6668eacSPetr Machata } 1920f6668eacSPetr Machata 1921f6668eacSPetr Machata static void mlxsw_sp_qevent_block_deconfigure(struct mlxsw_sp_qevent_block *qevent_block) 1922f6668eacSPetr Machata { 1923f6668eacSPetr Machata struct mlxsw_sp_qevent_binding *qevent_binding; 1924f6668eacSPetr Machata 1925f6668eacSPetr Machata list_for_each_entry(qevent_binding, &qevent_block->binding_list, list) 1926f6668eacSPetr Machata mlxsw_sp_qevent_binding_deconfigure(qevent_block, qevent_binding); 1927f6668eacSPetr Machata } 1928f6668eacSPetr Machata 1929f6668eacSPetr Machata static struct mlxsw_sp_mall_entry * 1930f6668eacSPetr Machata mlxsw_sp_qevent_mall_entry_find(struct mlxsw_sp_qevent_block *block, unsigned long cookie) 1931f6668eacSPetr Machata { 1932f6668eacSPetr Machata struct mlxsw_sp_mall_entry *mall_entry; 1933f6668eacSPetr Machata 1934f6668eacSPetr Machata list_for_each_entry(mall_entry, &block->mall_entry_list, list) 1935f6668eacSPetr Machata if (mall_entry->cookie == cookie) 1936f6668eacSPetr Machata return mall_entry; 1937f6668eacSPetr Machata 1938f6668eacSPetr Machata return NULL; 1939f6668eacSPetr Machata } 1940f6668eacSPetr Machata 1941f6668eacSPetr Machata static int mlxsw_sp_qevent_mall_replace(struct mlxsw_sp *mlxsw_sp, 1942f6668eacSPetr Machata struct mlxsw_sp_qevent_block *qevent_block, 1943f6668eacSPetr Machata struct tc_cls_matchall_offload *f) 1944f6668eacSPetr Machata { 1945f6668eacSPetr Machata struct mlxsw_sp_mall_entry *mall_entry; 1946f6668eacSPetr Machata struct flow_action_entry *act; 1947f6668eacSPetr Machata int err; 1948f6668eacSPetr Machata 1949f6668eacSPetr Machata /* It should not currently be possible to replace a matchall rule. So 1950f6668eacSPetr Machata * this must be a new rule. 1951f6668eacSPetr Machata */ 1952f6668eacSPetr Machata if (!list_empty(&qevent_block->mall_entry_list)) { 1953f6668eacSPetr Machata NL_SET_ERR_MSG(f->common.extack, "At most one filter supported"); 1954f6668eacSPetr Machata return -EOPNOTSUPP; 1955f6668eacSPetr Machata } 1956f6668eacSPetr Machata if (f->rule->action.num_entries != 1) { 1957f6668eacSPetr Machata NL_SET_ERR_MSG(f->common.extack, "Only singular actions supported"); 1958f6668eacSPetr Machata return -EOPNOTSUPP; 1959f6668eacSPetr Machata } 1960f6668eacSPetr Machata if (f->common.chain_index) { 1961f6668eacSPetr Machata NL_SET_ERR_MSG(f->common.extack, "Only chain 0 is supported"); 1962f6668eacSPetr Machata return -EOPNOTSUPP; 1963f6668eacSPetr Machata } 1964f6668eacSPetr Machata if (f->common.protocol != htons(ETH_P_ALL)) { 1965f6668eacSPetr Machata NL_SET_ERR_MSG(f->common.extack, "Protocol matching not supported"); 1966f6668eacSPetr Machata return -EOPNOTSUPP; 1967f6668eacSPetr Machata } 1968f6668eacSPetr Machata 1969f6668eacSPetr Machata act = &f->rule->action.entries[0]; 1970f6668eacSPetr Machata if (!(act->hw_stats & FLOW_ACTION_HW_STATS_DISABLED)) { 1971f6668eacSPetr Machata NL_SET_ERR_MSG(f->common.extack, "HW counters not supported on qevents"); 1972f6668eacSPetr Machata return -EOPNOTSUPP; 1973f6668eacSPetr Machata } 1974f6668eacSPetr Machata 1975f6668eacSPetr Machata mall_entry = kzalloc(sizeof(*mall_entry), GFP_KERNEL); 1976f6668eacSPetr Machata if (!mall_entry) 1977f6668eacSPetr Machata return -ENOMEM; 1978f6668eacSPetr Machata mall_entry->cookie = f->cookie; 1979f6668eacSPetr Machata 1980f6668eacSPetr Machata if (act->id == FLOW_ACTION_MIRRED) { 1981f6668eacSPetr Machata mall_entry->type = MLXSW_SP_MALL_ACTION_TYPE_MIRROR; 1982f6668eacSPetr Machata mall_entry->mirror.to_dev = act->dev; 198354a92385SPetr Machata } else if (act->id == FLOW_ACTION_TRAP) { 198454a92385SPetr Machata mall_entry->type = MLXSW_SP_MALL_ACTION_TYPE_TRAP; 1985f6668eacSPetr Machata } else { 1986f6668eacSPetr Machata NL_SET_ERR_MSG(f->common.extack, "Unsupported action"); 1987f6668eacSPetr Machata err = -EOPNOTSUPP; 1988f6668eacSPetr Machata goto err_unsupported_action; 1989f6668eacSPetr Machata } 1990f6668eacSPetr Machata 1991f6668eacSPetr Machata list_add_tail(&mall_entry->list, &qevent_block->mall_entry_list); 1992f6668eacSPetr Machata 1993a34dda72SPetr Machata err = mlxsw_sp_qevent_block_configure(qevent_block, f->common.extack); 1994f6668eacSPetr Machata if (err) 1995f6668eacSPetr Machata goto err_block_configure; 1996f6668eacSPetr Machata 1997f6668eacSPetr Machata return 0; 1998f6668eacSPetr Machata 1999f6668eacSPetr Machata err_block_configure: 2000f6668eacSPetr Machata list_del(&mall_entry->list); 2001f6668eacSPetr Machata err_unsupported_action: 2002f6668eacSPetr Machata kfree(mall_entry); 2003f6668eacSPetr Machata return err; 2004f6668eacSPetr Machata } 2005f6668eacSPetr Machata 2006f6668eacSPetr Machata static void mlxsw_sp_qevent_mall_destroy(struct mlxsw_sp_qevent_block *qevent_block, 2007f6668eacSPetr Machata struct tc_cls_matchall_offload *f) 2008f6668eacSPetr Machata { 2009f6668eacSPetr Machata struct mlxsw_sp_mall_entry *mall_entry; 2010f6668eacSPetr Machata 2011f6668eacSPetr Machata mall_entry = mlxsw_sp_qevent_mall_entry_find(qevent_block, f->cookie); 2012f6668eacSPetr Machata if (!mall_entry) 2013f6668eacSPetr Machata return; 2014f6668eacSPetr Machata 2015f6668eacSPetr Machata mlxsw_sp_qevent_block_deconfigure(qevent_block); 2016f6668eacSPetr Machata 2017f6668eacSPetr Machata list_del(&mall_entry->list); 2018f6668eacSPetr Machata kfree(mall_entry); 2019f6668eacSPetr Machata } 2020f6668eacSPetr Machata 2021f6668eacSPetr Machata static int mlxsw_sp_qevent_block_mall_cb(struct mlxsw_sp_qevent_block *qevent_block, 2022f6668eacSPetr Machata struct tc_cls_matchall_offload *f) 2023f6668eacSPetr Machata { 2024f6668eacSPetr Machata struct mlxsw_sp *mlxsw_sp = qevent_block->mlxsw_sp; 2025f6668eacSPetr Machata 2026f6668eacSPetr Machata switch (f->command) { 2027f6668eacSPetr Machata case TC_CLSMATCHALL_REPLACE: 2028f6668eacSPetr Machata return mlxsw_sp_qevent_mall_replace(mlxsw_sp, qevent_block, f); 2029f6668eacSPetr Machata case TC_CLSMATCHALL_DESTROY: 2030f6668eacSPetr Machata mlxsw_sp_qevent_mall_destroy(qevent_block, f); 2031f6668eacSPetr Machata return 0; 2032f6668eacSPetr Machata default: 2033f6668eacSPetr Machata return -EOPNOTSUPP; 2034f6668eacSPetr Machata } 2035f6668eacSPetr Machata } 2036f6668eacSPetr Machata 2037f6668eacSPetr Machata static int mlxsw_sp_qevent_block_cb(enum tc_setup_type type, void *type_data, void *cb_priv) 2038f6668eacSPetr Machata { 2039f6668eacSPetr Machata struct mlxsw_sp_qevent_block *qevent_block = cb_priv; 2040f6668eacSPetr Machata 2041f6668eacSPetr Machata switch (type) { 2042f6668eacSPetr Machata case TC_SETUP_CLSMATCHALL: 2043f6668eacSPetr Machata return mlxsw_sp_qevent_block_mall_cb(qevent_block, type_data); 2044f6668eacSPetr Machata default: 2045f6668eacSPetr Machata return -EOPNOTSUPP; 2046f6668eacSPetr Machata } 2047f6668eacSPetr Machata } 2048f6668eacSPetr Machata 2049f6668eacSPetr Machata static struct mlxsw_sp_qevent_block *mlxsw_sp_qevent_block_create(struct mlxsw_sp *mlxsw_sp, 2050f6668eacSPetr Machata struct net *net) 2051f6668eacSPetr Machata { 2052f6668eacSPetr Machata struct mlxsw_sp_qevent_block *qevent_block; 2053f6668eacSPetr Machata 2054f6668eacSPetr Machata qevent_block = kzalloc(sizeof(*qevent_block), GFP_KERNEL); 2055f6668eacSPetr Machata if (!qevent_block) 2056f6668eacSPetr Machata return NULL; 2057f6668eacSPetr Machata 2058f6668eacSPetr Machata INIT_LIST_HEAD(&qevent_block->binding_list); 2059f6668eacSPetr Machata INIT_LIST_HEAD(&qevent_block->mall_entry_list); 2060f6668eacSPetr Machata qevent_block->mlxsw_sp = mlxsw_sp; 2061f6668eacSPetr Machata return qevent_block; 2062f6668eacSPetr Machata } 2063f6668eacSPetr Machata 2064f6668eacSPetr Machata static void 2065f6668eacSPetr Machata mlxsw_sp_qevent_block_destroy(struct mlxsw_sp_qevent_block *qevent_block) 2066f6668eacSPetr Machata { 2067f6668eacSPetr Machata WARN_ON(!list_empty(&qevent_block->binding_list)); 2068f6668eacSPetr Machata WARN_ON(!list_empty(&qevent_block->mall_entry_list)); 2069f6668eacSPetr Machata kfree(qevent_block); 2070f6668eacSPetr Machata } 2071f6668eacSPetr Machata 2072f6668eacSPetr Machata static void mlxsw_sp_qevent_block_release(void *cb_priv) 2073f6668eacSPetr Machata { 2074f6668eacSPetr Machata struct mlxsw_sp_qevent_block *qevent_block = cb_priv; 2075f6668eacSPetr Machata 2076f6668eacSPetr Machata mlxsw_sp_qevent_block_destroy(qevent_block); 2077f6668eacSPetr Machata } 2078f6668eacSPetr Machata 2079f6668eacSPetr Machata static struct mlxsw_sp_qevent_binding * 2080f6668eacSPetr Machata mlxsw_sp_qevent_binding_create(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, int tclass_num, 2081099bf89dSPetr Machata enum mlxsw_sp_span_trigger span_trigger, 2082099bf89dSPetr Machata unsigned int action_mask) 2083f6668eacSPetr Machata { 2084f6668eacSPetr Machata struct mlxsw_sp_qevent_binding *binding; 2085f6668eacSPetr Machata 2086f6668eacSPetr Machata binding = kzalloc(sizeof(*binding), GFP_KERNEL); 2087f6668eacSPetr Machata if (!binding) 2088f6668eacSPetr Machata return ERR_PTR(-ENOMEM); 2089f6668eacSPetr Machata 2090f6668eacSPetr Machata binding->mlxsw_sp_port = mlxsw_sp_port; 2091f6668eacSPetr Machata binding->handle = handle; 2092f6668eacSPetr Machata binding->tclass_num = tclass_num; 2093f6668eacSPetr Machata binding->span_trigger = span_trigger; 2094099bf89dSPetr Machata binding->action_mask = action_mask; 2095f6668eacSPetr Machata return binding; 2096f6668eacSPetr Machata } 2097f6668eacSPetr Machata 2098f6668eacSPetr Machata static void 2099f6668eacSPetr Machata mlxsw_sp_qevent_binding_destroy(struct mlxsw_sp_qevent_binding *binding) 2100f6668eacSPetr Machata { 2101f6668eacSPetr Machata kfree(binding); 2102f6668eacSPetr Machata } 2103f6668eacSPetr Machata 2104f6668eacSPetr Machata static struct mlxsw_sp_qevent_binding * 2105f6668eacSPetr Machata mlxsw_sp_qevent_binding_lookup(struct mlxsw_sp_qevent_block *block, 2106f6668eacSPetr Machata struct mlxsw_sp_port *mlxsw_sp_port, 2107f6668eacSPetr Machata u32 handle, 2108f6668eacSPetr Machata enum mlxsw_sp_span_trigger span_trigger) 2109f6668eacSPetr Machata { 2110f6668eacSPetr Machata struct mlxsw_sp_qevent_binding *qevent_binding; 2111f6668eacSPetr Machata 2112f6668eacSPetr Machata list_for_each_entry(qevent_binding, &block->binding_list, list) 2113f6668eacSPetr Machata if (qevent_binding->mlxsw_sp_port == mlxsw_sp_port && 2114f6668eacSPetr Machata qevent_binding->handle == handle && 2115f6668eacSPetr Machata qevent_binding->span_trigger == span_trigger) 2116f6668eacSPetr Machata return qevent_binding; 2117f6668eacSPetr Machata return NULL; 2118f6668eacSPetr Machata } 2119f6668eacSPetr Machata 2120099bf89dSPetr Machata static int 2121099bf89dSPetr Machata mlxsw_sp_setup_tc_block_qevent_bind(struct mlxsw_sp_port *mlxsw_sp_port, 2122f6668eacSPetr Machata struct flow_block_offload *f, 2123099bf89dSPetr Machata enum mlxsw_sp_span_trigger span_trigger, 2124099bf89dSPetr Machata unsigned int action_mask) 2125f6668eacSPetr Machata { 2126f6668eacSPetr Machata struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 2127f6668eacSPetr Machata struct mlxsw_sp_qevent_binding *qevent_binding; 2128f6668eacSPetr Machata struct mlxsw_sp_qevent_block *qevent_block; 2129f6668eacSPetr Machata struct flow_block_cb *block_cb; 2130f6668eacSPetr Machata struct mlxsw_sp_qdisc *qdisc; 2131f6668eacSPetr Machata bool register_block = false; 213276ff72a7SPetr Machata int tclass_num; 2133f6668eacSPetr Machata int err; 2134f6668eacSPetr Machata 2135f6668eacSPetr Machata block_cb = flow_block_cb_lookup(f->block, mlxsw_sp_qevent_block_cb, mlxsw_sp); 2136f6668eacSPetr Machata if (!block_cb) { 2137f6668eacSPetr Machata qevent_block = mlxsw_sp_qevent_block_create(mlxsw_sp, f->net); 2138f6668eacSPetr Machata if (!qevent_block) 2139f6668eacSPetr Machata return -ENOMEM; 2140f6668eacSPetr Machata block_cb = flow_block_cb_alloc(mlxsw_sp_qevent_block_cb, mlxsw_sp, qevent_block, 2141f6668eacSPetr Machata mlxsw_sp_qevent_block_release); 2142f6668eacSPetr Machata if (IS_ERR(block_cb)) { 2143f6668eacSPetr Machata mlxsw_sp_qevent_block_destroy(qevent_block); 2144f6668eacSPetr Machata return PTR_ERR(block_cb); 2145f6668eacSPetr Machata } 2146f6668eacSPetr Machata register_block = true; 2147f6668eacSPetr Machata } else { 2148f6668eacSPetr Machata qevent_block = flow_block_cb_priv(block_cb); 2149f6668eacSPetr Machata } 2150f6668eacSPetr Machata flow_block_cb_incref(block_cb); 2151f6668eacSPetr Machata 2152f6668eacSPetr Machata qdisc = mlxsw_sp_qdisc_find_by_handle(mlxsw_sp_port, f->sch->handle); 2153f6668eacSPetr Machata if (!qdisc) { 2154f6668eacSPetr Machata NL_SET_ERR_MSG(f->extack, "Qdisc not offloaded"); 2155f6668eacSPetr Machata err = -ENOENT; 2156f6668eacSPetr Machata goto err_find_qdisc; 2157f6668eacSPetr Machata } 2158f6668eacSPetr Machata 2159f6668eacSPetr Machata if (WARN_ON(mlxsw_sp_qevent_binding_lookup(qevent_block, mlxsw_sp_port, f->sch->handle, 2160f6668eacSPetr Machata span_trigger))) { 2161f6668eacSPetr Machata err = -EEXIST; 2162f6668eacSPetr Machata goto err_binding_exists; 2163f6668eacSPetr Machata } 2164f6668eacSPetr Machata 216576ff72a7SPetr Machata tclass_num = mlxsw_sp_qdisc_get_tclass_num(mlxsw_sp_port, qdisc); 2166099bf89dSPetr Machata qevent_binding = mlxsw_sp_qevent_binding_create(mlxsw_sp_port, 2167099bf89dSPetr Machata f->sch->handle, 216876ff72a7SPetr Machata tclass_num, 2169099bf89dSPetr Machata span_trigger, 2170099bf89dSPetr Machata action_mask); 2171f6668eacSPetr Machata if (IS_ERR(qevent_binding)) { 2172f6668eacSPetr Machata err = PTR_ERR(qevent_binding); 2173f6668eacSPetr Machata goto err_binding_create; 2174f6668eacSPetr Machata } 2175f6668eacSPetr Machata 2176a34dda72SPetr Machata err = mlxsw_sp_qevent_binding_configure(qevent_block, qevent_binding, 2177a34dda72SPetr Machata f->extack); 2178f6668eacSPetr Machata if (err) 2179f6668eacSPetr Machata goto err_binding_configure; 2180f6668eacSPetr Machata 2181f6668eacSPetr Machata list_add(&qevent_binding->list, &qevent_block->binding_list); 2182f6668eacSPetr Machata 2183f6668eacSPetr Machata if (register_block) { 2184f6668eacSPetr Machata flow_block_cb_add(block_cb, f); 2185f6668eacSPetr Machata list_add_tail(&block_cb->driver_list, &mlxsw_sp_qevent_block_cb_list); 2186f6668eacSPetr Machata } 2187f6668eacSPetr Machata 2188f6668eacSPetr Machata return 0; 2189f6668eacSPetr Machata 2190f6668eacSPetr Machata err_binding_configure: 2191f6668eacSPetr Machata mlxsw_sp_qevent_binding_destroy(qevent_binding); 2192f6668eacSPetr Machata err_binding_create: 2193f6668eacSPetr Machata err_binding_exists: 2194f6668eacSPetr Machata err_find_qdisc: 2195f6668eacSPetr Machata if (!flow_block_cb_decref(block_cb)) 2196f6668eacSPetr Machata flow_block_cb_free(block_cb); 2197f6668eacSPetr Machata return err; 2198f6668eacSPetr Machata } 2199f6668eacSPetr Machata 2200f6668eacSPetr Machata static void mlxsw_sp_setup_tc_block_qevent_unbind(struct mlxsw_sp_port *mlxsw_sp_port, 2201f6668eacSPetr Machata struct flow_block_offload *f, 2202f6668eacSPetr Machata enum mlxsw_sp_span_trigger span_trigger) 2203f6668eacSPetr Machata { 2204f6668eacSPetr Machata struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 2205f6668eacSPetr Machata struct mlxsw_sp_qevent_binding *qevent_binding; 2206f6668eacSPetr Machata struct mlxsw_sp_qevent_block *qevent_block; 2207f6668eacSPetr Machata struct flow_block_cb *block_cb; 2208f6668eacSPetr Machata 2209f6668eacSPetr Machata block_cb = flow_block_cb_lookup(f->block, mlxsw_sp_qevent_block_cb, mlxsw_sp); 2210f6668eacSPetr Machata if (!block_cb) 2211f6668eacSPetr Machata return; 2212f6668eacSPetr Machata qevent_block = flow_block_cb_priv(block_cb); 2213f6668eacSPetr Machata 2214f6668eacSPetr Machata qevent_binding = mlxsw_sp_qevent_binding_lookup(qevent_block, mlxsw_sp_port, f->sch->handle, 2215f6668eacSPetr Machata span_trigger); 2216f6668eacSPetr Machata if (!qevent_binding) 2217f6668eacSPetr Machata return; 2218f6668eacSPetr Machata 2219f6668eacSPetr Machata list_del(&qevent_binding->list); 2220f6668eacSPetr Machata mlxsw_sp_qevent_binding_deconfigure(qevent_block, qevent_binding); 2221f6668eacSPetr Machata mlxsw_sp_qevent_binding_destroy(qevent_binding); 2222f6668eacSPetr Machata 2223f6668eacSPetr Machata if (!flow_block_cb_decref(block_cb)) { 2224f6668eacSPetr Machata flow_block_cb_remove(block_cb, f); 2225f6668eacSPetr Machata list_del(&block_cb->driver_list); 2226f6668eacSPetr Machata } 2227f6668eacSPetr Machata } 2228f6668eacSPetr Machata 2229099bf89dSPetr Machata static int 2230099bf89dSPetr Machata mlxsw_sp_setup_tc_block_qevent(struct mlxsw_sp_port *mlxsw_sp_port, 2231f6668eacSPetr Machata struct flow_block_offload *f, 2232099bf89dSPetr Machata enum mlxsw_sp_span_trigger span_trigger, 2233099bf89dSPetr Machata unsigned int action_mask) 2234f6668eacSPetr Machata { 2235f6668eacSPetr Machata f->driver_block_list = &mlxsw_sp_qevent_block_cb_list; 2236f6668eacSPetr Machata 2237f6668eacSPetr Machata switch (f->command) { 2238f6668eacSPetr Machata case FLOW_BLOCK_BIND: 2239099bf89dSPetr Machata return mlxsw_sp_setup_tc_block_qevent_bind(mlxsw_sp_port, f, 2240099bf89dSPetr Machata span_trigger, 2241099bf89dSPetr Machata action_mask); 2242f6668eacSPetr Machata case FLOW_BLOCK_UNBIND: 2243f6668eacSPetr Machata mlxsw_sp_setup_tc_block_qevent_unbind(mlxsw_sp_port, f, span_trigger); 2244f6668eacSPetr Machata return 0; 2245f6668eacSPetr Machata default: 2246f6668eacSPetr Machata return -EOPNOTSUPP; 2247f6668eacSPetr Machata } 2248f6668eacSPetr Machata } 2249f6668eacSPetr Machata 2250f6668eacSPetr Machata int mlxsw_sp_setup_tc_block_qevent_early_drop(struct mlxsw_sp_port *mlxsw_sp_port, 2251f6668eacSPetr Machata struct flow_block_offload *f) 2252f6668eacSPetr Machata { 2253099bf89dSPetr Machata unsigned int action_mask = BIT(MLXSW_SP_MALL_ACTION_TYPE_MIRROR) | 2254099bf89dSPetr Machata BIT(MLXSW_SP_MALL_ACTION_TYPE_TRAP); 2255099bf89dSPetr Machata 2256099bf89dSPetr Machata return mlxsw_sp_setup_tc_block_qevent(mlxsw_sp_port, f, 2257099bf89dSPetr Machata MLXSW_SP_SPAN_TRIGGER_EARLY_DROP, 2258099bf89dSPetr Machata action_mask); 2259f6668eacSPetr Machata } 2260f6668eacSPetr Machata 22619c18eaf2SPetr Machata int mlxsw_sp_setup_tc_block_qevent_mark(struct mlxsw_sp_port *mlxsw_sp_port, 22629c18eaf2SPetr Machata struct flow_block_offload *f) 22639c18eaf2SPetr Machata { 22649c18eaf2SPetr Machata unsigned int action_mask = BIT(MLXSW_SP_MALL_ACTION_TYPE_MIRROR); 22659c18eaf2SPetr Machata 22669c18eaf2SPetr Machata return mlxsw_sp_setup_tc_block_qevent(mlxsw_sp_port, f, 22679c18eaf2SPetr Machata MLXSW_SP_SPAN_TRIGGER_ECN, 22689c18eaf2SPetr Machata action_mask); 22699c18eaf2SPetr Machata } 22709c18eaf2SPetr Machata 2271371b437aSNogah Frankel int mlxsw_sp_tc_qdisc_init(struct mlxsw_sp_port *mlxsw_sp_port) 2272371b437aSNogah Frankel { 2273ee88450dSPetr Machata struct mlxsw_sp_qdisc_state *qdisc_state; 2274371b437aSNogah Frankel 2275ee88450dSPetr Machata qdisc_state = kzalloc(sizeof(*qdisc_state), GFP_KERNEL); 2276ee88450dSPetr Machata if (!qdisc_state) 2277eed4baebSNogah Frankel return -ENOMEM; 2278ee88450dSPetr Machata 2279cff99e20SPetr Machata mutex_init(&qdisc_state->lock); 2280ee88450dSPetr Machata mlxsw_sp_port->qdisc = qdisc_state; 2281ee88450dSPetr Machata return 0; 2282371b437aSNogah Frankel } 2283371b437aSNogah Frankel 2284371b437aSNogah Frankel void mlxsw_sp_tc_qdisc_fini(struct mlxsw_sp_port *mlxsw_sp_port) 2285371b437aSNogah Frankel { 2286cff99e20SPetr Machata mutex_destroy(&mlxsw_sp_port->qdisc->lock); 2287ee88450dSPetr Machata kfree(mlxsw_sp_port->qdisc); 2288371b437aSNogah Frankel } 2289