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" 1196f17e07SNogah Frankel #include "reg.h" 1296f17e07SNogah Frankel 1346a3615bSNogah Frankel #define MLXSW_SP_PRIO_BAND_TO_TCLASS(band) (IEEE_8021QAZ_MAX_TCS - band - 1) 14eed4baebSNogah Frankel #define MLXSW_SP_PRIO_CHILD_TO_TCLASS(child) \ 15eed4baebSNogah Frankel MLXSW_SP_PRIO_BAND_TO_TCLASS((child - 1)) 1646a3615bSNogah Frankel 17371b437aSNogah Frankel enum mlxsw_sp_qdisc_type { 18371b437aSNogah Frankel MLXSW_SP_QDISC_NO_QDISC, 19371b437aSNogah Frankel MLXSW_SP_QDISC_RED, 2046a3615bSNogah Frankel MLXSW_SP_QDISC_PRIO, 21371b437aSNogah Frankel }; 22371b437aSNogah Frankel 23562ffbc4SNogah Frankel struct mlxsw_sp_qdisc_ops { 249cf6c9c7SNogah Frankel enum mlxsw_sp_qdisc_type type; 259cf6c9c7SNogah Frankel int (*check_params)(struct mlxsw_sp_port *mlxsw_sp_port, 269cf6c9c7SNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 279cf6c9c7SNogah Frankel void *params); 289cf6c9c7SNogah Frankel int (*replace)(struct mlxsw_sp_port *mlxsw_sp_port, 299cf6c9c7SNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, void *params); 309a37a59fSNogah Frankel int (*destroy)(struct mlxsw_sp_port *mlxsw_sp_port, 319a37a59fSNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc); 32562ffbc4SNogah Frankel int (*get_stats)(struct mlxsw_sp_port *mlxsw_sp_port, 33562ffbc4SNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 34562ffbc4SNogah Frankel struct tc_qopt_offload_stats *stats_ptr); 35562ffbc4SNogah Frankel int (*get_xstats)(struct mlxsw_sp_port *mlxsw_sp_port, 36562ffbc4SNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 37562ffbc4SNogah Frankel void *xstats_ptr); 389cf6c9c7SNogah Frankel void (*clean_stats)(struct mlxsw_sp_port *mlxsw_sp_port, 399cf6c9c7SNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc); 4093d8a4c1SNogah Frankel /* unoffload - to be used for a qdisc that stops being offloaded without 4193d8a4c1SNogah Frankel * being destroyed. 4293d8a4c1SNogah Frankel */ 4393d8a4c1SNogah Frankel void (*unoffload)(struct mlxsw_sp_port *mlxsw_sp_port, 4493d8a4c1SNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, void *params); 45562ffbc4SNogah Frankel }; 46562ffbc4SNogah Frankel 47371b437aSNogah Frankel struct mlxsw_sp_qdisc { 48371b437aSNogah Frankel u32 handle; 49d56c8955SNogah Frankel u8 tclass_num; 501631ab2eSNogah Frankel u8 prio_bitmap; 51371b437aSNogah Frankel union { 524d1a4b84SNogah Frankel struct red_stats red; 534d1a4b84SNogah Frankel } xstats_base; 544d1a4b84SNogah Frankel struct mlxsw_sp_qdisc_stats { 55371b437aSNogah Frankel u64 tx_bytes; 56371b437aSNogah Frankel u64 tx_packets; 57371b437aSNogah Frankel u64 drops; 58371b437aSNogah Frankel u64 overlimits; 5993d8a4c1SNogah Frankel u64 backlog; 604d1a4b84SNogah Frankel } stats_base; 61562ffbc4SNogah Frankel 62562ffbc4SNogah Frankel struct mlxsw_sp_qdisc_ops *ops; 63371b437aSNogah Frankel }; 64371b437aSNogah Frankel 65cba7158fSNogah Frankel static bool 66cba7158fSNogah Frankel mlxsw_sp_qdisc_compare(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, u32 handle, 67cba7158fSNogah Frankel enum mlxsw_sp_qdisc_type type) 68cba7158fSNogah Frankel { 699cf6c9c7SNogah Frankel return mlxsw_sp_qdisc && mlxsw_sp_qdisc->ops && 709cf6c9c7SNogah Frankel mlxsw_sp_qdisc->ops->type == type && 719cf6c9c7SNogah Frankel mlxsw_sp_qdisc->handle == handle; 72cba7158fSNogah Frankel } 73cba7158fSNogah Frankel 74eed4baebSNogah Frankel static struct mlxsw_sp_qdisc * 75eed4baebSNogah Frankel mlxsw_sp_qdisc_find(struct mlxsw_sp_port *mlxsw_sp_port, u32 parent, 76eed4baebSNogah Frankel bool root_only) 77eed4baebSNogah Frankel { 78eed4baebSNogah Frankel int tclass, child_index; 79eed4baebSNogah Frankel 80eed4baebSNogah Frankel if (parent == TC_H_ROOT) 81eed4baebSNogah Frankel return mlxsw_sp_port->root_qdisc; 82eed4baebSNogah Frankel 83eed4baebSNogah Frankel if (root_only || !mlxsw_sp_port->root_qdisc || 84eed4baebSNogah Frankel !mlxsw_sp_port->root_qdisc->ops || 85eed4baebSNogah Frankel TC_H_MAJ(parent) != mlxsw_sp_port->root_qdisc->handle || 86eed4baebSNogah Frankel TC_H_MIN(parent) > IEEE_8021QAZ_MAX_TCS) 87eed4baebSNogah Frankel return NULL; 88eed4baebSNogah Frankel 89eed4baebSNogah Frankel child_index = TC_H_MIN(parent); 90eed4baebSNogah Frankel tclass = MLXSW_SP_PRIO_CHILD_TO_TCLASS(child_index); 91eed4baebSNogah Frankel return &mlxsw_sp_port->tclass_qdiscs[tclass]; 92eed4baebSNogah Frankel } 93eed4baebSNogah Frankel 9432dc5efcSNogah Frankel static struct mlxsw_sp_qdisc * 9532dc5efcSNogah Frankel mlxsw_sp_qdisc_find_by_handle(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle) 9632dc5efcSNogah Frankel { 9732dc5efcSNogah Frankel int i; 9832dc5efcSNogah Frankel 9932dc5efcSNogah Frankel if (mlxsw_sp_port->root_qdisc->handle == handle) 10032dc5efcSNogah Frankel return mlxsw_sp_port->root_qdisc; 10132dc5efcSNogah Frankel 10232dc5efcSNogah Frankel if (mlxsw_sp_port->root_qdisc->handle == TC_H_UNSPEC) 10332dc5efcSNogah Frankel return NULL; 10432dc5efcSNogah Frankel 10532dc5efcSNogah Frankel for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) 10632dc5efcSNogah Frankel if (mlxsw_sp_port->tclass_qdiscs[i].handle == handle) 10732dc5efcSNogah Frankel return &mlxsw_sp_port->tclass_qdiscs[i]; 10832dc5efcSNogah Frankel 10932dc5efcSNogah Frankel return NULL; 11032dc5efcSNogah Frankel } 11132dc5efcSNogah Frankel 11296f17e07SNogah Frankel static int 1139a37a59fSNogah Frankel mlxsw_sp_qdisc_destroy(struct mlxsw_sp_port *mlxsw_sp_port, 1149a37a59fSNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 1159a37a59fSNogah Frankel { 1169a37a59fSNogah Frankel int err = 0; 1179a37a59fSNogah Frankel 1189a37a59fSNogah Frankel if (!mlxsw_sp_qdisc) 1199a37a59fSNogah Frankel return 0; 1209a37a59fSNogah Frankel 1219a37a59fSNogah Frankel if (mlxsw_sp_qdisc->ops && mlxsw_sp_qdisc->ops->destroy) 1229a37a59fSNogah Frankel err = mlxsw_sp_qdisc->ops->destroy(mlxsw_sp_port, 1239a37a59fSNogah Frankel mlxsw_sp_qdisc); 1249a37a59fSNogah Frankel 1259a37a59fSNogah Frankel mlxsw_sp_qdisc->handle = TC_H_UNSPEC; 1269a37a59fSNogah Frankel mlxsw_sp_qdisc->ops = NULL; 1279a37a59fSNogah Frankel return err; 1289a37a59fSNogah Frankel } 1299a37a59fSNogah Frankel 1309a37a59fSNogah Frankel static int 1319cf6c9c7SNogah Frankel mlxsw_sp_qdisc_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, 1329cf6c9c7SNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 1339cf6c9c7SNogah Frankel struct mlxsw_sp_qdisc_ops *ops, void *params) 1349cf6c9c7SNogah Frankel { 1359cf6c9c7SNogah Frankel int err; 1369cf6c9c7SNogah Frankel 13756202ca4SNogah Frankel if (mlxsw_sp_qdisc->ops && mlxsw_sp_qdisc->ops->type != ops->type) 13856202ca4SNogah Frankel /* In case this location contained a different qdisc of the 13956202ca4SNogah Frankel * same type we can override the old qdisc configuration. 14056202ca4SNogah Frankel * Otherwise, we need to remove the old qdisc before setting the 14156202ca4SNogah Frankel * new one. 14256202ca4SNogah Frankel */ 14356202ca4SNogah Frankel mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc); 1449cf6c9c7SNogah Frankel err = ops->check_params(mlxsw_sp_port, mlxsw_sp_qdisc, params); 1459cf6c9c7SNogah Frankel if (err) 1469cf6c9c7SNogah Frankel goto err_bad_param; 1479cf6c9c7SNogah Frankel 1489cf6c9c7SNogah Frankel err = ops->replace(mlxsw_sp_port, mlxsw_sp_qdisc, params); 1499cf6c9c7SNogah Frankel if (err) 1509cf6c9c7SNogah Frankel goto err_config; 1519cf6c9c7SNogah Frankel 1529cf6c9c7SNogah Frankel if (mlxsw_sp_qdisc->handle != handle) { 1539cf6c9c7SNogah Frankel mlxsw_sp_qdisc->ops = ops; 1549cf6c9c7SNogah Frankel if (ops->clean_stats) 1559cf6c9c7SNogah Frankel ops->clean_stats(mlxsw_sp_port, mlxsw_sp_qdisc); 1569cf6c9c7SNogah Frankel } 1579cf6c9c7SNogah Frankel 1589cf6c9c7SNogah Frankel mlxsw_sp_qdisc->handle = handle; 1599cf6c9c7SNogah Frankel return 0; 1609cf6c9c7SNogah Frankel 1619cf6c9c7SNogah Frankel err_bad_param: 1629cf6c9c7SNogah Frankel err_config: 16393d8a4c1SNogah Frankel if (mlxsw_sp_qdisc->handle == handle && ops->unoffload) 16493d8a4c1SNogah Frankel ops->unoffload(mlxsw_sp_port, mlxsw_sp_qdisc, params); 16593d8a4c1SNogah Frankel 1669cf6c9c7SNogah Frankel mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc); 1679cf6c9c7SNogah Frankel return err; 1689cf6c9c7SNogah Frankel } 1699cf6c9c7SNogah Frankel 1709cf6c9c7SNogah Frankel static int 171562ffbc4SNogah Frankel mlxsw_sp_qdisc_get_stats(struct mlxsw_sp_port *mlxsw_sp_port, 172562ffbc4SNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 173562ffbc4SNogah Frankel struct tc_qopt_offload_stats *stats_ptr) 174562ffbc4SNogah Frankel { 175562ffbc4SNogah Frankel if (mlxsw_sp_qdisc && mlxsw_sp_qdisc->ops && 176562ffbc4SNogah Frankel mlxsw_sp_qdisc->ops->get_stats) 177562ffbc4SNogah Frankel return mlxsw_sp_qdisc->ops->get_stats(mlxsw_sp_port, 178562ffbc4SNogah Frankel mlxsw_sp_qdisc, 179562ffbc4SNogah Frankel stats_ptr); 180562ffbc4SNogah Frankel 181562ffbc4SNogah Frankel return -EOPNOTSUPP; 182562ffbc4SNogah Frankel } 183562ffbc4SNogah Frankel 184562ffbc4SNogah Frankel static int 185562ffbc4SNogah Frankel mlxsw_sp_qdisc_get_xstats(struct mlxsw_sp_port *mlxsw_sp_port, 186562ffbc4SNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 187562ffbc4SNogah Frankel void *xstats_ptr) 188562ffbc4SNogah Frankel { 189562ffbc4SNogah Frankel if (mlxsw_sp_qdisc && mlxsw_sp_qdisc->ops && 190562ffbc4SNogah Frankel mlxsw_sp_qdisc->ops->get_xstats) 191562ffbc4SNogah Frankel return mlxsw_sp_qdisc->ops->get_xstats(mlxsw_sp_port, 192562ffbc4SNogah Frankel mlxsw_sp_qdisc, 193562ffbc4SNogah Frankel xstats_ptr); 194562ffbc4SNogah Frankel 195562ffbc4SNogah Frankel return -EOPNOTSUPP; 196562ffbc4SNogah Frankel } 197562ffbc4SNogah Frankel 19804cc0bf5SNogah Frankel static void 19904cc0bf5SNogah Frankel mlxsw_sp_qdisc_bstats_per_priority_get(struct mlxsw_sp_port_xstats *xstats, 20004cc0bf5SNogah Frankel u8 prio_bitmap, u64 *tx_packets, 20104cc0bf5SNogah Frankel u64 *tx_bytes) 20204cc0bf5SNogah Frankel { 20304cc0bf5SNogah Frankel int i; 20404cc0bf5SNogah Frankel 20504cc0bf5SNogah Frankel *tx_packets = 0; 20604cc0bf5SNogah Frankel *tx_bytes = 0; 20704cc0bf5SNogah Frankel for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { 20804cc0bf5SNogah Frankel if (prio_bitmap & BIT(i)) { 20904cc0bf5SNogah Frankel *tx_packets += xstats->tx_packets[i]; 21004cc0bf5SNogah Frankel *tx_bytes += xstats->tx_bytes[i]; 21104cc0bf5SNogah Frankel } 21204cc0bf5SNogah Frankel } 21304cc0bf5SNogah Frankel } 21404cc0bf5SNogah Frankel 215562ffbc4SNogah Frankel static int 21696f17e07SNogah Frankel mlxsw_sp_tclass_congestion_enable(struct mlxsw_sp_port *mlxsw_sp_port, 21796f17e07SNogah Frankel int tclass_num, u32 min, u32 max, 21896f17e07SNogah Frankel u32 probability, bool is_ecn) 21996f17e07SNogah Frankel { 220db84924cSJiri Pirko char cwtpm_cmd[MLXSW_REG_CWTPM_LEN]; 221db84924cSJiri Pirko char cwtp_cmd[MLXSW_REG_CWTP_LEN]; 22296f17e07SNogah Frankel struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 22396f17e07SNogah Frankel int err; 22496f17e07SNogah Frankel 22596f17e07SNogah Frankel mlxsw_reg_cwtp_pack(cwtp_cmd, mlxsw_sp_port->local_port, tclass_num); 22696f17e07SNogah Frankel mlxsw_reg_cwtp_profile_pack(cwtp_cmd, MLXSW_REG_CWTP_DEFAULT_PROFILE, 22796f17e07SNogah Frankel roundup(min, MLXSW_REG_CWTP_MIN_VALUE), 22896f17e07SNogah Frankel roundup(max, MLXSW_REG_CWTP_MIN_VALUE), 22996f17e07SNogah Frankel probability); 23096f17e07SNogah Frankel 23196f17e07SNogah Frankel err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(cwtp), cwtp_cmd); 23296f17e07SNogah Frankel if (err) 23396f17e07SNogah Frankel return err; 23496f17e07SNogah Frankel 235db84924cSJiri Pirko mlxsw_reg_cwtpm_pack(cwtpm_cmd, mlxsw_sp_port->local_port, tclass_num, 23696f17e07SNogah Frankel MLXSW_REG_CWTP_DEFAULT_PROFILE, true, is_ecn); 23796f17e07SNogah Frankel 238db84924cSJiri Pirko return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(cwtpm), cwtpm_cmd); 23996f17e07SNogah Frankel } 24096f17e07SNogah Frankel 24196f17e07SNogah Frankel static int 24296f17e07SNogah Frankel mlxsw_sp_tclass_congestion_disable(struct mlxsw_sp_port *mlxsw_sp_port, 24396f17e07SNogah Frankel int tclass_num) 24496f17e07SNogah Frankel { 24596f17e07SNogah Frankel struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 24696f17e07SNogah Frankel char cwtpm_cmd[MLXSW_REG_CWTPM_LEN]; 24796f17e07SNogah Frankel 24896f17e07SNogah Frankel mlxsw_reg_cwtpm_pack(cwtpm_cmd, mlxsw_sp_port->local_port, tclass_num, 24996f17e07SNogah Frankel MLXSW_REG_CWTPM_RESET_PROFILE, false, false); 25096f17e07SNogah Frankel return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(cwtpm), cwtpm_cmd); 25196f17e07SNogah Frankel } 25296f17e07SNogah Frankel 253861fb829SNogah Frankel static void 254c2ed6db7SNogah Frankel mlxsw_sp_setup_tc_qdisc_red_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port, 255d56c8955SNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 256861fb829SNogah Frankel { 257d56c8955SNogah Frankel u8 tclass_num = mlxsw_sp_qdisc->tclass_num; 2584d1a4b84SNogah Frankel struct mlxsw_sp_qdisc_stats *stats_base; 259861fb829SNogah Frankel struct mlxsw_sp_port_xstats *xstats; 2604d1a4b84SNogah Frankel struct red_stats *red_base; 261861fb829SNogah Frankel 262861fb829SNogah Frankel xstats = &mlxsw_sp_port->periodic_hw_stats.xstats; 2634d1a4b84SNogah Frankel stats_base = &mlxsw_sp_qdisc->stats_base; 264c2ed6db7SNogah Frankel red_base = &mlxsw_sp_qdisc->xstats_base.red; 2653670756fSNogah Frankel 26604cc0bf5SNogah Frankel mlxsw_sp_qdisc_bstats_per_priority_get(xstats, 26704cc0bf5SNogah Frankel mlxsw_sp_qdisc->prio_bitmap, 26804cc0bf5SNogah Frankel &stats_base->tx_packets, 26904cc0bf5SNogah Frankel &stats_base->tx_bytes); 2704d1a4b84SNogah Frankel red_base->prob_mark = xstats->ecn; 2714d1a4b84SNogah Frankel red_base->prob_drop = xstats->wred_drop[tclass_num]; 2724d1a4b84SNogah Frankel red_base->pdrop = xstats->tail_drop[tclass_num]; 2733670756fSNogah Frankel 274c2ed6db7SNogah Frankel stats_base->overlimits = red_base->prob_drop + red_base->prob_mark; 2754d1a4b84SNogah Frankel stats_base->drops = red_base->prob_drop + red_base->pdrop; 276416ef9b1SJakub Kicinski 277416ef9b1SJakub Kicinski stats_base->backlog = 0; 278861fb829SNogah Frankel } 279861fb829SNogah Frankel 28096f17e07SNogah Frankel static int 281cba7158fSNogah Frankel mlxsw_sp_qdisc_red_destroy(struct mlxsw_sp_port *mlxsw_sp_port, 282d56c8955SNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 28396f17e07SNogah Frankel { 284cc6e5c13SNogah Frankel struct mlxsw_sp_qdisc *root_qdisc = mlxsw_sp_port->root_qdisc; 285cc6e5c13SNogah Frankel 286cc6e5c13SNogah Frankel if (root_qdisc != mlxsw_sp_qdisc) 287cc6e5c13SNogah Frankel root_qdisc->stats_base.backlog -= 288cc6e5c13SNogah Frankel mlxsw_sp_qdisc->stats_base.backlog; 289cc6e5c13SNogah Frankel 2909a37a59fSNogah Frankel return mlxsw_sp_tclass_congestion_disable(mlxsw_sp_port, 291d56c8955SNogah Frankel mlxsw_sp_qdisc->tclass_num); 29296f17e07SNogah Frankel } 29396f17e07SNogah Frankel 29496f17e07SNogah Frankel static int 2959cf6c9c7SNogah Frankel mlxsw_sp_qdisc_red_check_params(struct mlxsw_sp_port *mlxsw_sp_port, 29696f17e07SNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 2979cf6c9c7SNogah Frankel void *params) 29896f17e07SNogah Frankel { 29996f17e07SNogah Frankel struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 3009cf6c9c7SNogah Frankel struct tc_red_qopt_offload_params *p = params; 30196f17e07SNogah Frankel 30296f17e07SNogah Frankel if (p->min > p->max) { 30396f17e07SNogah Frankel dev_err(mlxsw_sp->bus_info->dev, 30496f17e07SNogah Frankel "spectrum: RED: min %u is bigger then max %u\n", p->min, 30596f17e07SNogah Frankel p->max); 3069cf6c9c7SNogah Frankel return -EINVAL; 30796f17e07SNogah Frankel } 308914c4fc1SPetr Machata if (p->max > MLXSW_CORE_RES_GET(mlxsw_sp->core, 309914c4fc1SPetr Machata GUARANTEED_SHARED_BUFFER)) { 31096f17e07SNogah Frankel dev_err(mlxsw_sp->bus_info->dev, 31196f17e07SNogah Frankel "spectrum: RED: max value %u is too big\n", p->max); 3129cf6c9c7SNogah Frankel return -EINVAL; 31396f17e07SNogah Frankel } 31496f17e07SNogah Frankel if (p->min == 0 || p->max == 0) { 31596f17e07SNogah Frankel dev_err(mlxsw_sp->bus_info->dev, 31696f17e07SNogah Frankel "spectrum: RED: 0 value is illegal for min and max\n"); 3179cf6c9c7SNogah Frankel return -EINVAL; 31896f17e07SNogah Frankel } 3199cf6c9c7SNogah Frankel return 0; 3209cf6c9c7SNogah Frankel } 3219cf6c9c7SNogah Frankel 3229cf6c9c7SNogah Frankel static int 3239cf6c9c7SNogah Frankel mlxsw_sp_qdisc_red_replace(struct mlxsw_sp_port *mlxsw_sp_port, 3249cf6c9c7SNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 3259cf6c9c7SNogah Frankel void *params) 3269cf6c9c7SNogah Frankel { 3279cf6c9c7SNogah Frankel struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 3289cf6c9c7SNogah Frankel struct tc_red_qopt_offload_params *p = params; 3299cf6c9c7SNogah Frankel u8 tclass_num = mlxsw_sp_qdisc->tclass_num; 3309cf6c9c7SNogah Frankel u32 min, max; 3319cf6c9c7SNogah Frankel u64 prob; 33296f17e07SNogah Frankel 33396f17e07SNogah Frankel /* calculate probability in percentage */ 33496f17e07SNogah Frankel prob = p->probability; 33596f17e07SNogah Frankel prob *= 100; 33696f17e07SNogah Frankel prob = DIV_ROUND_UP(prob, 1 << 16); 33796f17e07SNogah Frankel prob = DIV_ROUND_UP(prob, 1 << 16); 33896f17e07SNogah Frankel min = mlxsw_sp_bytes_cells(mlxsw_sp, p->min); 33996f17e07SNogah Frankel max = mlxsw_sp_bytes_cells(mlxsw_sp, p->max); 3409cf6c9c7SNogah Frankel return mlxsw_sp_tclass_congestion_enable(mlxsw_sp_port, tclass_num, min, 34196f17e07SNogah Frankel max, prob, p->is_ecn); 34296f17e07SNogah Frankel } 34396f17e07SNogah Frankel 344416ef9b1SJakub Kicinski static void 345416ef9b1SJakub Kicinski mlxsw_sp_qdisc_red_unoffload(struct mlxsw_sp_port *mlxsw_sp_port, 346416ef9b1SJakub Kicinski struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 347416ef9b1SJakub Kicinski void *params) 348416ef9b1SJakub Kicinski { 349416ef9b1SJakub Kicinski struct tc_red_qopt_offload_params *p = params; 350416ef9b1SJakub Kicinski u64 backlog; 351416ef9b1SJakub Kicinski 352416ef9b1SJakub Kicinski backlog = mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp, 353416ef9b1SJakub Kicinski mlxsw_sp_qdisc->stats_base.backlog); 354416ef9b1SJakub Kicinski p->qstats->backlog -= backlog; 355cc6e5c13SNogah Frankel mlxsw_sp_qdisc->stats_base.backlog = 0; 356416ef9b1SJakub Kicinski } 357416ef9b1SJakub Kicinski 358861fb829SNogah Frankel static int 359cba7158fSNogah Frankel mlxsw_sp_qdisc_get_red_xstats(struct mlxsw_sp_port *mlxsw_sp_port, 360861fb829SNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 361562ffbc4SNogah Frankel void *xstats_ptr) 362861fb829SNogah Frankel { 3634d1a4b84SNogah Frankel struct red_stats *xstats_base = &mlxsw_sp_qdisc->xstats_base.red; 364d56c8955SNogah Frankel u8 tclass_num = mlxsw_sp_qdisc->tclass_num; 365861fb829SNogah Frankel struct mlxsw_sp_port_xstats *xstats; 366562ffbc4SNogah Frankel struct red_stats *res = xstats_ptr; 367f8253df5SNogah Frankel int early_drops, marks, pdrops; 368861fb829SNogah Frankel 369861fb829SNogah Frankel xstats = &mlxsw_sp_port->periodic_hw_stats.xstats; 370861fb829SNogah Frankel 371f8253df5SNogah Frankel early_drops = xstats->wred_drop[tclass_num] - xstats_base->prob_drop; 372f8253df5SNogah Frankel marks = xstats->ecn - xstats_base->prob_mark; 373f8253df5SNogah Frankel pdrops = xstats->tail_drop[tclass_num] - xstats_base->pdrop; 374f8253df5SNogah Frankel 375f8253df5SNogah Frankel res->pdrop += pdrops; 376f8253df5SNogah Frankel res->prob_drop += early_drops; 377f8253df5SNogah Frankel res->prob_mark += marks; 378f8253df5SNogah Frankel 379f8253df5SNogah Frankel xstats_base->pdrop += pdrops; 380f8253df5SNogah Frankel xstats_base->prob_drop += early_drops; 381f8253df5SNogah Frankel xstats_base->prob_mark += marks; 382861fb829SNogah Frankel return 0; 383861fb829SNogah Frankel } 384861fb829SNogah Frankel 3853670756fSNogah Frankel static int 386cba7158fSNogah Frankel mlxsw_sp_qdisc_get_red_stats(struct mlxsw_sp_port *mlxsw_sp_port, 3873670756fSNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 388562ffbc4SNogah Frankel struct tc_qopt_offload_stats *stats_ptr) 3893670756fSNogah Frankel { 390416ef9b1SJakub Kicinski u64 tx_bytes, tx_packets, overlimits, drops, backlog; 391d56c8955SNogah Frankel u8 tclass_num = mlxsw_sp_qdisc->tclass_num; 3924d1a4b84SNogah Frankel struct mlxsw_sp_qdisc_stats *stats_base; 3933670756fSNogah Frankel struct mlxsw_sp_port_xstats *xstats; 3943670756fSNogah Frankel 3953670756fSNogah Frankel xstats = &mlxsw_sp_port->periodic_hw_stats.xstats; 3964d1a4b84SNogah Frankel stats_base = &mlxsw_sp_qdisc->stats_base; 3973670756fSNogah Frankel 39804cc0bf5SNogah Frankel mlxsw_sp_qdisc_bstats_per_priority_get(xstats, 39904cc0bf5SNogah Frankel mlxsw_sp_qdisc->prio_bitmap, 40004cc0bf5SNogah Frankel &tx_packets, &tx_bytes); 40104cc0bf5SNogah Frankel tx_bytes = tx_bytes - stats_base->tx_bytes; 40204cc0bf5SNogah Frankel tx_packets = tx_packets - stats_base->tx_packets; 40304cc0bf5SNogah Frankel 4043670756fSNogah Frankel overlimits = xstats->wred_drop[tclass_num] + xstats->ecn - 4054d1a4b84SNogah Frankel stats_base->overlimits; 4063670756fSNogah Frankel drops = xstats->wred_drop[tclass_num] + xstats->tail_drop[tclass_num] - 4074d1a4b84SNogah Frankel stats_base->drops; 408416ef9b1SJakub Kicinski backlog = xstats->backlog[tclass_num]; 4093670756fSNogah Frankel 410562ffbc4SNogah Frankel _bstats_update(stats_ptr->bstats, tx_bytes, tx_packets); 411562ffbc4SNogah Frankel stats_ptr->qstats->overlimits += overlimits; 412562ffbc4SNogah Frankel stats_ptr->qstats->drops += drops; 413562ffbc4SNogah Frankel stats_ptr->qstats->backlog += 414562ffbc4SNogah Frankel mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp, 415416ef9b1SJakub Kicinski backlog) - 416416ef9b1SJakub Kicinski mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp, 417416ef9b1SJakub Kicinski stats_base->backlog); 4183670756fSNogah Frankel 419416ef9b1SJakub Kicinski stats_base->backlog = backlog; 4204d1a4b84SNogah Frankel stats_base->drops += drops; 4214d1a4b84SNogah Frankel stats_base->overlimits += overlimits; 4224d1a4b84SNogah Frankel stats_base->tx_bytes += tx_bytes; 4234d1a4b84SNogah Frankel stats_base->tx_packets += tx_packets; 4243670756fSNogah Frankel return 0; 4253670756fSNogah Frankel } 4263670756fSNogah Frankel 42796f17e07SNogah Frankel #define MLXSW_SP_PORT_DEFAULT_TCLASS 0 42896f17e07SNogah Frankel 429562ffbc4SNogah Frankel static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_red = { 4309cf6c9c7SNogah Frankel .type = MLXSW_SP_QDISC_RED, 4319cf6c9c7SNogah Frankel .check_params = mlxsw_sp_qdisc_red_check_params, 4329cf6c9c7SNogah Frankel .replace = mlxsw_sp_qdisc_red_replace, 433416ef9b1SJakub Kicinski .unoffload = mlxsw_sp_qdisc_red_unoffload, 4349a37a59fSNogah Frankel .destroy = mlxsw_sp_qdisc_red_destroy, 435562ffbc4SNogah Frankel .get_stats = mlxsw_sp_qdisc_get_red_stats, 436562ffbc4SNogah Frankel .get_xstats = mlxsw_sp_qdisc_get_red_xstats, 4379cf6c9c7SNogah Frankel .clean_stats = mlxsw_sp_setup_tc_qdisc_red_clean_stats, 438562ffbc4SNogah Frankel }; 439562ffbc4SNogah Frankel 44096f17e07SNogah Frankel int mlxsw_sp_setup_tc_red(struct mlxsw_sp_port *mlxsw_sp_port, 44196f17e07SNogah Frankel struct tc_red_qopt_offload *p) 44296f17e07SNogah Frankel { 44396f17e07SNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc; 44496f17e07SNogah Frankel 445eed4baebSNogah Frankel mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent, false); 446eed4baebSNogah Frankel if (!mlxsw_sp_qdisc) 44796f17e07SNogah Frankel return -EOPNOTSUPP; 44896f17e07SNogah Frankel 449cba7158fSNogah Frankel if (p->command == TC_RED_REPLACE) 4509cf6c9c7SNogah Frankel return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle, 451562ffbc4SNogah Frankel mlxsw_sp_qdisc, 452562ffbc4SNogah Frankel &mlxsw_sp_qdisc_ops_red, 453562ffbc4SNogah Frankel &p->set); 454cba7158fSNogah Frankel 455cba7158fSNogah Frankel if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle, 456cba7158fSNogah Frankel MLXSW_SP_QDISC_RED)) 457cba7158fSNogah Frankel return -EOPNOTSUPP; 458cba7158fSNogah Frankel 459cba7158fSNogah Frankel switch (p->command) { 46096f17e07SNogah Frankel case TC_RED_DESTROY: 4619a37a59fSNogah Frankel return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc); 462861fb829SNogah Frankel case TC_RED_XSTATS: 463562ffbc4SNogah Frankel return mlxsw_sp_qdisc_get_xstats(mlxsw_sp_port, mlxsw_sp_qdisc, 464861fb829SNogah Frankel p->xstats); 4653670756fSNogah Frankel case TC_RED_STATS: 466562ffbc4SNogah Frankel return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc, 4673670756fSNogah Frankel &p->stats); 46896f17e07SNogah Frankel default: 46996f17e07SNogah Frankel return -EOPNOTSUPP; 47096f17e07SNogah Frankel } 47196f17e07SNogah Frankel } 472371b437aSNogah Frankel 47346a3615bSNogah Frankel static int 47446a3615bSNogah Frankel mlxsw_sp_qdisc_prio_destroy(struct mlxsw_sp_port *mlxsw_sp_port, 47546a3615bSNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 47646a3615bSNogah Frankel { 47746a3615bSNogah Frankel int i; 47846a3615bSNogah Frankel 479eed4baebSNogah Frankel for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { 48046a3615bSNogah Frankel mlxsw_sp_port_prio_tc_set(mlxsw_sp_port, i, 48146a3615bSNogah Frankel MLXSW_SP_PORT_DEFAULT_TCLASS); 482eed4baebSNogah Frankel mlxsw_sp_qdisc_destroy(mlxsw_sp_port, 483eed4baebSNogah Frankel &mlxsw_sp_port->tclass_qdiscs[i]); 4841631ab2eSNogah Frankel mlxsw_sp_port->tclass_qdiscs[i].prio_bitmap = 0; 485eed4baebSNogah Frankel } 48646a3615bSNogah Frankel 48746a3615bSNogah Frankel return 0; 48846a3615bSNogah Frankel } 48946a3615bSNogah Frankel 49046a3615bSNogah Frankel static int 49146a3615bSNogah Frankel mlxsw_sp_qdisc_prio_check_params(struct mlxsw_sp_port *mlxsw_sp_port, 49246a3615bSNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 49346a3615bSNogah Frankel void *params) 49446a3615bSNogah Frankel { 49546a3615bSNogah Frankel struct tc_prio_qopt_offload_params *p = params; 49646a3615bSNogah Frankel 49746a3615bSNogah Frankel if (p->bands > IEEE_8021QAZ_MAX_TCS) 49846a3615bSNogah Frankel return -EOPNOTSUPP; 49946a3615bSNogah Frankel 50046a3615bSNogah Frankel return 0; 50146a3615bSNogah Frankel } 50246a3615bSNogah Frankel 50346a3615bSNogah Frankel static int 50446a3615bSNogah Frankel mlxsw_sp_qdisc_prio_replace(struct mlxsw_sp_port *mlxsw_sp_port, 50546a3615bSNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 50646a3615bSNogah Frankel void *params) 50746a3615bSNogah Frankel { 50846a3615bSNogah Frankel struct tc_prio_qopt_offload_params *p = params; 50904cc0bf5SNogah Frankel struct mlxsw_sp_qdisc *child_qdisc; 510cc6e5c13SNogah Frankel int tclass, i, band, backlog; 51104cc0bf5SNogah Frankel u8 old_priomap; 51246a3615bSNogah Frankel int err; 51346a3615bSNogah Frankel 51404cc0bf5SNogah Frankel for (band = 0; band < p->bands; band++) { 51504cc0bf5SNogah Frankel tclass = MLXSW_SP_PRIO_BAND_TO_TCLASS(band); 51604cc0bf5SNogah Frankel child_qdisc = &mlxsw_sp_port->tclass_qdiscs[tclass]; 51704cc0bf5SNogah Frankel old_priomap = child_qdisc->prio_bitmap; 51804cc0bf5SNogah Frankel child_qdisc->prio_bitmap = 0; 51946a3615bSNogah Frankel for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { 52004cc0bf5SNogah Frankel if (p->priomap[i] == band) { 52104cc0bf5SNogah Frankel child_qdisc->prio_bitmap |= BIT(i); 52204cc0bf5SNogah Frankel if (BIT(i) & old_priomap) 52304cc0bf5SNogah Frankel continue; 52404cc0bf5SNogah Frankel err = mlxsw_sp_port_prio_tc_set(mlxsw_sp_port, 52504cc0bf5SNogah Frankel i, tclass); 52646a3615bSNogah Frankel if (err) 52746a3615bSNogah Frankel return err; 52804cc0bf5SNogah Frankel } 52904cc0bf5SNogah Frankel } 53004cc0bf5SNogah Frankel if (old_priomap != child_qdisc->prio_bitmap && 531cc6e5c13SNogah Frankel child_qdisc->ops && child_qdisc->ops->clean_stats) { 532cc6e5c13SNogah Frankel backlog = child_qdisc->stats_base.backlog; 53304cc0bf5SNogah Frankel child_qdisc->ops->clean_stats(mlxsw_sp_port, 53404cc0bf5SNogah Frankel child_qdisc); 535cc6e5c13SNogah Frankel child_qdisc->stats_base.backlog = backlog; 536cc6e5c13SNogah Frankel } 53746a3615bSNogah Frankel } 53898ceb7b6SNogah Frankel for (; band < IEEE_8021QAZ_MAX_TCS; band++) { 53998ceb7b6SNogah Frankel tclass = MLXSW_SP_PRIO_BAND_TO_TCLASS(band); 54098ceb7b6SNogah Frankel child_qdisc = &mlxsw_sp_port->tclass_qdiscs[tclass]; 54198ceb7b6SNogah Frankel child_qdisc->prio_bitmap = 0; 54298ceb7b6SNogah Frankel mlxsw_sp_qdisc_destroy(mlxsw_sp_port, child_qdisc); 54398ceb7b6SNogah Frankel } 54446a3615bSNogah Frankel return 0; 54546a3615bSNogah Frankel } 54646a3615bSNogah Frankel 547e02f08a0SWei Yongjun static void 54893d8a4c1SNogah Frankel mlxsw_sp_qdisc_prio_unoffload(struct mlxsw_sp_port *mlxsw_sp_port, 54993d8a4c1SNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 55093d8a4c1SNogah Frankel void *params) 55193d8a4c1SNogah Frankel { 55293d8a4c1SNogah Frankel struct tc_prio_qopt_offload_params *p = params; 55393d8a4c1SNogah Frankel u64 backlog; 55493d8a4c1SNogah Frankel 55593d8a4c1SNogah Frankel backlog = mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp, 55693d8a4c1SNogah Frankel mlxsw_sp_qdisc->stats_base.backlog); 55793d8a4c1SNogah Frankel p->qstats->backlog -= backlog; 55893d8a4c1SNogah Frankel } 55993d8a4c1SNogah Frankel 56093d8a4c1SNogah Frankel static int 56193d8a4c1SNogah Frankel mlxsw_sp_qdisc_get_prio_stats(struct mlxsw_sp_port *mlxsw_sp_port, 56293d8a4c1SNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 56393d8a4c1SNogah Frankel struct tc_qopt_offload_stats *stats_ptr) 56493d8a4c1SNogah Frankel { 56593d8a4c1SNogah Frankel u64 tx_bytes, tx_packets, drops = 0, backlog = 0; 56693d8a4c1SNogah Frankel struct mlxsw_sp_qdisc_stats *stats_base; 56793d8a4c1SNogah Frankel struct mlxsw_sp_port_xstats *xstats; 56893d8a4c1SNogah Frankel struct rtnl_link_stats64 *stats; 56993d8a4c1SNogah Frankel int i; 57093d8a4c1SNogah Frankel 57193d8a4c1SNogah Frankel xstats = &mlxsw_sp_port->periodic_hw_stats.xstats; 57293d8a4c1SNogah Frankel stats = &mlxsw_sp_port->periodic_hw_stats.stats; 57393d8a4c1SNogah Frankel stats_base = &mlxsw_sp_qdisc->stats_base; 57493d8a4c1SNogah Frankel 57593d8a4c1SNogah Frankel tx_bytes = stats->tx_bytes - stats_base->tx_bytes; 57693d8a4c1SNogah Frankel tx_packets = stats->tx_packets - stats_base->tx_packets; 57793d8a4c1SNogah Frankel 57893d8a4c1SNogah Frankel for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { 57993d8a4c1SNogah Frankel drops += xstats->tail_drop[i]; 58023f2b404SNogah Frankel drops += xstats->wred_drop[i]; 58193d8a4c1SNogah Frankel backlog += xstats->backlog[i]; 58293d8a4c1SNogah Frankel } 58393d8a4c1SNogah Frankel drops = drops - stats_base->drops; 58493d8a4c1SNogah Frankel 58593d8a4c1SNogah Frankel _bstats_update(stats_ptr->bstats, tx_bytes, tx_packets); 58693d8a4c1SNogah Frankel stats_ptr->qstats->drops += drops; 58793d8a4c1SNogah Frankel stats_ptr->qstats->backlog += 58893d8a4c1SNogah Frankel mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp, 58993d8a4c1SNogah Frankel backlog) - 59093d8a4c1SNogah Frankel mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp, 59193d8a4c1SNogah Frankel stats_base->backlog); 59293d8a4c1SNogah Frankel stats_base->backlog = backlog; 59393d8a4c1SNogah Frankel stats_base->drops += drops; 59493d8a4c1SNogah Frankel stats_base->tx_bytes += tx_bytes; 59593d8a4c1SNogah Frankel stats_base->tx_packets += tx_packets; 59693d8a4c1SNogah Frankel return 0; 59793d8a4c1SNogah Frankel } 59893d8a4c1SNogah Frankel 59993d8a4c1SNogah Frankel static void 60093d8a4c1SNogah Frankel mlxsw_sp_setup_tc_qdisc_prio_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port, 60193d8a4c1SNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 60293d8a4c1SNogah Frankel { 60393d8a4c1SNogah Frankel struct mlxsw_sp_qdisc_stats *stats_base; 60493d8a4c1SNogah Frankel struct mlxsw_sp_port_xstats *xstats; 60593d8a4c1SNogah Frankel struct rtnl_link_stats64 *stats; 60693d8a4c1SNogah Frankel int i; 60793d8a4c1SNogah Frankel 60893d8a4c1SNogah Frankel xstats = &mlxsw_sp_port->periodic_hw_stats.xstats; 60993d8a4c1SNogah Frankel stats = &mlxsw_sp_port->periodic_hw_stats.stats; 61093d8a4c1SNogah Frankel stats_base = &mlxsw_sp_qdisc->stats_base; 61193d8a4c1SNogah Frankel 61293d8a4c1SNogah Frankel stats_base->tx_packets = stats->tx_packets; 61393d8a4c1SNogah Frankel stats_base->tx_bytes = stats->tx_bytes; 61493d8a4c1SNogah Frankel 61593d8a4c1SNogah Frankel stats_base->drops = 0; 61623f2b404SNogah Frankel for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { 61793d8a4c1SNogah Frankel stats_base->drops += xstats->tail_drop[i]; 61823f2b404SNogah Frankel stats_base->drops += xstats->wred_drop[i]; 61923f2b404SNogah Frankel } 62093d8a4c1SNogah Frankel 62193d8a4c1SNogah Frankel mlxsw_sp_qdisc->stats_base.backlog = 0; 62293d8a4c1SNogah Frankel } 62393d8a4c1SNogah Frankel 62446a3615bSNogah Frankel static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_prio = { 62546a3615bSNogah Frankel .type = MLXSW_SP_QDISC_PRIO, 62646a3615bSNogah Frankel .check_params = mlxsw_sp_qdisc_prio_check_params, 62746a3615bSNogah Frankel .replace = mlxsw_sp_qdisc_prio_replace, 62893d8a4c1SNogah Frankel .unoffload = mlxsw_sp_qdisc_prio_unoffload, 62946a3615bSNogah Frankel .destroy = mlxsw_sp_qdisc_prio_destroy, 63093d8a4c1SNogah Frankel .get_stats = mlxsw_sp_qdisc_get_prio_stats, 63193d8a4c1SNogah Frankel .clean_stats = mlxsw_sp_setup_tc_qdisc_prio_clean_stats, 63246a3615bSNogah Frankel }; 63346a3615bSNogah Frankel 63432dc5efcSNogah Frankel /* Grafting is not supported in mlxsw. It will result in un-offloading of the 63532dc5efcSNogah Frankel * grafted qdisc as well as the qdisc in the qdisc new location. 63632dc5efcSNogah Frankel * (However, if the graft is to the location where the qdisc is already at, it 63732dc5efcSNogah Frankel * will be ignored completely and won't cause un-offloading). 63832dc5efcSNogah Frankel */ 63932dc5efcSNogah Frankel static int 64032dc5efcSNogah Frankel mlxsw_sp_qdisc_prio_graft(struct mlxsw_sp_port *mlxsw_sp_port, 64132dc5efcSNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 64232dc5efcSNogah Frankel struct tc_prio_qopt_offload_graft_params *p) 64332dc5efcSNogah Frankel { 64432dc5efcSNogah Frankel int tclass_num = MLXSW_SP_PRIO_BAND_TO_TCLASS(p->band); 64532dc5efcSNogah Frankel struct mlxsw_sp_qdisc *old_qdisc; 64632dc5efcSNogah Frankel 64732dc5efcSNogah Frankel /* Check if the grafted qdisc is already in its "new" location. If so - 64832dc5efcSNogah Frankel * nothing needs to be done. 64932dc5efcSNogah Frankel */ 65032dc5efcSNogah Frankel if (p->band < IEEE_8021QAZ_MAX_TCS && 65132dc5efcSNogah Frankel mlxsw_sp_port->tclass_qdiscs[tclass_num].handle == p->child_handle) 65232dc5efcSNogah Frankel return 0; 65332dc5efcSNogah Frankel 6543971a535SPetr Machata if (!p->child_handle) { 6553971a535SPetr Machata /* This is an invisible FIFO replacing the original Qdisc. 6563971a535SPetr Machata * Ignore it--the original Qdisc's destroy will follow. 6573971a535SPetr Machata */ 6583971a535SPetr Machata return 0; 6593971a535SPetr Machata } 6603971a535SPetr Machata 66132dc5efcSNogah Frankel /* See if the grafted qdisc is already offloaded on any tclass. If so, 66232dc5efcSNogah Frankel * unoffload it. 66332dc5efcSNogah Frankel */ 66432dc5efcSNogah Frankel old_qdisc = mlxsw_sp_qdisc_find_by_handle(mlxsw_sp_port, 66532dc5efcSNogah Frankel p->child_handle); 66632dc5efcSNogah Frankel if (old_qdisc) 66732dc5efcSNogah Frankel mlxsw_sp_qdisc_destroy(mlxsw_sp_port, old_qdisc); 66832dc5efcSNogah Frankel 66932dc5efcSNogah Frankel mlxsw_sp_qdisc_destroy(mlxsw_sp_port, 67032dc5efcSNogah Frankel &mlxsw_sp_port->tclass_qdiscs[tclass_num]); 67132dc5efcSNogah Frankel return -EOPNOTSUPP; 67232dc5efcSNogah Frankel } 67332dc5efcSNogah Frankel 67446a3615bSNogah Frankel int mlxsw_sp_setup_tc_prio(struct mlxsw_sp_port *mlxsw_sp_port, 67546a3615bSNogah Frankel struct tc_prio_qopt_offload *p) 67646a3615bSNogah Frankel { 67746a3615bSNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc; 67846a3615bSNogah Frankel 679eed4baebSNogah Frankel mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent, true); 680eed4baebSNogah Frankel if (!mlxsw_sp_qdisc) 68146a3615bSNogah Frankel return -EOPNOTSUPP; 68246a3615bSNogah Frankel 68346a3615bSNogah Frankel if (p->command == TC_PRIO_REPLACE) 68446a3615bSNogah Frankel return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle, 68546a3615bSNogah Frankel mlxsw_sp_qdisc, 68646a3615bSNogah Frankel &mlxsw_sp_qdisc_ops_prio, 68746a3615bSNogah Frankel &p->replace_params); 68846a3615bSNogah Frankel 68946a3615bSNogah Frankel if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle, 69046a3615bSNogah Frankel MLXSW_SP_QDISC_PRIO)) 69146a3615bSNogah Frankel return -EOPNOTSUPP; 69246a3615bSNogah Frankel 69346a3615bSNogah Frankel switch (p->command) { 69446a3615bSNogah Frankel case TC_PRIO_DESTROY: 69546a3615bSNogah Frankel return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc); 69693d8a4c1SNogah Frankel case TC_PRIO_STATS: 69793d8a4c1SNogah Frankel return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc, 69893d8a4c1SNogah Frankel &p->stats); 69932dc5efcSNogah Frankel case TC_PRIO_GRAFT: 70032dc5efcSNogah Frankel return mlxsw_sp_qdisc_prio_graft(mlxsw_sp_port, mlxsw_sp_qdisc, 70132dc5efcSNogah Frankel &p->graft_params); 70246a3615bSNogah Frankel default: 70346a3615bSNogah Frankel return -EOPNOTSUPP; 70446a3615bSNogah Frankel } 70546a3615bSNogah Frankel } 70646a3615bSNogah Frankel 707371b437aSNogah Frankel int mlxsw_sp_tc_qdisc_init(struct mlxsw_sp_port *mlxsw_sp_port) 708371b437aSNogah Frankel { 709eed4baebSNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc; 710eed4baebSNogah Frankel int i; 711371b437aSNogah Frankel 712eed4baebSNogah Frankel mlxsw_sp_qdisc = kzalloc(sizeof(*mlxsw_sp_qdisc), GFP_KERNEL); 713eed4baebSNogah Frankel if (!mlxsw_sp_qdisc) 714eed4baebSNogah Frankel goto err_root_qdisc_init; 715eed4baebSNogah Frankel 716eed4baebSNogah Frankel mlxsw_sp_port->root_qdisc = mlxsw_sp_qdisc; 7171631ab2eSNogah Frankel mlxsw_sp_port->root_qdisc->prio_bitmap = 0xff; 718d56c8955SNogah Frankel mlxsw_sp_port->root_qdisc->tclass_num = MLXSW_SP_PORT_DEFAULT_TCLASS; 719d56c8955SNogah Frankel 7206396bb22SKees Cook mlxsw_sp_qdisc = kcalloc(IEEE_8021QAZ_MAX_TCS, 7216396bb22SKees Cook sizeof(*mlxsw_sp_qdisc), 722eed4baebSNogah Frankel GFP_KERNEL); 723eed4baebSNogah Frankel if (!mlxsw_sp_qdisc) 724eed4baebSNogah Frankel goto err_tclass_qdiscs_init; 725eed4baebSNogah Frankel 726eed4baebSNogah Frankel mlxsw_sp_port->tclass_qdiscs = mlxsw_sp_qdisc; 727eed4baebSNogah Frankel for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) 728eed4baebSNogah Frankel mlxsw_sp_port->tclass_qdiscs[i].tclass_num = i; 729eed4baebSNogah Frankel 730371b437aSNogah Frankel return 0; 731eed4baebSNogah Frankel 732eed4baebSNogah Frankel err_tclass_qdiscs_init: 733eed4baebSNogah Frankel kfree(mlxsw_sp_port->root_qdisc); 734eed4baebSNogah Frankel err_root_qdisc_init: 735eed4baebSNogah Frankel return -ENOMEM; 736371b437aSNogah Frankel } 737371b437aSNogah Frankel 738371b437aSNogah Frankel void mlxsw_sp_tc_qdisc_fini(struct mlxsw_sp_port *mlxsw_sp_port) 739371b437aSNogah Frankel { 740eed4baebSNogah Frankel kfree(mlxsw_sp_port->tclass_qdiscs); 741371b437aSNogah Frankel kfree(mlxsw_sp_port->root_qdisc); 742371b437aSNogah Frankel } 743