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 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 339cf6c9c7SNogah Frankel void *params); 34c4e372e2SPetr Machata int (*replace)(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, 359cf6c9c7SNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, void *params); 369a37a59fSNogah Frankel int (*destroy)(struct mlxsw_sp_port *mlxsw_sp_port, 379a37a59fSNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc); 38562ffbc4SNogah Frankel int (*get_stats)(struct mlxsw_sp_port *mlxsw_sp_port, 39562ffbc4SNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 40562ffbc4SNogah Frankel struct tc_qopt_offload_stats *stats_ptr); 41562ffbc4SNogah Frankel int (*get_xstats)(struct mlxsw_sp_port *mlxsw_sp_port, 42562ffbc4SNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 43562ffbc4SNogah Frankel void *xstats_ptr); 449cf6c9c7SNogah Frankel void (*clean_stats)(struct mlxsw_sp_port *mlxsw_sp_port, 459cf6c9c7SNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc); 4693d8a4c1SNogah Frankel /* unoffload - to be used for a qdisc that stops being offloaded without 4793d8a4c1SNogah Frankel * being destroyed. 4893d8a4c1SNogah Frankel */ 4993d8a4c1SNogah Frankel void (*unoffload)(struct mlxsw_sp_port *mlxsw_sp_port, 5093d8a4c1SNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, void *params); 51562ffbc4SNogah Frankel }; 52562ffbc4SNogah Frankel 53371b437aSNogah Frankel struct mlxsw_sp_qdisc { 54371b437aSNogah Frankel u32 handle; 55d56c8955SNogah Frankel u8 tclass_num; 561631ab2eSNogah Frankel u8 prio_bitmap; 57371b437aSNogah Frankel union { 584d1a4b84SNogah Frankel struct red_stats red; 594d1a4b84SNogah Frankel } xstats_base; 604d1a4b84SNogah Frankel struct mlxsw_sp_qdisc_stats { 61371b437aSNogah Frankel u64 tx_bytes; 62371b437aSNogah Frankel u64 tx_packets; 63371b437aSNogah Frankel u64 drops; 64371b437aSNogah Frankel u64 overlimits; 6593d8a4c1SNogah Frankel u64 backlog; 664d1a4b84SNogah Frankel } stats_base; 67562ffbc4SNogah Frankel 68562ffbc4SNogah Frankel struct mlxsw_sp_qdisc_ops *ops; 69371b437aSNogah Frankel }; 70371b437aSNogah Frankel 71ee88450dSPetr Machata struct mlxsw_sp_qdisc_state { 72ee88450dSPetr Machata struct mlxsw_sp_qdisc root_qdisc; 73ee88450dSPetr Machata struct mlxsw_sp_qdisc tclass_qdiscs[IEEE_8021QAZ_MAX_TCS]; 747bec1a45SPetr Machata 757bec1a45SPetr Machata /* When a PRIO or ETS are added, the invisible FIFOs in their bands are 767bec1a45SPetr Machata * created first. When notifications for these FIFOs arrive, it is not 777bec1a45SPetr Machata * known what qdisc their parent handle refers to. It could be a 787bec1a45SPetr Machata * newly-created PRIO that will replace the currently-offloaded one, or 797bec1a45SPetr Machata * it could be e.g. a RED that will be attached below it. 807bec1a45SPetr Machata * 817bec1a45SPetr Machata * As the notifications start to arrive, use them to note what the 827bec1a45SPetr Machata * future parent handle is, and keep track of which child FIFOs were 837bec1a45SPetr Machata * seen. Then when the parent is known, retroactively offload those 847bec1a45SPetr Machata * FIFOs. 857bec1a45SPetr Machata */ 867bec1a45SPetr Machata u32 future_handle; 877bec1a45SPetr Machata bool future_fifos[IEEE_8021QAZ_MAX_TCS]; 88ee88450dSPetr Machata }; 89ee88450dSPetr Machata 90cba7158fSNogah Frankel static bool 91cba7158fSNogah Frankel mlxsw_sp_qdisc_compare(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, u32 handle, 92cba7158fSNogah Frankel enum mlxsw_sp_qdisc_type type) 93cba7158fSNogah Frankel { 949cf6c9c7SNogah Frankel return mlxsw_sp_qdisc && mlxsw_sp_qdisc->ops && 959cf6c9c7SNogah Frankel mlxsw_sp_qdisc->ops->type == type && 969cf6c9c7SNogah Frankel mlxsw_sp_qdisc->handle == handle; 97cba7158fSNogah Frankel } 98cba7158fSNogah Frankel 99eed4baebSNogah Frankel static struct mlxsw_sp_qdisc * 100eed4baebSNogah Frankel mlxsw_sp_qdisc_find(struct mlxsw_sp_port *mlxsw_sp_port, u32 parent, 101eed4baebSNogah Frankel bool root_only) 102eed4baebSNogah Frankel { 103ee88450dSPetr Machata struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc; 104eed4baebSNogah Frankel int tclass, child_index; 105eed4baebSNogah Frankel 106eed4baebSNogah Frankel if (parent == TC_H_ROOT) 107ee88450dSPetr Machata return &qdisc_state->root_qdisc; 108eed4baebSNogah Frankel 109ee88450dSPetr Machata if (root_only || !qdisc_state || 110ee88450dSPetr Machata !qdisc_state->root_qdisc.ops || 111ee88450dSPetr Machata TC_H_MAJ(parent) != qdisc_state->root_qdisc.handle || 112eed4baebSNogah Frankel TC_H_MIN(parent) > IEEE_8021QAZ_MAX_TCS) 113eed4baebSNogah Frankel return NULL; 114eed4baebSNogah Frankel 115eed4baebSNogah Frankel child_index = TC_H_MIN(parent); 116eed4baebSNogah Frankel tclass = MLXSW_SP_PRIO_CHILD_TO_TCLASS(child_index); 117ee88450dSPetr Machata return &qdisc_state->tclass_qdiscs[tclass]; 118eed4baebSNogah Frankel } 119eed4baebSNogah Frankel 12032dc5efcSNogah Frankel static struct mlxsw_sp_qdisc * 12132dc5efcSNogah Frankel mlxsw_sp_qdisc_find_by_handle(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle) 12232dc5efcSNogah Frankel { 123ee88450dSPetr Machata struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc; 12432dc5efcSNogah Frankel int i; 12532dc5efcSNogah Frankel 126ee88450dSPetr Machata if (qdisc_state->root_qdisc.handle == handle) 127ee88450dSPetr Machata return &qdisc_state->root_qdisc; 12832dc5efcSNogah Frankel 129ee88450dSPetr Machata if (qdisc_state->root_qdisc.handle == TC_H_UNSPEC) 13032dc5efcSNogah Frankel return NULL; 13132dc5efcSNogah Frankel 13232dc5efcSNogah Frankel for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) 133ee88450dSPetr Machata if (qdisc_state->tclass_qdiscs[i].handle == handle) 134ee88450dSPetr Machata return &qdisc_state->tclass_qdiscs[i]; 13532dc5efcSNogah Frankel 13632dc5efcSNogah Frankel return NULL; 13732dc5efcSNogah Frankel } 13832dc5efcSNogah Frankel 13996f17e07SNogah Frankel static int 1409a37a59fSNogah Frankel mlxsw_sp_qdisc_destroy(struct mlxsw_sp_port *mlxsw_sp_port, 1419a37a59fSNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 1429a37a59fSNogah Frankel { 143509f04caSPetr Machata struct mlxsw_sp_qdisc *root_qdisc = &mlxsw_sp_port->qdisc->root_qdisc; 144509f04caSPetr Machata int err_hdroom = 0; 1459a37a59fSNogah Frankel int err = 0; 1469a37a59fSNogah Frankel 1479a37a59fSNogah Frankel if (!mlxsw_sp_qdisc) 1489a37a59fSNogah Frankel return 0; 1499a37a59fSNogah Frankel 150509f04caSPetr Machata if (root_qdisc == mlxsw_sp_qdisc) { 151509f04caSPetr Machata struct mlxsw_sp_hdroom hdroom = *mlxsw_sp_port->hdroom; 152509f04caSPetr Machata 153509f04caSPetr Machata hdroom.mode = MLXSW_SP_HDROOM_MODE_DCB; 154509f04caSPetr Machata mlxsw_sp_hdroom_prios_reset_buf_idx(&hdroom); 155509f04caSPetr Machata mlxsw_sp_hdroom_bufs_reset_lossiness(&hdroom); 156509f04caSPetr Machata mlxsw_sp_hdroom_bufs_reset_sizes(mlxsw_sp_port, &hdroom); 157509f04caSPetr Machata err_hdroom = mlxsw_sp_hdroom_configure(mlxsw_sp_port, &hdroom); 158509f04caSPetr Machata } 159509f04caSPetr Machata 1609a37a59fSNogah Frankel if (mlxsw_sp_qdisc->ops && mlxsw_sp_qdisc->ops->destroy) 1619a37a59fSNogah Frankel err = mlxsw_sp_qdisc->ops->destroy(mlxsw_sp_port, 1629a37a59fSNogah Frankel mlxsw_sp_qdisc); 1639a37a59fSNogah Frankel 1649a37a59fSNogah Frankel mlxsw_sp_qdisc->handle = TC_H_UNSPEC; 1659a37a59fSNogah Frankel mlxsw_sp_qdisc->ops = NULL; 166509f04caSPetr Machata 167509f04caSPetr Machata return err_hdroom ?: err; 1689a37a59fSNogah Frankel } 1699a37a59fSNogah Frankel 1709a37a59fSNogah Frankel static int 1719cf6c9c7SNogah Frankel mlxsw_sp_qdisc_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, 1729cf6c9c7SNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 1739cf6c9c7SNogah Frankel struct mlxsw_sp_qdisc_ops *ops, void *params) 1749cf6c9c7SNogah Frankel { 175509f04caSPetr Machata struct mlxsw_sp_qdisc *root_qdisc = &mlxsw_sp_port->qdisc->root_qdisc; 176509f04caSPetr Machata struct mlxsw_sp_hdroom orig_hdroom; 1779cf6c9c7SNogah Frankel int err; 1789cf6c9c7SNogah Frankel 17956202ca4SNogah Frankel if (mlxsw_sp_qdisc->ops && mlxsw_sp_qdisc->ops->type != ops->type) 18056202ca4SNogah Frankel /* In case this location contained a different qdisc of the 18156202ca4SNogah Frankel * same type we can override the old qdisc configuration. 18256202ca4SNogah Frankel * Otherwise, we need to remove the old qdisc before setting the 18356202ca4SNogah Frankel * new one. 18456202ca4SNogah Frankel */ 18556202ca4SNogah Frankel mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc); 186509f04caSPetr Machata 187509f04caSPetr Machata orig_hdroom = *mlxsw_sp_port->hdroom; 188509f04caSPetr Machata if (root_qdisc == mlxsw_sp_qdisc) { 189509f04caSPetr Machata struct mlxsw_sp_hdroom hdroom = orig_hdroom; 190509f04caSPetr Machata 191509f04caSPetr Machata hdroom.mode = MLXSW_SP_HDROOM_MODE_TC; 192509f04caSPetr Machata mlxsw_sp_hdroom_prios_reset_buf_idx(&hdroom); 193509f04caSPetr Machata mlxsw_sp_hdroom_bufs_reset_lossiness(&hdroom); 194509f04caSPetr Machata mlxsw_sp_hdroom_bufs_reset_sizes(mlxsw_sp_port, &hdroom); 195509f04caSPetr Machata 196509f04caSPetr Machata err = mlxsw_sp_hdroom_configure(mlxsw_sp_port, &hdroom); 197509f04caSPetr Machata if (err) 198509f04caSPetr Machata goto err_hdroom_configure; 199509f04caSPetr Machata } 200509f04caSPetr Machata 2019cf6c9c7SNogah Frankel err = ops->check_params(mlxsw_sp_port, mlxsw_sp_qdisc, params); 2029cf6c9c7SNogah Frankel if (err) 2039cf6c9c7SNogah Frankel goto err_bad_param; 2049cf6c9c7SNogah Frankel 205c4e372e2SPetr Machata err = ops->replace(mlxsw_sp_port, handle, mlxsw_sp_qdisc, params); 2069cf6c9c7SNogah Frankel if (err) 2079cf6c9c7SNogah Frankel goto err_config; 2089cf6c9c7SNogah Frankel 2097bec1a45SPetr Machata /* Check if the Qdisc changed. That includes a situation where an 2107bec1a45SPetr Machata * invisible Qdisc replaces another one, or is being added for the 2117bec1a45SPetr Machata * first time. 2127bec1a45SPetr Machata */ 2137bec1a45SPetr Machata if (mlxsw_sp_qdisc->handle != handle || handle == TC_H_UNSPEC) { 2149cf6c9c7SNogah Frankel mlxsw_sp_qdisc->ops = ops; 2159cf6c9c7SNogah Frankel if (ops->clean_stats) 2169cf6c9c7SNogah Frankel ops->clean_stats(mlxsw_sp_port, mlxsw_sp_qdisc); 2179cf6c9c7SNogah Frankel } 2189cf6c9c7SNogah Frankel 2199cf6c9c7SNogah Frankel mlxsw_sp_qdisc->handle = handle; 2209cf6c9c7SNogah Frankel return 0; 2219cf6c9c7SNogah Frankel 2229cf6c9c7SNogah Frankel err_bad_param: 2239cf6c9c7SNogah Frankel err_config: 224509f04caSPetr Machata mlxsw_sp_hdroom_configure(mlxsw_sp_port, &orig_hdroom); 225509f04caSPetr Machata err_hdroom_configure: 22693d8a4c1SNogah Frankel if (mlxsw_sp_qdisc->handle == handle && ops->unoffload) 22793d8a4c1SNogah Frankel ops->unoffload(mlxsw_sp_port, mlxsw_sp_qdisc, params); 22893d8a4c1SNogah Frankel 2299cf6c9c7SNogah Frankel mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc); 2309cf6c9c7SNogah Frankel return err; 2319cf6c9c7SNogah Frankel } 2329cf6c9c7SNogah Frankel 2339cf6c9c7SNogah Frankel static int 234562ffbc4SNogah Frankel mlxsw_sp_qdisc_get_stats(struct mlxsw_sp_port *mlxsw_sp_port, 235562ffbc4SNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 236562ffbc4SNogah Frankel struct tc_qopt_offload_stats *stats_ptr) 237562ffbc4SNogah Frankel { 238562ffbc4SNogah Frankel if (mlxsw_sp_qdisc && mlxsw_sp_qdisc->ops && 239562ffbc4SNogah Frankel mlxsw_sp_qdisc->ops->get_stats) 240562ffbc4SNogah Frankel return mlxsw_sp_qdisc->ops->get_stats(mlxsw_sp_port, 241562ffbc4SNogah Frankel mlxsw_sp_qdisc, 242562ffbc4SNogah Frankel stats_ptr); 243562ffbc4SNogah Frankel 244562ffbc4SNogah Frankel return -EOPNOTSUPP; 245562ffbc4SNogah Frankel } 246562ffbc4SNogah Frankel 247562ffbc4SNogah Frankel static int 248562ffbc4SNogah Frankel mlxsw_sp_qdisc_get_xstats(struct mlxsw_sp_port *mlxsw_sp_port, 249562ffbc4SNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 250562ffbc4SNogah Frankel void *xstats_ptr) 251562ffbc4SNogah Frankel { 252562ffbc4SNogah Frankel if (mlxsw_sp_qdisc && mlxsw_sp_qdisc->ops && 253562ffbc4SNogah Frankel mlxsw_sp_qdisc->ops->get_xstats) 254562ffbc4SNogah Frankel return mlxsw_sp_qdisc->ops->get_xstats(mlxsw_sp_port, 255562ffbc4SNogah Frankel mlxsw_sp_qdisc, 256562ffbc4SNogah Frankel xstats_ptr); 257562ffbc4SNogah Frankel 258562ffbc4SNogah Frankel return -EOPNOTSUPP; 259562ffbc4SNogah Frankel } 260562ffbc4SNogah Frankel 26185005b82SPetr Machata static u64 26285005b82SPetr Machata mlxsw_sp_xstats_backlog(struct mlxsw_sp_port_xstats *xstats, int tclass_num) 26385005b82SPetr Machata { 26485005b82SPetr Machata return xstats->backlog[tclass_num] + 26585005b82SPetr Machata xstats->backlog[tclass_num + 8]; 26685005b82SPetr Machata } 26785005b82SPetr Machata 26885005b82SPetr Machata static u64 26985005b82SPetr Machata mlxsw_sp_xstats_tail_drop(struct mlxsw_sp_port_xstats *xstats, int tclass_num) 27085005b82SPetr Machata { 27185005b82SPetr Machata return xstats->tail_drop[tclass_num] + 27285005b82SPetr Machata xstats->tail_drop[tclass_num + 8]; 27385005b82SPetr Machata } 27485005b82SPetr Machata 27504cc0bf5SNogah Frankel static void 27604cc0bf5SNogah Frankel mlxsw_sp_qdisc_bstats_per_priority_get(struct mlxsw_sp_port_xstats *xstats, 27704cc0bf5SNogah Frankel u8 prio_bitmap, u64 *tx_packets, 27804cc0bf5SNogah Frankel u64 *tx_bytes) 27904cc0bf5SNogah Frankel { 28004cc0bf5SNogah Frankel int i; 28104cc0bf5SNogah Frankel 28204cc0bf5SNogah Frankel *tx_packets = 0; 28304cc0bf5SNogah Frankel *tx_bytes = 0; 28404cc0bf5SNogah Frankel for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { 28504cc0bf5SNogah Frankel if (prio_bitmap & BIT(i)) { 28604cc0bf5SNogah Frankel *tx_packets += xstats->tx_packets[i]; 28704cc0bf5SNogah Frankel *tx_bytes += xstats->tx_bytes[i]; 28804cc0bf5SNogah Frankel } 28904cc0bf5SNogah Frankel } 29004cc0bf5SNogah Frankel } 29104cc0bf5SNogah Frankel 292cf9af379SPetr Machata static void 293cf9af379SPetr Machata mlxsw_sp_qdisc_collect_tc_stats(struct mlxsw_sp_port *mlxsw_sp_port, 294cf9af379SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 295cf9af379SPetr Machata u64 *p_tx_bytes, u64 *p_tx_packets, 296cf9af379SPetr Machata u64 *p_drops, u64 *p_backlog) 297cf9af379SPetr Machata { 298cf9af379SPetr Machata u8 tclass_num = mlxsw_sp_qdisc->tclass_num; 299cf9af379SPetr Machata struct mlxsw_sp_port_xstats *xstats; 300cf9af379SPetr Machata u64 tx_bytes, tx_packets; 301cf9af379SPetr Machata 302cf9af379SPetr Machata xstats = &mlxsw_sp_port->periodic_hw_stats.xstats; 303cf9af379SPetr Machata mlxsw_sp_qdisc_bstats_per_priority_get(xstats, 304cf9af379SPetr Machata mlxsw_sp_qdisc->prio_bitmap, 305cf9af379SPetr Machata &tx_packets, &tx_bytes); 306cf9af379SPetr Machata 307cf9af379SPetr Machata *p_tx_packets += tx_packets; 308cf9af379SPetr Machata *p_tx_bytes += tx_bytes; 309cf9af379SPetr Machata *p_drops += xstats->wred_drop[tclass_num] + 310cf9af379SPetr Machata mlxsw_sp_xstats_tail_drop(xstats, tclass_num); 311cf9af379SPetr Machata *p_backlog += mlxsw_sp_xstats_backlog(xstats, tclass_num); 312cf9af379SPetr Machata } 313cf9af379SPetr Machata 314cf9af379SPetr Machata static void 315cf9af379SPetr Machata mlxsw_sp_qdisc_update_stats(struct mlxsw_sp *mlxsw_sp, 316cf9af379SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 317cf9af379SPetr Machata u64 tx_bytes, u64 tx_packets, 318cf9af379SPetr Machata u64 drops, u64 backlog, 319cf9af379SPetr Machata struct tc_qopt_offload_stats *stats_ptr) 320cf9af379SPetr Machata { 321cf9af379SPetr Machata struct mlxsw_sp_qdisc_stats *stats_base = &mlxsw_sp_qdisc->stats_base; 322cf9af379SPetr Machata 323cf9af379SPetr Machata tx_bytes -= stats_base->tx_bytes; 324cf9af379SPetr Machata tx_packets -= stats_base->tx_packets; 325cf9af379SPetr Machata drops -= stats_base->drops; 326cf9af379SPetr Machata backlog -= stats_base->backlog; 327cf9af379SPetr Machata 328cf9af379SPetr Machata _bstats_update(stats_ptr->bstats, tx_bytes, tx_packets); 329cf9af379SPetr Machata stats_ptr->qstats->drops += drops; 330cf9af379SPetr Machata stats_ptr->qstats->backlog += mlxsw_sp_cells_bytes(mlxsw_sp, backlog); 331cf9af379SPetr Machata 332cf9af379SPetr Machata stats_base->backlog += backlog; 333cf9af379SPetr Machata stats_base->drops += drops; 334cf9af379SPetr Machata stats_base->tx_bytes += tx_bytes; 335cf9af379SPetr Machata stats_base->tx_packets += tx_packets; 336cf9af379SPetr Machata } 337cf9af379SPetr Machata 3383d0d5921SPetr Machata static void 3393d0d5921SPetr Machata mlxsw_sp_qdisc_get_tc_stats(struct mlxsw_sp_port *mlxsw_sp_port, 3403d0d5921SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 3413d0d5921SPetr Machata struct tc_qopt_offload_stats *stats_ptr) 3423d0d5921SPetr Machata { 3433d0d5921SPetr Machata u64 tx_packets = 0; 3443d0d5921SPetr Machata u64 tx_bytes = 0; 3453d0d5921SPetr Machata u64 backlog = 0; 3463d0d5921SPetr Machata u64 drops = 0; 3473d0d5921SPetr Machata 3483d0d5921SPetr Machata mlxsw_sp_qdisc_collect_tc_stats(mlxsw_sp_port, mlxsw_sp_qdisc, 3493d0d5921SPetr Machata &tx_bytes, &tx_packets, 3503d0d5921SPetr Machata &drops, &backlog); 3513d0d5921SPetr Machata mlxsw_sp_qdisc_update_stats(mlxsw_sp_port->mlxsw_sp, mlxsw_sp_qdisc, 3523d0d5921SPetr Machata tx_bytes, tx_packets, drops, backlog, 3533d0d5921SPetr Machata stats_ptr); 3543d0d5921SPetr Machata } 3553d0d5921SPetr Machata 356562ffbc4SNogah Frankel static int 35796f17e07SNogah Frankel mlxsw_sp_tclass_congestion_enable(struct mlxsw_sp_port *mlxsw_sp_port, 35896f17e07SNogah Frankel int tclass_num, u32 min, u32 max, 3598040c96bSPetr Machata u32 probability, bool is_wred, bool is_ecn) 36096f17e07SNogah Frankel { 361db84924cSJiri Pirko char cwtpm_cmd[MLXSW_REG_CWTPM_LEN]; 362db84924cSJiri Pirko char cwtp_cmd[MLXSW_REG_CWTP_LEN]; 36396f17e07SNogah Frankel struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 36496f17e07SNogah Frankel int err; 36596f17e07SNogah Frankel 36696f17e07SNogah Frankel mlxsw_reg_cwtp_pack(cwtp_cmd, mlxsw_sp_port->local_port, tclass_num); 36796f17e07SNogah Frankel mlxsw_reg_cwtp_profile_pack(cwtp_cmd, MLXSW_REG_CWTP_DEFAULT_PROFILE, 36896f17e07SNogah Frankel roundup(min, MLXSW_REG_CWTP_MIN_VALUE), 36996f17e07SNogah Frankel roundup(max, MLXSW_REG_CWTP_MIN_VALUE), 37096f17e07SNogah Frankel probability); 37196f17e07SNogah Frankel 37296f17e07SNogah Frankel err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(cwtp), cwtp_cmd); 37396f17e07SNogah Frankel if (err) 37496f17e07SNogah Frankel return err; 37596f17e07SNogah Frankel 376db84924cSJiri Pirko mlxsw_reg_cwtpm_pack(cwtpm_cmd, mlxsw_sp_port->local_port, tclass_num, 3778040c96bSPetr Machata MLXSW_REG_CWTP_DEFAULT_PROFILE, is_wred, is_ecn); 37896f17e07SNogah Frankel 379db84924cSJiri Pirko return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(cwtpm), cwtpm_cmd); 38096f17e07SNogah Frankel } 38196f17e07SNogah Frankel 38296f17e07SNogah Frankel static int 38396f17e07SNogah Frankel mlxsw_sp_tclass_congestion_disable(struct mlxsw_sp_port *mlxsw_sp_port, 38496f17e07SNogah Frankel int tclass_num) 38596f17e07SNogah Frankel { 38696f17e07SNogah Frankel struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 38796f17e07SNogah Frankel char cwtpm_cmd[MLXSW_REG_CWTPM_LEN]; 38896f17e07SNogah Frankel 38996f17e07SNogah Frankel mlxsw_reg_cwtpm_pack(cwtpm_cmd, mlxsw_sp_port->local_port, tclass_num, 39096f17e07SNogah Frankel MLXSW_REG_CWTPM_RESET_PROFILE, false, false); 39196f17e07SNogah Frankel return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(cwtpm), cwtpm_cmd); 39296f17e07SNogah Frankel } 39396f17e07SNogah Frankel 394861fb829SNogah Frankel static void 395c2ed6db7SNogah Frankel mlxsw_sp_setup_tc_qdisc_red_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port, 396d56c8955SNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 397861fb829SNogah Frankel { 398d56c8955SNogah Frankel u8 tclass_num = mlxsw_sp_qdisc->tclass_num; 3994d1a4b84SNogah Frankel struct mlxsw_sp_qdisc_stats *stats_base; 400861fb829SNogah Frankel struct mlxsw_sp_port_xstats *xstats; 4014d1a4b84SNogah Frankel struct red_stats *red_base; 402861fb829SNogah Frankel 403861fb829SNogah Frankel xstats = &mlxsw_sp_port->periodic_hw_stats.xstats; 4044d1a4b84SNogah Frankel stats_base = &mlxsw_sp_qdisc->stats_base; 405c2ed6db7SNogah Frankel red_base = &mlxsw_sp_qdisc->xstats_base.red; 4063670756fSNogah Frankel 40704cc0bf5SNogah Frankel mlxsw_sp_qdisc_bstats_per_priority_get(xstats, 40804cc0bf5SNogah Frankel mlxsw_sp_qdisc->prio_bitmap, 40904cc0bf5SNogah Frankel &stats_base->tx_packets, 41004cc0bf5SNogah Frankel &stats_base->tx_bytes); 4114d1a4b84SNogah Frankel red_base->prob_drop = xstats->wred_drop[tclass_num]; 41285005b82SPetr Machata red_base->pdrop = mlxsw_sp_xstats_tail_drop(xstats, tclass_num); 4133670756fSNogah Frankel 414c2ed6db7SNogah Frankel stats_base->overlimits = red_base->prob_drop + red_base->prob_mark; 4154d1a4b84SNogah Frankel stats_base->drops = red_base->prob_drop + red_base->pdrop; 416416ef9b1SJakub Kicinski 417416ef9b1SJakub Kicinski stats_base->backlog = 0; 418861fb829SNogah Frankel } 419861fb829SNogah Frankel 42096f17e07SNogah Frankel static int 421cba7158fSNogah Frankel mlxsw_sp_qdisc_red_destroy(struct mlxsw_sp_port *mlxsw_sp_port, 422d56c8955SNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 42396f17e07SNogah Frankel { 424ee88450dSPetr Machata struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc; 425ee88450dSPetr Machata struct mlxsw_sp_qdisc *root_qdisc = &qdisc_state->root_qdisc; 426cc6e5c13SNogah Frankel 427cc6e5c13SNogah Frankel if (root_qdisc != mlxsw_sp_qdisc) 428cc6e5c13SNogah Frankel root_qdisc->stats_base.backlog -= 429cc6e5c13SNogah Frankel mlxsw_sp_qdisc->stats_base.backlog; 430cc6e5c13SNogah Frankel 4319a37a59fSNogah Frankel return mlxsw_sp_tclass_congestion_disable(mlxsw_sp_port, 432d56c8955SNogah Frankel mlxsw_sp_qdisc->tclass_num); 43396f17e07SNogah Frankel } 43496f17e07SNogah Frankel 43596f17e07SNogah Frankel static int 4369cf6c9c7SNogah Frankel mlxsw_sp_qdisc_red_check_params(struct mlxsw_sp_port *mlxsw_sp_port, 43796f17e07SNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 4389cf6c9c7SNogah Frankel void *params) 43996f17e07SNogah Frankel { 44096f17e07SNogah Frankel struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 4419cf6c9c7SNogah Frankel struct tc_red_qopt_offload_params *p = params; 44296f17e07SNogah Frankel 44396f17e07SNogah Frankel if (p->min > p->max) { 44496f17e07SNogah Frankel dev_err(mlxsw_sp->bus_info->dev, 44596f17e07SNogah Frankel "spectrum: RED: min %u is bigger then max %u\n", p->min, 44696f17e07SNogah Frankel p->max); 4479cf6c9c7SNogah Frankel return -EINVAL; 44896f17e07SNogah Frankel } 449914c4fc1SPetr Machata if (p->max > MLXSW_CORE_RES_GET(mlxsw_sp->core, 450914c4fc1SPetr Machata GUARANTEED_SHARED_BUFFER)) { 45196f17e07SNogah Frankel dev_err(mlxsw_sp->bus_info->dev, 45296f17e07SNogah Frankel "spectrum: RED: max value %u is too big\n", p->max); 4539cf6c9c7SNogah Frankel return -EINVAL; 45496f17e07SNogah Frankel } 45596f17e07SNogah Frankel if (p->min == 0 || p->max == 0) { 45696f17e07SNogah Frankel dev_err(mlxsw_sp->bus_info->dev, 45796f17e07SNogah Frankel "spectrum: RED: 0 value is illegal for min and max\n"); 4589cf6c9c7SNogah Frankel return -EINVAL; 45996f17e07SNogah Frankel } 4609cf6c9c7SNogah Frankel return 0; 4619cf6c9c7SNogah Frankel } 4629cf6c9c7SNogah Frankel 4639cf6c9c7SNogah Frankel static int 464c4e372e2SPetr Machata mlxsw_sp_qdisc_red_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, 4659cf6c9c7SNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 4669cf6c9c7SNogah Frankel void *params) 4679cf6c9c7SNogah Frankel { 4689cf6c9c7SNogah Frankel struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 4699cf6c9c7SNogah Frankel struct tc_red_qopt_offload_params *p = params; 4709cf6c9c7SNogah Frankel u8 tclass_num = mlxsw_sp_qdisc->tclass_num; 4719cf6c9c7SNogah Frankel u32 min, max; 4729cf6c9c7SNogah Frankel u64 prob; 47396f17e07SNogah Frankel 47496f17e07SNogah Frankel /* calculate probability in percentage */ 47596f17e07SNogah Frankel prob = p->probability; 47696f17e07SNogah Frankel prob *= 100; 47796f17e07SNogah Frankel prob = DIV_ROUND_UP(prob, 1 << 16); 47896f17e07SNogah Frankel prob = DIV_ROUND_UP(prob, 1 << 16); 47996f17e07SNogah Frankel min = mlxsw_sp_bytes_cells(mlxsw_sp, p->min); 48096f17e07SNogah Frankel max = mlxsw_sp_bytes_cells(mlxsw_sp, p->max); 4818040c96bSPetr Machata return mlxsw_sp_tclass_congestion_enable(mlxsw_sp_port, tclass_num, 4828040c96bSPetr Machata min, max, prob, 4838040c96bSPetr Machata !p->is_nodrop, p->is_ecn); 48496f17e07SNogah Frankel } 48596f17e07SNogah Frankel 486416ef9b1SJakub Kicinski static void 487be1d5a8aSPetr Machata mlxsw_sp_qdisc_leaf_unoffload(struct mlxsw_sp_port *mlxsw_sp_port, 488be1d5a8aSPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 489be1d5a8aSPetr Machata struct gnet_stats_queue *qstats) 490be1d5a8aSPetr Machata { 491be1d5a8aSPetr Machata u64 backlog; 492be1d5a8aSPetr Machata 493be1d5a8aSPetr Machata backlog = mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp, 494be1d5a8aSPetr Machata mlxsw_sp_qdisc->stats_base.backlog); 495be1d5a8aSPetr Machata qstats->backlog -= backlog; 496be1d5a8aSPetr Machata mlxsw_sp_qdisc->stats_base.backlog = 0; 497be1d5a8aSPetr Machata } 498be1d5a8aSPetr Machata 499be1d5a8aSPetr Machata static void 500416ef9b1SJakub Kicinski mlxsw_sp_qdisc_red_unoffload(struct mlxsw_sp_port *mlxsw_sp_port, 501416ef9b1SJakub Kicinski struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 502416ef9b1SJakub Kicinski void *params) 503416ef9b1SJakub Kicinski { 504416ef9b1SJakub Kicinski struct tc_red_qopt_offload_params *p = params; 505416ef9b1SJakub Kicinski 506be1d5a8aSPetr Machata mlxsw_sp_qdisc_leaf_unoffload(mlxsw_sp_port, mlxsw_sp_qdisc, p->qstats); 507416ef9b1SJakub Kicinski } 508416ef9b1SJakub Kicinski 509861fb829SNogah Frankel static int 510cba7158fSNogah Frankel mlxsw_sp_qdisc_get_red_xstats(struct mlxsw_sp_port *mlxsw_sp_port, 511861fb829SNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 512562ffbc4SNogah Frankel void *xstats_ptr) 513861fb829SNogah Frankel { 5144d1a4b84SNogah Frankel struct red_stats *xstats_base = &mlxsw_sp_qdisc->xstats_base.red; 515d56c8955SNogah Frankel u8 tclass_num = mlxsw_sp_qdisc->tclass_num; 516861fb829SNogah Frankel struct mlxsw_sp_port_xstats *xstats; 517562ffbc4SNogah Frankel struct red_stats *res = xstats_ptr; 5188a29581eSPetr Machata int early_drops, pdrops; 519861fb829SNogah Frankel 520861fb829SNogah Frankel xstats = &mlxsw_sp_port->periodic_hw_stats.xstats; 521861fb829SNogah Frankel 522f8253df5SNogah Frankel early_drops = xstats->wred_drop[tclass_num] - xstats_base->prob_drop; 52385005b82SPetr Machata pdrops = mlxsw_sp_xstats_tail_drop(xstats, tclass_num) - 52485005b82SPetr Machata xstats_base->pdrop; 525f8253df5SNogah Frankel 526f8253df5SNogah Frankel res->pdrop += pdrops; 527f8253df5SNogah Frankel res->prob_drop += early_drops; 528f8253df5SNogah Frankel 529f8253df5SNogah Frankel xstats_base->pdrop += pdrops; 530f8253df5SNogah Frankel xstats_base->prob_drop += early_drops; 531861fb829SNogah Frankel return 0; 532861fb829SNogah Frankel } 533861fb829SNogah Frankel 5343670756fSNogah Frankel static int 535cba7158fSNogah Frankel mlxsw_sp_qdisc_get_red_stats(struct mlxsw_sp_port *mlxsw_sp_port, 5363670756fSNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 537562ffbc4SNogah Frankel struct tc_qopt_offload_stats *stats_ptr) 5383670756fSNogah Frankel { 539d56c8955SNogah Frankel u8 tclass_num = mlxsw_sp_qdisc->tclass_num; 5404d1a4b84SNogah Frankel struct mlxsw_sp_qdisc_stats *stats_base; 5413670756fSNogah Frankel struct mlxsw_sp_port_xstats *xstats; 542cf9af379SPetr Machata u64 overlimits; 5433670756fSNogah Frankel 5443670756fSNogah Frankel xstats = &mlxsw_sp_port->periodic_hw_stats.xstats; 5454d1a4b84SNogah Frankel stats_base = &mlxsw_sp_qdisc->stats_base; 5463670756fSNogah Frankel 5473d0d5921SPetr Machata mlxsw_sp_qdisc_get_tc_stats(mlxsw_sp_port, mlxsw_sp_qdisc, stats_ptr); 5488a29581eSPetr Machata overlimits = xstats->wred_drop[tclass_num] - stats_base->overlimits; 5493670756fSNogah Frankel 550562ffbc4SNogah Frankel stats_ptr->qstats->overlimits += overlimits; 5514d1a4b84SNogah Frankel stats_base->overlimits += overlimits; 552cf9af379SPetr Machata 5533670756fSNogah Frankel return 0; 5543670756fSNogah Frankel } 5553670756fSNogah Frankel 55696f17e07SNogah Frankel #define MLXSW_SP_PORT_DEFAULT_TCLASS 0 55796f17e07SNogah Frankel 558562ffbc4SNogah Frankel static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_red = { 5599cf6c9c7SNogah Frankel .type = MLXSW_SP_QDISC_RED, 5609cf6c9c7SNogah Frankel .check_params = mlxsw_sp_qdisc_red_check_params, 5619cf6c9c7SNogah Frankel .replace = mlxsw_sp_qdisc_red_replace, 562416ef9b1SJakub Kicinski .unoffload = mlxsw_sp_qdisc_red_unoffload, 5639a37a59fSNogah Frankel .destroy = mlxsw_sp_qdisc_red_destroy, 564562ffbc4SNogah Frankel .get_stats = mlxsw_sp_qdisc_get_red_stats, 565562ffbc4SNogah Frankel .get_xstats = mlxsw_sp_qdisc_get_red_xstats, 5669cf6c9c7SNogah Frankel .clean_stats = mlxsw_sp_setup_tc_qdisc_red_clean_stats, 567562ffbc4SNogah Frankel }; 568562ffbc4SNogah Frankel 56996f17e07SNogah Frankel int mlxsw_sp_setup_tc_red(struct mlxsw_sp_port *mlxsw_sp_port, 57096f17e07SNogah Frankel struct tc_red_qopt_offload *p) 57196f17e07SNogah Frankel { 57296f17e07SNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc; 57396f17e07SNogah Frankel 574eed4baebSNogah Frankel mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent, false); 575eed4baebSNogah Frankel if (!mlxsw_sp_qdisc) 57696f17e07SNogah Frankel return -EOPNOTSUPP; 57796f17e07SNogah Frankel 578cba7158fSNogah Frankel if (p->command == TC_RED_REPLACE) 5799cf6c9c7SNogah Frankel return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle, 580562ffbc4SNogah Frankel mlxsw_sp_qdisc, 581562ffbc4SNogah Frankel &mlxsw_sp_qdisc_ops_red, 582562ffbc4SNogah Frankel &p->set); 583cba7158fSNogah Frankel 584cba7158fSNogah Frankel if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle, 585cba7158fSNogah Frankel MLXSW_SP_QDISC_RED)) 586cba7158fSNogah Frankel return -EOPNOTSUPP; 587cba7158fSNogah Frankel 588cba7158fSNogah Frankel switch (p->command) { 58996f17e07SNogah Frankel case TC_RED_DESTROY: 5909a37a59fSNogah Frankel return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc); 591861fb829SNogah Frankel case TC_RED_XSTATS: 592562ffbc4SNogah Frankel return mlxsw_sp_qdisc_get_xstats(mlxsw_sp_port, mlxsw_sp_qdisc, 593861fb829SNogah Frankel p->xstats); 5943670756fSNogah Frankel case TC_RED_STATS: 595562ffbc4SNogah Frankel return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc, 5963670756fSNogah Frankel &p->stats); 59796f17e07SNogah Frankel default: 59896f17e07SNogah Frankel return -EOPNOTSUPP; 59996f17e07SNogah Frankel } 60096f17e07SNogah Frankel } 601371b437aSNogah Frankel 602a44f58c4SPetr Machata static void 603a44f58c4SPetr Machata mlxsw_sp_setup_tc_qdisc_leaf_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port, 604a44f58c4SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 605a44f58c4SPetr Machata { 606a44f58c4SPetr Machata u64 backlog_cells = 0; 607a44f58c4SPetr Machata u64 tx_packets = 0; 608a44f58c4SPetr Machata u64 tx_bytes = 0; 609a44f58c4SPetr Machata u64 drops = 0; 610a44f58c4SPetr Machata 611a44f58c4SPetr Machata mlxsw_sp_qdisc_collect_tc_stats(mlxsw_sp_port, mlxsw_sp_qdisc, 612a44f58c4SPetr Machata &tx_bytes, &tx_packets, 613a44f58c4SPetr Machata &drops, &backlog_cells); 614a44f58c4SPetr Machata 615a44f58c4SPetr Machata mlxsw_sp_qdisc->stats_base.tx_packets = tx_packets; 616a44f58c4SPetr Machata mlxsw_sp_qdisc->stats_base.tx_bytes = tx_bytes; 617a44f58c4SPetr Machata mlxsw_sp_qdisc->stats_base.drops = drops; 618a44f58c4SPetr Machata mlxsw_sp_qdisc->stats_base.backlog = 0; 619a44f58c4SPetr Machata } 620a44f58c4SPetr Machata 621a44f58c4SPetr Machata static int 622a44f58c4SPetr Machata mlxsw_sp_qdisc_tbf_destroy(struct mlxsw_sp_port *mlxsw_sp_port, 623a44f58c4SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 624a44f58c4SPetr Machata { 625ee88450dSPetr Machata struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc; 626ee88450dSPetr Machata struct mlxsw_sp_qdisc *root_qdisc = &qdisc_state->root_qdisc; 627a44f58c4SPetr Machata 628a44f58c4SPetr Machata if (root_qdisc != mlxsw_sp_qdisc) 629a44f58c4SPetr Machata root_qdisc->stats_base.backlog -= 630a44f58c4SPetr Machata mlxsw_sp_qdisc->stats_base.backlog; 631a44f58c4SPetr Machata 632a44f58c4SPetr Machata return mlxsw_sp_port_ets_maxrate_set(mlxsw_sp_port, 633a44f58c4SPetr Machata MLXSW_REG_QEEC_HR_SUBGROUP, 634a44f58c4SPetr Machata mlxsw_sp_qdisc->tclass_num, 0, 635a44f58c4SPetr Machata MLXSW_REG_QEEC_MAS_DIS, 0); 636a44f58c4SPetr Machata } 637a44f58c4SPetr Machata 638a44f58c4SPetr Machata static int 639a44f58c4SPetr Machata mlxsw_sp_qdisc_tbf_bs(struct mlxsw_sp_port *mlxsw_sp_port, 640a44f58c4SPetr Machata u32 max_size, u8 *p_burst_size) 641a44f58c4SPetr Machata { 642a44f58c4SPetr Machata /* TBF burst size is configured in bytes. The ASIC burst size value is 643a44f58c4SPetr Machata * ((2 ^ bs) * 512 bits. Convert the TBF bytes to 512-bit units. 644a44f58c4SPetr Machata */ 645a44f58c4SPetr Machata u32 bs512 = max_size / 64; 646a44f58c4SPetr Machata u8 bs = fls(bs512); 647a44f58c4SPetr Machata 648a44f58c4SPetr Machata if (!bs) 649a44f58c4SPetr Machata return -EINVAL; 650a44f58c4SPetr Machata --bs; 651a44f58c4SPetr Machata 652a44f58c4SPetr Machata /* Demand a power of two. */ 653a44f58c4SPetr Machata if ((1 << bs) != bs512) 654a44f58c4SPetr Machata return -EINVAL; 655a44f58c4SPetr Machata 656a44f58c4SPetr Machata if (bs < mlxsw_sp_port->mlxsw_sp->lowest_shaper_bs || 657a44f58c4SPetr Machata bs > MLXSW_REG_QEEC_HIGHEST_SHAPER_BS) 658a44f58c4SPetr Machata return -EINVAL; 659a44f58c4SPetr Machata 660a44f58c4SPetr Machata *p_burst_size = bs; 661a44f58c4SPetr Machata return 0; 662a44f58c4SPetr Machata } 663a44f58c4SPetr Machata 664a44f58c4SPetr Machata static u32 665a44f58c4SPetr Machata mlxsw_sp_qdisc_tbf_max_size(u8 bs) 666a44f58c4SPetr Machata { 667a44f58c4SPetr Machata return (1U << bs) * 64; 668a44f58c4SPetr Machata } 669a44f58c4SPetr Machata 670a44f58c4SPetr Machata static u64 671a44f58c4SPetr Machata mlxsw_sp_qdisc_tbf_rate_kbps(struct tc_tbf_qopt_offload_replace_params *p) 672a44f58c4SPetr Machata { 673a44f58c4SPetr Machata /* TBF interface is in bytes/s, whereas Spectrum ASIC is configured in 674a44f58c4SPetr Machata * Kbits/s. 675a44f58c4SPetr Machata */ 67691a7d4bfSNathan Chancellor return div_u64(p->rate.rate_bytes_ps, 1000) * 8; 677a44f58c4SPetr Machata } 678a44f58c4SPetr Machata 679a44f58c4SPetr Machata static int 680a44f58c4SPetr Machata mlxsw_sp_qdisc_tbf_check_params(struct mlxsw_sp_port *mlxsw_sp_port, 681a44f58c4SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 682a44f58c4SPetr Machata void *params) 683a44f58c4SPetr Machata { 684a44f58c4SPetr Machata struct tc_tbf_qopt_offload_replace_params *p = params; 685a44f58c4SPetr Machata struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 686a44f58c4SPetr Machata u64 rate_kbps = mlxsw_sp_qdisc_tbf_rate_kbps(p); 687a44f58c4SPetr Machata u8 burst_size; 688a44f58c4SPetr Machata int err; 689a44f58c4SPetr Machata 690a44f58c4SPetr Machata if (rate_kbps >= MLXSW_REG_QEEC_MAS_DIS) { 691a44f58c4SPetr Machata dev_err(mlxsw_sp_port->mlxsw_sp->bus_info->dev, 692a44f58c4SPetr Machata "spectrum: TBF: rate of %lluKbps must be below %u\n", 693a44f58c4SPetr Machata rate_kbps, MLXSW_REG_QEEC_MAS_DIS); 694a44f58c4SPetr Machata return -EINVAL; 695a44f58c4SPetr Machata } 696a44f58c4SPetr Machata 697a44f58c4SPetr Machata err = mlxsw_sp_qdisc_tbf_bs(mlxsw_sp_port, p->max_size, &burst_size); 698a44f58c4SPetr Machata if (err) { 699a44f58c4SPetr Machata u8 highest_shaper_bs = MLXSW_REG_QEEC_HIGHEST_SHAPER_BS; 700a44f58c4SPetr Machata 701a44f58c4SPetr Machata dev_err(mlxsw_sp->bus_info->dev, 702a44f58c4SPetr Machata "spectrum: TBF: invalid burst size of %u, must be a power of two between %u and %u", 703a44f58c4SPetr Machata p->max_size, 704a44f58c4SPetr Machata mlxsw_sp_qdisc_tbf_max_size(mlxsw_sp->lowest_shaper_bs), 705a44f58c4SPetr Machata mlxsw_sp_qdisc_tbf_max_size(highest_shaper_bs)); 706a44f58c4SPetr Machata return -EINVAL; 707a44f58c4SPetr Machata } 708a44f58c4SPetr Machata 709a44f58c4SPetr Machata return 0; 710a44f58c4SPetr Machata } 711a44f58c4SPetr Machata 712a44f58c4SPetr Machata static int 713c4e372e2SPetr Machata mlxsw_sp_qdisc_tbf_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, 714a44f58c4SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 715a44f58c4SPetr Machata void *params) 716a44f58c4SPetr Machata { 717a44f58c4SPetr Machata struct tc_tbf_qopt_offload_replace_params *p = params; 718a44f58c4SPetr Machata u64 rate_kbps = mlxsw_sp_qdisc_tbf_rate_kbps(p); 719a44f58c4SPetr Machata u8 burst_size; 720a44f58c4SPetr Machata int err; 721a44f58c4SPetr Machata 722a44f58c4SPetr Machata err = mlxsw_sp_qdisc_tbf_bs(mlxsw_sp_port, p->max_size, &burst_size); 723a44f58c4SPetr Machata if (WARN_ON_ONCE(err)) 724a44f58c4SPetr Machata /* check_params above was supposed to reject this value. */ 725a44f58c4SPetr Machata return -EINVAL; 726a44f58c4SPetr Machata 727a44f58c4SPetr Machata /* Configure subgroup shaper, so that both UC and MC traffic is subject 728a44f58c4SPetr Machata * to shaping. That is unlike RED, however UC queue lengths are going to 729a44f58c4SPetr Machata * be different than MC ones due to different pool and quota 730a44f58c4SPetr Machata * configurations, so the configuration is not applicable. For shaper on 731a44f58c4SPetr Machata * the other hand, subjecting the overall stream to the configured 732a44f58c4SPetr Machata * shaper makes sense. Also note that that is what we do for 733a44f58c4SPetr Machata * ieee_setmaxrate(). 734a44f58c4SPetr Machata */ 735a44f58c4SPetr Machata return mlxsw_sp_port_ets_maxrate_set(mlxsw_sp_port, 736a44f58c4SPetr Machata MLXSW_REG_QEEC_HR_SUBGROUP, 737a44f58c4SPetr Machata mlxsw_sp_qdisc->tclass_num, 0, 738a44f58c4SPetr Machata rate_kbps, burst_size); 739a44f58c4SPetr Machata } 740a44f58c4SPetr Machata 741a44f58c4SPetr Machata static void 742a44f58c4SPetr Machata mlxsw_sp_qdisc_tbf_unoffload(struct mlxsw_sp_port *mlxsw_sp_port, 743a44f58c4SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 744a44f58c4SPetr Machata void *params) 745a44f58c4SPetr Machata { 746a44f58c4SPetr Machata struct tc_tbf_qopt_offload_replace_params *p = params; 747a44f58c4SPetr Machata 748a44f58c4SPetr Machata mlxsw_sp_qdisc_leaf_unoffload(mlxsw_sp_port, mlxsw_sp_qdisc, p->qstats); 749a44f58c4SPetr Machata } 750a44f58c4SPetr Machata 751a44f58c4SPetr Machata static int 752a44f58c4SPetr Machata mlxsw_sp_qdisc_get_tbf_stats(struct mlxsw_sp_port *mlxsw_sp_port, 753a44f58c4SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 754a44f58c4SPetr Machata struct tc_qopt_offload_stats *stats_ptr) 755a44f58c4SPetr Machata { 756a44f58c4SPetr Machata mlxsw_sp_qdisc_get_tc_stats(mlxsw_sp_port, mlxsw_sp_qdisc, 757a44f58c4SPetr Machata stats_ptr); 758a44f58c4SPetr Machata return 0; 759a44f58c4SPetr Machata } 760a44f58c4SPetr Machata 761a44f58c4SPetr Machata static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_tbf = { 762a44f58c4SPetr Machata .type = MLXSW_SP_QDISC_TBF, 763a44f58c4SPetr Machata .check_params = mlxsw_sp_qdisc_tbf_check_params, 764a44f58c4SPetr Machata .replace = mlxsw_sp_qdisc_tbf_replace, 765a44f58c4SPetr Machata .unoffload = mlxsw_sp_qdisc_tbf_unoffload, 766a44f58c4SPetr Machata .destroy = mlxsw_sp_qdisc_tbf_destroy, 767a44f58c4SPetr Machata .get_stats = mlxsw_sp_qdisc_get_tbf_stats, 768a44f58c4SPetr Machata .clean_stats = mlxsw_sp_setup_tc_qdisc_leaf_clean_stats, 769a44f58c4SPetr Machata }; 770a44f58c4SPetr Machata 771a44f58c4SPetr Machata int mlxsw_sp_setup_tc_tbf(struct mlxsw_sp_port *mlxsw_sp_port, 772a44f58c4SPetr Machata struct tc_tbf_qopt_offload *p) 773a44f58c4SPetr Machata { 774a44f58c4SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc; 775a44f58c4SPetr Machata 776a44f58c4SPetr Machata mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent, false); 777a44f58c4SPetr Machata if (!mlxsw_sp_qdisc) 778a44f58c4SPetr Machata return -EOPNOTSUPP; 779a44f58c4SPetr Machata 780a44f58c4SPetr Machata if (p->command == TC_TBF_REPLACE) 781a44f58c4SPetr Machata return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle, 782a44f58c4SPetr Machata mlxsw_sp_qdisc, 783a44f58c4SPetr Machata &mlxsw_sp_qdisc_ops_tbf, 784a44f58c4SPetr Machata &p->replace_params); 785a44f58c4SPetr Machata 786a44f58c4SPetr Machata if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle, 787a44f58c4SPetr Machata MLXSW_SP_QDISC_TBF)) 788a44f58c4SPetr Machata return -EOPNOTSUPP; 789a44f58c4SPetr Machata 790a44f58c4SPetr Machata switch (p->command) { 791a44f58c4SPetr Machata case TC_TBF_DESTROY: 792a44f58c4SPetr Machata return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc); 793a44f58c4SPetr Machata case TC_TBF_STATS: 794a44f58c4SPetr Machata return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc, 795a44f58c4SPetr Machata &p->stats); 796a44f58c4SPetr Machata default: 797a44f58c4SPetr Machata return -EOPNOTSUPP; 798a44f58c4SPetr Machata } 799a44f58c4SPetr Machata } 800a44f58c4SPetr Machata 80146a3615bSNogah Frankel static int 8027bec1a45SPetr Machata mlxsw_sp_qdisc_fifo_destroy(struct mlxsw_sp_port *mlxsw_sp_port, 8037bec1a45SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 8047bec1a45SPetr Machata { 8057bec1a45SPetr Machata struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc; 8067bec1a45SPetr Machata struct mlxsw_sp_qdisc *root_qdisc = &qdisc_state->root_qdisc; 8077bec1a45SPetr Machata 8087bec1a45SPetr Machata if (root_qdisc != mlxsw_sp_qdisc) 8097bec1a45SPetr Machata root_qdisc->stats_base.backlog -= 8107bec1a45SPetr Machata mlxsw_sp_qdisc->stats_base.backlog; 8117bec1a45SPetr Machata return 0; 8127bec1a45SPetr Machata } 8137bec1a45SPetr Machata 8147bec1a45SPetr Machata static int 8157bec1a45SPetr Machata mlxsw_sp_qdisc_fifo_check_params(struct mlxsw_sp_port *mlxsw_sp_port, 8167bec1a45SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 8177bec1a45SPetr Machata void *params) 8187bec1a45SPetr Machata { 8197bec1a45SPetr Machata return 0; 8207bec1a45SPetr Machata } 8217bec1a45SPetr Machata 8227bec1a45SPetr Machata static int 8237bec1a45SPetr Machata mlxsw_sp_qdisc_fifo_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, 8247bec1a45SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 8257bec1a45SPetr Machata void *params) 8267bec1a45SPetr Machata { 8277bec1a45SPetr Machata return 0; 8287bec1a45SPetr Machata } 8297bec1a45SPetr Machata 8307bec1a45SPetr Machata static int 8317bec1a45SPetr Machata mlxsw_sp_qdisc_get_fifo_stats(struct mlxsw_sp_port *mlxsw_sp_port, 8327bec1a45SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 8337bec1a45SPetr Machata struct tc_qopt_offload_stats *stats_ptr) 8347bec1a45SPetr Machata { 8357bec1a45SPetr Machata mlxsw_sp_qdisc_get_tc_stats(mlxsw_sp_port, mlxsw_sp_qdisc, 8367bec1a45SPetr Machata stats_ptr); 8377bec1a45SPetr Machata return 0; 8387bec1a45SPetr Machata } 8397bec1a45SPetr Machata 8407bec1a45SPetr Machata static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_fifo = { 8417bec1a45SPetr Machata .type = MLXSW_SP_QDISC_FIFO, 8427bec1a45SPetr Machata .check_params = mlxsw_sp_qdisc_fifo_check_params, 8437bec1a45SPetr Machata .replace = mlxsw_sp_qdisc_fifo_replace, 8447bec1a45SPetr Machata .destroy = mlxsw_sp_qdisc_fifo_destroy, 8457bec1a45SPetr Machata .get_stats = mlxsw_sp_qdisc_get_fifo_stats, 8467bec1a45SPetr Machata .clean_stats = mlxsw_sp_setup_tc_qdisc_leaf_clean_stats, 8477bec1a45SPetr Machata }; 8487bec1a45SPetr Machata 8497bec1a45SPetr Machata int mlxsw_sp_setup_tc_fifo(struct mlxsw_sp_port *mlxsw_sp_port, 8507bec1a45SPetr Machata struct tc_fifo_qopt_offload *p) 8517bec1a45SPetr Machata { 8527bec1a45SPetr Machata struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc; 8537bec1a45SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc; 8547bec1a45SPetr Machata int tclass, child_index; 8557bec1a45SPetr Machata u32 parent_handle; 8567bec1a45SPetr Machata 8577bec1a45SPetr Machata /* Invisible FIFOs are tracked in future_handle and future_fifos. Make 8587bec1a45SPetr Machata * sure that not more than one qdisc is created for a port at a time. 8597bec1a45SPetr Machata * RTNL is a simple proxy for that. 8607bec1a45SPetr Machata */ 8617bec1a45SPetr Machata ASSERT_RTNL(); 8627bec1a45SPetr Machata 8637bec1a45SPetr Machata mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent, false); 8647bec1a45SPetr Machata if (!mlxsw_sp_qdisc && p->handle == TC_H_UNSPEC) { 8657bec1a45SPetr Machata parent_handle = TC_H_MAJ(p->parent); 8667bec1a45SPetr Machata if (parent_handle != qdisc_state->future_handle) { 8677bec1a45SPetr Machata /* This notifications is for a different Qdisc than 8687bec1a45SPetr Machata * previously. Wipe the future cache. 8697bec1a45SPetr Machata */ 8707bec1a45SPetr Machata memset(qdisc_state->future_fifos, 0, 8717bec1a45SPetr Machata sizeof(qdisc_state->future_fifos)); 8727bec1a45SPetr Machata qdisc_state->future_handle = parent_handle; 8737bec1a45SPetr Machata } 8747bec1a45SPetr Machata 8757bec1a45SPetr Machata child_index = TC_H_MIN(p->parent); 8767bec1a45SPetr Machata tclass = MLXSW_SP_PRIO_CHILD_TO_TCLASS(child_index); 8777bec1a45SPetr Machata if (tclass < IEEE_8021QAZ_MAX_TCS) { 8787bec1a45SPetr Machata if (p->command == TC_FIFO_REPLACE) 8797bec1a45SPetr Machata qdisc_state->future_fifos[tclass] = true; 8807bec1a45SPetr Machata else if (p->command == TC_FIFO_DESTROY) 8817bec1a45SPetr Machata qdisc_state->future_fifos[tclass] = false; 8827bec1a45SPetr Machata } 8837bec1a45SPetr Machata } 8847bec1a45SPetr Machata if (!mlxsw_sp_qdisc) 8857bec1a45SPetr Machata return -EOPNOTSUPP; 8867bec1a45SPetr Machata 8877bec1a45SPetr Machata if (p->command == TC_FIFO_REPLACE) { 8887bec1a45SPetr Machata return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle, 8897bec1a45SPetr Machata mlxsw_sp_qdisc, 8907bec1a45SPetr Machata &mlxsw_sp_qdisc_ops_fifo, NULL); 8917bec1a45SPetr Machata } 8927bec1a45SPetr Machata 8937bec1a45SPetr Machata if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle, 8947bec1a45SPetr Machata MLXSW_SP_QDISC_FIFO)) 8957bec1a45SPetr Machata return -EOPNOTSUPP; 8967bec1a45SPetr Machata 8977bec1a45SPetr Machata switch (p->command) { 8987bec1a45SPetr Machata case TC_FIFO_DESTROY: 8997bec1a45SPetr Machata if (p->handle == mlxsw_sp_qdisc->handle) 9007bec1a45SPetr Machata return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, 9017bec1a45SPetr Machata mlxsw_sp_qdisc); 9027bec1a45SPetr Machata return 0; 9037bec1a45SPetr Machata case TC_FIFO_STATS: 9047bec1a45SPetr Machata return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc, 9057bec1a45SPetr Machata &p->stats); 9067bec1a45SPetr Machata case TC_FIFO_REPLACE: /* Handled above. */ 9077bec1a45SPetr Machata break; 9087bec1a45SPetr Machata } 9097bec1a45SPetr Machata 9107bec1a45SPetr Machata return -EOPNOTSUPP; 9117bec1a45SPetr Machata } 9127bec1a45SPetr Machata 9137bec1a45SPetr Machata static int 9147917f52aSPetr Machata __mlxsw_sp_qdisc_ets_destroy(struct mlxsw_sp_port *mlxsw_sp_port) 91546a3615bSNogah Frankel { 916ee88450dSPetr Machata struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc; 91746a3615bSNogah Frankel int i; 91846a3615bSNogah Frankel 919eed4baebSNogah Frankel for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { 92046a3615bSNogah Frankel mlxsw_sp_port_prio_tc_set(mlxsw_sp_port, i, 92146a3615bSNogah Frankel MLXSW_SP_PORT_DEFAULT_TCLASS); 9227917f52aSPetr Machata mlxsw_sp_port_ets_set(mlxsw_sp_port, 9237917f52aSPetr Machata MLXSW_REG_QEEC_HR_SUBGROUP, 9247917f52aSPetr Machata i, 0, false, 0); 925eed4baebSNogah Frankel mlxsw_sp_qdisc_destroy(mlxsw_sp_port, 926ee88450dSPetr Machata &qdisc_state->tclass_qdiscs[i]); 927ee88450dSPetr Machata qdisc_state->tclass_qdiscs[i].prio_bitmap = 0; 928eed4baebSNogah Frankel } 92946a3615bSNogah Frankel 93046a3615bSNogah Frankel return 0; 93146a3615bSNogah Frankel } 93246a3615bSNogah Frankel 93346a3615bSNogah Frankel static int 9347917f52aSPetr Machata mlxsw_sp_qdisc_prio_destroy(struct mlxsw_sp_port *mlxsw_sp_port, 9357917f52aSPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 9367917f52aSPetr Machata { 9377917f52aSPetr Machata return __mlxsw_sp_qdisc_ets_destroy(mlxsw_sp_port); 9387917f52aSPetr Machata } 9397917f52aSPetr Machata 9407917f52aSPetr Machata static int 9417917f52aSPetr Machata __mlxsw_sp_qdisc_ets_check_params(unsigned int nbands) 9427917f52aSPetr Machata { 9437917f52aSPetr Machata if (nbands > IEEE_8021QAZ_MAX_TCS) 9447917f52aSPetr Machata return -EOPNOTSUPP; 9457917f52aSPetr Machata 9467917f52aSPetr Machata return 0; 9477917f52aSPetr Machata } 9487917f52aSPetr Machata 9497917f52aSPetr Machata static int 95046a3615bSNogah Frankel mlxsw_sp_qdisc_prio_check_params(struct mlxsw_sp_port *mlxsw_sp_port, 95146a3615bSNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 95246a3615bSNogah Frankel void *params) 95346a3615bSNogah Frankel { 95446a3615bSNogah Frankel struct tc_prio_qopt_offload_params *p = params; 95546a3615bSNogah Frankel 9567917f52aSPetr Machata return __mlxsw_sp_qdisc_ets_check_params(p->bands); 95746a3615bSNogah Frankel } 95846a3615bSNogah Frankel 95946a3615bSNogah Frankel static int 960c4e372e2SPetr Machata __mlxsw_sp_qdisc_ets_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, 9617917f52aSPetr Machata unsigned int nbands, 9627917f52aSPetr Machata const unsigned int *quanta, 9637917f52aSPetr Machata const unsigned int *weights, 9647917f52aSPetr Machata const u8 *priomap) 96546a3615bSNogah Frankel { 966ee88450dSPetr Machata struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc; 96704cc0bf5SNogah Frankel struct mlxsw_sp_qdisc *child_qdisc; 968cc6e5c13SNogah Frankel int tclass, i, band, backlog; 96904cc0bf5SNogah Frankel u8 old_priomap; 97046a3615bSNogah Frankel int err; 97146a3615bSNogah Frankel 9727917f52aSPetr Machata for (band = 0; band < nbands; band++) { 97304cc0bf5SNogah Frankel tclass = MLXSW_SP_PRIO_BAND_TO_TCLASS(band); 974ee88450dSPetr Machata child_qdisc = &qdisc_state->tclass_qdiscs[tclass]; 97504cc0bf5SNogah Frankel old_priomap = child_qdisc->prio_bitmap; 97604cc0bf5SNogah Frankel child_qdisc->prio_bitmap = 0; 9777917f52aSPetr Machata 9787917f52aSPetr Machata err = mlxsw_sp_port_ets_set(mlxsw_sp_port, 9797917f52aSPetr Machata MLXSW_REG_QEEC_HR_SUBGROUP, 9807917f52aSPetr Machata tclass, 0, !!quanta[band], 9817917f52aSPetr Machata weights[band]); 9827917f52aSPetr Machata if (err) 9837917f52aSPetr Machata return err; 9847917f52aSPetr Machata 98546a3615bSNogah Frankel for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { 9867917f52aSPetr Machata if (priomap[i] == band) { 98704cc0bf5SNogah Frankel child_qdisc->prio_bitmap |= BIT(i); 98804cc0bf5SNogah Frankel if (BIT(i) & old_priomap) 98904cc0bf5SNogah Frankel continue; 99004cc0bf5SNogah Frankel err = mlxsw_sp_port_prio_tc_set(mlxsw_sp_port, 99104cc0bf5SNogah Frankel i, tclass); 99246a3615bSNogah Frankel if (err) 99346a3615bSNogah Frankel return err; 99404cc0bf5SNogah Frankel } 99504cc0bf5SNogah Frankel } 99604cc0bf5SNogah Frankel if (old_priomap != child_qdisc->prio_bitmap && 997cc6e5c13SNogah Frankel child_qdisc->ops && child_qdisc->ops->clean_stats) { 998cc6e5c13SNogah Frankel backlog = child_qdisc->stats_base.backlog; 99904cc0bf5SNogah Frankel child_qdisc->ops->clean_stats(mlxsw_sp_port, 100004cc0bf5SNogah Frankel child_qdisc); 1001cc6e5c13SNogah Frankel child_qdisc->stats_base.backlog = backlog; 1002cc6e5c13SNogah Frankel } 10037bec1a45SPetr Machata 10047bec1a45SPetr Machata if (handle == qdisc_state->future_handle && 10057bec1a45SPetr Machata qdisc_state->future_fifos[tclass]) { 10067bec1a45SPetr Machata err = mlxsw_sp_qdisc_replace(mlxsw_sp_port, TC_H_UNSPEC, 10077bec1a45SPetr Machata child_qdisc, 10087bec1a45SPetr Machata &mlxsw_sp_qdisc_ops_fifo, 10097bec1a45SPetr Machata NULL); 10107bec1a45SPetr Machata if (err) 10117bec1a45SPetr Machata return err; 10127bec1a45SPetr Machata } 101346a3615bSNogah Frankel } 101498ceb7b6SNogah Frankel for (; band < IEEE_8021QAZ_MAX_TCS; band++) { 101598ceb7b6SNogah Frankel tclass = MLXSW_SP_PRIO_BAND_TO_TCLASS(band); 1016ee88450dSPetr Machata child_qdisc = &qdisc_state->tclass_qdiscs[tclass]; 101798ceb7b6SNogah Frankel child_qdisc->prio_bitmap = 0; 101898ceb7b6SNogah Frankel mlxsw_sp_qdisc_destroy(mlxsw_sp_port, child_qdisc); 10197917f52aSPetr Machata mlxsw_sp_port_ets_set(mlxsw_sp_port, 10207917f52aSPetr Machata MLXSW_REG_QEEC_HR_SUBGROUP, 10217917f52aSPetr Machata tclass, 0, false, 0); 102298ceb7b6SNogah Frankel } 10237bec1a45SPetr Machata 10247bec1a45SPetr Machata qdisc_state->future_handle = TC_H_UNSPEC; 10257bec1a45SPetr Machata memset(qdisc_state->future_fifos, 0, sizeof(qdisc_state->future_fifos)); 102646a3615bSNogah Frankel return 0; 102746a3615bSNogah Frankel } 102846a3615bSNogah Frankel 10297917f52aSPetr Machata static int 1030c4e372e2SPetr Machata mlxsw_sp_qdisc_prio_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, 10317917f52aSPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 10327917f52aSPetr Machata void *params) 10337917f52aSPetr Machata { 10347917f52aSPetr Machata struct tc_prio_qopt_offload_params *p = params; 10357917f52aSPetr Machata unsigned int zeroes[TCQ_ETS_MAX_BANDS] = {0}; 10367917f52aSPetr Machata 1037c4e372e2SPetr Machata return __mlxsw_sp_qdisc_ets_replace(mlxsw_sp_port, handle, p->bands, 10387917f52aSPetr Machata zeroes, zeroes, p->priomap); 10397917f52aSPetr Machata } 10407917f52aSPetr Machata 10417917f52aSPetr Machata static void 10427917f52aSPetr Machata __mlxsw_sp_qdisc_ets_unoffload(struct mlxsw_sp_port *mlxsw_sp_port, 10437917f52aSPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 10447917f52aSPetr Machata struct gnet_stats_queue *qstats) 10457917f52aSPetr Machata { 10467917f52aSPetr Machata u64 backlog; 10477917f52aSPetr Machata 10487917f52aSPetr Machata backlog = mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp, 10497917f52aSPetr Machata mlxsw_sp_qdisc->stats_base.backlog); 10507917f52aSPetr Machata qstats->backlog -= backlog; 10517917f52aSPetr Machata } 10527917f52aSPetr Machata 1053e02f08a0SWei Yongjun static void 105493d8a4c1SNogah Frankel mlxsw_sp_qdisc_prio_unoffload(struct mlxsw_sp_port *mlxsw_sp_port, 105593d8a4c1SNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 105693d8a4c1SNogah Frankel void *params) 105793d8a4c1SNogah Frankel { 105893d8a4c1SNogah Frankel struct tc_prio_qopt_offload_params *p = params; 105993d8a4c1SNogah Frankel 10607917f52aSPetr Machata __mlxsw_sp_qdisc_ets_unoffload(mlxsw_sp_port, mlxsw_sp_qdisc, 10617917f52aSPetr Machata p->qstats); 106293d8a4c1SNogah Frankel } 106393d8a4c1SNogah Frankel 106493d8a4c1SNogah Frankel static int 106593d8a4c1SNogah Frankel mlxsw_sp_qdisc_get_prio_stats(struct mlxsw_sp_port *mlxsw_sp_port, 106693d8a4c1SNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 106793d8a4c1SNogah Frankel struct tc_qopt_offload_stats *stats_ptr) 106893d8a4c1SNogah Frankel { 1069ee88450dSPetr Machata struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc; 1070cf9af379SPetr Machata struct mlxsw_sp_qdisc *tc_qdisc; 1071cf9af379SPetr Machata u64 tx_packets = 0; 1072cf9af379SPetr Machata u64 tx_bytes = 0; 1073cf9af379SPetr Machata u64 backlog = 0; 1074cf9af379SPetr Machata u64 drops = 0; 107593d8a4c1SNogah Frankel int i; 107693d8a4c1SNogah Frankel 107793d8a4c1SNogah Frankel for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { 1078ee88450dSPetr Machata tc_qdisc = &qdisc_state->tclass_qdiscs[i]; 1079cf9af379SPetr Machata mlxsw_sp_qdisc_collect_tc_stats(mlxsw_sp_port, tc_qdisc, 1080cf9af379SPetr Machata &tx_bytes, &tx_packets, 1081cf9af379SPetr Machata &drops, &backlog); 108293d8a4c1SNogah Frankel } 108393d8a4c1SNogah Frankel 1084cf9af379SPetr Machata mlxsw_sp_qdisc_update_stats(mlxsw_sp_port->mlxsw_sp, mlxsw_sp_qdisc, 1085cf9af379SPetr Machata tx_bytes, tx_packets, drops, backlog, 1086cf9af379SPetr Machata stats_ptr); 108793d8a4c1SNogah Frankel return 0; 108893d8a4c1SNogah Frankel } 108993d8a4c1SNogah Frankel 109093d8a4c1SNogah Frankel static void 109193d8a4c1SNogah Frankel mlxsw_sp_setup_tc_qdisc_prio_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port, 109293d8a4c1SNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 109393d8a4c1SNogah Frankel { 109493d8a4c1SNogah Frankel struct mlxsw_sp_qdisc_stats *stats_base; 109593d8a4c1SNogah Frankel struct mlxsw_sp_port_xstats *xstats; 109693d8a4c1SNogah Frankel struct rtnl_link_stats64 *stats; 109793d8a4c1SNogah Frankel int i; 109893d8a4c1SNogah Frankel 109993d8a4c1SNogah Frankel xstats = &mlxsw_sp_port->periodic_hw_stats.xstats; 110093d8a4c1SNogah Frankel stats = &mlxsw_sp_port->periodic_hw_stats.stats; 110193d8a4c1SNogah Frankel stats_base = &mlxsw_sp_qdisc->stats_base; 110293d8a4c1SNogah Frankel 110393d8a4c1SNogah Frankel stats_base->tx_packets = stats->tx_packets; 110493d8a4c1SNogah Frankel stats_base->tx_bytes = stats->tx_bytes; 110593d8a4c1SNogah Frankel 110693d8a4c1SNogah Frankel stats_base->drops = 0; 110723f2b404SNogah Frankel for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { 110885005b82SPetr Machata stats_base->drops += mlxsw_sp_xstats_tail_drop(xstats, i); 110923f2b404SNogah Frankel stats_base->drops += xstats->wred_drop[i]; 111023f2b404SNogah Frankel } 111193d8a4c1SNogah Frankel 111293d8a4c1SNogah Frankel mlxsw_sp_qdisc->stats_base.backlog = 0; 111393d8a4c1SNogah Frankel } 111493d8a4c1SNogah Frankel 111546a3615bSNogah Frankel static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_prio = { 111646a3615bSNogah Frankel .type = MLXSW_SP_QDISC_PRIO, 111746a3615bSNogah Frankel .check_params = mlxsw_sp_qdisc_prio_check_params, 111846a3615bSNogah Frankel .replace = mlxsw_sp_qdisc_prio_replace, 111993d8a4c1SNogah Frankel .unoffload = mlxsw_sp_qdisc_prio_unoffload, 112046a3615bSNogah Frankel .destroy = mlxsw_sp_qdisc_prio_destroy, 112193d8a4c1SNogah Frankel .get_stats = mlxsw_sp_qdisc_get_prio_stats, 112293d8a4c1SNogah Frankel .clean_stats = mlxsw_sp_setup_tc_qdisc_prio_clean_stats, 112346a3615bSNogah Frankel }; 112446a3615bSNogah Frankel 112519f405b9SPetr Machata static int 112619f405b9SPetr Machata mlxsw_sp_qdisc_ets_check_params(struct mlxsw_sp_port *mlxsw_sp_port, 112719f405b9SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 112819f405b9SPetr Machata void *params) 112919f405b9SPetr Machata { 113019f405b9SPetr Machata struct tc_ets_qopt_offload_replace_params *p = params; 113119f405b9SPetr Machata 113219f405b9SPetr Machata return __mlxsw_sp_qdisc_ets_check_params(p->bands); 113319f405b9SPetr Machata } 113419f405b9SPetr Machata 113519f405b9SPetr Machata static int 1136c4e372e2SPetr Machata mlxsw_sp_qdisc_ets_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, 113719f405b9SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 113819f405b9SPetr Machata void *params) 113919f405b9SPetr Machata { 114019f405b9SPetr Machata struct tc_ets_qopt_offload_replace_params *p = params; 114119f405b9SPetr Machata 1142c4e372e2SPetr Machata return __mlxsw_sp_qdisc_ets_replace(mlxsw_sp_port, handle, p->bands, 114319f405b9SPetr Machata p->quanta, p->weights, p->priomap); 114419f405b9SPetr Machata } 114519f405b9SPetr Machata 114619f405b9SPetr Machata static void 114719f405b9SPetr Machata mlxsw_sp_qdisc_ets_unoffload(struct mlxsw_sp_port *mlxsw_sp_port, 114819f405b9SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 114919f405b9SPetr Machata void *params) 115019f405b9SPetr Machata { 115119f405b9SPetr Machata struct tc_ets_qopt_offload_replace_params *p = params; 115219f405b9SPetr Machata 115319f405b9SPetr Machata __mlxsw_sp_qdisc_ets_unoffload(mlxsw_sp_port, mlxsw_sp_qdisc, 115419f405b9SPetr Machata p->qstats); 115519f405b9SPetr Machata } 115619f405b9SPetr Machata 115719f405b9SPetr Machata static int 115819f405b9SPetr Machata mlxsw_sp_qdisc_ets_destroy(struct mlxsw_sp_port *mlxsw_sp_port, 115919f405b9SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 116019f405b9SPetr Machata { 116119f405b9SPetr Machata return __mlxsw_sp_qdisc_ets_destroy(mlxsw_sp_port); 116219f405b9SPetr Machata } 116319f405b9SPetr Machata 116419f405b9SPetr Machata static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_ets = { 116519f405b9SPetr Machata .type = MLXSW_SP_QDISC_ETS, 116619f405b9SPetr Machata .check_params = mlxsw_sp_qdisc_ets_check_params, 116719f405b9SPetr Machata .replace = mlxsw_sp_qdisc_ets_replace, 116819f405b9SPetr Machata .unoffload = mlxsw_sp_qdisc_ets_unoffload, 116919f405b9SPetr Machata .destroy = mlxsw_sp_qdisc_ets_destroy, 117019f405b9SPetr Machata .get_stats = mlxsw_sp_qdisc_get_prio_stats, 117119f405b9SPetr Machata .clean_stats = mlxsw_sp_setup_tc_qdisc_prio_clean_stats, 117219f405b9SPetr Machata }; 117319f405b9SPetr Machata 11745bc146c9SPetr Machata /* Linux allows linking of Qdiscs to arbitrary classes (so long as the resulting 11755bc146c9SPetr Machata * graph is free of cycles). These operations do not change the parent handle 11765bc146c9SPetr Machata * though, which means it can be incomplete (if there is more than one class 11775bc146c9SPetr Machata * where the Qdisc in question is grafted) or outright wrong (if the Qdisc was 11785bc146c9SPetr Machata * linked to a different class and then removed from the original class). 11795bc146c9SPetr Machata * 11805bc146c9SPetr Machata * E.g. consider this sequence of operations: 11815bc146c9SPetr Machata * 11825bc146c9SPetr Machata * # tc qdisc add dev swp1 root handle 1: prio 11835bc146c9SPetr Machata * # tc qdisc add dev swp1 parent 1:3 handle 13: red limit 1000000 avpkt 10000 11845bc146c9SPetr Machata * RED: set bandwidth to 10Mbit 11855bc146c9SPetr Machata * # tc qdisc link dev swp1 handle 13: parent 1:2 11865bc146c9SPetr Machata * 11875bc146c9SPetr Machata * At this point, both 1:2 and 1:3 have the same RED Qdisc instance as their 11885bc146c9SPetr Machata * child. But RED will still only claim that 1:3 is its parent. If it's removed 11895bc146c9SPetr Machata * from that band, its only parent will be 1:2, but it will continue to claim 11905bc146c9SPetr Machata * that it is in fact 1:3. 11915bc146c9SPetr Machata * 11925bc146c9SPetr Machata * The notification for child Qdisc replace (e.g. TC_RED_REPLACE) comes before 11935bc146c9SPetr Machata * the notification for parent graft (e.g. TC_PRIO_GRAFT). We take the replace 11945bc146c9SPetr Machata * notification to offload the child Qdisc, based on its parent handle, and use 11955bc146c9SPetr Machata * the graft operation to validate that the class where the child is actually 11965bc146c9SPetr Machata * grafted corresponds to the parent handle. If the two don't match, we 11975bc146c9SPetr Machata * unoffload the child. 119832dc5efcSNogah Frankel */ 119932dc5efcSNogah Frankel static int 12007917f52aSPetr Machata __mlxsw_sp_qdisc_ets_graft(struct mlxsw_sp_port *mlxsw_sp_port, 120132dc5efcSNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 12027917f52aSPetr Machata u8 band, u32 child_handle) 120332dc5efcSNogah Frankel { 1204ee88450dSPetr Machata struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc; 12057917f52aSPetr Machata int tclass_num = MLXSW_SP_PRIO_BAND_TO_TCLASS(band); 120632dc5efcSNogah Frankel struct mlxsw_sp_qdisc *old_qdisc; 120732dc5efcSNogah Frankel 12087917f52aSPetr Machata if (band < IEEE_8021QAZ_MAX_TCS && 1209ee88450dSPetr Machata qdisc_state->tclass_qdiscs[tclass_num].handle == child_handle) 121032dc5efcSNogah Frankel return 0; 121132dc5efcSNogah Frankel 1212a2d6d7aeSDavid S. Miller if (!child_handle) { 12133971a535SPetr Machata /* This is an invisible FIFO replacing the original Qdisc. 12143971a535SPetr Machata * Ignore it--the original Qdisc's destroy will follow. 12153971a535SPetr Machata */ 12163971a535SPetr Machata return 0; 12173971a535SPetr Machata } 12183971a535SPetr Machata 121932dc5efcSNogah Frankel /* See if the grafted qdisc is already offloaded on any tclass. If so, 122032dc5efcSNogah Frankel * unoffload it. 122132dc5efcSNogah Frankel */ 122232dc5efcSNogah Frankel old_qdisc = mlxsw_sp_qdisc_find_by_handle(mlxsw_sp_port, 12237917f52aSPetr Machata child_handle); 122432dc5efcSNogah Frankel if (old_qdisc) 122532dc5efcSNogah Frankel mlxsw_sp_qdisc_destroy(mlxsw_sp_port, old_qdisc); 122632dc5efcSNogah Frankel 122732dc5efcSNogah Frankel mlxsw_sp_qdisc_destroy(mlxsw_sp_port, 1228ee88450dSPetr Machata &qdisc_state->tclass_qdiscs[tclass_num]); 122932dc5efcSNogah Frankel return -EOPNOTSUPP; 123032dc5efcSNogah Frankel } 123132dc5efcSNogah Frankel 12327917f52aSPetr Machata static int 12337917f52aSPetr Machata mlxsw_sp_qdisc_prio_graft(struct mlxsw_sp_port *mlxsw_sp_port, 12347917f52aSPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 12357917f52aSPetr Machata struct tc_prio_qopt_offload_graft_params *p) 12367917f52aSPetr Machata { 12377917f52aSPetr Machata return __mlxsw_sp_qdisc_ets_graft(mlxsw_sp_port, mlxsw_sp_qdisc, 12387917f52aSPetr Machata p->band, p->child_handle); 12397917f52aSPetr Machata } 12407917f52aSPetr Machata 124146a3615bSNogah Frankel int mlxsw_sp_setup_tc_prio(struct mlxsw_sp_port *mlxsw_sp_port, 124246a3615bSNogah Frankel struct tc_prio_qopt_offload *p) 124346a3615bSNogah Frankel { 124446a3615bSNogah Frankel struct mlxsw_sp_qdisc *mlxsw_sp_qdisc; 124546a3615bSNogah Frankel 1246eed4baebSNogah Frankel mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent, true); 1247eed4baebSNogah Frankel if (!mlxsw_sp_qdisc) 124846a3615bSNogah Frankel return -EOPNOTSUPP; 124946a3615bSNogah Frankel 125046a3615bSNogah Frankel if (p->command == TC_PRIO_REPLACE) 125146a3615bSNogah Frankel return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle, 125246a3615bSNogah Frankel mlxsw_sp_qdisc, 125346a3615bSNogah Frankel &mlxsw_sp_qdisc_ops_prio, 125446a3615bSNogah Frankel &p->replace_params); 125546a3615bSNogah Frankel 125646a3615bSNogah Frankel if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle, 125746a3615bSNogah Frankel MLXSW_SP_QDISC_PRIO)) 125846a3615bSNogah Frankel return -EOPNOTSUPP; 125946a3615bSNogah Frankel 126046a3615bSNogah Frankel switch (p->command) { 126146a3615bSNogah Frankel case TC_PRIO_DESTROY: 126246a3615bSNogah Frankel return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc); 126393d8a4c1SNogah Frankel case TC_PRIO_STATS: 126493d8a4c1SNogah Frankel return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc, 126593d8a4c1SNogah Frankel &p->stats); 126632dc5efcSNogah Frankel case TC_PRIO_GRAFT: 126732dc5efcSNogah Frankel return mlxsw_sp_qdisc_prio_graft(mlxsw_sp_port, mlxsw_sp_qdisc, 126832dc5efcSNogah Frankel &p->graft_params); 126946a3615bSNogah Frankel default: 127046a3615bSNogah Frankel return -EOPNOTSUPP; 127146a3615bSNogah Frankel } 127246a3615bSNogah Frankel } 127346a3615bSNogah Frankel 127419f405b9SPetr Machata int mlxsw_sp_setup_tc_ets(struct mlxsw_sp_port *mlxsw_sp_port, 127519f405b9SPetr Machata struct tc_ets_qopt_offload *p) 127619f405b9SPetr Machata { 127719f405b9SPetr Machata struct mlxsw_sp_qdisc *mlxsw_sp_qdisc; 127819f405b9SPetr Machata 127919f405b9SPetr Machata mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent, true); 128019f405b9SPetr Machata if (!mlxsw_sp_qdisc) 128119f405b9SPetr Machata return -EOPNOTSUPP; 128219f405b9SPetr Machata 128319f405b9SPetr Machata if (p->command == TC_ETS_REPLACE) 128419f405b9SPetr Machata return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle, 128519f405b9SPetr Machata mlxsw_sp_qdisc, 128619f405b9SPetr Machata &mlxsw_sp_qdisc_ops_ets, 128719f405b9SPetr Machata &p->replace_params); 128819f405b9SPetr Machata 128919f405b9SPetr Machata if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle, 129019f405b9SPetr Machata MLXSW_SP_QDISC_ETS)) 129119f405b9SPetr Machata return -EOPNOTSUPP; 129219f405b9SPetr Machata 129319f405b9SPetr Machata switch (p->command) { 129419f405b9SPetr Machata case TC_ETS_DESTROY: 129519f405b9SPetr Machata return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc); 129619f405b9SPetr Machata case TC_ETS_STATS: 129719f405b9SPetr Machata return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc, 129819f405b9SPetr Machata &p->stats); 129919f405b9SPetr Machata case TC_ETS_GRAFT: 130019f405b9SPetr Machata return __mlxsw_sp_qdisc_ets_graft(mlxsw_sp_port, mlxsw_sp_qdisc, 130119f405b9SPetr Machata p->graft_params.band, 130219f405b9SPetr Machata p->graft_params.child_handle); 130319f405b9SPetr Machata default: 130419f405b9SPetr Machata return -EOPNOTSUPP; 130519f405b9SPetr Machata } 130619f405b9SPetr Machata } 130719f405b9SPetr Machata 1308f6668eacSPetr Machata struct mlxsw_sp_qevent_block { 1309f6668eacSPetr Machata struct list_head binding_list; 1310f6668eacSPetr Machata struct list_head mall_entry_list; 1311f6668eacSPetr Machata struct mlxsw_sp *mlxsw_sp; 1312f6668eacSPetr Machata }; 1313f6668eacSPetr Machata 1314f6668eacSPetr Machata struct mlxsw_sp_qevent_binding { 1315f6668eacSPetr Machata struct list_head list; 1316f6668eacSPetr Machata struct mlxsw_sp_port *mlxsw_sp_port; 1317f6668eacSPetr Machata u32 handle; 1318f6668eacSPetr Machata int tclass_num; 1319f6668eacSPetr Machata enum mlxsw_sp_span_trigger span_trigger; 1320f6668eacSPetr Machata }; 1321f6668eacSPetr Machata 1322f6668eacSPetr Machata static LIST_HEAD(mlxsw_sp_qevent_block_cb_list); 1323f6668eacSPetr Machata 132454a92385SPetr Machata static int mlxsw_sp_qevent_span_configure(struct mlxsw_sp *mlxsw_sp, 1325f6668eacSPetr Machata struct mlxsw_sp_mall_entry *mall_entry, 132654a92385SPetr Machata struct mlxsw_sp_qevent_binding *qevent_binding, 132754a92385SPetr Machata const struct mlxsw_sp_span_agent_parms *agent_parms, 132854a92385SPetr Machata int *p_span_id) 1329f6668eacSPetr Machata { 1330f6668eacSPetr Machata struct mlxsw_sp_port *mlxsw_sp_port = qevent_binding->mlxsw_sp_port; 1331f6668eacSPetr Machata struct mlxsw_sp_span_trigger_parms trigger_parms = {}; 1332f6668eacSPetr Machata int span_id; 1333f6668eacSPetr Machata int err; 1334f6668eacSPetr Machata 133554a92385SPetr Machata err = mlxsw_sp_span_agent_get(mlxsw_sp, &span_id, agent_parms); 1336f6668eacSPetr Machata if (err) 1337f6668eacSPetr Machata return err; 1338f6668eacSPetr Machata 1339f6668eacSPetr Machata err = mlxsw_sp_span_analyzed_port_get(mlxsw_sp_port, true); 1340f6668eacSPetr Machata if (err) 1341f6668eacSPetr Machata goto err_analyzed_port_get; 1342f6668eacSPetr Machata 1343f6668eacSPetr Machata trigger_parms.span_id = span_id; 1344f6668eacSPetr Machata err = mlxsw_sp_span_agent_bind(mlxsw_sp, qevent_binding->span_trigger, mlxsw_sp_port, 1345f6668eacSPetr Machata &trigger_parms); 1346f6668eacSPetr Machata if (err) 1347f6668eacSPetr Machata goto err_agent_bind; 1348f6668eacSPetr Machata 1349f6668eacSPetr Machata err = mlxsw_sp_span_trigger_enable(mlxsw_sp_port, qevent_binding->span_trigger, 1350f6668eacSPetr Machata qevent_binding->tclass_num); 1351f6668eacSPetr Machata if (err) 1352f6668eacSPetr Machata goto err_trigger_enable; 1353f6668eacSPetr Machata 135454a92385SPetr Machata *p_span_id = span_id; 1355f6668eacSPetr Machata return 0; 1356f6668eacSPetr Machata 1357f6668eacSPetr Machata err_trigger_enable: 1358f6668eacSPetr Machata mlxsw_sp_span_agent_unbind(mlxsw_sp, qevent_binding->span_trigger, mlxsw_sp_port, 1359f6668eacSPetr Machata &trigger_parms); 1360f6668eacSPetr Machata err_agent_bind: 1361f6668eacSPetr Machata mlxsw_sp_span_analyzed_port_put(mlxsw_sp_port, true); 1362f6668eacSPetr Machata err_analyzed_port_get: 1363f6668eacSPetr Machata mlxsw_sp_span_agent_put(mlxsw_sp, span_id); 1364f6668eacSPetr Machata return err; 1365f6668eacSPetr Machata } 1366f6668eacSPetr Machata 136754a92385SPetr Machata static void mlxsw_sp_qevent_span_deconfigure(struct mlxsw_sp *mlxsw_sp, 136854a92385SPetr Machata struct mlxsw_sp_qevent_binding *qevent_binding, 136954a92385SPetr Machata int span_id) 1370f6668eacSPetr Machata { 1371f6668eacSPetr Machata struct mlxsw_sp_port *mlxsw_sp_port = qevent_binding->mlxsw_sp_port; 1372f6668eacSPetr Machata struct mlxsw_sp_span_trigger_parms trigger_parms = { 137354a92385SPetr Machata .span_id = span_id, 1374f6668eacSPetr Machata }; 1375f6668eacSPetr Machata 1376f6668eacSPetr Machata mlxsw_sp_span_trigger_disable(mlxsw_sp_port, qevent_binding->span_trigger, 1377f6668eacSPetr Machata qevent_binding->tclass_num); 1378f6668eacSPetr Machata mlxsw_sp_span_agent_unbind(mlxsw_sp, qevent_binding->span_trigger, mlxsw_sp_port, 1379f6668eacSPetr Machata &trigger_parms); 1380f6668eacSPetr Machata mlxsw_sp_span_analyzed_port_put(mlxsw_sp_port, true); 138154a92385SPetr Machata mlxsw_sp_span_agent_put(mlxsw_sp, span_id); 138254a92385SPetr Machata } 138354a92385SPetr Machata 138454a92385SPetr Machata static int mlxsw_sp_qevent_mirror_configure(struct mlxsw_sp *mlxsw_sp, 138554a92385SPetr Machata struct mlxsw_sp_mall_entry *mall_entry, 138654a92385SPetr Machata struct mlxsw_sp_qevent_binding *qevent_binding) 138754a92385SPetr Machata { 138854a92385SPetr Machata struct mlxsw_sp_span_agent_parms agent_parms = { 138954a92385SPetr Machata .to_dev = mall_entry->mirror.to_dev, 139054a92385SPetr Machata }; 139154a92385SPetr Machata 139254a92385SPetr Machata return mlxsw_sp_qevent_span_configure(mlxsw_sp, mall_entry, qevent_binding, 139354a92385SPetr Machata &agent_parms, &mall_entry->mirror.span_id); 139454a92385SPetr Machata } 139554a92385SPetr Machata 139654a92385SPetr Machata static void mlxsw_sp_qevent_mirror_deconfigure(struct mlxsw_sp *mlxsw_sp, 139754a92385SPetr Machata struct mlxsw_sp_mall_entry *mall_entry, 139854a92385SPetr Machata struct mlxsw_sp_qevent_binding *qevent_binding) 139954a92385SPetr Machata { 140054a92385SPetr Machata mlxsw_sp_qevent_span_deconfigure(mlxsw_sp, qevent_binding, mall_entry->mirror.span_id); 140154a92385SPetr Machata } 140254a92385SPetr Machata 140354a92385SPetr Machata static int mlxsw_sp_qevent_trap_configure(struct mlxsw_sp *mlxsw_sp, 140454a92385SPetr Machata struct mlxsw_sp_mall_entry *mall_entry, 140554a92385SPetr Machata struct mlxsw_sp_qevent_binding *qevent_binding) 140654a92385SPetr Machata { 140754a92385SPetr Machata struct mlxsw_sp_span_agent_parms agent_parms = {}; 140854a92385SPetr Machata int err; 140954a92385SPetr Machata 141054a92385SPetr Machata err = mlxsw_sp_trap_group_policer_hw_id_get(mlxsw_sp, 141154a92385SPetr Machata DEVLINK_TRAP_GROUP_GENERIC_ID_BUFFER_DROPS, 141254a92385SPetr Machata &agent_parms.policer_enable, 141354a92385SPetr Machata &agent_parms.policer_id); 141454a92385SPetr Machata if (err) 141554a92385SPetr Machata return err; 141654a92385SPetr Machata 141754a92385SPetr Machata return mlxsw_sp_qevent_span_configure(mlxsw_sp, mall_entry, qevent_binding, 141854a92385SPetr Machata &agent_parms, &mall_entry->trap.span_id); 141954a92385SPetr Machata } 142054a92385SPetr Machata 142154a92385SPetr Machata static void mlxsw_sp_qevent_trap_deconfigure(struct mlxsw_sp *mlxsw_sp, 142254a92385SPetr Machata struct mlxsw_sp_mall_entry *mall_entry, 142354a92385SPetr Machata struct mlxsw_sp_qevent_binding *qevent_binding) 142454a92385SPetr Machata { 142554a92385SPetr Machata mlxsw_sp_qevent_span_deconfigure(mlxsw_sp, qevent_binding, mall_entry->trap.span_id); 1426f6668eacSPetr Machata } 1427f6668eacSPetr Machata 1428f6668eacSPetr Machata static int mlxsw_sp_qevent_entry_configure(struct mlxsw_sp *mlxsw_sp, 1429f6668eacSPetr Machata struct mlxsw_sp_mall_entry *mall_entry, 1430f6668eacSPetr Machata struct mlxsw_sp_qevent_binding *qevent_binding) 1431f6668eacSPetr Machata { 1432f6668eacSPetr Machata switch (mall_entry->type) { 1433f6668eacSPetr Machata case MLXSW_SP_MALL_ACTION_TYPE_MIRROR: 1434f6668eacSPetr Machata return mlxsw_sp_qevent_mirror_configure(mlxsw_sp, mall_entry, qevent_binding); 143554a92385SPetr Machata case MLXSW_SP_MALL_ACTION_TYPE_TRAP: 143654a92385SPetr Machata return mlxsw_sp_qevent_trap_configure(mlxsw_sp, mall_entry, qevent_binding); 1437f6668eacSPetr Machata default: 1438f6668eacSPetr Machata /* This should have been validated away. */ 1439f6668eacSPetr Machata WARN_ON(1); 1440f6668eacSPetr Machata return -EOPNOTSUPP; 1441f6668eacSPetr Machata } 1442f6668eacSPetr Machata } 1443f6668eacSPetr Machata 1444f6668eacSPetr Machata static void mlxsw_sp_qevent_entry_deconfigure(struct mlxsw_sp *mlxsw_sp, 1445f6668eacSPetr Machata struct mlxsw_sp_mall_entry *mall_entry, 1446f6668eacSPetr Machata struct mlxsw_sp_qevent_binding *qevent_binding) 1447f6668eacSPetr Machata { 1448f6668eacSPetr Machata switch (mall_entry->type) { 1449f6668eacSPetr Machata case MLXSW_SP_MALL_ACTION_TYPE_MIRROR: 1450f6668eacSPetr Machata return mlxsw_sp_qevent_mirror_deconfigure(mlxsw_sp, mall_entry, qevent_binding); 145154a92385SPetr Machata case MLXSW_SP_MALL_ACTION_TYPE_TRAP: 145254a92385SPetr Machata return mlxsw_sp_qevent_trap_deconfigure(mlxsw_sp, mall_entry, qevent_binding); 1453f6668eacSPetr Machata default: 1454f6668eacSPetr Machata WARN_ON(1); 1455f6668eacSPetr Machata return; 1456f6668eacSPetr Machata } 1457f6668eacSPetr Machata } 1458f6668eacSPetr Machata 1459f6668eacSPetr Machata static int mlxsw_sp_qevent_binding_configure(struct mlxsw_sp_qevent_block *qevent_block, 1460f6668eacSPetr Machata struct mlxsw_sp_qevent_binding *qevent_binding) 1461f6668eacSPetr Machata { 1462f6668eacSPetr Machata struct mlxsw_sp_mall_entry *mall_entry; 1463f6668eacSPetr Machata int err; 1464f6668eacSPetr Machata 1465f6668eacSPetr Machata list_for_each_entry(mall_entry, &qevent_block->mall_entry_list, list) { 1466f6668eacSPetr Machata err = mlxsw_sp_qevent_entry_configure(qevent_block->mlxsw_sp, mall_entry, 1467f6668eacSPetr Machata qevent_binding); 1468f6668eacSPetr Machata if (err) 1469f6668eacSPetr Machata goto err_entry_configure; 1470f6668eacSPetr Machata } 1471f6668eacSPetr Machata 1472f6668eacSPetr Machata return 0; 1473f6668eacSPetr Machata 1474f6668eacSPetr Machata err_entry_configure: 1475f6668eacSPetr Machata list_for_each_entry_continue_reverse(mall_entry, &qevent_block->mall_entry_list, list) 1476f6668eacSPetr Machata mlxsw_sp_qevent_entry_deconfigure(qevent_block->mlxsw_sp, mall_entry, 1477f6668eacSPetr Machata qevent_binding); 1478f6668eacSPetr Machata return err; 1479f6668eacSPetr Machata } 1480f6668eacSPetr Machata 1481f6668eacSPetr Machata static void mlxsw_sp_qevent_binding_deconfigure(struct mlxsw_sp_qevent_block *qevent_block, 1482f6668eacSPetr Machata struct mlxsw_sp_qevent_binding *qevent_binding) 1483f6668eacSPetr Machata { 1484f6668eacSPetr Machata struct mlxsw_sp_mall_entry *mall_entry; 1485f6668eacSPetr Machata 1486f6668eacSPetr Machata list_for_each_entry(mall_entry, &qevent_block->mall_entry_list, list) 1487f6668eacSPetr Machata mlxsw_sp_qevent_entry_deconfigure(qevent_block->mlxsw_sp, mall_entry, 1488f6668eacSPetr Machata qevent_binding); 1489f6668eacSPetr Machata } 1490f6668eacSPetr Machata 1491f6668eacSPetr Machata static int mlxsw_sp_qevent_block_configure(struct mlxsw_sp_qevent_block *qevent_block) 1492f6668eacSPetr Machata { 1493f6668eacSPetr Machata struct mlxsw_sp_qevent_binding *qevent_binding; 1494f6668eacSPetr Machata int err; 1495f6668eacSPetr Machata 1496f6668eacSPetr Machata list_for_each_entry(qevent_binding, &qevent_block->binding_list, list) { 1497f6668eacSPetr Machata err = mlxsw_sp_qevent_binding_configure(qevent_block, qevent_binding); 1498f6668eacSPetr Machata if (err) 1499f6668eacSPetr Machata goto err_binding_configure; 1500f6668eacSPetr Machata } 1501f6668eacSPetr Machata 1502f6668eacSPetr Machata return 0; 1503f6668eacSPetr Machata 1504f6668eacSPetr Machata err_binding_configure: 1505f6668eacSPetr Machata list_for_each_entry_continue_reverse(qevent_binding, &qevent_block->binding_list, list) 1506f6668eacSPetr Machata mlxsw_sp_qevent_binding_deconfigure(qevent_block, qevent_binding); 1507f6668eacSPetr Machata return err; 1508f6668eacSPetr Machata } 1509f6668eacSPetr Machata 1510f6668eacSPetr Machata static void mlxsw_sp_qevent_block_deconfigure(struct mlxsw_sp_qevent_block *qevent_block) 1511f6668eacSPetr Machata { 1512f6668eacSPetr Machata struct mlxsw_sp_qevent_binding *qevent_binding; 1513f6668eacSPetr Machata 1514f6668eacSPetr Machata list_for_each_entry(qevent_binding, &qevent_block->binding_list, list) 1515f6668eacSPetr Machata mlxsw_sp_qevent_binding_deconfigure(qevent_block, qevent_binding); 1516f6668eacSPetr Machata } 1517f6668eacSPetr Machata 1518f6668eacSPetr Machata static struct mlxsw_sp_mall_entry * 1519f6668eacSPetr Machata mlxsw_sp_qevent_mall_entry_find(struct mlxsw_sp_qevent_block *block, unsigned long cookie) 1520f6668eacSPetr Machata { 1521f6668eacSPetr Machata struct mlxsw_sp_mall_entry *mall_entry; 1522f6668eacSPetr Machata 1523f6668eacSPetr Machata list_for_each_entry(mall_entry, &block->mall_entry_list, list) 1524f6668eacSPetr Machata if (mall_entry->cookie == cookie) 1525f6668eacSPetr Machata return mall_entry; 1526f6668eacSPetr Machata 1527f6668eacSPetr Machata return NULL; 1528f6668eacSPetr Machata } 1529f6668eacSPetr Machata 1530f6668eacSPetr Machata static int mlxsw_sp_qevent_mall_replace(struct mlxsw_sp *mlxsw_sp, 1531f6668eacSPetr Machata struct mlxsw_sp_qevent_block *qevent_block, 1532f6668eacSPetr Machata struct tc_cls_matchall_offload *f) 1533f6668eacSPetr Machata { 1534f6668eacSPetr Machata struct mlxsw_sp_mall_entry *mall_entry; 1535f6668eacSPetr Machata struct flow_action_entry *act; 1536f6668eacSPetr Machata int err; 1537f6668eacSPetr Machata 1538f6668eacSPetr Machata /* It should not currently be possible to replace a matchall rule. So 1539f6668eacSPetr Machata * this must be a new rule. 1540f6668eacSPetr Machata */ 1541f6668eacSPetr Machata if (!list_empty(&qevent_block->mall_entry_list)) { 1542f6668eacSPetr Machata NL_SET_ERR_MSG(f->common.extack, "At most one filter supported"); 1543f6668eacSPetr Machata return -EOPNOTSUPP; 1544f6668eacSPetr Machata } 1545f6668eacSPetr Machata if (f->rule->action.num_entries != 1) { 1546f6668eacSPetr Machata NL_SET_ERR_MSG(f->common.extack, "Only singular actions supported"); 1547f6668eacSPetr Machata return -EOPNOTSUPP; 1548f6668eacSPetr Machata } 1549f6668eacSPetr Machata if (f->common.chain_index) { 1550f6668eacSPetr Machata NL_SET_ERR_MSG(f->common.extack, "Only chain 0 is supported"); 1551f6668eacSPetr Machata return -EOPNOTSUPP; 1552f6668eacSPetr Machata } 1553f6668eacSPetr Machata if (f->common.protocol != htons(ETH_P_ALL)) { 1554f6668eacSPetr Machata NL_SET_ERR_MSG(f->common.extack, "Protocol matching not supported"); 1555f6668eacSPetr Machata return -EOPNOTSUPP; 1556f6668eacSPetr Machata } 1557f6668eacSPetr Machata 1558f6668eacSPetr Machata act = &f->rule->action.entries[0]; 1559f6668eacSPetr Machata if (!(act->hw_stats & FLOW_ACTION_HW_STATS_DISABLED)) { 1560f6668eacSPetr Machata NL_SET_ERR_MSG(f->common.extack, "HW counters not supported on qevents"); 1561f6668eacSPetr Machata return -EOPNOTSUPP; 1562f6668eacSPetr Machata } 1563f6668eacSPetr Machata 1564f6668eacSPetr Machata mall_entry = kzalloc(sizeof(*mall_entry), GFP_KERNEL); 1565f6668eacSPetr Machata if (!mall_entry) 1566f6668eacSPetr Machata return -ENOMEM; 1567f6668eacSPetr Machata mall_entry->cookie = f->cookie; 1568f6668eacSPetr Machata 1569f6668eacSPetr Machata if (act->id == FLOW_ACTION_MIRRED) { 1570f6668eacSPetr Machata mall_entry->type = MLXSW_SP_MALL_ACTION_TYPE_MIRROR; 1571f6668eacSPetr Machata mall_entry->mirror.to_dev = act->dev; 157254a92385SPetr Machata } else if (act->id == FLOW_ACTION_TRAP) { 157354a92385SPetr Machata mall_entry->type = MLXSW_SP_MALL_ACTION_TYPE_TRAP; 1574f6668eacSPetr Machata } else { 1575f6668eacSPetr Machata NL_SET_ERR_MSG(f->common.extack, "Unsupported action"); 1576f6668eacSPetr Machata err = -EOPNOTSUPP; 1577f6668eacSPetr Machata goto err_unsupported_action; 1578f6668eacSPetr Machata } 1579f6668eacSPetr Machata 1580f6668eacSPetr Machata list_add_tail(&mall_entry->list, &qevent_block->mall_entry_list); 1581f6668eacSPetr Machata 1582f6668eacSPetr Machata err = mlxsw_sp_qevent_block_configure(qevent_block); 1583f6668eacSPetr Machata if (err) 1584f6668eacSPetr Machata goto err_block_configure; 1585f6668eacSPetr Machata 1586f6668eacSPetr Machata return 0; 1587f6668eacSPetr Machata 1588f6668eacSPetr Machata err_block_configure: 1589f6668eacSPetr Machata list_del(&mall_entry->list); 1590f6668eacSPetr Machata err_unsupported_action: 1591f6668eacSPetr Machata kfree(mall_entry); 1592f6668eacSPetr Machata return err; 1593f6668eacSPetr Machata } 1594f6668eacSPetr Machata 1595f6668eacSPetr Machata static void mlxsw_sp_qevent_mall_destroy(struct mlxsw_sp_qevent_block *qevent_block, 1596f6668eacSPetr Machata struct tc_cls_matchall_offload *f) 1597f6668eacSPetr Machata { 1598f6668eacSPetr Machata struct mlxsw_sp_mall_entry *mall_entry; 1599f6668eacSPetr Machata 1600f6668eacSPetr Machata mall_entry = mlxsw_sp_qevent_mall_entry_find(qevent_block, f->cookie); 1601f6668eacSPetr Machata if (!mall_entry) 1602f6668eacSPetr Machata return; 1603f6668eacSPetr Machata 1604f6668eacSPetr Machata mlxsw_sp_qevent_block_deconfigure(qevent_block); 1605f6668eacSPetr Machata 1606f6668eacSPetr Machata list_del(&mall_entry->list); 1607f6668eacSPetr Machata kfree(mall_entry); 1608f6668eacSPetr Machata } 1609f6668eacSPetr Machata 1610f6668eacSPetr Machata static int mlxsw_sp_qevent_block_mall_cb(struct mlxsw_sp_qevent_block *qevent_block, 1611f6668eacSPetr Machata struct tc_cls_matchall_offload *f) 1612f6668eacSPetr Machata { 1613f6668eacSPetr Machata struct mlxsw_sp *mlxsw_sp = qevent_block->mlxsw_sp; 1614f6668eacSPetr Machata 1615f6668eacSPetr Machata switch (f->command) { 1616f6668eacSPetr Machata case TC_CLSMATCHALL_REPLACE: 1617f6668eacSPetr Machata return mlxsw_sp_qevent_mall_replace(mlxsw_sp, qevent_block, f); 1618f6668eacSPetr Machata case TC_CLSMATCHALL_DESTROY: 1619f6668eacSPetr Machata mlxsw_sp_qevent_mall_destroy(qevent_block, f); 1620f6668eacSPetr Machata return 0; 1621f6668eacSPetr Machata default: 1622f6668eacSPetr Machata return -EOPNOTSUPP; 1623f6668eacSPetr Machata } 1624f6668eacSPetr Machata } 1625f6668eacSPetr Machata 1626f6668eacSPetr Machata static int mlxsw_sp_qevent_block_cb(enum tc_setup_type type, void *type_data, void *cb_priv) 1627f6668eacSPetr Machata { 1628f6668eacSPetr Machata struct mlxsw_sp_qevent_block *qevent_block = cb_priv; 1629f6668eacSPetr Machata 1630f6668eacSPetr Machata switch (type) { 1631f6668eacSPetr Machata case TC_SETUP_CLSMATCHALL: 1632f6668eacSPetr Machata return mlxsw_sp_qevent_block_mall_cb(qevent_block, type_data); 1633f6668eacSPetr Machata default: 1634f6668eacSPetr Machata return -EOPNOTSUPP; 1635f6668eacSPetr Machata } 1636f6668eacSPetr Machata } 1637f6668eacSPetr Machata 1638f6668eacSPetr Machata static struct mlxsw_sp_qevent_block *mlxsw_sp_qevent_block_create(struct mlxsw_sp *mlxsw_sp, 1639f6668eacSPetr Machata struct net *net) 1640f6668eacSPetr Machata { 1641f6668eacSPetr Machata struct mlxsw_sp_qevent_block *qevent_block; 1642f6668eacSPetr Machata 1643f6668eacSPetr Machata qevent_block = kzalloc(sizeof(*qevent_block), GFP_KERNEL); 1644f6668eacSPetr Machata if (!qevent_block) 1645f6668eacSPetr Machata return NULL; 1646f6668eacSPetr Machata 1647f6668eacSPetr Machata INIT_LIST_HEAD(&qevent_block->binding_list); 1648f6668eacSPetr Machata INIT_LIST_HEAD(&qevent_block->mall_entry_list); 1649f6668eacSPetr Machata qevent_block->mlxsw_sp = mlxsw_sp; 1650f6668eacSPetr Machata return qevent_block; 1651f6668eacSPetr Machata } 1652f6668eacSPetr Machata 1653f6668eacSPetr Machata static void 1654f6668eacSPetr Machata mlxsw_sp_qevent_block_destroy(struct mlxsw_sp_qevent_block *qevent_block) 1655f6668eacSPetr Machata { 1656f6668eacSPetr Machata WARN_ON(!list_empty(&qevent_block->binding_list)); 1657f6668eacSPetr Machata WARN_ON(!list_empty(&qevent_block->mall_entry_list)); 1658f6668eacSPetr Machata kfree(qevent_block); 1659f6668eacSPetr Machata } 1660f6668eacSPetr Machata 1661f6668eacSPetr Machata static void mlxsw_sp_qevent_block_release(void *cb_priv) 1662f6668eacSPetr Machata { 1663f6668eacSPetr Machata struct mlxsw_sp_qevent_block *qevent_block = cb_priv; 1664f6668eacSPetr Machata 1665f6668eacSPetr Machata mlxsw_sp_qevent_block_destroy(qevent_block); 1666f6668eacSPetr Machata } 1667f6668eacSPetr Machata 1668f6668eacSPetr Machata static struct mlxsw_sp_qevent_binding * 1669f6668eacSPetr Machata mlxsw_sp_qevent_binding_create(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, int tclass_num, 1670f6668eacSPetr Machata enum mlxsw_sp_span_trigger span_trigger) 1671f6668eacSPetr Machata { 1672f6668eacSPetr Machata struct mlxsw_sp_qevent_binding *binding; 1673f6668eacSPetr Machata 1674f6668eacSPetr Machata binding = kzalloc(sizeof(*binding), GFP_KERNEL); 1675f6668eacSPetr Machata if (!binding) 1676f6668eacSPetr Machata return ERR_PTR(-ENOMEM); 1677f6668eacSPetr Machata 1678f6668eacSPetr Machata binding->mlxsw_sp_port = mlxsw_sp_port; 1679f6668eacSPetr Machata binding->handle = handle; 1680f6668eacSPetr Machata binding->tclass_num = tclass_num; 1681f6668eacSPetr Machata binding->span_trigger = span_trigger; 1682f6668eacSPetr Machata return binding; 1683f6668eacSPetr Machata } 1684f6668eacSPetr Machata 1685f6668eacSPetr Machata static void 1686f6668eacSPetr Machata mlxsw_sp_qevent_binding_destroy(struct mlxsw_sp_qevent_binding *binding) 1687f6668eacSPetr Machata { 1688f6668eacSPetr Machata kfree(binding); 1689f6668eacSPetr Machata } 1690f6668eacSPetr Machata 1691f6668eacSPetr Machata static struct mlxsw_sp_qevent_binding * 1692f6668eacSPetr Machata mlxsw_sp_qevent_binding_lookup(struct mlxsw_sp_qevent_block *block, 1693f6668eacSPetr Machata struct mlxsw_sp_port *mlxsw_sp_port, 1694f6668eacSPetr Machata u32 handle, 1695f6668eacSPetr Machata enum mlxsw_sp_span_trigger span_trigger) 1696f6668eacSPetr Machata { 1697f6668eacSPetr Machata struct mlxsw_sp_qevent_binding *qevent_binding; 1698f6668eacSPetr Machata 1699f6668eacSPetr Machata list_for_each_entry(qevent_binding, &block->binding_list, list) 1700f6668eacSPetr Machata if (qevent_binding->mlxsw_sp_port == mlxsw_sp_port && 1701f6668eacSPetr Machata qevent_binding->handle == handle && 1702f6668eacSPetr Machata qevent_binding->span_trigger == span_trigger) 1703f6668eacSPetr Machata return qevent_binding; 1704f6668eacSPetr Machata return NULL; 1705f6668eacSPetr Machata } 1706f6668eacSPetr Machata 1707f6668eacSPetr Machata static int mlxsw_sp_setup_tc_block_qevent_bind(struct mlxsw_sp_port *mlxsw_sp_port, 1708f6668eacSPetr Machata struct flow_block_offload *f, 1709f6668eacSPetr Machata enum mlxsw_sp_span_trigger span_trigger) 1710f6668eacSPetr Machata { 1711f6668eacSPetr Machata struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 1712f6668eacSPetr Machata struct mlxsw_sp_qevent_binding *qevent_binding; 1713f6668eacSPetr Machata struct mlxsw_sp_qevent_block *qevent_block; 1714f6668eacSPetr Machata struct flow_block_cb *block_cb; 1715f6668eacSPetr Machata struct mlxsw_sp_qdisc *qdisc; 1716f6668eacSPetr Machata bool register_block = false; 1717f6668eacSPetr Machata int err; 1718f6668eacSPetr Machata 1719f6668eacSPetr Machata block_cb = flow_block_cb_lookup(f->block, mlxsw_sp_qevent_block_cb, mlxsw_sp); 1720f6668eacSPetr Machata if (!block_cb) { 1721f6668eacSPetr Machata qevent_block = mlxsw_sp_qevent_block_create(mlxsw_sp, f->net); 1722f6668eacSPetr Machata if (!qevent_block) 1723f6668eacSPetr Machata return -ENOMEM; 1724f6668eacSPetr Machata block_cb = flow_block_cb_alloc(mlxsw_sp_qevent_block_cb, mlxsw_sp, qevent_block, 1725f6668eacSPetr Machata mlxsw_sp_qevent_block_release); 1726f6668eacSPetr Machata if (IS_ERR(block_cb)) { 1727f6668eacSPetr Machata mlxsw_sp_qevent_block_destroy(qevent_block); 1728f6668eacSPetr Machata return PTR_ERR(block_cb); 1729f6668eacSPetr Machata } 1730f6668eacSPetr Machata register_block = true; 1731f6668eacSPetr Machata } else { 1732f6668eacSPetr Machata qevent_block = flow_block_cb_priv(block_cb); 1733f6668eacSPetr Machata } 1734f6668eacSPetr Machata flow_block_cb_incref(block_cb); 1735f6668eacSPetr Machata 1736f6668eacSPetr Machata qdisc = mlxsw_sp_qdisc_find_by_handle(mlxsw_sp_port, f->sch->handle); 1737f6668eacSPetr Machata if (!qdisc) { 1738f6668eacSPetr Machata NL_SET_ERR_MSG(f->extack, "Qdisc not offloaded"); 1739f6668eacSPetr Machata err = -ENOENT; 1740f6668eacSPetr Machata goto err_find_qdisc; 1741f6668eacSPetr Machata } 1742f6668eacSPetr Machata 1743f6668eacSPetr Machata if (WARN_ON(mlxsw_sp_qevent_binding_lookup(qevent_block, mlxsw_sp_port, f->sch->handle, 1744f6668eacSPetr Machata span_trigger))) { 1745f6668eacSPetr Machata err = -EEXIST; 1746f6668eacSPetr Machata goto err_binding_exists; 1747f6668eacSPetr Machata } 1748f6668eacSPetr Machata 1749f6668eacSPetr Machata qevent_binding = mlxsw_sp_qevent_binding_create(mlxsw_sp_port, f->sch->handle, 1750f6668eacSPetr Machata qdisc->tclass_num, span_trigger); 1751f6668eacSPetr Machata if (IS_ERR(qevent_binding)) { 1752f6668eacSPetr Machata err = PTR_ERR(qevent_binding); 1753f6668eacSPetr Machata goto err_binding_create; 1754f6668eacSPetr Machata } 1755f6668eacSPetr Machata 1756f6668eacSPetr Machata err = mlxsw_sp_qevent_binding_configure(qevent_block, qevent_binding); 1757f6668eacSPetr Machata if (err) 1758f6668eacSPetr Machata goto err_binding_configure; 1759f6668eacSPetr Machata 1760f6668eacSPetr Machata list_add(&qevent_binding->list, &qevent_block->binding_list); 1761f6668eacSPetr Machata 1762f6668eacSPetr Machata if (register_block) { 1763f6668eacSPetr Machata flow_block_cb_add(block_cb, f); 1764f6668eacSPetr Machata list_add_tail(&block_cb->driver_list, &mlxsw_sp_qevent_block_cb_list); 1765f6668eacSPetr Machata } 1766f6668eacSPetr Machata 1767f6668eacSPetr Machata return 0; 1768f6668eacSPetr Machata 1769f6668eacSPetr Machata err_binding_configure: 1770f6668eacSPetr Machata mlxsw_sp_qevent_binding_destroy(qevent_binding); 1771f6668eacSPetr Machata err_binding_create: 1772f6668eacSPetr Machata err_binding_exists: 1773f6668eacSPetr Machata err_find_qdisc: 1774f6668eacSPetr Machata if (!flow_block_cb_decref(block_cb)) 1775f6668eacSPetr Machata flow_block_cb_free(block_cb); 1776f6668eacSPetr Machata return err; 1777f6668eacSPetr Machata } 1778f6668eacSPetr Machata 1779f6668eacSPetr Machata static void mlxsw_sp_setup_tc_block_qevent_unbind(struct mlxsw_sp_port *mlxsw_sp_port, 1780f6668eacSPetr Machata struct flow_block_offload *f, 1781f6668eacSPetr Machata enum mlxsw_sp_span_trigger span_trigger) 1782f6668eacSPetr Machata { 1783f6668eacSPetr Machata struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 1784f6668eacSPetr Machata struct mlxsw_sp_qevent_binding *qevent_binding; 1785f6668eacSPetr Machata struct mlxsw_sp_qevent_block *qevent_block; 1786f6668eacSPetr Machata struct flow_block_cb *block_cb; 1787f6668eacSPetr Machata 1788f6668eacSPetr Machata block_cb = flow_block_cb_lookup(f->block, mlxsw_sp_qevent_block_cb, mlxsw_sp); 1789f6668eacSPetr Machata if (!block_cb) 1790f6668eacSPetr Machata return; 1791f6668eacSPetr Machata qevent_block = flow_block_cb_priv(block_cb); 1792f6668eacSPetr Machata 1793f6668eacSPetr Machata qevent_binding = mlxsw_sp_qevent_binding_lookup(qevent_block, mlxsw_sp_port, f->sch->handle, 1794f6668eacSPetr Machata span_trigger); 1795f6668eacSPetr Machata if (!qevent_binding) 1796f6668eacSPetr Machata return; 1797f6668eacSPetr Machata 1798f6668eacSPetr Machata list_del(&qevent_binding->list); 1799f6668eacSPetr Machata mlxsw_sp_qevent_binding_deconfigure(qevent_block, qevent_binding); 1800f6668eacSPetr Machata mlxsw_sp_qevent_binding_destroy(qevent_binding); 1801f6668eacSPetr Machata 1802f6668eacSPetr Machata if (!flow_block_cb_decref(block_cb)) { 1803f6668eacSPetr Machata flow_block_cb_remove(block_cb, f); 1804f6668eacSPetr Machata list_del(&block_cb->driver_list); 1805f6668eacSPetr Machata } 1806f6668eacSPetr Machata } 1807f6668eacSPetr Machata 1808f6668eacSPetr Machata static int mlxsw_sp_setup_tc_block_qevent(struct mlxsw_sp_port *mlxsw_sp_port, 1809f6668eacSPetr Machata struct flow_block_offload *f, 1810f6668eacSPetr Machata enum mlxsw_sp_span_trigger span_trigger) 1811f6668eacSPetr Machata { 1812f6668eacSPetr Machata f->driver_block_list = &mlxsw_sp_qevent_block_cb_list; 1813f6668eacSPetr Machata 1814f6668eacSPetr Machata switch (f->command) { 1815f6668eacSPetr Machata case FLOW_BLOCK_BIND: 1816f6668eacSPetr Machata return mlxsw_sp_setup_tc_block_qevent_bind(mlxsw_sp_port, f, span_trigger); 1817f6668eacSPetr Machata case FLOW_BLOCK_UNBIND: 1818f6668eacSPetr Machata mlxsw_sp_setup_tc_block_qevent_unbind(mlxsw_sp_port, f, span_trigger); 1819f6668eacSPetr Machata return 0; 1820f6668eacSPetr Machata default: 1821f6668eacSPetr Machata return -EOPNOTSUPP; 1822f6668eacSPetr Machata } 1823f6668eacSPetr Machata } 1824f6668eacSPetr Machata 1825f6668eacSPetr Machata int mlxsw_sp_setup_tc_block_qevent_early_drop(struct mlxsw_sp_port *mlxsw_sp_port, 1826f6668eacSPetr Machata struct flow_block_offload *f) 1827f6668eacSPetr Machata { 1828f6668eacSPetr Machata return mlxsw_sp_setup_tc_block_qevent(mlxsw_sp_port, f, MLXSW_SP_SPAN_TRIGGER_EARLY_DROP); 1829f6668eacSPetr Machata } 1830f6668eacSPetr Machata 1831371b437aSNogah Frankel int mlxsw_sp_tc_qdisc_init(struct mlxsw_sp_port *mlxsw_sp_port) 1832371b437aSNogah Frankel { 1833ee88450dSPetr Machata struct mlxsw_sp_qdisc_state *qdisc_state; 1834eed4baebSNogah Frankel int i; 1835371b437aSNogah Frankel 1836ee88450dSPetr Machata qdisc_state = kzalloc(sizeof(*qdisc_state), GFP_KERNEL); 1837ee88450dSPetr Machata if (!qdisc_state) 1838eed4baebSNogah Frankel return -ENOMEM; 1839ee88450dSPetr Machata 1840ee88450dSPetr Machata qdisc_state->root_qdisc.prio_bitmap = 0xff; 1841ee88450dSPetr Machata qdisc_state->root_qdisc.tclass_num = MLXSW_SP_PORT_DEFAULT_TCLASS; 1842ee88450dSPetr Machata for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) 1843ee88450dSPetr Machata qdisc_state->tclass_qdiscs[i].tclass_num = i; 1844ee88450dSPetr Machata 1845ee88450dSPetr Machata mlxsw_sp_port->qdisc = qdisc_state; 1846ee88450dSPetr Machata return 0; 1847371b437aSNogah Frankel } 1848371b437aSNogah Frankel 1849371b437aSNogah Frankel void mlxsw_sp_tc_qdisc_fini(struct mlxsw_sp_port *mlxsw_sp_port) 1850371b437aSNogah Frankel { 1851ee88450dSPetr Machata kfree(mlxsw_sp_port->qdisc); 1852371b437aSNogah Frankel } 1853