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); 50*51d52ed9SPetr Machata struct mlxsw_sp_qdisc *(*find_class)(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 51*51d52ed9SPetr Machata u32 parent); 52562ffbc4SNogah Frankel }; 53562ffbc4SNogah Frankel 54371b437aSNogah Frankel struct mlxsw_sp_qdisc { 55371b437aSNogah Frankel u32 handle; 56017a131cSPetr Machata int tclass_num; 571631ab2eSNogah Frankel u8 prio_bitmap; 58371b437aSNogah Frankel union { 594d1a4b84SNogah Frankel struct red_stats red; 604d1a4b84SNogah Frankel } xstats_base; 614d1a4b84SNogah Frankel struct mlxsw_sp_qdisc_stats { 62371b437aSNogah Frankel u64 tx_bytes; 63371b437aSNogah Frankel u64 tx_packets; 64371b437aSNogah Frankel u64 drops; 65371b437aSNogah Frankel u64 overlimits; 6693d8a4c1SNogah Frankel u64 backlog; 674d1a4b84SNogah Frankel } stats_base; 68562ffbc4SNogah Frankel 69562ffbc4SNogah Frankel struct mlxsw_sp_qdisc_ops *ops; 70b21832b5SPetr Machata struct mlxsw_sp_qdisc *parent; 71*51d52ed9SPetr Machata struct mlxsw_sp_qdisc *qdiscs; 72*51d52ed9SPetr Machata unsigned int num_classes; 73371b437aSNogah Frankel }; 74371b437aSNogah Frankel 75ee88450dSPetr Machata struct mlxsw_sp_qdisc_state { 76ee88450dSPetr Machata struct mlxsw_sp_qdisc root_qdisc; 77ee88450dSPetr Machata struct mlxsw_sp_qdisc tclass_qdiscs[IEEE_8021QAZ_MAX_TCS]; 787bec1a45SPetr Machata 797bec1a45SPetr Machata /* When a PRIO or ETS are added, the invisible FIFOs in their bands are 807bec1a45SPetr Machata * created first. When notifications for these FIFOs arrive, it is not 817bec1a45SPetr Machata * known what qdisc their parent handle refers to. It could be a 827bec1a45SPetr Machata * newly-created PRIO that will replace the currently-offloaded one, or 837bec1a45SPetr Machata * it could be e.g. a RED that will be attached below it. 847bec1a45SPetr Machata * 857bec1a45SPetr Machata * As the notifications start to arrive, use them to note what the 867bec1a45SPetr Machata * future parent handle is, and keep track of which child FIFOs were 877bec1a45SPetr Machata * seen. Then when the parent is known, retroactively offload those 887bec1a45SPetr Machata * FIFOs. 897bec1a45SPetr Machata */ 907bec1a45SPetr Machata u32 future_handle; 917bec1a45SPetr Machata bool future_fifos[IEEE_8021QAZ_MAX_TCS]; 92ee88450dSPetr Machata }; 93ee88450dSPetr Machata 94cba7158fSNogah Frankel static bool 95290fe2c5SPetr Machata mlxsw_sp_qdisc_compare(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, u32 handle) 96cba7158fSNogah Frankel { 97290fe2c5SPetr Machata return mlxsw_sp_qdisc->ops && mlxsw_sp_qdisc->handle == handle; 98cba7158fSNogah Frankel } 99cba7158fSNogah Frankel 100eed4baebSNogah Frankel static struct mlxsw_sp_qdisc * 101*51d52ed9SPetr Machata mlxsw_sp_qdisc_walk(struct mlxsw_sp_qdisc *qdisc, 102*51d52ed9SPetr Machata struct mlxsw_sp_qdisc *(*pre)(struct mlxsw_sp_qdisc *, 103*51d52ed9SPetr Machata void *), 104*51d52ed9SPetr Machata void *data) 105*51d52ed9SPetr Machata { 106*51d52ed9SPetr Machata struct mlxsw_sp_qdisc *tmp; 107*51d52ed9SPetr Machata unsigned int i; 108*51d52ed9SPetr Machata 109*51d52ed9SPetr Machata if (pre) { 110*51d52ed9SPetr Machata tmp = pre(qdisc, data); 111*51d52ed9SPetr Machata if (tmp) 112*51d52ed9SPetr Machata return tmp; 113*51d52ed9SPetr Machata } 114*51d52ed9SPetr Machata 115*51d52ed9SPetr Machata if (qdisc->ops) { 116*51d52ed9SPetr Machata for (i = 0; i < qdisc->num_classes; i++) { 117*51d52ed9SPetr Machata tmp = &qdisc->qdiscs[i]; 118*51d52ed9SPetr Machata if (qdisc->ops) { 119*51d52ed9SPetr Machata tmp = mlxsw_sp_qdisc_walk(tmp, pre, data); 120*51d52ed9SPetr Machata if (tmp) 121*51d52ed9SPetr Machata return tmp; 122*51d52ed9SPetr Machata } 123*51d52ed9SPetr Machata } 124*51d52ed9SPetr Machata } 125*51d52ed9SPetr Machata 126*51d52ed9SPetr Machata return NULL; 127*51d52ed9SPetr Machata } 128*51d52ed9SPetr Machata 129*51d52ed9SPetr Machata static struct mlxsw_sp_qdisc * 130*51d52ed9SPetr Machata mlxsw_sp_qdisc_walk_cb_find(struct mlxsw_sp_qdisc *qdisc, void *data) 131*51d52ed9SPetr Machata { 132*51d52ed9SPetr Machata u32 parent = *(u32 *)data; 133*51d52ed9SPetr Machata 134*51d52ed9SPetr Machata if (qdisc->ops && TC_H_MAJ(qdisc->handle) == TC_H_MAJ(parent)) { 135*51d52ed9SPetr Machata if (qdisc->ops->find_class) 136*51d52ed9SPetr Machata return qdisc->ops->find_class(qdisc, parent); 137*51d52ed9SPetr Machata } 138*51d52ed9SPetr Machata 139*51d52ed9SPetr Machata return NULL; 140*51d52ed9SPetr Machata } 141*51d52ed9SPetr Machata 142*51d52ed9SPetr Machata static struct mlxsw_sp_qdisc * 143eed4baebSNogah Frankel mlxsw_sp_qdisc_find(struct mlxsw_sp_port *mlxsw_sp_port, u32 parent, 144eed4baebSNogah Frankel bool root_only) 145eed4baebSNogah Frankel { 146ee88450dSPetr Machata struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc; 147eed4baebSNogah Frankel 148*51d52ed9SPetr Machata if (!qdisc_state) 149*51d52ed9SPetr Machata return NULL; 150eed4baebSNogah Frankel if (parent == TC_H_ROOT) 151ee88450dSPetr Machata return &qdisc_state->root_qdisc; 152*51d52ed9SPetr Machata if (root_only) 153eed4baebSNogah Frankel return NULL; 154*51d52ed9SPetr Machata return mlxsw_sp_qdisc_walk(&qdisc_state->root_qdisc, 155*51d52ed9SPetr Machata mlxsw_sp_qdisc_walk_cb_find, &parent); 156*51d52ed9SPetr Machata } 157eed4baebSNogah Frankel 158*51d52ed9SPetr Machata static struct mlxsw_sp_qdisc * 159*51d52ed9SPetr Machata mlxsw_sp_qdisc_walk_cb_find_by_handle(struct mlxsw_sp_qdisc *qdisc, void *data) 160*51d52ed9SPetr Machata { 161*51d52ed9SPetr Machata u32 handle = *(u32 *)data; 162*51d52ed9SPetr Machata 163*51d52ed9SPetr Machata if (qdisc->ops && qdisc->handle == handle) 164*51d52ed9SPetr Machata return qdisc; 165*51d52ed9SPetr Machata return NULL; 166eed4baebSNogah Frankel } 167eed4baebSNogah Frankel 16832dc5efcSNogah Frankel static struct mlxsw_sp_qdisc * 16932dc5efcSNogah Frankel mlxsw_sp_qdisc_find_by_handle(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle) 17032dc5efcSNogah Frankel { 171ee88450dSPetr Machata struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc; 17232dc5efcSNogah Frankel 173*51d52ed9SPetr Machata if (!qdisc_state) 17432dc5efcSNogah Frankel return NULL; 175*51d52ed9SPetr Machata return mlxsw_sp_qdisc_walk(&qdisc_state->root_qdisc, 176*51d52ed9SPetr Machata mlxsw_sp_qdisc_walk_cb_find_by_handle, 177*51d52ed9SPetr Machata &handle); 17832dc5efcSNogah Frankel } 17932dc5efcSNogah Frankel 180b21832b5SPetr Machata static void 181b21832b5SPetr Machata mlxsw_sp_qdisc_reduce_parent_backlog(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 182b21832b5SPetr Machata { 183b21832b5SPetr Machata struct mlxsw_sp_qdisc *tmp; 184b21832b5SPetr Machata 185b21832b5SPetr Machata for (tmp = mlxsw_sp_qdisc->parent; tmp; tmp = tmp->parent) 186b21832b5SPetr Machata tmp->stats_base.backlog -= mlxsw_sp_qdisc->stats_base.backlog; 187b21832b5SPetr Machata } 188b21832b5SPetr Machata 18996f17e07SNogah Frankel static int 1909a37a59fSNogah Frankel mlxsw_sp_qdisc_destroy(struct mlxsw_sp_port *mlxsw_sp_port, 1919a37a59fSNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 1929a37a59fSNogah Frankel { 193509f04caSPetr Machata struct mlxsw_sp_qdisc *root_qdisc = &mlxsw_sp_port->qdisc->root_qdisc; 194509f04caSPetr Machata int err_hdroom = 0; 1959a37a59fSNogah Frankel int err = 0; 1969a37a59fSNogah Frankel 1979a37a59fSNogah Frankel if (!mlxsw_sp_qdisc) 1989a37a59fSNogah Frankel return 0; 1999a37a59fSNogah Frankel 200509f04caSPetr Machata if (root_qdisc == mlxsw_sp_qdisc) { 201509f04caSPetr Machata struct mlxsw_sp_hdroom hdroom = *mlxsw_sp_port->hdroom; 202509f04caSPetr Machata 203509f04caSPetr Machata hdroom.mode = MLXSW_SP_HDROOM_MODE_DCB; 204509f04caSPetr Machata mlxsw_sp_hdroom_prios_reset_buf_idx(&hdroom); 205509f04caSPetr Machata mlxsw_sp_hdroom_bufs_reset_lossiness(&hdroom); 206509f04caSPetr Machata mlxsw_sp_hdroom_bufs_reset_sizes(mlxsw_sp_port, &hdroom); 207509f04caSPetr Machata err_hdroom = mlxsw_sp_hdroom_configure(mlxsw_sp_port, &hdroom); 208509f04caSPetr Machata } 209509f04caSPetr Machata 210b21832b5SPetr Machata if (!mlxsw_sp_qdisc->ops) 211b21832b5SPetr Machata return 0; 212b21832b5SPetr Machata 213b21832b5SPetr Machata mlxsw_sp_qdisc_reduce_parent_backlog(mlxsw_sp_qdisc); 214b21832b5SPetr Machata if (mlxsw_sp_qdisc->ops->destroy) 2159a37a59fSNogah Frankel err = mlxsw_sp_qdisc->ops->destroy(mlxsw_sp_port, 2169a37a59fSNogah Frankel mlxsw_sp_qdisc); 2179a37a59fSNogah Frankel 2189a37a59fSNogah Frankel mlxsw_sp_qdisc->handle = TC_H_UNSPEC; 2199a37a59fSNogah Frankel mlxsw_sp_qdisc->ops = NULL; 220509f04caSPetr Machata 221509f04caSPetr Machata return err_hdroom ?: err; 2229a37a59fSNogah Frankel } 2239a37a59fSNogah Frankel 2249a37a59fSNogah Frankel static int 2259cf6c9c7SNogah Frankel mlxsw_sp_qdisc_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, 2269cf6c9c7SNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 2279cf6c9c7SNogah Frankel struct mlxsw_sp_qdisc_ops *ops, void *params) 2289cf6c9c7SNogah Frankel { 229509f04caSPetr Machata struct mlxsw_sp_qdisc *root_qdisc = &mlxsw_sp_port->qdisc->root_qdisc; 230509f04caSPetr Machata struct mlxsw_sp_hdroom orig_hdroom; 2319cf6c9c7SNogah Frankel int err; 2329cf6c9c7SNogah Frankel 23356202ca4SNogah Frankel if (mlxsw_sp_qdisc->ops && mlxsw_sp_qdisc->ops->type != ops->type) 23456202ca4SNogah Frankel /* In case this location contained a different qdisc of the 23556202ca4SNogah Frankel * same type we can override the old qdisc configuration. 23656202ca4SNogah Frankel * Otherwise, we need to remove the old qdisc before setting the 23756202ca4SNogah Frankel * new one. 23856202ca4SNogah Frankel */ 23956202ca4SNogah Frankel mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc); 240509f04caSPetr Machata 241509f04caSPetr Machata orig_hdroom = *mlxsw_sp_port->hdroom; 242509f04caSPetr Machata if (root_qdisc == mlxsw_sp_qdisc) { 243509f04caSPetr Machata struct mlxsw_sp_hdroom hdroom = orig_hdroom; 244509f04caSPetr Machata 245509f04caSPetr Machata hdroom.mode = MLXSW_SP_HDROOM_MODE_TC; 246509f04caSPetr Machata mlxsw_sp_hdroom_prios_reset_buf_idx(&hdroom); 247509f04caSPetr Machata mlxsw_sp_hdroom_bufs_reset_lossiness(&hdroom); 248509f04caSPetr Machata mlxsw_sp_hdroom_bufs_reset_sizes(mlxsw_sp_port, &hdroom); 249509f04caSPetr Machata 250509f04caSPetr Machata err = mlxsw_sp_hdroom_configure(mlxsw_sp_port, &hdroom); 251509f04caSPetr Machata if (err) 252509f04caSPetr Machata goto err_hdroom_configure; 253509f04caSPetr Machata } 254509f04caSPetr Machata 25517c0e6d1SPetr Machata err = ops->check_params(mlxsw_sp_port, params); 2569cf6c9c7SNogah Frankel if (err) 2579cf6c9c7SNogah Frankel goto err_bad_param; 2589cf6c9c7SNogah Frankel 259c4e372e2SPetr Machata err = ops->replace(mlxsw_sp_port, handle, mlxsw_sp_qdisc, params); 2609cf6c9c7SNogah Frankel if (err) 2619cf6c9c7SNogah Frankel goto err_config; 2629cf6c9c7SNogah Frankel 2637bec1a45SPetr Machata /* Check if the Qdisc changed. That includes a situation where an 2647bec1a45SPetr Machata * invisible Qdisc replaces another one, or is being added for the 2657bec1a45SPetr Machata * first time. 2667bec1a45SPetr Machata */ 2677bec1a45SPetr Machata if (mlxsw_sp_qdisc->handle != handle || handle == TC_H_UNSPEC) { 2689cf6c9c7SNogah Frankel mlxsw_sp_qdisc->ops = ops; 2699cf6c9c7SNogah Frankel if (ops->clean_stats) 2709cf6c9c7SNogah Frankel ops->clean_stats(mlxsw_sp_port, mlxsw_sp_qdisc); 2719cf6c9c7SNogah Frankel } 2729cf6c9c7SNogah Frankel 2739cf6c9c7SNogah Frankel mlxsw_sp_qdisc->handle = handle; 2749cf6c9c7SNogah Frankel return 0; 2759cf6c9c7SNogah Frankel 2769cf6c9c7SNogah Frankel err_bad_param: 2779cf6c9c7SNogah Frankel err_config: 278509f04caSPetr Machata mlxsw_sp_hdroom_configure(mlxsw_sp_port, &orig_hdroom); 279509f04caSPetr Machata err_hdroom_configure: 28093d8a4c1SNogah Frankel if (mlxsw_sp_qdisc->handle == handle && ops->unoffload) 28193d8a4c1SNogah Frankel ops->unoffload(mlxsw_sp_port, mlxsw_sp_qdisc, params); 28293d8a4c1SNogah Frankel 2839cf6c9c7SNogah Frankel mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc); 2849cf6c9c7SNogah Frankel return err; 2859cf6c9c7SNogah Frankel } 2869cf6c9c7SNogah Frankel 2879cf6c9c7SNogah Frankel static int 288562ffbc4SNogah Frankel mlxsw_sp_qdisc_get_stats(struct mlxsw_sp_port *mlxsw_sp_port, 289562ffbc4SNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 290562ffbc4SNogah Frankel struct tc_qopt_offload_stats *stats_ptr) 291562ffbc4SNogah Frankel { 292562ffbc4SNogah Frankel if (mlxsw_sp_qdisc && mlxsw_sp_qdisc->ops && 293562ffbc4SNogah Frankel mlxsw_sp_qdisc->ops->get_stats) 294562ffbc4SNogah Frankel return mlxsw_sp_qdisc->ops->get_stats(mlxsw_sp_port, 295562ffbc4SNogah Frankel mlxsw_sp_qdisc, 296562ffbc4SNogah Frankel stats_ptr); 297562ffbc4SNogah Frankel 298562ffbc4SNogah Frankel return -EOPNOTSUPP; 299562ffbc4SNogah Frankel } 300562ffbc4SNogah Frankel 301562ffbc4SNogah Frankel static int 302562ffbc4SNogah Frankel mlxsw_sp_qdisc_get_xstats(struct mlxsw_sp_port *mlxsw_sp_port, 303562ffbc4SNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 304562ffbc4SNogah Frankel void *xstats_ptr) 305562ffbc4SNogah Frankel { 306562ffbc4SNogah Frankel if (mlxsw_sp_qdisc && mlxsw_sp_qdisc->ops && 307562ffbc4SNogah Frankel mlxsw_sp_qdisc->ops->get_xstats) 308562ffbc4SNogah Frankel return mlxsw_sp_qdisc->ops->get_xstats(mlxsw_sp_port, 309562ffbc4SNogah Frankel mlxsw_sp_qdisc, 310562ffbc4SNogah Frankel xstats_ptr); 311562ffbc4SNogah Frankel 312562ffbc4SNogah Frankel return -EOPNOTSUPP; 313562ffbc4SNogah Frankel } 314562ffbc4SNogah Frankel 31585005b82SPetr Machata static u64 31685005b82SPetr Machata mlxsw_sp_xstats_backlog(struct mlxsw_sp_port_xstats *xstats, int tclass_num) 31785005b82SPetr Machata { 31885005b82SPetr Machata return xstats->backlog[tclass_num] + 31985005b82SPetr Machata xstats->backlog[tclass_num + 8]; 32085005b82SPetr Machata } 32185005b82SPetr Machata 32285005b82SPetr Machata static u64 32385005b82SPetr Machata mlxsw_sp_xstats_tail_drop(struct mlxsw_sp_port_xstats *xstats, int tclass_num) 32485005b82SPetr Machata { 32585005b82SPetr Machata return xstats->tail_drop[tclass_num] + 32685005b82SPetr Machata xstats->tail_drop[tclass_num + 8]; 32785005b82SPetr Machata } 32885005b82SPetr Machata 32904cc0bf5SNogah Frankel static void 33004cc0bf5SNogah Frankel mlxsw_sp_qdisc_bstats_per_priority_get(struct mlxsw_sp_port_xstats *xstats, 33104cc0bf5SNogah Frankel u8 prio_bitmap, u64 *tx_packets, 33204cc0bf5SNogah Frankel u64 *tx_bytes) 33304cc0bf5SNogah Frankel { 33404cc0bf5SNogah Frankel int i; 33504cc0bf5SNogah Frankel 33604cc0bf5SNogah Frankel *tx_packets = 0; 33704cc0bf5SNogah Frankel *tx_bytes = 0; 33804cc0bf5SNogah Frankel for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { 33904cc0bf5SNogah Frankel if (prio_bitmap & BIT(i)) { 34004cc0bf5SNogah Frankel *tx_packets += xstats->tx_packets[i]; 34104cc0bf5SNogah Frankel *tx_bytes += xstats->tx_bytes[i]; 34204cc0bf5SNogah Frankel } 34304cc0bf5SNogah Frankel } 34404cc0bf5SNogah Frankel } 34504cc0bf5SNogah Frankel 346cf9af379SPetr Machata static void 347cf9af379SPetr Machata mlxsw_sp_qdisc_collect_tc_stats(struct mlxsw_sp_port *mlxsw_sp_port, 348cf9af379SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 349cf9af379SPetr Machata u64 *p_tx_bytes, u64 *p_tx_packets, 350cf9af379SPetr Machata u64 *p_drops, u64 *p_backlog) 351cf9af379SPetr Machata { 352017a131cSPetr Machata int tclass_num = mlxsw_sp_qdisc->tclass_num; 353cf9af379SPetr Machata struct mlxsw_sp_port_xstats *xstats; 354cf9af379SPetr Machata u64 tx_bytes, tx_packets; 355cf9af379SPetr Machata 356cf9af379SPetr Machata xstats = &mlxsw_sp_port->periodic_hw_stats.xstats; 357cf9af379SPetr Machata mlxsw_sp_qdisc_bstats_per_priority_get(xstats, 358cf9af379SPetr Machata mlxsw_sp_qdisc->prio_bitmap, 359cf9af379SPetr Machata &tx_packets, &tx_bytes); 360cf9af379SPetr Machata 361cf9af379SPetr Machata *p_tx_packets += tx_packets; 362cf9af379SPetr Machata *p_tx_bytes += tx_bytes; 363cf9af379SPetr Machata *p_drops += xstats->wred_drop[tclass_num] + 364cf9af379SPetr Machata mlxsw_sp_xstats_tail_drop(xstats, tclass_num); 365cf9af379SPetr Machata *p_backlog += mlxsw_sp_xstats_backlog(xstats, tclass_num); 366cf9af379SPetr Machata } 367cf9af379SPetr Machata 368cf9af379SPetr Machata static void 369cf9af379SPetr Machata mlxsw_sp_qdisc_update_stats(struct mlxsw_sp *mlxsw_sp, 370cf9af379SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 371cf9af379SPetr Machata u64 tx_bytes, u64 tx_packets, 372cf9af379SPetr Machata u64 drops, u64 backlog, 373cf9af379SPetr Machata struct tc_qopt_offload_stats *stats_ptr) 374cf9af379SPetr Machata { 375cf9af379SPetr Machata struct mlxsw_sp_qdisc_stats *stats_base = &mlxsw_sp_qdisc->stats_base; 376cf9af379SPetr Machata 377cf9af379SPetr Machata tx_bytes -= stats_base->tx_bytes; 378cf9af379SPetr Machata tx_packets -= stats_base->tx_packets; 379cf9af379SPetr Machata drops -= stats_base->drops; 380cf9af379SPetr Machata backlog -= stats_base->backlog; 381cf9af379SPetr Machata 382cf9af379SPetr Machata _bstats_update(stats_ptr->bstats, tx_bytes, tx_packets); 383cf9af379SPetr Machata stats_ptr->qstats->drops += drops; 384cf9af379SPetr Machata stats_ptr->qstats->backlog += mlxsw_sp_cells_bytes(mlxsw_sp, backlog); 385cf9af379SPetr Machata 386cf9af379SPetr Machata stats_base->backlog += backlog; 387cf9af379SPetr Machata stats_base->drops += drops; 388cf9af379SPetr Machata stats_base->tx_bytes += tx_bytes; 389cf9af379SPetr Machata stats_base->tx_packets += tx_packets; 390cf9af379SPetr Machata } 391cf9af379SPetr Machata 3923d0d5921SPetr Machata static void 3933d0d5921SPetr Machata mlxsw_sp_qdisc_get_tc_stats(struct mlxsw_sp_port *mlxsw_sp_port, 3943d0d5921SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 3953d0d5921SPetr Machata struct tc_qopt_offload_stats *stats_ptr) 3963d0d5921SPetr Machata { 3973d0d5921SPetr Machata u64 tx_packets = 0; 3983d0d5921SPetr Machata u64 tx_bytes = 0; 3993d0d5921SPetr Machata u64 backlog = 0; 4003d0d5921SPetr Machata u64 drops = 0; 4013d0d5921SPetr Machata 4023d0d5921SPetr Machata mlxsw_sp_qdisc_collect_tc_stats(mlxsw_sp_port, mlxsw_sp_qdisc, 4033d0d5921SPetr Machata &tx_bytes, &tx_packets, 4043d0d5921SPetr Machata &drops, &backlog); 4053d0d5921SPetr Machata mlxsw_sp_qdisc_update_stats(mlxsw_sp_port->mlxsw_sp, mlxsw_sp_qdisc, 4063d0d5921SPetr Machata tx_bytes, tx_packets, drops, backlog, 4073d0d5921SPetr Machata stats_ptr); 4083d0d5921SPetr Machata } 4093d0d5921SPetr Machata 410562ffbc4SNogah Frankel static int 41196f17e07SNogah Frankel mlxsw_sp_tclass_congestion_enable(struct mlxsw_sp_port *mlxsw_sp_port, 41296f17e07SNogah Frankel int tclass_num, u32 min, u32 max, 4138040c96bSPetr Machata u32 probability, bool is_wred, bool is_ecn) 41496f17e07SNogah Frankel { 415db84924cSJiri Pirko char cwtpm_cmd[MLXSW_REG_CWTPM_LEN]; 416db84924cSJiri Pirko char cwtp_cmd[MLXSW_REG_CWTP_LEN]; 41796f17e07SNogah Frankel struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 41896f17e07SNogah Frankel int err; 41996f17e07SNogah Frankel 42096f17e07SNogah Frankel mlxsw_reg_cwtp_pack(cwtp_cmd, mlxsw_sp_port->local_port, tclass_num); 42196f17e07SNogah Frankel mlxsw_reg_cwtp_profile_pack(cwtp_cmd, MLXSW_REG_CWTP_DEFAULT_PROFILE, 42296f17e07SNogah Frankel roundup(min, MLXSW_REG_CWTP_MIN_VALUE), 42396f17e07SNogah Frankel roundup(max, MLXSW_REG_CWTP_MIN_VALUE), 42496f17e07SNogah Frankel probability); 42596f17e07SNogah Frankel 42696f17e07SNogah Frankel err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(cwtp), cwtp_cmd); 42796f17e07SNogah Frankel if (err) 42896f17e07SNogah Frankel return err; 42996f17e07SNogah Frankel 430db84924cSJiri Pirko mlxsw_reg_cwtpm_pack(cwtpm_cmd, mlxsw_sp_port->local_port, tclass_num, 4318040c96bSPetr Machata MLXSW_REG_CWTP_DEFAULT_PROFILE, is_wred, is_ecn); 43296f17e07SNogah Frankel 433db84924cSJiri Pirko return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(cwtpm), cwtpm_cmd); 43496f17e07SNogah Frankel } 43596f17e07SNogah Frankel 43696f17e07SNogah Frankel static int 43796f17e07SNogah Frankel mlxsw_sp_tclass_congestion_disable(struct mlxsw_sp_port *mlxsw_sp_port, 43896f17e07SNogah Frankel int tclass_num) 43996f17e07SNogah Frankel { 44096f17e07SNogah Frankel struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 44196f17e07SNogah Frankel char cwtpm_cmd[MLXSW_REG_CWTPM_LEN]; 44296f17e07SNogah Frankel 44396f17e07SNogah Frankel mlxsw_reg_cwtpm_pack(cwtpm_cmd, mlxsw_sp_port->local_port, tclass_num, 44496f17e07SNogah Frankel MLXSW_REG_CWTPM_RESET_PROFILE, false, false); 44596f17e07SNogah Frankel return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(cwtpm), cwtpm_cmd); 44696f17e07SNogah Frankel } 44796f17e07SNogah Frankel 448861fb829SNogah Frankel static void 449c2ed6db7SNogah Frankel mlxsw_sp_setup_tc_qdisc_red_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port, 450d56c8955SNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 451861fb829SNogah Frankel { 452017a131cSPetr Machata int tclass_num = mlxsw_sp_qdisc->tclass_num; 4534d1a4b84SNogah Frankel struct mlxsw_sp_qdisc_stats *stats_base; 454861fb829SNogah Frankel struct mlxsw_sp_port_xstats *xstats; 4554d1a4b84SNogah Frankel struct red_stats *red_base; 456861fb829SNogah Frankel 457861fb829SNogah Frankel xstats = &mlxsw_sp_port->periodic_hw_stats.xstats; 4584d1a4b84SNogah Frankel stats_base = &mlxsw_sp_qdisc->stats_base; 459c2ed6db7SNogah Frankel red_base = &mlxsw_sp_qdisc->xstats_base.red; 4603670756fSNogah Frankel 46104cc0bf5SNogah Frankel mlxsw_sp_qdisc_bstats_per_priority_get(xstats, 46204cc0bf5SNogah Frankel mlxsw_sp_qdisc->prio_bitmap, 46304cc0bf5SNogah Frankel &stats_base->tx_packets, 46404cc0bf5SNogah Frankel &stats_base->tx_bytes); 4654d1a4b84SNogah Frankel red_base->prob_drop = xstats->wred_drop[tclass_num]; 46685005b82SPetr Machata red_base->pdrop = mlxsw_sp_xstats_tail_drop(xstats, tclass_num); 4673670756fSNogah Frankel 468c2ed6db7SNogah Frankel stats_base->overlimits = red_base->prob_drop + red_base->prob_mark; 4694d1a4b84SNogah Frankel stats_base->drops = red_base->prob_drop + red_base->pdrop; 470416ef9b1SJakub Kicinski 471416ef9b1SJakub Kicinski stats_base->backlog = 0; 472861fb829SNogah Frankel } 473861fb829SNogah Frankel 47496f17e07SNogah Frankel static int 475cba7158fSNogah Frankel mlxsw_sp_qdisc_red_destroy(struct mlxsw_sp_port *mlxsw_sp_port, 476d56c8955SNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 47796f17e07SNogah Frankel { 4789a37a59fSNogah Frankel return mlxsw_sp_tclass_congestion_disable(mlxsw_sp_port, 479d56c8955SNogah Frankel mlxsw_sp_qdisc->tclass_num); 48096f17e07SNogah Frankel } 48196f17e07SNogah Frankel 48296f17e07SNogah Frankel static int 4839cf6c9c7SNogah Frankel mlxsw_sp_qdisc_red_check_params(struct mlxsw_sp_port *mlxsw_sp_port, 4849cf6c9c7SNogah Frankel void *params) 48596f17e07SNogah Frankel { 48696f17e07SNogah Frankel struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 4879cf6c9c7SNogah Frankel struct tc_red_qopt_offload_params *p = params; 48896f17e07SNogah Frankel 48996f17e07SNogah Frankel if (p->min > p->max) { 49096f17e07SNogah Frankel dev_err(mlxsw_sp->bus_info->dev, 49196f17e07SNogah Frankel "spectrum: RED: min %u is bigger then max %u\n", p->min, 49296f17e07SNogah Frankel p->max); 4939cf6c9c7SNogah Frankel return -EINVAL; 49496f17e07SNogah Frankel } 495914c4fc1SPetr Machata if (p->max > MLXSW_CORE_RES_GET(mlxsw_sp->core, 496914c4fc1SPetr Machata GUARANTEED_SHARED_BUFFER)) { 49796f17e07SNogah Frankel dev_err(mlxsw_sp->bus_info->dev, 49896f17e07SNogah Frankel "spectrum: RED: max value %u is too big\n", p->max); 4999cf6c9c7SNogah Frankel return -EINVAL; 50096f17e07SNogah Frankel } 50196f17e07SNogah Frankel if (p->min == 0 || p->max == 0) { 50296f17e07SNogah Frankel dev_err(mlxsw_sp->bus_info->dev, 50396f17e07SNogah Frankel "spectrum: RED: 0 value is illegal for min and max\n"); 5049cf6c9c7SNogah Frankel return -EINVAL; 50596f17e07SNogah Frankel } 5069cf6c9c7SNogah Frankel return 0; 5079cf6c9c7SNogah Frankel } 5089cf6c9c7SNogah Frankel 5099cf6c9c7SNogah Frankel static int 510c4e372e2SPetr Machata mlxsw_sp_qdisc_red_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, 5119cf6c9c7SNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 5129cf6c9c7SNogah Frankel void *params) 5139cf6c9c7SNogah Frankel { 5149cf6c9c7SNogah Frankel struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 5159cf6c9c7SNogah Frankel struct tc_red_qopt_offload_params *p = params; 516017a131cSPetr Machata int tclass_num = mlxsw_sp_qdisc->tclass_num; 5179cf6c9c7SNogah Frankel u32 min, max; 5189cf6c9c7SNogah Frankel u64 prob; 51996f17e07SNogah Frankel 52096f17e07SNogah Frankel /* calculate probability in percentage */ 52196f17e07SNogah Frankel prob = p->probability; 52296f17e07SNogah Frankel prob *= 100; 52396f17e07SNogah Frankel prob = DIV_ROUND_UP(prob, 1 << 16); 52496f17e07SNogah Frankel prob = DIV_ROUND_UP(prob, 1 << 16); 52596f17e07SNogah Frankel min = mlxsw_sp_bytes_cells(mlxsw_sp, p->min); 52696f17e07SNogah Frankel max = mlxsw_sp_bytes_cells(mlxsw_sp, p->max); 5278040c96bSPetr Machata return mlxsw_sp_tclass_congestion_enable(mlxsw_sp_port, tclass_num, 5288040c96bSPetr Machata min, max, prob, 5298040c96bSPetr Machata !p->is_nodrop, p->is_ecn); 53096f17e07SNogah Frankel } 53196f17e07SNogah Frankel 532416ef9b1SJakub Kicinski static void 533be1d5a8aSPetr Machata mlxsw_sp_qdisc_leaf_unoffload(struct mlxsw_sp_port *mlxsw_sp_port, 534be1d5a8aSPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 535be1d5a8aSPetr Machata struct gnet_stats_queue *qstats) 536be1d5a8aSPetr Machata { 537be1d5a8aSPetr Machata u64 backlog; 538be1d5a8aSPetr Machata 539be1d5a8aSPetr Machata backlog = mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp, 540be1d5a8aSPetr Machata mlxsw_sp_qdisc->stats_base.backlog); 541be1d5a8aSPetr Machata qstats->backlog -= backlog; 542be1d5a8aSPetr Machata mlxsw_sp_qdisc->stats_base.backlog = 0; 543be1d5a8aSPetr Machata } 544be1d5a8aSPetr Machata 545be1d5a8aSPetr Machata static void 546416ef9b1SJakub Kicinski mlxsw_sp_qdisc_red_unoffload(struct mlxsw_sp_port *mlxsw_sp_port, 547416ef9b1SJakub Kicinski struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 548416ef9b1SJakub Kicinski void *params) 549416ef9b1SJakub Kicinski { 550416ef9b1SJakub Kicinski struct tc_red_qopt_offload_params *p = params; 551416ef9b1SJakub Kicinski 552be1d5a8aSPetr Machata mlxsw_sp_qdisc_leaf_unoffload(mlxsw_sp_port, mlxsw_sp_qdisc, p->qstats); 553416ef9b1SJakub Kicinski } 554416ef9b1SJakub Kicinski 555861fb829SNogah Frankel static int 556cba7158fSNogah Frankel mlxsw_sp_qdisc_get_red_xstats(struct mlxsw_sp_port *mlxsw_sp_port, 557861fb829SNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 558562ffbc4SNogah Frankel void *xstats_ptr) 559861fb829SNogah Frankel { 5604d1a4b84SNogah Frankel struct red_stats *xstats_base = &mlxsw_sp_qdisc->xstats_base.red; 561017a131cSPetr Machata int tclass_num = mlxsw_sp_qdisc->tclass_num; 562861fb829SNogah Frankel struct mlxsw_sp_port_xstats *xstats; 563562ffbc4SNogah Frankel struct red_stats *res = xstats_ptr; 5648a29581eSPetr Machata int early_drops, pdrops; 565861fb829SNogah Frankel 566861fb829SNogah Frankel xstats = &mlxsw_sp_port->periodic_hw_stats.xstats; 567861fb829SNogah Frankel 568f8253df5SNogah Frankel early_drops = xstats->wred_drop[tclass_num] - xstats_base->prob_drop; 56985005b82SPetr Machata pdrops = mlxsw_sp_xstats_tail_drop(xstats, tclass_num) - 57085005b82SPetr Machata xstats_base->pdrop; 571f8253df5SNogah Frankel 572f8253df5SNogah Frankel res->pdrop += pdrops; 573f8253df5SNogah Frankel res->prob_drop += early_drops; 574f8253df5SNogah Frankel 575f8253df5SNogah Frankel xstats_base->pdrop += pdrops; 576f8253df5SNogah Frankel xstats_base->prob_drop += early_drops; 577861fb829SNogah Frankel return 0; 578861fb829SNogah Frankel } 579861fb829SNogah Frankel 5803670756fSNogah Frankel static int 581cba7158fSNogah Frankel mlxsw_sp_qdisc_get_red_stats(struct mlxsw_sp_port *mlxsw_sp_port, 5823670756fSNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 583562ffbc4SNogah Frankel struct tc_qopt_offload_stats *stats_ptr) 5843670756fSNogah Frankel { 585017a131cSPetr Machata int tclass_num = mlxsw_sp_qdisc->tclass_num; 5864d1a4b84SNogah Frankel struct mlxsw_sp_qdisc_stats *stats_base; 5873670756fSNogah Frankel struct mlxsw_sp_port_xstats *xstats; 588cf9af379SPetr Machata u64 overlimits; 5893670756fSNogah Frankel 5903670756fSNogah Frankel xstats = &mlxsw_sp_port->periodic_hw_stats.xstats; 5914d1a4b84SNogah Frankel stats_base = &mlxsw_sp_qdisc->stats_base; 5923670756fSNogah Frankel 5933d0d5921SPetr Machata mlxsw_sp_qdisc_get_tc_stats(mlxsw_sp_port, mlxsw_sp_qdisc, stats_ptr); 5948a29581eSPetr Machata overlimits = xstats->wred_drop[tclass_num] - stats_base->overlimits; 5953670756fSNogah Frankel 596562ffbc4SNogah Frankel stats_ptr->qstats->overlimits += overlimits; 5974d1a4b84SNogah Frankel stats_base->overlimits += overlimits; 598cf9af379SPetr Machata 5993670756fSNogah Frankel return 0; 6003670756fSNogah Frankel } 6013670756fSNogah Frankel 602*51d52ed9SPetr Machata static struct mlxsw_sp_qdisc * 603*51d52ed9SPetr Machata mlxsw_sp_qdisc_leaf_find_class(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 604*51d52ed9SPetr Machata u32 parent) 605*51d52ed9SPetr Machata { 606*51d52ed9SPetr Machata return NULL; 607*51d52ed9SPetr Machata } 608*51d52ed9SPetr Machata 60996f17e07SNogah Frankel #define MLXSW_SP_PORT_DEFAULT_TCLASS 0 61096f17e07SNogah Frankel 611562ffbc4SNogah Frankel static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_red = { 6129cf6c9c7SNogah Frankel .type = MLXSW_SP_QDISC_RED, 6139cf6c9c7SNogah Frankel .check_params = mlxsw_sp_qdisc_red_check_params, 6149cf6c9c7SNogah Frankel .replace = mlxsw_sp_qdisc_red_replace, 615416ef9b1SJakub Kicinski .unoffload = mlxsw_sp_qdisc_red_unoffload, 6169a37a59fSNogah Frankel .destroy = mlxsw_sp_qdisc_red_destroy, 617562ffbc4SNogah Frankel .get_stats = mlxsw_sp_qdisc_get_red_stats, 618562ffbc4SNogah Frankel .get_xstats = mlxsw_sp_qdisc_get_red_xstats, 6199cf6c9c7SNogah Frankel .clean_stats = mlxsw_sp_setup_tc_qdisc_red_clean_stats, 620*51d52ed9SPetr Machata .find_class = mlxsw_sp_qdisc_leaf_find_class, 621562ffbc4SNogah Frankel }; 622562ffbc4SNogah Frankel 62396f17e07SNogah Frankel int mlxsw_sp_setup_tc_red(struct mlxsw_sp_port *mlxsw_sp_port, 62496f17e07SNogah Frankel struct tc_red_qopt_offload *p) 62596f17e07SNogah Frankel { 62696f17e07SNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc; 62796f17e07SNogah Frankel 628eed4baebSNogah Frankel mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent, false); 629eed4baebSNogah Frankel if (!mlxsw_sp_qdisc) 63096f17e07SNogah Frankel return -EOPNOTSUPP; 63196f17e07SNogah Frankel 632cba7158fSNogah Frankel if (p->command == TC_RED_REPLACE) 6339cf6c9c7SNogah Frankel return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle, 634562ffbc4SNogah Frankel mlxsw_sp_qdisc, 635562ffbc4SNogah Frankel &mlxsw_sp_qdisc_ops_red, 636562ffbc4SNogah Frankel &p->set); 637cba7158fSNogah Frankel 638290fe2c5SPetr Machata if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle)) 639cba7158fSNogah Frankel return -EOPNOTSUPP; 640cba7158fSNogah Frankel 641cba7158fSNogah Frankel switch (p->command) { 64296f17e07SNogah Frankel case TC_RED_DESTROY: 6439a37a59fSNogah Frankel return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc); 644861fb829SNogah Frankel case TC_RED_XSTATS: 645562ffbc4SNogah Frankel return mlxsw_sp_qdisc_get_xstats(mlxsw_sp_port, mlxsw_sp_qdisc, 646861fb829SNogah Frankel p->xstats); 6473670756fSNogah Frankel case TC_RED_STATS: 648562ffbc4SNogah Frankel return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc, 6493670756fSNogah Frankel &p->stats); 65096f17e07SNogah Frankel default: 65196f17e07SNogah Frankel return -EOPNOTSUPP; 65296f17e07SNogah Frankel } 65396f17e07SNogah Frankel } 654371b437aSNogah Frankel 655a44f58c4SPetr Machata static void 656a44f58c4SPetr Machata mlxsw_sp_setup_tc_qdisc_leaf_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port, 657a44f58c4SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 658a44f58c4SPetr Machata { 659a44f58c4SPetr Machata u64 backlog_cells = 0; 660a44f58c4SPetr Machata u64 tx_packets = 0; 661a44f58c4SPetr Machata u64 tx_bytes = 0; 662a44f58c4SPetr Machata u64 drops = 0; 663a44f58c4SPetr Machata 664a44f58c4SPetr Machata mlxsw_sp_qdisc_collect_tc_stats(mlxsw_sp_port, mlxsw_sp_qdisc, 665a44f58c4SPetr Machata &tx_bytes, &tx_packets, 666a44f58c4SPetr Machata &drops, &backlog_cells); 667a44f58c4SPetr Machata 668a44f58c4SPetr Machata mlxsw_sp_qdisc->stats_base.tx_packets = tx_packets; 669a44f58c4SPetr Machata mlxsw_sp_qdisc->stats_base.tx_bytes = tx_bytes; 670a44f58c4SPetr Machata mlxsw_sp_qdisc->stats_base.drops = drops; 671a44f58c4SPetr Machata mlxsw_sp_qdisc->stats_base.backlog = 0; 672a44f58c4SPetr Machata } 673a44f58c4SPetr Machata 674a44f58c4SPetr Machata static int 675a44f58c4SPetr Machata mlxsw_sp_qdisc_tbf_destroy(struct mlxsw_sp_port *mlxsw_sp_port, 676a44f58c4SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 677a44f58c4SPetr Machata { 678a44f58c4SPetr Machata return mlxsw_sp_port_ets_maxrate_set(mlxsw_sp_port, 679a44f58c4SPetr Machata MLXSW_REG_QEEC_HR_SUBGROUP, 680a44f58c4SPetr Machata mlxsw_sp_qdisc->tclass_num, 0, 681a44f58c4SPetr Machata MLXSW_REG_QEEC_MAS_DIS, 0); 682a44f58c4SPetr Machata } 683a44f58c4SPetr Machata 684a44f58c4SPetr Machata static int 685a44f58c4SPetr Machata mlxsw_sp_qdisc_tbf_bs(struct mlxsw_sp_port *mlxsw_sp_port, 686a44f58c4SPetr Machata u32 max_size, u8 *p_burst_size) 687a44f58c4SPetr Machata { 688a44f58c4SPetr Machata /* TBF burst size is configured in bytes. The ASIC burst size value is 689a44f58c4SPetr Machata * ((2 ^ bs) * 512 bits. Convert the TBF bytes to 512-bit units. 690a44f58c4SPetr Machata */ 691a44f58c4SPetr Machata u32 bs512 = max_size / 64; 692a44f58c4SPetr Machata u8 bs = fls(bs512); 693a44f58c4SPetr Machata 694a44f58c4SPetr Machata if (!bs) 695a44f58c4SPetr Machata return -EINVAL; 696a44f58c4SPetr Machata --bs; 697a44f58c4SPetr Machata 698a44f58c4SPetr Machata /* Demand a power of two. */ 699a44f58c4SPetr Machata if ((1 << bs) != bs512) 700a44f58c4SPetr Machata return -EINVAL; 701a44f58c4SPetr Machata 702a44f58c4SPetr Machata if (bs < mlxsw_sp_port->mlxsw_sp->lowest_shaper_bs || 703a44f58c4SPetr Machata bs > MLXSW_REG_QEEC_HIGHEST_SHAPER_BS) 704a44f58c4SPetr Machata return -EINVAL; 705a44f58c4SPetr Machata 706a44f58c4SPetr Machata *p_burst_size = bs; 707a44f58c4SPetr Machata return 0; 708a44f58c4SPetr Machata } 709a44f58c4SPetr Machata 710a44f58c4SPetr Machata static u32 711a44f58c4SPetr Machata mlxsw_sp_qdisc_tbf_max_size(u8 bs) 712a44f58c4SPetr Machata { 713a44f58c4SPetr Machata return (1U << bs) * 64; 714a44f58c4SPetr Machata } 715a44f58c4SPetr Machata 716a44f58c4SPetr Machata static u64 717a44f58c4SPetr Machata mlxsw_sp_qdisc_tbf_rate_kbps(struct tc_tbf_qopt_offload_replace_params *p) 718a44f58c4SPetr Machata { 719a44f58c4SPetr Machata /* TBF interface is in bytes/s, whereas Spectrum ASIC is configured in 720a44f58c4SPetr Machata * Kbits/s. 721a44f58c4SPetr Machata */ 72291a7d4bfSNathan Chancellor return div_u64(p->rate.rate_bytes_ps, 1000) * 8; 723a44f58c4SPetr Machata } 724a44f58c4SPetr Machata 725a44f58c4SPetr Machata static int 726a44f58c4SPetr Machata mlxsw_sp_qdisc_tbf_check_params(struct mlxsw_sp_port *mlxsw_sp_port, 727a44f58c4SPetr Machata void *params) 728a44f58c4SPetr Machata { 729a44f58c4SPetr Machata struct tc_tbf_qopt_offload_replace_params *p = params; 730a44f58c4SPetr Machata struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 731a44f58c4SPetr Machata u64 rate_kbps = mlxsw_sp_qdisc_tbf_rate_kbps(p); 732a44f58c4SPetr Machata u8 burst_size; 733a44f58c4SPetr Machata int err; 734a44f58c4SPetr Machata 735a44f58c4SPetr Machata if (rate_kbps >= MLXSW_REG_QEEC_MAS_DIS) { 736a44f58c4SPetr Machata dev_err(mlxsw_sp_port->mlxsw_sp->bus_info->dev, 737a44f58c4SPetr Machata "spectrum: TBF: rate of %lluKbps must be below %u\n", 738a44f58c4SPetr Machata rate_kbps, MLXSW_REG_QEEC_MAS_DIS); 739a44f58c4SPetr Machata return -EINVAL; 740a44f58c4SPetr Machata } 741a44f58c4SPetr Machata 742a44f58c4SPetr Machata err = mlxsw_sp_qdisc_tbf_bs(mlxsw_sp_port, p->max_size, &burst_size); 743a44f58c4SPetr Machata if (err) { 744a44f58c4SPetr Machata u8 highest_shaper_bs = MLXSW_REG_QEEC_HIGHEST_SHAPER_BS; 745a44f58c4SPetr Machata 746a44f58c4SPetr Machata dev_err(mlxsw_sp->bus_info->dev, 747a44f58c4SPetr Machata "spectrum: TBF: invalid burst size of %u, must be a power of two between %u and %u", 748a44f58c4SPetr Machata p->max_size, 749a44f58c4SPetr Machata mlxsw_sp_qdisc_tbf_max_size(mlxsw_sp->lowest_shaper_bs), 750a44f58c4SPetr Machata mlxsw_sp_qdisc_tbf_max_size(highest_shaper_bs)); 751a44f58c4SPetr Machata return -EINVAL; 752a44f58c4SPetr Machata } 753a44f58c4SPetr Machata 754a44f58c4SPetr Machata return 0; 755a44f58c4SPetr Machata } 756a44f58c4SPetr Machata 757a44f58c4SPetr Machata static int 758c4e372e2SPetr Machata mlxsw_sp_qdisc_tbf_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, 759a44f58c4SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 760a44f58c4SPetr Machata void *params) 761a44f58c4SPetr Machata { 762a44f58c4SPetr Machata struct tc_tbf_qopt_offload_replace_params *p = params; 763a44f58c4SPetr Machata u64 rate_kbps = mlxsw_sp_qdisc_tbf_rate_kbps(p); 764a44f58c4SPetr Machata u8 burst_size; 765a44f58c4SPetr Machata int err; 766a44f58c4SPetr Machata 767a44f58c4SPetr Machata err = mlxsw_sp_qdisc_tbf_bs(mlxsw_sp_port, p->max_size, &burst_size); 768a44f58c4SPetr Machata if (WARN_ON_ONCE(err)) 769a44f58c4SPetr Machata /* check_params above was supposed to reject this value. */ 770a44f58c4SPetr Machata return -EINVAL; 771a44f58c4SPetr Machata 772a44f58c4SPetr Machata /* Configure subgroup shaper, so that both UC and MC traffic is subject 773a44f58c4SPetr Machata * to shaping. That is unlike RED, however UC queue lengths are going to 774a44f58c4SPetr Machata * be different than MC ones due to different pool and quota 775a44f58c4SPetr Machata * configurations, so the configuration is not applicable. For shaper on 776a44f58c4SPetr Machata * the other hand, subjecting the overall stream to the configured 777a44f58c4SPetr Machata * shaper makes sense. Also note that that is what we do for 778a44f58c4SPetr Machata * ieee_setmaxrate(). 779a44f58c4SPetr Machata */ 780a44f58c4SPetr Machata return mlxsw_sp_port_ets_maxrate_set(mlxsw_sp_port, 781a44f58c4SPetr Machata MLXSW_REG_QEEC_HR_SUBGROUP, 782a44f58c4SPetr Machata mlxsw_sp_qdisc->tclass_num, 0, 783a44f58c4SPetr Machata rate_kbps, burst_size); 784a44f58c4SPetr Machata } 785a44f58c4SPetr Machata 786a44f58c4SPetr Machata static void 787a44f58c4SPetr Machata mlxsw_sp_qdisc_tbf_unoffload(struct mlxsw_sp_port *mlxsw_sp_port, 788a44f58c4SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 789a44f58c4SPetr Machata void *params) 790a44f58c4SPetr Machata { 791a44f58c4SPetr Machata struct tc_tbf_qopt_offload_replace_params *p = params; 792a44f58c4SPetr Machata 793a44f58c4SPetr Machata mlxsw_sp_qdisc_leaf_unoffload(mlxsw_sp_port, mlxsw_sp_qdisc, p->qstats); 794a44f58c4SPetr Machata } 795a44f58c4SPetr Machata 796a44f58c4SPetr Machata static int 797a44f58c4SPetr Machata mlxsw_sp_qdisc_get_tbf_stats(struct mlxsw_sp_port *mlxsw_sp_port, 798a44f58c4SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 799a44f58c4SPetr Machata struct tc_qopt_offload_stats *stats_ptr) 800a44f58c4SPetr Machata { 801a44f58c4SPetr Machata mlxsw_sp_qdisc_get_tc_stats(mlxsw_sp_port, mlxsw_sp_qdisc, 802a44f58c4SPetr Machata stats_ptr); 803a44f58c4SPetr Machata return 0; 804a44f58c4SPetr Machata } 805a44f58c4SPetr Machata 806a44f58c4SPetr Machata static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_tbf = { 807a44f58c4SPetr Machata .type = MLXSW_SP_QDISC_TBF, 808a44f58c4SPetr Machata .check_params = mlxsw_sp_qdisc_tbf_check_params, 809a44f58c4SPetr Machata .replace = mlxsw_sp_qdisc_tbf_replace, 810a44f58c4SPetr Machata .unoffload = mlxsw_sp_qdisc_tbf_unoffload, 811a44f58c4SPetr Machata .destroy = mlxsw_sp_qdisc_tbf_destroy, 812a44f58c4SPetr Machata .get_stats = mlxsw_sp_qdisc_get_tbf_stats, 813a44f58c4SPetr Machata .clean_stats = mlxsw_sp_setup_tc_qdisc_leaf_clean_stats, 814*51d52ed9SPetr Machata .find_class = mlxsw_sp_qdisc_leaf_find_class, 815a44f58c4SPetr Machata }; 816a44f58c4SPetr Machata 817a44f58c4SPetr Machata int mlxsw_sp_setup_tc_tbf(struct mlxsw_sp_port *mlxsw_sp_port, 818a44f58c4SPetr Machata struct tc_tbf_qopt_offload *p) 819a44f58c4SPetr Machata { 820a44f58c4SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc; 821a44f58c4SPetr Machata 822a44f58c4SPetr Machata mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent, false); 823a44f58c4SPetr Machata if (!mlxsw_sp_qdisc) 824a44f58c4SPetr Machata return -EOPNOTSUPP; 825a44f58c4SPetr Machata 826a44f58c4SPetr Machata if (p->command == TC_TBF_REPLACE) 827a44f58c4SPetr Machata return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle, 828a44f58c4SPetr Machata mlxsw_sp_qdisc, 829a44f58c4SPetr Machata &mlxsw_sp_qdisc_ops_tbf, 830a44f58c4SPetr Machata &p->replace_params); 831a44f58c4SPetr Machata 832290fe2c5SPetr Machata if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle)) 833a44f58c4SPetr Machata return -EOPNOTSUPP; 834a44f58c4SPetr Machata 835a44f58c4SPetr Machata switch (p->command) { 836a44f58c4SPetr Machata case TC_TBF_DESTROY: 837a44f58c4SPetr Machata return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc); 838a44f58c4SPetr Machata case TC_TBF_STATS: 839a44f58c4SPetr Machata return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc, 840a44f58c4SPetr Machata &p->stats); 841a44f58c4SPetr Machata default: 842a44f58c4SPetr Machata return -EOPNOTSUPP; 843a44f58c4SPetr Machata } 844a44f58c4SPetr Machata } 845a44f58c4SPetr Machata 84646a3615bSNogah Frankel static int 8477bec1a45SPetr Machata mlxsw_sp_qdisc_fifo_check_params(struct mlxsw_sp_port *mlxsw_sp_port, 8487bec1a45SPetr Machata void *params) 8497bec1a45SPetr Machata { 8507bec1a45SPetr Machata return 0; 8517bec1a45SPetr Machata } 8527bec1a45SPetr Machata 8537bec1a45SPetr Machata static int 8547bec1a45SPetr Machata mlxsw_sp_qdisc_fifo_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, 8557bec1a45SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 8567bec1a45SPetr Machata void *params) 8577bec1a45SPetr Machata { 8587bec1a45SPetr Machata return 0; 8597bec1a45SPetr Machata } 8607bec1a45SPetr Machata 8617bec1a45SPetr Machata static int 8627bec1a45SPetr Machata mlxsw_sp_qdisc_get_fifo_stats(struct mlxsw_sp_port *mlxsw_sp_port, 8637bec1a45SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 8647bec1a45SPetr Machata struct tc_qopt_offload_stats *stats_ptr) 8657bec1a45SPetr Machata { 8667bec1a45SPetr Machata mlxsw_sp_qdisc_get_tc_stats(mlxsw_sp_port, mlxsw_sp_qdisc, 8677bec1a45SPetr Machata stats_ptr); 8687bec1a45SPetr Machata return 0; 8697bec1a45SPetr Machata } 8707bec1a45SPetr Machata 8717bec1a45SPetr Machata static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_fifo = { 8727bec1a45SPetr Machata .type = MLXSW_SP_QDISC_FIFO, 8737bec1a45SPetr Machata .check_params = mlxsw_sp_qdisc_fifo_check_params, 8747bec1a45SPetr Machata .replace = mlxsw_sp_qdisc_fifo_replace, 8757bec1a45SPetr Machata .get_stats = mlxsw_sp_qdisc_get_fifo_stats, 8767bec1a45SPetr Machata .clean_stats = mlxsw_sp_setup_tc_qdisc_leaf_clean_stats, 8777bec1a45SPetr Machata }; 8787bec1a45SPetr Machata 8797bec1a45SPetr Machata int mlxsw_sp_setup_tc_fifo(struct mlxsw_sp_port *mlxsw_sp_port, 8807bec1a45SPetr Machata struct tc_fifo_qopt_offload *p) 8817bec1a45SPetr Machata { 8827bec1a45SPetr Machata struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc; 8837bec1a45SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc; 8847bec1a45SPetr Machata int tclass, child_index; 8857bec1a45SPetr Machata u32 parent_handle; 8867bec1a45SPetr Machata 8877bec1a45SPetr Machata /* Invisible FIFOs are tracked in future_handle and future_fifos. Make 8887bec1a45SPetr Machata * sure that not more than one qdisc is created for a port at a time. 8897bec1a45SPetr Machata * RTNL is a simple proxy for that. 8907bec1a45SPetr Machata */ 8917bec1a45SPetr Machata ASSERT_RTNL(); 8927bec1a45SPetr Machata 8937bec1a45SPetr Machata mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent, false); 8947bec1a45SPetr Machata if (!mlxsw_sp_qdisc && p->handle == TC_H_UNSPEC) { 8957bec1a45SPetr Machata parent_handle = TC_H_MAJ(p->parent); 8967bec1a45SPetr Machata if (parent_handle != qdisc_state->future_handle) { 8977bec1a45SPetr Machata /* This notifications is for a different Qdisc than 8987bec1a45SPetr Machata * previously. Wipe the future cache. 8997bec1a45SPetr Machata */ 9007bec1a45SPetr Machata memset(qdisc_state->future_fifos, 0, 9017bec1a45SPetr Machata sizeof(qdisc_state->future_fifos)); 9027bec1a45SPetr Machata qdisc_state->future_handle = parent_handle; 9037bec1a45SPetr Machata } 9047bec1a45SPetr Machata 9057bec1a45SPetr Machata child_index = TC_H_MIN(p->parent); 9067bec1a45SPetr Machata tclass = MLXSW_SP_PRIO_CHILD_TO_TCLASS(child_index); 9077bec1a45SPetr Machata if (tclass < IEEE_8021QAZ_MAX_TCS) { 9087bec1a45SPetr Machata if (p->command == TC_FIFO_REPLACE) 9097bec1a45SPetr Machata qdisc_state->future_fifos[tclass] = true; 9107bec1a45SPetr Machata else if (p->command == TC_FIFO_DESTROY) 9117bec1a45SPetr Machata qdisc_state->future_fifos[tclass] = false; 9127bec1a45SPetr Machata } 9137bec1a45SPetr Machata } 9147bec1a45SPetr Machata if (!mlxsw_sp_qdisc) 9157bec1a45SPetr Machata return -EOPNOTSUPP; 9167bec1a45SPetr Machata 9177bec1a45SPetr Machata if (p->command == TC_FIFO_REPLACE) { 9187bec1a45SPetr Machata return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle, 9197bec1a45SPetr Machata mlxsw_sp_qdisc, 9207bec1a45SPetr Machata &mlxsw_sp_qdisc_ops_fifo, NULL); 9217bec1a45SPetr Machata } 9227bec1a45SPetr Machata 923290fe2c5SPetr Machata if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle)) 9247bec1a45SPetr Machata return -EOPNOTSUPP; 9257bec1a45SPetr Machata 9267bec1a45SPetr Machata switch (p->command) { 9277bec1a45SPetr Machata case TC_FIFO_DESTROY: 928549f2aaeSPetr Machata return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc); 9297bec1a45SPetr Machata case TC_FIFO_STATS: 9307bec1a45SPetr Machata return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc, 9317bec1a45SPetr Machata &p->stats); 9327bec1a45SPetr Machata case TC_FIFO_REPLACE: /* Handled above. */ 9337bec1a45SPetr Machata break; 9347bec1a45SPetr Machata } 9357bec1a45SPetr Machata 9367bec1a45SPetr Machata return -EOPNOTSUPP; 9377bec1a45SPetr Machata } 9387bec1a45SPetr Machata 939*51d52ed9SPetr Machata static int __mlxsw_sp_qdisc_ets_destroy(struct mlxsw_sp_port *mlxsw_sp_port, 940*51d52ed9SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 94146a3615bSNogah Frankel { 94246a3615bSNogah Frankel int i; 94346a3615bSNogah Frankel 944*51d52ed9SPetr Machata for (i = 0; i < mlxsw_sp_qdisc->num_classes; i++) { 94546a3615bSNogah Frankel mlxsw_sp_port_prio_tc_set(mlxsw_sp_port, i, 94646a3615bSNogah Frankel MLXSW_SP_PORT_DEFAULT_TCLASS); 9477917f52aSPetr Machata mlxsw_sp_port_ets_set(mlxsw_sp_port, 9487917f52aSPetr Machata MLXSW_REG_QEEC_HR_SUBGROUP, 9497917f52aSPetr Machata i, 0, false, 0); 950eed4baebSNogah Frankel mlxsw_sp_qdisc_destroy(mlxsw_sp_port, 951*51d52ed9SPetr Machata &mlxsw_sp_qdisc->qdiscs[i]); 952*51d52ed9SPetr Machata mlxsw_sp_qdisc->qdiscs[i].prio_bitmap = 0; 953eed4baebSNogah Frankel } 95446a3615bSNogah Frankel 95546a3615bSNogah Frankel return 0; 95646a3615bSNogah Frankel } 95746a3615bSNogah Frankel 95846a3615bSNogah Frankel static int 9597917f52aSPetr Machata mlxsw_sp_qdisc_prio_destroy(struct mlxsw_sp_port *mlxsw_sp_port, 9607917f52aSPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 9617917f52aSPetr Machata { 962*51d52ed9SPetr Machata return __mlxsw_sp_qdisc_ets_destroy(mlxsw_sp_port, mlxsw_sp_qdisc); 9637917f52aSPetr Machata } 9647917f52aSPetr Machata 9657917f52aSPetr Machata static int 9667917f52aSPetr Machata __mlxsw_sp_qdisc_ets_check_params(unsigned int nbands) 9677917f52aSPetr Machata { 9687917f52aSPetr Machata if (nbands > IEEE_8021QAZ_MAX_TCS) 9697917f52aSPetr Machata return -EOPNOTSUPP; 9707917f52aSPetr Machata 9717917f52aSPetr Machata return 0; 9727917f52aSPetr Machata } 9737917f52aSPetr Machata 9747917f52aSPetr Machata static int 97546a3615bSNogah Frankel mlxsw_sp_qdisc_prio_check_params(struct mlxsw_sp_port *mlxsw_sp_port, 97646a3615bSNogah Frankel void *params) 97746a3615bSNogah Frankel { 97846a3615bSNogah Frankel struct tc_prio_qopt_offload_params *p = params; 97946a3615bSNogah Frankel 9807917f52aSPetr Machata return __mlxsw_sp_qdisc_ets_check_params(p->bands); 98146a3615bSNogah Frankel } 98246a3615bSNogah Frankel 98346a3615bSNogah Frankel static int 984*51d52ed9SPetr Machata __mlxsw_sp_qdisc_ets_replace(struct mlxsw_sp_port *mlxsw_sp_port, 985*51d52ed9SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 986*51d52ed9SPetr Machata u32 handle, unsigned int nbands, 9877917f52aSPetr Machata const unsigned int *quanta, 9887917f52aSPetr Machata const unsigned int *weights, 9897917f52aSPetr Machata const u8 *priomap) 99046a3615bSNogah Frankel { 991ee88450dSPetr Machata struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc; 99204cc0bf5SNogah Frankel struct mlxsw_sp_qdisc *child_qdisc; 993cc6e5c13SNogah Frankel int tclass, i, band, backlog; 99404cc0bf5SNogah Frankel u8 old_priomap; 99546a3615bSNogah Frankel int err; 99646a3615bSNogah Frankel 9977917f52aSPetr Machata for (band = 0; band < nbands; band++) { 99804cc0bf5SNogah Frankel tclass = MLXSW_SP_PRIO_BAND_TO_TCLASS(band); 999*51d52ed9SPetr Machata child_qdisc = &mlxsw_sp_qdisc->qdiscs[band]; 100004cc0bf5SNogah Frankel old_priomap = child_qdisc->prio_bitmap; 100104cc0bf5SNogah Frankel child_qdisc->prio_bitmap = 0; 10027917f52aSPetr Machata 10037917f52aSPetr Machata err = mlxsw_sp_port_ets_set(mlxsw_sp_port, 10047917f52aSPetr Machata MLXSW_REG_QEEC_HR_SUBGROUP, 10057917f52aSPetr Machata tclass, 0, !!quanta[band], 10067917f52aSPetr Machata weights[band]); 10077917f52aSPetr Machata if (err) 10087917f52aSPetr Machata return err; 10097917f52aSPetr Machata 101046a3615bSNogah Frankel for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { 10117917f52aSPetr Machata if (priomap[i] == band) { 101204cc0bf5SNogah Frankel child_qdisc->prio_bitmap |= BIT(i); 101304cc0bf5SNogah Frankel if (BIT(i) & old_priomap) 101404cc0bf5SNogah Frankel continue; 101504cc0bf5SNogah Frankel err = mlxsw_sp_port_prio_tc_set(mlxsw_sp_port, 101604cc0bf5SNogah Frankel i, tclass); 101746a3615bSNogah Frankel if (err) 101846a3615bSNogah Frankel return err; 101904cc0bf5SNogah Frankel } 102004cc0bf5SNogah Frankel } 102104cc0bf5SNogah Frankel if (old_priomap != child_qdisc->prio_bitmap && 1022cc6e5c13SNogah Frankel child_qdisc->ops && child_qdisc->ops->clean_stats) { 1023cc6e5c13SNogah Frankel backlog = child_qdisc->stats_base.backlog; 102404cc0bf5SNogah Frankel child_qdisc->ops->clean_stats(mlxsw_sp_port, 102504cc0bf5SNogah Frankel child_qdisc); 1026cc6e5c13SNogah Frankel child_qdisc->stats_base.backlog = backlog; 1027cc6e5c13SNogah Frankel } 10287bec1a45SPetr Machata 10297bec1a45SPetr Machata if (handle == qdisc_state->future_handle && 10307bec1a45SPetr Machata qdisc_state->future_fifos[tclass]) { 10317bec1a45SPetr Machata err = mlxsw_sp_qdisc_replace(mlxsw_sp_port, TC_H_UNSPEC, 10327bec1a45SPetr Machata child_qdisc, 10337bec1a45SPetr Machata &mlxsw_sp_qdisc_ops_fifo, 10347bec1a45SPetr Machata NULL); 10357bec1a45SPetr Machata if (err) 10367bec1a45SPetr Machata return err; 10377bec1a45SPetr Machata } 103846a3615bSNogah Frankel } 103998ceb7b6SNogah Frankel for (; band < IEEE_8021QAZ_MAX_TCS; band++) { 104098ceb7b6SNogah Frankel tclass = MLXSW_SP_PRIO_BAND_TO_TCLASS(band); 1041*51d52ed9SPetr Machata child_qdisc = &mlxsw_sp_qdisc->qdiscs[band]; 104298ceb7b6SNogah Frankel child_qdisc->prio_bitmap = 0; 104398ceb7b6SNogah Frankel mlxsw_sp_qdisc_destroy(mlxsw_sp_port, child_qdisc); 10447917f52aSPetr Machata mlxsw_sp_port_ets_set(mlxsw_sp_port, 10457917f52aSPetr Machata MLXSW_REG_QEEC_HR_SUBGROUP, 10467917f52aSPetr Machata tclass, 0, false, 0); 104798ceb7b6SNogah Frankel } 10487bec1a45SPetr Machata 10497bec1a45SPetr Machata qdisc_state->future_handle = TC_H_UNSPEC; 10507bec1a45SPetr Machata memset(qdisc_state->future_fifos, 0, sizeof(qdisc_state->future_fifos)); 105146a3615bSNogah Frankel return 0; 105246a3615bSNogah Frankel } 105346a3615bSNogah Frankel 10547917f52aSPetr Machata static int 1055c4e372e2SPetr Machata mlxsw_sp_qdisc_prio_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, 10567917f52aSPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 10577917f52aSPetr Machata void *params) 10587917f52aSPetr Machata { 10597917f52aSPetr Machata struct tc_prio_qopt_offload_params *p = params; 10607917f52aSPetr Machata unsigned int zeroes[TCQ_ETS_MAX_BANDS] = {0}; 10617917f52aSPetr Machata 1062*51d52ed9SPetr Machata return __mlxsw_sp_qdisc_ets_replace(mlxsw_sp_port, mlxsw_sp_qdisc, 1063*51d52ed9SPetr Machata handle, p->bands, zeroes, 1064*51d52ed9SPetr Machata zeroes, p->priomap); 10657917f52aSPetr Machata } 10667917f52aSPetr Machata 10677917f52aSPetr Machata static void 10687917f52aSPetr Machata __mlxsw_sp_qdisc_ets_unoffload(struct mlxsw_sp_port *mlxsw_sp_port, 10697917f52aSPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 10707917f52aSPetr Machata struct gnet_stats_queue *qstats) 10717917f52aSPetr Machata { 10727917f52aSPetr Machata u64 backlog; 10737917f52aSPetr Machata 10747917f52aSPetr Machata backlog = mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp, 10757917f52aSPetr Machata mlxsw_sp_qdisc->stats_base.backlog); 10767917f52aSPetr Machata qstats->backlog -= backlog; 10777917f52aSPetr Machata } 10787917f52aSPetr Machata 1079e02f08a0SWei Yongjun static void 108093d8a4c1SNogah Frankel mlxsw_sp_qdisc_prio_unoffload(struct mlxsw_sp_port *mlxsw_sp_port, 108193d8a4c1SNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 108293d8a4c1SNogah Frankel void *params) 108393d8a4c1SNogah Frankel { 108493d8a4c1SNogah Frankel struct tc_prio_qopt_offload_params *p = params; 108593d8a4c1SNogah Frankel 10867917f52aSPetr Machata __mlxsw_sp_qdisc_ets_unoffload(mlxsw_sp_port, mlxsw_sp_qdisc, 10877917f52aSPetr Machata p->qstats); 108893d8a4c1SNogah Frankel } 108993d8a4c1SNogah Frankel 109093d8a4c1SNogah Frankel static int 109193d8a4c1SNogah Frankel mlxsw_sp_qdisc_get_prio_stats(struct mlxsw_sp_port *mlxsw_sp_port, 109293d8a4c1SNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 109393d8a4c1SNogah Frankel struct tc_qopt_offload_stats *stats_ptr) 109493d8a4c1SNogah Frankel { 1095cf9af379SPetr Machata struct mlxsw_sp_qdisc *tc_qdisc; 1096cf9af379SPetr Machata u64 tx_packets = 0; 1097cf9af379SPetr Machata u64 tx_bytes = 0; 1098cf9af379SPetr Machata u64 backlog = 0; 1099cf9af379SPetr Machata u64 drops = 0; 110093d8a4c1SNogah Frankel int i; 110193d8a4c1SNogah Frankel 1102*51d52ed9SPetr Machata for (i = 0; i < mlxsw_sp_qdisc->num_classes; i++) { 1103*51d52ed9SPetr Machata tc_qdisc = &mlxsw_sp_qdisc->qdiscs[i]; 1104cf9af379SPetr Machata mlxsw_sp_qdisc_collect_tc_stats(mlxsw_sp_port, tc_qdisc, 1105cf9af379SPetr Machata &tx_bytes, &tx_packets, 1106cf9af379SPetr Machata &drops, &backlog); 110793d8a4c1SNogah Frankel } 110893d8a4c1SNogah Frankel 1109cf9af379SPetr Machata mlxsw_sp_qdisc_update_stats(mlxsw_sp_port->mlxsw_sp, mlxsw_sp_qdisc, 1110cf9af379SPetr Machata tx_bytes, tx_packets, drops, backlog, 1111cf9af379SPetr Machata stats_ptr); 111293d8a4c1SNogah Frankel return 0; 111393d8a4c1SNogah Frankel } 111493d8a4c1SNogah Frankel 111593d8a4c1SNogah Frankel static void 111693d8a4c1SNogah Frankel mlxsw_sp_setup_tc_qdisc_prio_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port, 111793d8a4c1SNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 111893d8a4c1SNogah Frankel { 111993d8a4c1SNogah Frankel struct mlxsw_sp_qdisc_stats *stats_base; 112093d8a4c1SNogah Frankel struct mlxsw_sp_port_xstats *xstats; 112193d8a4c1SNogah Frankel struct rtnl_link_stats64 *stats; 112293d8a4c1SNogah Frankel int i; 112393d8a4c1SNogah Frankel 112493d8a4c1SNogah Frankel xstats = &mlxsw_sp_port->periodic_hw_stats.xstats; 112593d8a4c1SNogah Frankel stats = &mlxsw_sp_port->periodic_hw_stats.stats; 112693d8a4c1SNogah Frankel stats_base = &mlxsw_sp_qdisc->stats_base; 112793d8a4c1SNogah Frankel 112893d8a4c1SNogah Frankel stats_base->tx_packets = stats->tx_packets; 112993d8a4c1SNogah Frankel stats_base->tx_bytes = stats->tx_bytes; 113093d8a4c1SNogah Frankel 113193d8a4c1SNogah Frankel stats_base->drops = 0; 113223f2b404SNogah Frankel for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { 113385005b82SPetr Machata stats_base->drops += mlxsw_sp_xstats_tail_drop(xstats, i); 113423f2b404SNogah Frankel stats_base->drops += xstats->wred_drop[i]; 113523f2b404SNogah Frankel } 113693d8a4c1SNogah Frankel 113793d8a4c1SNogah Frankel mlxsw_sp_qdisc->stats_base.backlog = 0; 113893d8a4c1SNogah Frankel } 113993d8a4c1SNogah Frankel 1140*51d52ed9SPetr Machata static struct mlxsw_sp_qdisc * 1141*51d52ed9SPetr Machata mlxsw_sp_qdisc_prio_find_class(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 1142*51d52ed9SPetr Machata u32 parent) 1143*51d52ed9SPetr Machata { 1144*51d52ed9SPetr Machata int child_index = TC_H_MIN(parent); 1145*51d52ed9SPetr Machata int band = child_index - 1; 1146*51d52ed9SPetr Machata 1147*51d52ed9SPetr Machata if (band < 0 || band >= mlxsw_sp_qdisc->num_classes) 1148*51d52ed9SPetr Machata return NULL; 1149*51d52ed9SPetr Machata return &mlxsw_sp_qdisc->qdiscs[band]; 1150*51d52ed9SPetr Machata } 1151*51d52ed9SPetr Machata 115246a3615bSNogah Frankel static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_prio = { 115346a3615bSNogah Frankel .type = MLXSW_SP_QDISC_PRIO, 115446a3615bSNogah Frankel .check_params = mlxsw_sp_qdisc_prio_check_params, 115546a3615bSNogah Frankel .replace = mlxsw_sp_qdisc_prio_replace, 115693d8a4c1SNogah Frankel .unoffload = mlxsw_sp_qdisc_prio_unoffload, 115746a3615bSNogah Frankel .destroy = mlxsw_sp_qdisc_prio_destroy, 115893d8a4c1SNogah Frankel .get_stats = mlxsw_sp_qdisc_get_prio_stats, 115993d8a4c1SNogah Frankel .clean_stats = mlxsw_sp_setup_tc_qdisc_prio_clean_stats, 1160*51d52ed9SPetr Machata .find_class = mlxsw_sp_qdisc_prio_find_class, 116146a3615bSNogah Frankel }; 116246a3615bSNogah Frankel 116319f405b9SPetr Machata static int 116419f405b9SPetr Machata mlxsw_sp_qdisc_ets_check_params(struct mlxsw_sp_port *mlxsw_sp_port, 116519f405b9SPetr Machata void *params) 116619f405b9SPetr Machata { 116719f405b9SPetr Machata struct tc_ets_qopt_offload_replace_params *p = params; 116819f405b9SPetr Machata 116919f405b9SPetr Machata return __mlxsw_sp_qdisc_ets_check_params(p->bands); 117019f405b9SPetr Machata } 117119f405b9SPetr Machata 117219f405b9SPetr Machata static int 1173c4e372e2SPetr Machata mlxsw_sp_qdisc_ets_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, 117419f405b9SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 117519f405b9SPetr Machata void *params) 117619f405b9SPetr Machata { 117719f405b9SPetr Machata struct tc_ets_qopt_offload_replace_params *p = params; 117819f405b9SPetr Machata 1179*51d52ed9SPetr Machata return __mlxsw_sp_qdisc_ets_replace(mlxsw_sp_port, mlxsw_sp_qdisc, 1180*51d52ed9SPetr Machata handle, p->bands, p->quanta, 1181*51d52ed9SPetr Machata p->weights, p->priomap); 118219f405b9SPetr Machata } 118319f405b9SPetr Machata 118419f405b9SPetr Machata static void 118519f405b9SPetr Machata mlxsw_sp_qdisc_ets_unoffload(struct mlxsw_sp_port *mlxsw_sp_port, 118619f405b9SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 118719f405b9SPetr Machata void *params) 118819f405b9SPetr Machata { 118919f405b9SPetr Machata struct tc_ets_qopt_offload_replace_params *p = params; 119019f405b9SPetr Machata 119119f405b9SPetr Machata __mlxsw_sp_qdisc_ets_unoffload(mlxsw_sp_port, mlxsw_sp_qdisc, 119219f405b9SPetr Machata p->qstats); 119319f405b9SPetr Machata } 119419f405b9SPetr Machata 119519f405b9SPetr Machata static int 119619f405b9SPetr Machata mlxsw_sp_qdisc_ets_destroy(struct mlxsw_sp_port *mlxsw_sp_port, 119719f405b9SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 119819f405b9SPetr Machata { 1199*51d52ed9SPetr Machata return __mlxsw_sp_qdisc_ets_destroy(mlxsw_sp_port, mlxsw_sp_qdisc); 120019f405b9SPetr Machata } 120119f405b9SPetr Machata 120219f405b9SPetr Machata static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_ets = { 120319f405b9SPetr Machata .type = MLXSW_SP_QDISC_ETS, 120419f405b9SPetr Machata .check_params = mlxsw_sp_qdisc_ets_check_params, 120519f405b9SPetr Machata .replace = mlxsw_sp_qdisc_ets_replace, 120619f405b9SPetr Machata .unoffload = mlxsw_sp_qdisc_ets_unoffload, 120719f405b9SPetr Machata .destroy = mlxsw_sp_qdisc_ets_destroy, 120819f405b9SPetr Machata .get_stats = mlxsw_sp_qdisc_get_prio_stats, 120919f405b9SPetr Machata .clean_stats = mlxsw_sp_setup_tc_qdisc_prio_clean_stats, 1210*51d52ed9SPetr Machata .find_class = mlxsw_sp_qdisc_prio_find_class, 121119f405b9SPetr Machata }; 121219f405b9SPetr Machata 12135bc146c9SPetr Machata /* Linux allows linking of Qdiscs to arbitrary classes (so long as the resulting 12145bc146c9SPetr Machata * graph is free of cycles). These operations do not change the parent handle 12155bc146c9SPetr Machata * though, which means it can be incomplete (if there is more than one class 12165bc146c9SPetr Machata * where the Qdisc in question is grafted) or outright wrong (if the Qdisc was 12175bc146c9SPetr Machata * linked to a different class and then removed from the original class). 12185bc146c9SPetr Machata * 12195bc146c9SPetr Machata * E.g. consider this sequence of operations: 12205bc146c9SPetr Machata * 12215bc146c9SPetr Machata * # tc qdisc add dev swp1 root handle 1: prio 12225bc146c9SPetr Machata * # tc qdisc add dev swp1 parent 1:3 handle 13: red limit 1000000 avpkt 10000 12235bc146c9SPetr Machata * RED: set bandwidth to 10Mbit 12245bc146c9SPetr Machata * # tc qdisc link dev swp1 handle 13: parent 1:2 12255bc146c9SPetr Machata * 12265bc146c9SPetr Machata * At this point, both 1:2 and 1:3 have the same RED Qdisc instance as their 12275bc146c9SPetr Machata * child. But RED will still only claim that 1:3 is its parent. If it's removed 12285bc146c9SPetr Machata * from that band, its only parent will be 1:2, but it will continue to claim 12295bc146c9SPetr Machata * that it is in fact 1:3. 12305bc146c9SPetr Machata * 12315bc146c9SPetr Machata * The notification for child Qdisc replace (e.g. TC_RED_REPLACE) comes before 12325bc146c9SPetr Machata * the notification for parent graft (e.g. TC_PRIO_GRAFT). We take the replace 12335bc146c9SPetr Machata * notification to offload the child Qdisc, based on its parent handle, and use 12345bc146c9SPetr Machata * the graft operation to validate that the class where the child is actually 12355bc146c9SPetr Machata * grafted corresponds to the parent handle. If the two don't match, we 12365bc146c9SPetr Machata * unoffload the child. 123732dc5efcSNogah Frankel */ 123832dc5efcSNogah Frankel static int 12397917f52aSPetr Machata __mlxsw_sp_qdisc_ets_graft(struct mlxsw_sp_port *mlxsw_sp_port, 124032dc5efcSNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 12417917f52aSPetr Machata u8 band, u32 child_handle) 124232dc5efcSNogah Frankel { 124332dc5efcSNogah Frankel struct mlxsw_sp_qdisc *old_qdisc; 124432dc5efcSNogah Frankel 1245*51d52ed9SPetr Machata if (band < mlxsw_sp_qdisc->num_classes && 1246*51d52ed9SPetr Machata mlxsw_sp_qdisc->qdiscs[band].handle == child_handle) 124732dc5efcSNogah Frankel return 0; 124832dc5efcSNogah Frankel 1249a2d6d7aeSDavid S. Miller if (!child_handle) { 12503971a535SPetr Machata /* This is an invisible FIFO replacing the original Qdisc. 12513971a535SPetr Machata * Ignore it--the original Qdisc's destroy will follow. 12523971a535SPetr Machata */ 12533971a535SPetr Machata return 0; 12543971a535SPetr Machata } 12553971a535SPetr Machata 125632dc5efcSNogah Frankel /* See if the grafted qdisc is already offloaded on any tclass. If so, 125732dc5efcSNogah Frankel * unoffload it. 125832dc5efcSNogah Frankel */ 125932dc5efcSNogah Frankel old_qdisc = mlxsw_sp_qdisc_find_by_handle(mlxsw_sp_port, 12607917f52aSPetr Machata child_handle); 126132dc5efcSNogah Frankel if (old_qdisc) 126232dc5efcSNogah Frankel mlxsw_sp_qdisc_destroy(mlxsw_sp_port, old_qdisc); 126332dc5efcSNogah Frankel 1264*51d52ed9SPetr Machata mlxsw_sp_qdisc = mlxsw_sp_qdisc->ops->find_class(mlxsw_sp_qdisc, band); 1265*51d52ed9SPetr Machata if (!WARN_ON(!mlxsw_sp_qdisc)) 1266*51d52ed9SPetr Machata mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc); 1267*51d52ed9SPetr Machata 126832dc5efcSNogah Frankel return -EOPNOTSUPP; 126932dc5efcSNogah Frankel } 127032dc5efcSNogah Frankel 12717917f52aSPetr Machata static int 12727917f52aSPetr Machata mlxsw_sp_qdisc_prio_graft(struct mlxsw_sp_port *mlxsw_sp_port, 12737917f52aSPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 12747917f52aSPetr Machata struct tc_prio_qopt_offload_graft_params *p) 12757917f52aSPetr Machata { 12767917f52aSPetr Machata return __mlxsw_sp_qdisc_ets_graft(mlxsw_sp_port, mlxsw_sp_qdisc, 12777917f52aSPetr Machata p->band, p->child_handle); 12787917f52aSPetr Machata } 12797917f52aSPetr Machata 128046a3615bSNogah Frankel int mlxsw_sp_setup_tc_prio(struct mlxsw_sp_port *mlxsw_sp_port, 128146a3615bSNogah Frankel struct tc_prio_qopt_offload *p) 128246a3615bSNogah Frankel { 128346a3615bSNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc; 128446a3615bSNogah Frankel 1285eed4baebSNogah Frankel mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent, true); 1286eed4baebSNogah Frankel if (!mlxsw_sp_qdisc) 128746a3615bSNogah Frankel return -EOPNOTSUPP; 128846a3615bSNogah Frankel 128946a3615bSNogah Frankel if (p->command == TC_PRIO_REPLACE) 129046a3615bSNogah Frankel return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle, 129146a3615bSNogah Frankel mlxsw_sp_qdisc, 129246a3615bSNogah Frankel &mlxsw_sp_qdisc_ops_prio, 129346a3615bSNogah Frankel &p->replace_params); 129446a3615bSNogah Frankel 1295290fe2c5SPetr Machata if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle)) 129646a3615bSNogah Frankel return -EOPNOTSUPP; 129746a3615bSNogah Frankel 129846a3615bSNogah Frankel switch (p->command) { 129946a3615bSNogah Frankel case TC_PRIO_DESTROY: 130046a3615bSNogah Frankel return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc); 130193d8a4c1SNogah Frankel case TC_PRIO_STATS: 130293d8a4c1SNogah Frankel return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc, 130393d8a4c1SNogah Frankel &p->stats); 130432dc5efcSNogah Frankel case TC_PRIO_GRAFT: 130532dc5efcSNogah Frankel return mlxsw_sp_qdisc_prio_graft(mlxsw_sp_port, mlxsw_sp_qdisc, 130632dc5efcSNogah Frankel &p->graft_params); 130746a3615bSNogah Frankel default: 130846a3615bSNogah Frankel return -EOPNOTSUPP; 130946a3615bSNogah Frankel } 131046a3615bSNogah Frankel } 131146a3615bSNogah Frankel 131219f405b9SPetr Machata int mlxsw_sp_setup_tc_ets(struct mlxsw_sp_port *mlxsw_sp_port, 131319f405b9SPetr Machata struct tc_ets_qopt_offload *p) 131419f405b9SPetr Machata { 131519f405b9SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc; 131619f405b9SPetr Machata 131719f405b9SPetr Machata mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent, true); 131819f405b9SPetr Machata if (!mlxsw_sp_qdisc) 131919f405b9SPetr Machata return -EOPNOTSUPP; 132019f405b9SPetr Machata 132119f405b9SPetr Machata if (p->command == TC_ETS_REPLACE) 132219f405b9SPetr Machata return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle, 132319f405b9SPetr Machata mlxsw_sp_qdisc, 132419f405b9SPetr Machata &mlxsw_sp_qdisc_ops_ets, 132519f405b9SPetr Machata &p->replace_params); 132619f405b9SPetr Machata 1327290fe2c5SPetr Machata if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle)) 132819f405b9SPetr Machata return -EOPNOTSUPP; 132919f405b9SPetr Machata 133019f405b9SPetr Machata switch (p->command) { 133119f405b9SPetr Machata case TC_ETS_DESTROY: 133219f405b9SPetr Machata return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc); 133319f405b9SPetr Machata case TC_ETS_STATS: 133419f405b9SPetr Machata return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc, 133519f405b9SPetr Machata &p->stats); 133619f405b9SPetr Machata case TC_ETS_GRAFT: 133719f405b9SPetr Machata return __mlxsw_sp_qdisc_ets_graft(mlxsw_sp_port, mlxsw_sp_qdisc, 133819f405b9SPetr Machata p->graft_params.band, 133919f405b9SPetr Machata p->graft_params.child_handle); 134019f405b9SPetr Machata default: 134119f405b9SPetr Machata return -EOPNOTSUPP; 134219f405b9SPetr Machata } 134319f405b9SPetr Machata } 134419f405b9SPetr Machata 1345f6668eacSPetr Machata struct mlxsw_sp_qevent_block { 1346f6668eacSPetr Machata struct list_head binding_list; 1347f6668eacSPetr Machata struct list_head mall_entry_list; 1348f6668eacSPetr Machata struct mlxsw_sp *mlxsw_sp; 1349f6668eacSPetr Machata }; 1350f6668eacSPetr Machata 1351f6668eacSPetr Machata struct mlxsw_sp_qevent_binding { 1352f6668eacSPetr Machata struct list_head list; 1353f6668eacSPetr Machata struct mlxsw_sp_port *mlxsw_sp_port; 1354f6668eacSPetr Machata u32 handle; 1355f6668eacSPetr Machata int tclass_num; 1356f6668eacSPetr Machata enum mlxsw_sp_span_trigger span_trigger; 1357f6668eacSPetr Machata }; 1358f6668eacSPetr Machata 1359f6668eacSPetr Machata static LIST_HEAD(mlxsw_sp_qevent_block_cb_list); 1360f6668eacSPetr Machata 136154a92385SPetr Machata static int mlxsw_sp_qevent_span_configure(struct mlxsw_sp *mlxsw_sp, 1362f6668eacSPetr Machata struct mlxsw_sp_mall_entry *mall_entry, 136354a92385SPetr Machata struct mlxsw_sp_qevent_binding *qevent_binding, 136454a92385SPetr Machata const struct mlxsw_sp_span_agent_parms *agent_parms, 136554a92385SPetr Machata int *p_span_id) 1366f6668eacSPetr Machata { 1367f6668eacSPetr Machata struct mlxsw_sp_port *mlxsw_sp_port = qevent_binding->mlxsw_sp_port; 1368f6668eacSPetr Machata struct mlxsw_sp_span_trigger_parms trigger_parms = {}; 1369f6668eacSPetr Machata int span_id; 1370f6668eacSPetr Machata int err; 1371f6668eacSPetr Machata 137254a92385SPetr Machata err = mlxsw_sp_span_agent_get(mlxsw_sp, &span_id, agent_parms); 1373f6668eacSPetr Machata if (err) 1374f6668eacSPetr Machata return err; 1375f6668eacSPetr Machata 1376f6668eacSPetr Machata err = mlxsw_sp_span_analyzed_port_get(mlxsw_sp_port, true); 1377f6668eacSPetr Machata if (err) 1378f6668eacSPetr Machata goto err_analyzed_port_get; 1379f6668eacSPetr Machata 1380f6668eacSPetr Machata trigger_parms.span_id = span_id; 13812dcbd920SIdo Schimmel trigger_parms.probability_rate = 1; 1382f6668eacSPetr Machata err = mlxsw_sp_span_agent_bind(mlxsw_sp, qevent_binding->span_trigger, mlxsw_sp_port, 1383f6668eacSPetr Machata &trigger_parms); 1384f6668eacSPetr Machata if (err) 1385f6668eacSPetr Machata goto err_agent_bind; 1386f6668eacSPetr Machata 1387f6668eacSPetr Machata err = mlxsw_sp_span_trigger_enable(mlxsw_sp_port, qevent_binding->span_trigger, 1388f6668eacSPetr Machata qevent_binding->tclass_num); 1389f6668eacSPetr Machata if (err) 1390f6668eacSPetr Machata goto err_trigger_enable; 1391f6668eacSPetr Machata 139254a92385SPetr Machata *p_span_id = span_id; 1393f6668eacSPetr Machata return 0; 1394f6668eacSPetr Machata 1395f6668eacSPetr Machata err_trigger_enable: 1396f6668eacSPetr Machata mlxsw_sp_span_agent_unbind(mlxsw_sp, qevent_binding->span_trigger, mlxsw_sp_port, 1397f6668eacSPetr Machata &trigger_parms); 1398f6668eacSPetr Machata err_agent_bind: 1399f6668eacSPetr Machata mlxsw_sp_span_analyzed_port_put(mlxsw_sp_port, true); 1400f6668eacSPetr Machata err_analyzed_port_get: 1401f6668eacSPetr Machata mlxsw_sp_span_agent_put(mlxsw_sp, span_id); 1402f6668eacSPetr Machata return err; 1403f6668eacSPetr Machata } 1404f6668eacSPetr Machata 140554a92385SPetr Machata static void mlxsw_sp_qevent_span_deconfigure(struct mlxsw_sp *mlxsw_sp, 140654a92385SPetr Machata struct mlxsw_sp_qevent_binding *qevent_binding, 140754a92385SPetr Machata int span_id) 1408f6668eacSPetr Machata { 1409f6668eacSPetr Machata struct mlxsw_sp_port *mlxsw_sp_port = qevent_binding->mlxsw_sp_port; 1410f6668eacSPetr Machata struct mlxsw_sp_span_trigger_parms trigger_parms = { 141154a92385SPetr Machata .span_id = span_id, 1412f6668eacSPetr Machata }; 1413f6668eacSPetr Machata 1414f6668eacSPetr Machata mlxsw_sp_span_trigger_disable(mlxsw_sp_port, qevent_binding->span_trigger, 1415f6668eacSPetr Machata qevent_binding->tclass_num); 1416f6668eacSPetr Machata mlxsw_sp_span_agent_unbind(mlxsw_sp, qevent_binding->span_trigger, mlxsw_sp_port, 1417f6668eacSPetr Machata &trigger_parms); 1418f6668eacSPetr Machata mlxsw_sp_span_analyzed_port_put(mlxsw_sp_port, true); 141954a92385SPetr Machata mlxsw_sp_span_agent_put(mlxsw_sp, span_id); 142054a92385SPetr Machata } 142154a92385SPetr Machata 142254a92385SPetr Machata static int mlxsw_sp_qevent_mirror_configure(struct mlxsw_sp *mlxsw_sp, 142354a92385SPetr Machata struct mlxsw_sp_mall_entry *mall_entry, 142454a92385SPetr Machata struct mlxsw_sp_qevent_binding *qevent_binding) 142554a92385SPetr Machata { 142654a92385SPetr Machata struct mlxsw_sp_span_agent_parms agent_parms = { 142754a92385SPetr Machata .to_dev = mall_entry->mirror.to_dev, 142854a92385SPetr Machata }; 142954a92385SPetr Machata 143054a92385SPetr Machata return mlxsw_sp_qevent_span_configure(mlxsw_sp, mall_entry, qevent_binding, 143154a92385SPetr Machata &agent_parms, &mall_entry->mirror.span_id); 143254a92385SPetr Machata } 143354a92385SPetr Machata 143454a92385SPetr Machata static void mlxsw_sp_qevent_mirror_deconfigure(struct mlxsw_sp *mlxsw_sp, 143554a92385SPetr Machata struct mlxsw_sp_mall_entry *mall_entry, 143654a92385SPetr Machata struct mlxsw_sp_qevent_binding *qevent_binding) 143754a92385SPetr Machata { 143854a92385SPetr Machata mlxsw_sp_qevent_span_deconfigure(mlxsw_sp, qevent_binding, mall_entry->mirror.span_id); 143954a92385SPetr Machata } 144054a92385SPetr Machata 144154a92385SPetr Machata static int mlxsw_sp_qevent_trap_configure(struct mlxsw_sp *mlxsw_sp, 144254a92385SPetr Machata struct mlxsw_sp_mall_entry *mall_entry, 144354a92385SPetr Machata struct mlxsw_sp_qevent_binding *qevent_binding) 144454a92385SPetr Machata { 14455c7659ebSIdo Schimmel struct mlxsw_sp_span_agent_parms agent_parms = { 14465c7659ebSIdo Schimmel .session_id = MLXSW_SP_SPAN_SESSION_ID_BUFFER, 14475c7659ebSIdo Schimmel }; 144854a92385SPetr Machata int err; 144954a92385SPetr Machata 145054a92385SPetr Machata err = mlxsw_sp_trap_group_policer_hw_id_get(mlxsw_sp, 145154a92385SPetr Machata DEVLINK_TRAP_GROUP_GENERIC_ID_BUFFER_DROPS, 145254a92385SPetr Machata &agent_parms.policer_enable, 145354a92385SPetr Machata &agent_parms.policer_id); 145454a92385SPetr Machata if (err) 145554a92385SPetr Machata return err; 145654a92385SPetr Machata 145754a92385SPetr Machata return mlxsw_sp_qevent_span_configure(mlxsw_sp, mall_entry, qevent_binding, 145854a92385SPetr Machata &agent_parms, &mall_entry->trap.span_id); 145954a92385SPetr Machata } 146054a92385SPetr Machata 146154a92385SPetr Machata static void mlxsw_sp_qevent_trap_deconfigure(struct mlxsw_sp *mlxsw_sp, 146254a92385SPetr Machata struct mlxsw_sp_mall_entry *mall_entry, 146354a92385SPetr Machata struct mlxsw_sp_qevent_binding *qevent_binding) 146454a92385SPetr Machata { 146554a92385SPetr Machata mlxsw_sp_qevent_span_deconfigure(mlxsw_sp, qevent_binding, mall_entry->trap.span_id); 1466f6668eacSPetr Machata } 1467f6668eacSPetr Machata 1468f6668eacSPetr Machata static int mlxsw_sp_qevent_entry_configure(struct mlxsw_sp *mlxsw_sp, 1469f6668eacSPetr Machata struct mlxsw_sp_mall_entry *mall_entry, 1470f6668eacSPetr Machata struct mlxsw_sp_qevent_binding *qevent_binding) 1471f6668eacSPetr Machata { 1472f6668eacSPetr Machata switch (mall_entry->type) { 1473f6668eacSPetr Machata case MLXSW_SP_MALL_ACTION_TYPE_MIRROR: 1474f6668eacSPetr Machata return mlxsw_sp_qevent_mirror_configure(mlxsw_sp, mall_entry, qevent_binding); 147554a92385SPetr Machata case MLXSW_SP_MALL_ACTION_TYPE_TRAP: 147654a92385SPetr Machata return mlxsw_sp_qevent_trap_configure(mlxsw_sp, mall_entry, qevent_binding); 1477f6668eacSPetr Machata default: 1478f6668eacSPetr Machata /* This should have been validated away. */ 1479f6668eacSPetr Machata WARN_ON(1); 1480f6668eacSPetr Machata return -EOPNOTSUPP; 1481f6668eacSPetr Machata } 1482f6668eacSPetr Machata } 1483f6668eacSPetr Machata 1484f6668eacSPetr Machata static void mlxsw_sp_qevent_entry_deconfigure(struct mlxsw_sp *mlxsw_sp, 1485f6668eacSPetr Machata struct mlxsw_sp_mall_entry *mall_entry, 1486f6668eacSPetr Machata struct mlxsw_sp_qevent_binding *qevent_binding) 1487f6668eacSPetr Machata { 1488f6668eacSPetr Machata switch (mall_entry->type) { 1489f6668eacSPetr Machata case MLXSW_SP_MALL_ACTION_TYPE_MIRROR: 1490f6668eacSPetr Machata return mlxsw_sp_qevent_mirror_deconfigure(mlxsw_sp, mall_entry, qevent_binding); 149154a92385SPetr Machata case MLXSW_SP_MALL_ACTION_TYPE_TRAP: 149254a92385SPetr Machata return mlxsw_sp_qevent_trap_deconfigure(mlxsw_sp, mall_entry, qevent_binding); 1493f6668eacSPetr Machata default: 1494f6668eacSPetr Machata WARN_ON(1); 1495f6668eacSPetr Machata return; 1496f6668eacSPetr Machata } 1497f6668eacSPetr Machata } 1498f6668eacSPetr Machata 1499f6668eacSPetr Machata static int mlxsw_sp_qevent_binding_configure(struct mlxsw_sp_qevent_block *qevent_block, 1500f6668eacSPetr Machata struct mlxsw_sp_qevent_binding *qevent_binding) 1501f6668eacSPetr Machata { 1502f6668eacSPetr Machata struct mlxsw_sp_mall_entry *mall_entry; 1503f6668eacSPetr Machata int err; 1504f6668eacSPetr Machata 1505f6668eacSPetr Machata list_for_each_entry(mall_entry, &qevent_block->mall_entry_list, list) { 1506f6668eacSPetr Machata err = mlxsw_sp_qevent_entry_configure(qevent_block->mlxsw_sp, mall_entry, 1507f6668eacSPetr Machata qevent_binding); 1508f6668eacSPetr Machata if (err) 1509f6668eacSPetr Machata goto err_entry_configure; 1510f6668eacSPetr Machata } 1511f6668eacSPetr Machata 1512f6668eacSPetr Machata return 0; 1513f6668eacSPetr Machata 1514f6668eacSPetr Machata err_entry_configure: 1515f6668eacSPetr Machata list_for_each_entry_continue_reverse(mall_entry, &qevent_block->mall_entry_list, list) 1516f6668eacSPetr Machata mlxsw_sp_qevent_entry_deconfigure(qevent_block->mlxsw_sp, mall_entry, 1517f6668eacSPetr Machata qevent_binding); 1518f6668eacSPetr Machata return err; 1519f6668eacSPetr Machata } 1520f6668eacSPetr Machata 1521f6668eacSPetr Machata static void mlxsw_sp_qevent_binding_deconfigure(struct mlxsw_sp_qevent_block *qevent_block, 1522f6668eacSPetr Machata struct mlxsw_sp_qevent_binding *qevent_binding) 1523f6668eacSPetr Machata { 1524f6668eacSPetr Machata struct mlxsw_sp_mall_entry *mall_entry; 1525f6668eacSPetr Machata 1526f6668eacSPetr Machata list_for_each_entry(mall_entry, &qevent_block->mall_entry_list, list) 1527f6668eacSPetr Machata mlxsw_sp_qevent_entry_deconfigure(qevent_block->mlxsw_sp, mall_entry, 1528f6668eacSPetr Machata qevent_binding); 1529f6668eacSPetr Machata } 1530f6668eacSPetr Machata 1531f6668eacSPetr Machata static int mlxsw_sp_qevent_block_configure(struct mlxsw_sp_qevent_block *qevent_block) 1532f6668eacSPetr Machata { 1533f6668eacSPetr Machata struct mlxsw_sp_qevent_binding *qevent_binding; 1534f6668eacSPetr Machata int err; 1535f6668eacSPetr Machata 1536f6668eacSPetr Machata list_for_each_entry(qevent_binding, &qevent_block->binding_list, list) { 1537f6668eacSPetr Machata err = mlxsw_sp_qevent_binding_configure(qevent_block, qevent_binding); 1538f6668eacSPetr Machata if (err) 1539f6668eacSPetr Machata goto err_binding_configure; 1540f6668eacSPetr Machata } 1541f6668eacSPetr Machata 1542f6668eacSPetr Machata return 0; 1543f6668eacSPetr Machata 1544f6668eacSPetr Machata err_binding_configure: 1545f6668eacSPetr Machata list_for_each_entry_continue_reverse(qevent_binding, &qevent_block->binding_list, list) 1546f6668eacSPetr Machata mlxsw_sp_qevent_binding_deconfigure(qevent_block, qevent_binding); 1547f6668eacSPetr Machata return err; 1548f6668eacSPetr Machata } 1549f6668eacSPetr Machata 1550f6668eacSPetr Machata static void mlxsw_sp_qevent_block_deconfigure(struct mlxsw_sp_qevent_block *qevent_block) 1551f6668eacSPetr Machata { 1552f6668eacSPetr Machata struct mlxsw_sp_qevent_binding *qevent_binding; 1553f6668eacSPetr Machata 1554f6668eacSPetr Machata list_for_each_entry(qevent_binding, &qevent_block->binding_list, list) 1555f6668eacSPetr Machata mlxsw_sp_qevent_binding_deconfigure(qevent_block, qevent_binding); 1556f6668eacSPetr Machata } 1557f6668eacSPetr Machata 1558f6668eacSPetr Machata static struct mlxsw_sp_mall_entry * 1559f6668eacSPetr Machata mlxsw_sp_qevent_mall_entry_find(struct mlxsw_sp_qevent_block *block, unsigned long cookie) 1560f6668eacSPetr Machata { 1561f6668eacSPetr Machata struct mlxsw_sp_mall_entry *mall_entry; 1562f6668eacSPetr Machata 1563f6668eacSPetr Machata list_for_each_entry(mall_entry, &block->mall_entry_list, list) 1564f6668eacSPetr Machata if (mall_entry->cookie == cookie) 1565f6668eacSPetr Machata return mall_entry; 1566f6668eacSPetr Machata 1567f6668eacSPetr Machata return NULL; 1568f6668eacSPetr Machata } 1569f6668eacSPetr Machata 1570f6668eacSPetr Machata static int mlxsw_sp_qevent_mall_replace(struct mlxsw_sp *mlxsw_sp, 1571f6668eacSPetr Machata struct mlxsw_sp_qevent_block *qevent_block, 1572f6668eacSPetr Machata struct tc_cls_matchall_offload *f) 1573f6668eacSPetr Machata { 1574f6668eacSPetr Machata struct mlxsw_sp_mall_entry *mall_entry; 1575f6668eacSPetr Machata struct flow_action_entry *act; 1576f6668eacSPetr Machata int err; 1577f6668eacSPetr Machata 1578f6668eacSPetr Machata /* It should not currently be possible to replace a matchall rule. So 1579f6668eacSPetr Machata * this must be a new rule. 1580f6668eacSPetr Machata */ 1581f6668eacSPetr Machata if (!list_empty(&qevent_block->mall_entry_list)) { 1582f6668eacSPetr Machata NL_SET_ERR_MSG(f->common.extack, "At most one filter supported"); 1583f6668eacSPetr Machata return -EOPNOTSUPP; 1584f6668eacSPetr Machata } 1585f6668eacSPetr Machata if (f->rule->action.num_entries != 1) { 1586f6668eacSPetr Machata NL_SET_ERR_MSG(f->common.extack, "Only singular actions supported"); 1587f6668eacSPetr Machata return -EOPNOTSUPP; 1588f6668eacSPetr Machata } 1589f6668eacSPetr Machata if (f->common.chain_index) { 1590f6668eacSPetr Machata NL_SET_ERR_MSG(f->common.extack, "Only chain 0 is supported"); 1591f6668eacSPetr Machata return -EOPNOTSUPP; 1592f6668eacSPetr Machata } 1593f6668eacSPetr Machata if (f->common.protocol != htons(ETH_P_ALL)) { 1594f6668eacSPetr Machata NL_SET_ERR_MSG(f->common.extack, "Protocol matching not supported"); 1595f6668eacSPetr Machata return -EOPNOTSUPP; 1596f6668eacSPetr Machata } 1597f6668eacSPetr Machata 1598f6668eacSPetr Machata act = &f->rule->action.entries[0]; 1599f6668eacSPetr Machata if (!(act->hw_stats & FLOW_ACTION_HW_STATS_DISABLED)) { 1600f6668eacSPetr Machata NL_SET_ERR_MSG(f->common.extack, "HW counters not supported on qevents"); 1601f6668eacSPetr Machata return -EOPNOTSUPP; 1602f6668eacSPetr Machata } 1603f6668eacSPetr Machata 1604f6668eacSPetr Machata mall_entry = kzalloc(sizeof(*mall_entry), GFP_KERNEL); 1605f6668eacSPetr Machata if (!mall_entry) 1606f6668eacSPetr Machata return -ENOMEM; 1607f6668eacSPetr Machata mall_entry->cookie = f->cookie; 1608f6668eacSPetr Machata 1609f6668eacSPetr Machata if (act->id == FLOW_ACTION_MIRRED) { 1610f6668eacSPetr Machata mall_entry->type = MLXSW_SP_MALL_ACTION_TYPE_MIRROR; 1611f6668eacSPetr Machata mall_entry->mirror.to_dev = act->dev; 161254a92385SPetr Machata } else if (act->id == FLOW_ACTION_TRAP) { 161354a92385SPetr Machata mall_entry->type = MLXSW_SP_MALL_ACTION_TYPE_TRAP; 1614f6668eacSPetr Machata } else { 1615f6668eacSPetr Machata NL_SET_ERR_MSG(f->common.extack, "Unsupported action"); 1616f6668eacSPetr Machata err = -EOPNOTSUPP; 1617f6668eacSPetr Machata goto err_unsupported_action; 1618f6668eacSPetr Machata } 1619f6668eacSPetr Machata 1620f6668eacSPetr Machata list_add_tail(&mall_entry->list, &qevent_block->mall_entry_list); 1621f6668eacSPetr Machata 1622f6668eacSPetr Machata err = mlxsw_sp_qevent_block_configure(qevent_block); 1623f6668eacSPetr Machata if (err) 1624f6668eacSPetr Machata goto err_block_configure; 1625f6668eacSPetr Machata 1626f6668eacSPetr Machata return 0; 1627f6668eacSPetr Machata 1628f6668eacSPetr Machata err_block_configure: 1629f6668eacSPetr Machata list_del(&mall_entry->list); 1630f6668eacSPetr Machata err_unsupported_action: 1631f6668eacSPetr Machata kfree(mall_entry); 1632f6668eacSPetr Machata return err; 1633f6668eacSPetr Machata } 1634f6668eacSPetr Machata 1635f6668eacSPetr Machata static void mlxsw_sp_qevent_mall_destroy(struct mlxsw_sp_qevent_block *qevent_block, 1636f6668eacSPetr Machata struct tc_cls_matchall_offload *f) 1637f6668eacSPetr Machata { 1638f6668eacSPetr Machata struct mlxsw_sp_mall_entry *mall_entry; 1639f6668eacSPetr Machata 1640f6668eacSPetr Machata mall_entry = mlxsw_sp_qevent_mall_entry_find(qevent_block, f->cookie); 1641f6668eacSPetr Machata if (!mall_entry) 1642f6668eacSPetr Machata return; 1643f6668eacSPetr Machata 1644f6668eacSPetr Machata mlxsw_sp_qevent_block_deconfigure(qevent_block); 1645f6668eacSPetr Machata 1646f6668eacSPetr Machata list_del(&mall_entry->list); 1647f6668eacSPetr Machata kfree(mall_entry); 1648f6668eacSPetr Machata } 1649f6668eacSPetr Machata 1650f6668eacSPetr Machata static int mlxsw_sp_qevent_block_mall_cb(struct mlxsw_sp_qevent_block *qevent_block, 1651f6668eacSPetr Machata struct tc_cls_matchall_offload *f) 1652f6668eacSPetr Machata { 1653f6668eacSPetr Machata struct mlxsw_sp *mlxsw_sp = qevent_block->mlxsw_sp; 1654f6668eacSPetr Machata 1655f6668eacSPetr Machata switch (f->command) { 1656f6668eacSPetr Machata case TC_CLSMATCHALL_REPLACE: 1657f6668eacSPetr Machata return mlxsw_sp_qevent_mall_replace(mlxsw_sp, qevent_block, f); 1658f6668eacSPetr Machata case TC_CLSMATCHALL_DESTROY: 1659f6668eacSPetr Machata mlxsw_sp_qevent_mall_destroy(qevent_block, f); 1660f6668eacSPetr Machata return 0; 1661f6668eacSPetr Machata default: 1662f6668eacSPetr Machata return -EOPNOTSUPP; 1663f6668eacSPetr Machata } 1664f6668eacSPetr Machata } 1665f6668eacSPetr Machata 1666f6668eacSPetr Machata static int mlxsw_sp_qevent_block_cb(enum tc_setup_type type, void *type_data, void *cb_priv) 1667f6668eacSPetr Machata { 1668f6668eacSPetr Machata struct mlxsw_sp_qevent_block *qevent_block = cb_priv; 1669f6668eacSPetr Machata 1670f6668eacSPetr Machata switch (type) { 1671f6668eacSPetr Machata case TC_SETUP_CLSMATCHALL: 1672f6668eacSPetr Machata return mlxsw_sp_qevent_block_mall_cb(qevent_block, type_data); 1673f6668eacSPetr Machata default: 1674f6668eacSPetr Machata return -EOPNOTSUPP; 1675f6668eacSPetr Machata } 1676f6668eacSPetr Machata } 1677f6668eacSPetr Machata 1678f6668eacSPetr Machata static struct mlxsw_sp_qevent_block *mlxsw_sp_qevent_block_create(struct mlxsw_sp *mlxsw_sp, 1679f6668eacSPetr Machata struct net *net) 1680f6668eacSPetr Machata { 1681f6668eacSPetr Machata struct mlxsw_sp_qevent_block *qevent_block; 1682f6668eacSPetr Machata 1683f6668eacSPetr Machata qevent_block = kzalloc(sizeof(*qevent_block), GFP_KERNEL); 1684f6668eacSPetr Machata if (!qevent_block) 1685f6668eacSPetr Machata return NULL; 1686f6668eacSPetr Machata 1687f6668eacSPetr Machata INIT_LIST_HEAD(&qevent_block->binding_list); 1688f6668eacSPetr Machata INIT_LIST_HEAD(&qevent_block->mall_entry_list); 1689f6668eacSPetr Machata qevent_block->mlxsw_sp = mlxsw_sp; 1690f6668eacSPetr Machata return qevent_block; 1691f6668eacSPetr Machata } 1692f6668eacSPetr Machata 1693f6668eacSPetr Machata static void 1694f6668eacSPetr Machata mlxsw_sp_qevent_block_destroy(struct mlxsw_sp_qevent_block *qevent_block) 1695f6668eacSPetr Machata { 1696f6668eacSPetr Machata WARN_ON(!list_empty(&qevent_block->binding_list)); 1697f6668eacSPetr Machata WARN_ON(!list_empty(&qevent_block->mall_entry_list)); 1698f6668eacSPetr Machata kfree(qevent_block); 1699f6668eacSPetr Machata } 1700f6668eacSPetr Machata 1701f6668eacSPetr Machata static void mlxsw_sp_qevent_block_release(void *cb_priv) 1702f6668eacSPetr Machata { 1703f6668eacSPetr Machata struct mlxsw_sp_qevent_block *qevent_block = cb_priv; 1704f6668eacSPetr Machata 1705f6668eacSPetr Machata mlxsw_sp_qevent_block_destroy(qevent_block); 1706f6668eacSPetr Machata } 1707f6668eacSPetr Machata 1708f6668eacSPetr Machata static struct mlxsw_sp_qevent_binding * 1709f6668eacSPetr Machata mlxsw_sp_qevent_binding_create(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, int tclass_num, 1710f6668eacSPetr Machata enum mlxsw_sp_span_trigger span_trigger) 1711f6668eacSPetr Machata { 1712f6668eacSPetr Machata struct mlxsw_sp_qevent_binding *binding; 1713f6668eacSPetr Machata 1714f6668eacSPetr Machata binding = kzalloc(sizeof(*binding), GFP_KERNEL); 1715f6668eacSPetr Machata if (!binding) 1716f6668eacSPetr Machata return ERR_PTR(-ENOMEM); 1717f6668eacSPetr Machata 1718f6668eacSPetr Machata binding->mlxsw_sp_port = mlxsw_sp_port; 1719f6668eacSPetr Machata binding->handle = handle; 1720f6668eacSPetr Machata binding->tclass_num = tclass_num; 1721f6668eacSPetr Machata binding->span_trigger = span_trigger; 1722f6668eacSPetr Machata return binding; 1723f6668eacSPetr Machata } 1724f6668eacSPetr Machata 1725f6668eacSPetr Machata static void 1726f6668eacSPetr Machata mlxsw_sp_qevent_binding_destroy(struct mlxsw_sp_qevent_binding *binding) 1727f6668eacSPetr Machata { 1728f6668eacSPetr Machata kfree(binding); 1729f6668eacSPetr Machata } 1730f6668eacSPetr Machata 1731f6668eacSPetr Machata static struct mlxsw_sp_qevent_binding * 1732f6668eacSPetr Machata mlxsw_sp_qevent_binding_lookup(struct mlxsw_sp_qevent_block *block, 1733f6668eacSPetr Machata struct mlxsw_sp_port *mlxsw_sp_port, 1734f6668eacSPetr Machata u32 handle, 1735f6668eacSPetr Machata enum mlxsw_sp_span_trigger span_trigger) 1736f6668eacSPetr Machata { 1737f6668eacSPetr Machata struct mlxsw_sp_qevent_binding *qevent_binding; 1738f6668eacSPetr Machata 1739f6668eacSPetr Machata list_for_each_entry(qevent_binding, &block->binding_list, list) 1740f6668eacSPetr Machata if (qevent_binding->mlxsw_sp_port == mlxsw_sp_port && 1741f6668eacSPetr Machata qevent_binding->handle == handle && 1742f6668eacSPetr Machata qevent_binding->span_trigger == span_trigger) 1743f6668eacSPetr Machata return qevent_binding; 1744f6668eacSPetr Machata return NULL; 1745f6668eacSPetr Machata } 1746f6668eacSPetr Machata 1747f6668eacSPetr Machata static int mlxsw_sp_setup_tc_block_qevent_bind(struct mlxsw_sp_port *mlxsw_sp_port, 1748f6668eacSPetr Machata struct flow_block_offload *f, 1749f6668eacSPetr Machata enum mlxsw_sp_span_trigger span_trigger) 1750f6668eacSPetr Machata { 1751f6668eacSPetr Machata struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 1752f6668eacSPetr Machata struct mlxsw_sp_qevent_binding *qevent_binding; 1753f6668eacSPetr Machata struct mlxsw_sp_qevent_block *qevent_block; 1754f6668eacSPetr Machata struct flow_block_cb *block_cb; 1755f6668eacSPetr Machata struct mlxsw_sp_qdisc *qdisc; 1756f6668eacSPetr Machata bool register_block = false; 1757f6668eacSPetr Machata int err; 1758f6668eacSPetr Machata 1759f6668eacSPetr Machata block_cb = flow_block_cb_lookup(f->block, mlxsw_sp_qevent_block_cb, mlxsw_sp); 1760f6668eacSPetr Machata if (!block_cb) { 1761f6668eacSPetr Machata qevent_block = mlxsw_sp_qevent_block_create(mlxsw_sp, f->net); 1762f6668eacSPetr Machata if (!qevent_block) 1763f6668eacSPetr Machata return -ENOMEM; 1764f6668eacSPetr Machata block_cb = flow_block_cb_alloc(mlxsw_sp_qevent_block_cb, mlxsw_sp, qevent_block, 1765f6668eacSPetr Machata mlxsw_sp_qevent_block_release); 1766f6668eacSPetr Machata if (IS_ERR(block_cb)) { 1767f6668eacSPetr Machata mlxsw_sp_qevent_block_destroy(qevent_block); 1768f6668eacSPetr Machata return PTR_ERR(block_cb); 1769f6668eacSPetr Machata } 1770f6668eacSPetr Machata register_block = true; 1771f6668eacSPetr Machata } else { 1772f6668eacSPetr Machata qevent_block = flow_block_cb_priv(block_cb); 1773f6668eacSPetr Machata } 1774f6668eacSPetr Machata flow_block_cb_incref(block_cb); 1775f6668eacSPetr Machata 1776f6668eacSPetr Machata qdisc = mlxsw_sp_qdisc_find_by_handle(mlxsw_sp_port, f->sch->handle); 1777f6668eacSPetr Machata if (!qdisc) { 1778f6668eacSPetr Machata NL_SET_ERR_MSG(f->extack, "Qdisc not offloaded"); 1779f6668eacSPetr Machata err = -ENOENT; 1780f6668eacSPetr Machata goto err_find_qdisc; 1781f6668eacSPetr Machata } 1782f6668eacSPetr Machata 1783f6668eacSPetr Machata if (WARN_ON(mlxsw_sp_qevent_binding_lookup(qevent_block, mlxsw_sp_port, f->sch->handle, 1784f6668eacSPetr Machata span_trigger))) { 1785f6668eacSPetr Machata err = -EEXIST; 1786f6668eacSPetr Machata goto err_binding_exists; 1787f6668eacSPetr Machata } 1788f6668eacSPetr Machata 1789f6668eacSPetr Machata qevent_binding = mlxsw_sp_qevent_binding_create(mlxsw_sp_port, f->sch->handle, 1790f6668eacSPetr Machata qdisc->tclass_num, span_trigger); 1791f6668eacSPetr Machata if (IS_ERR(qevent_binding)) { 1792f6668eacSPetr Machata err = PTR_ERR(qevent_binding); 1793f6668eacSPetr Machata goto err_binding_create; 1794f6668eacSPetr Machata } 1795f6668eacSPetr Machata 1796f6668eacSPetr Machata err = mlxsw_sp_qevent_binding_configure(qevent_block, qevent_binding); 1797f6668eacSPetr Machata if (err) 1798f6668eacSPetr Machata goto err_binding_configure; 1799f6668eacSPetr Machata 1800f6668eacSPetr Machata list_add(&qevent_binding->list, &qevent_block->binding_list); 1801f6668eacSPetr Machata 1802f6668eacSPetr Machata if (register_block) { 1803f6668eacSPetr Machata flow_block_cb_add(block_cb, f); 1804f6668eacSPetr Machata list_add_tail(&block_cb->driver_list, &mlxsw_sp_qevent_block_cb_list); 1805f6668eacSPetr Machata } 1806f6668eacSPetr Machata 1807f6668eacSPetr Machata return 0; 1808f6668eacSPetr Machata 1809f6668eacSPetr Machata err_binding_configure: 1810f6668eacSPetr Machata mlxsw_sp_qevent_binding_destroy(qevent_binding); 1811f6668eacSPetr Machata err_binding_create: 1812f6668eacSPetr Machata err_binding_exists: 1813f6668eacSPetr Machata err_find_qdisc: 1814f6668eacSPetr Machata if (!flow_block_cb_decref(block_cb)) 1815f6668eacSPetr Machata flow_block_cb_free(block_cb); 1816f6668eacSPetr Machata return err; 1817f6668eacSPetr Machata } 1818f6668eacSPetr Machata 1819f6668eacSPetr Machata static void mlxsw_sp_setup_tc_block_qevent_unbind(struct mlxsw_sp_port *mlxsw_sp_port, 1820f6668eacSPetr Machata struct flow_block_offload *f, 1821f6668eacSPetr Machata enum mlxsw_sp_span_trigger span_trigger) 1822f6668eacSPetr Machata { 1823f6668eacSPetr Machata struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 1824f6668eacSPetr Machata struct mlxsw_sp_qevent_binding *qevent_binding; 1825f6668eacSPetr Machata struct mlxsw_sp_qevent_block *qevent_block; 1826f6668eacSPetr Machata struct flow_block_cb *block_cb; 1827f6668eacSPetr Machata 1828f6668eacSPetr Machata block_cb = flow_block_cb_lookup(f->block, mlxsw_sp_qevent_block_cb, mlxsw_sp); 1829f6668eacSPetr Machata if (!block_cb) 1830f6668eacSPetr Machata return; 1831f6668eacSPetr Machata qevent_block = flow_block_cb_priv(block_cb); 1832f6668eacSPetr Machata 1833f6668eacSPetr Machata qevent_binding = mlxsw_sp_qevent_binding_lookup(qevent_block, mlxsw_sp_port, f->sch->handle, 1834f6668eacSPetr Machata span_trigger); 1835f6668eacSPetr Machata if (!qevent_binding) 1836f6668eacSPetr Machata return; 1837f6668eacSPetr Machata 1838f6668eacSPetr Machata list_del(&qevent_binding->list); 1839f6668eacSPetr Machata mlxsw_sp_qevent_binding_deconfigure(qevent_block, qevent_binding); 1840f6668eacSPetr Machata mlxsw_sp_qevent_binding_destroy(qevent_binding); 1841f6668eacSPetr Machata 1842f6668eacSPetr Machata if (!flow_block_cb_decref(block_cb)) { 1843f6668eacSPetr Machata flow_block_cb_remove(block_cb, f); 1844f6668eacSPetr Machata list_del(&block_cb->driver_list); 1845f6668eacSPetr Machata } 1846f6668eacSPetr Machata } 1847f6668eacSPetr Machata 1848f6668eacSPetr Machata static int mlxsw_sp_setup_tc_block_qevent(struct mlxsw_sp_port *mlxsw_sp_port, 1849f6668eacSPetr Machata struct flow_block_offload *f, 1850f6668eacSPetr Machata enum mlxsw_sp_span_trigger span_trigger) 1851f6668eacSPetr Machata { 1852f6668eacSPetr Machata f->driver_block_list = &mlxsw_sp_qevent_block_cb_list; 1853f6668eacSPetr Machata 1854f6668eacSPetr Machata switch (f->command) { 1855f6668eacSPetr Machata case FLOW_BLOCK_BIND: 1856f6668eacSPetr Machata return mlxsw_sp_setup_tc_block_qevent_bind(mlxsw_sp_port, f, span_trigger); 1857f6668eacSPetr Machata case FLOW_BLOCK_UNBIND: 1858f6668eacSPetr Machata mlxsw_sp_setup_tc_block_qevent_unbind(mlxsw_sp_port, f, span_trigger); 1859f6668eacSPetr Machata return 0; 1860f6668eacSPetr Machata default: 1861f6668eacSPetr Machata return -EOPNOTSUPP; 1862f6668eacSPetr Machata } 1863f6668eacSPetr Machata } 1864f6668eacSPetr Machata 1865f6668eacSPetr Machata int mlxsw_sp_setup_tc_block_qevent_early_drop(struct mlxsw_sp_port *mlxsw_sp_port, 1866f6668eacSPetr Machata struct flow_block_offload *f) 1867f6668eacSPetr Machata { 1868f6668eacSPetr Machata return mlxsw_sp_setup_tc_block_qevent(mlxsw_sp_port, f, MLXSW_SP_SPAN_TRIGGER_EARLY_DROP); 1869f6668eacSPetr Machata } 1870f6668eacSPetr Machata 1871371b437aSNogah Frankel int mlxsw_sp_tc_qdisc_init(struct mlxsw_sp_port *mlxsw_sp_port) 1872371b437aSNogah Frankel { 1873ee88450dSPetr Machata struct mlxsw_sp_qdisc_state *qdisc_state; 1874eed4baebSNogah Frankel int i; 1875371b437aSNogah Frankel 1876ee88450dSPetr Machata qdisc_state = kzalloc(sizeof(*qdisc_state), GFP_KERNEL); 1877ee88450dSPetr Machata if (!qdisc_state) 1878eed4baebSNogah Frankel return -ENOMEM; 1879ee88450dSPetr Machata 1880ee88450dSPetr Machata qdisc_state->root_qdisc.prio_bitmap = 0xff; 1881ee88450dSPetr Machata qdisc_state->root_qdisc.tclass_num = MLXSW_SP_PORT_DEFAULT_TCLASS; 1882*51d52ed9SPetr Machata qdisc_state->root_qdisc.qdiscs = qdisc_state->tclass_qdiscs; 1883*51d52ed9SPetr Machata qdisc_state->root_qdisc.num_classes = IEEE_8021QAZ_MAX_TCS; 1884b21832b5SPetr Machata for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { 1885*51d52ed9SPetr Machata int tclass_num = MLXSW_SP_PRIO_BAND_TO_TCLASS(i); 1886*51d52ed9SPetr Machata 1887*51d52ed9SPetr Machata qdisc_state->tclass_qdiscs[i].tclass_num = tclass_num; 1888b21832b5SPetr Machata qdisc_state->tclass_qdiscs[i].parent = &qdisc_state->root_qdisc; 1889b21832b5SPetr Machata } 1890ee88450dSPetr Machata 1891ee88450dSPetr Machata mlxsw_sp_port->qdisc = qdisc_state; 1892ee88450dSPetr Machata return 0; 1893371b437aSNogah Frankel } 1894371b437aSNogah Frankel 1895371b437aSNogah Frankel void mlxsw_sp_tc_qdisc_fini(struct mlxsw_sp_port *mlxsw_sp_port) 1896371b437aSNogah Frankel { 1897ee88450dSPetr Machata kfree(mlxsw_sp_port->qdisc); 1898371b437aSNogah Frankel } 1899