18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 28c2ecf20Sopenharmony_ci/* Copyright (c) 2017-2018 Mellanox Technologies. All rights reserved */ 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci#include <linux/kernel.h> 58c2ecf20Sopenharmony_ci#include <linux/errno.h> 68c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 78c2ecf20Sopenharmony_ci#include <net/pkt_cls.h> 88c2ecf20Sopenharmony_ci#include <net/red.h> 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include "spectrum.h" 118c2ecf20Sopenharmony_ci#include "spectrum_span.h" 128c2ecf20Sopenharmony_ci#include "reg.h" 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#define MLXSW_SP_PRIO_BAND_TO_TCLASS(band) (IEEE_8021QAZ_MAX_TCS - band - 1) 158c2ecf20Sopenharmony_ci#define MLXSW_SP_PRIO_CHILD_TO_TCLASS(child) \ 168c2ecf20Sopenharmony_ci MLXSW_SP_PRIO_BAND_TO_TCLASS((child - 1)) 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_cienum mlxsw_sp_qdisc_type { 198c2ecf20Sopenharmony_ci MLXSW_SP_QDISC_NO_QDISC, 208c2ecf20Sopenharmony_ci MLXSW_SP_QDISC_RED, 218c2ecf20Sopenharmony_ci MLXSW_SP_QDISC_PRIO, 228c2ecf20Sopenharmony_ci MLXSW_SP_QDISC_ETS, 238c2ecf20Sopenharmony_ci MLXSW_SP_QDISC_TBF, 248c2ecf20Sopenharmony_ci MLXSW_SP_QDISC_FIFO, 258c2ecf20Sopenharmony_ci}; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistruct mlxsw_sp_qdisc; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistruct mlxsw_sp_qdisc_ops { 308c2ecf20Sopenharmony_ci enum mlxsw_sp_qdisc_type type; 318c2ecf20Sopenharmony_ci int (*check_params)(struct mlxsw_sp_port *mlxsw_sp_port, 328c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 338c2ecf20Sopenharmony_ci void *params); 348c2ecf20Sopenharmony_ci int (*replace)(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, 358c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, void *params); 368c2ecf20Sopenharmony_ci int (*destroy)(struct mlxsw_sp_port *mlxsw_sp_port, 378c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc); 388c2ecf20Sopenharmony_ci int (*get_stats)(struct mlxsw_sp_port *mlxsw_sp_port, 398c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 408c2ecf20Sopenharmony_ci struct tc_qopt_offload_stats *stats_ptr); 418c2ecf20Sopenharmony_ci int (*get_xstats)(struct mlxsw_sp_port *mlxsw_sp_port, 428c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 438c2ecf20Sopenharmony_ci void *xstats_ptr); 448c2ecf20Sopenharmony_ci void (*clean_stats)(struct mlxsw_sp_port *mlxsw_sp_port, 458c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc); 468c2ecf20Sopenharmony_ci /* unoffload - to be used for a qdisc that stops being offloaded without 478c2ecf20Sopenharmony_ci * being destroyed. 488c2ecf20Sopenharmony_ci */ 498c2ecf20Sopenharmony_ci void (*unoffload)(struct mlxsw_sp_port *mlxsw_sp_port, 508c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, void *params); 518c2ecf20Sopenharmony_ci}; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistruct mlxsw_sp_qdisc { 548c2ecf20Sopenharmony_ci u32 handle; 558c2ecf20Sopenharmony_ci u8 tclass_num; 568c2ecf20Sopenharmony_ci u8 prio_bitmap; 578c2ecf20Sopenharmony_ci union { 588c2ecf20Sopenharmony_ci struct red_stats red; 598c2ecf20Sopenharmony_ci } xstats_base; 608c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc_stats { 618c2ecf20Sopenharmony_ci u64 tx_bytes; 628c2ecf20Sopenharmony_ci u64 tx_packets; 638c2ecf20Sopenharmony_ci u64 drops; 648c2ecf20Sopenharmony_ci u64 overlimits; 658c2ecf20Sopenharmony_ci u64 backlog; 668c2ecf20Sopenharmony_ci } stats_base; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc_ops *ops; 698c2ecf20Sopenharmony_ci}; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistruct mlxsw_sp_qdisc_state { 728c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc root_qdisc; 738c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc tclass_qdiscs[IEEE_8021QAZ_MAX_TCS]; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci /* When a PRIO or ETS are added, the invisible FIFOs in their bands are 768c2ecf20Sopenharmony_ci * created first. When notifications for these FIFOs arrive, it is not 778c2ecf20Sopenharmony_ci * known what qdisc their parent handle refers to. It could be a 788c2ecf20Sopenharmony_ci * newly-created PRIO that will replace the currently-offloaded one, or 798c2ecf20Sopenharmony_ci * it could be e.g. a RED that will be attached below it. 808c2ecf20Sopenharmony_ci * 818c2ecf20Sopenharmony_ci * As the notifications start to arrive, use them to note what the 828c2ecf20Sopenharmony_ci * future parent handle is, and keep track of which child FIFOs were 838c2ecf20Sopenharmony_ci * seen. Then when the parent is known, retroactively offload those 848c2ecf20Sopenharmony_ci * FIFOs. 858c2ecf20Sopenharmony_ci */ 868c2ecf20Sopenharmony_ci u32 future_handle; 878c2ecf20Sopenharmony_ci bool future_fifos[IEEE_8021QAZ_MAX_TCS]; 888c2ecf20Sopenharmony_ci}; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic bool 918c2ecf20Sopenharmony_cimlxsw_sp_qdisc_compare(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, u32 handle, 928c2ecf20Sopenharmony_ci enum mlxsw_sp_qdisc_type type) 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci return mlxsw_sp_qdisc && mlxsw_sp_qdisc->ops && 958c2ecf20Sopenharmony_ci mlxsw_sp_qdisc->ops->type == type && 968c2ecf20Sopenharmony_ci mlxsw_sp_qdisc->handle == handle; 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic struct mlxsw_sp_qdisc * 1008c2ecf20Sopenharmony_cimlxsw_sp_qdisc_find(struct mlxsw_sp_port *mlxsw_sp_port, u32 parent, 1018c2ecf20Sopenharmony_ci bool root_only) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc; 1048c2ecf20Sopenharmony_ci int tclass, child_index; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci if (parent == TC_H_ROOT) 1078c2ecf20Sopenharmony_ci return &qdisc_state->root_qdisc; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci if (root_only || !qdisc_state || 1108c2ecf20Sopenharmony_ci !qdisc_state->root_qdisc.ops || 1118c2ecf20Sopenharmony_ci TC_H_MAJ(parent) != qdisc_state->root_qdisc.handle || 1128c2ecf20Sopenharmony_ci TC_H_MIN(parent) > IEEE_8021QAZ_MAX_TCS) 1138c2ecf20Sopenharmony_ci return NULL; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci child_index = TC_H_MIN(parent); 1168c2ecf20Sopenharmony_ci tclass = MLXSW_SP_PRIO_CHILD_TO_TCLASS(child_index); 1178c2ecf20Sopenharmony_ci return &qdisc_state->tclass_qdiscs[tclass]; 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic struct mlxsw_sp_qdisc * 1218c2ecf20Sopenharmony_cimlxsw_sp_qdisc_find_by_handle(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc; 1248c2ecf20Sopenharmony_ci int i; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci if (qdisc_state->root_qdisc.handle == handle) 1278c2ecf20Sopenharmony_ci return &qdisc_state->root_qdisc; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci if (qdisc_state->root_qdisc.handle == TC_H_UNSPEC) 1308c2ecf20Sopenharmony_ci return NULL; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) 1338c2ecf20Sopenharmony_ci if (qdisc_state->tclass_qdiscs[i].handle == handle) 1348c2ecf20Sopenharmony_ci return &qdisc_state->tclass_qdiscs[i]; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci return NULL; 1378c2ecf20Sopenharmony_ci} 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_cistatic int 1408c2ecf20Sopenharmony_cimlxsw_sp_qdisc_destroy(struct mlxsw_sp_port *mlxsw_sp_port, 1418c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc *root_qdisc = &mlxsw_sp_port->qdisc->root_qdisc; 1448c2ecf20Sopenharmony_ci int err_hdroom = 0; 1458c2ecf20Sopenharmony_ci int err = 0; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci if (!mlxsw_sp_qdisc) 1488c2ecf20Sopenharmony_ci return 0; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci if (root_qdisc == mlxsw_sp_qdisc) { 1518c2ecf20Sopenharmony_ci struct mlxsw_sp_hdroom hdroom = *mlxsw_sp_port->hdroom; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci hdroom.mode = MLXSW_SP_HDROOM_MODE_DCB; 1548c2ecf20Sopenharmony_ci mlxsw_sp_hdroom_prios_reset_buf_idx(&hdroom); 1558c2ecf20Sopenharmony_ci mlxsw_sp_hdroom_bufs_reset_lossiness(&hdroom); 1568c2ecf20Sopenharmony_ci mlxsw_sp_hdroom_bufs_reset_sizes(mlxsw_sp_port, &hdroom); 1578c2ecf20Sopenharmony_ci err_hdroom = mlxsw_sp_hdroom_configure(mlxsw_sp_port, &hdroom); 1588c2ecf20Sopenharmony_ci } 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci if (mlxsw_sp_qdisc->ops && mlxsw_sp_qdisc->ops->destroy) 1618c2ecf20Sopenharmony_ci err = mlxsw_sp_qdisc->ops->destroy(mlxsw_sp_port, 1628c2ecf20Sopenharmony_ci mlxsw_sp_qdisc); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci mlxsw_sp_qdisc->handle = TC_H_UNSPEC; 1658c2ecf20Sopenharmony_ci mlxsw_sp_qdisc->ops = NULL; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci return err_hdroom ?: err; 1688c2ecf20Sopenharmony_ci} 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_cistatic int 1718c2ecf20Sopenharmony_cimlxsw_sp_qdisc_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, 1728c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 1738c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc_ops *ops, void *params) 1748c2ecf20Sopenharmony_ci{ 1758c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc *root_qdisc = &mlxsw_sp_port->qdisc->root_qdisc; 1768c2ecf20Sopenharmony_ci struct mlxsw_sp_hdroom orig_hdroom; 1778c2ecf20Sopenharmony_ci int err; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci if (mlxsw_sp_qdisc->ops && mlxsw_sp_qdisc->ops->type != ops->type) 1808c2ecf20Sopenharmony_ci /* In case this location contained a different qdisc of the 1818c2ecf20Sopenharmony_ci * same type we can override the old qdisc configuration. 1828c2ecf20Sopenharmony_ci * Otherwise, we need to remove the old qdisc before setting the 1838c2ecf20Sopenharmony_ci * new one. 1848c2ecf20Sopenharmony_ci */ 1858c2ecf20Sopenharmony_ci mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc); 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci orig_hdroom = *mlxsw_sp_port->hdroom; 1888c2ecf20Sopenharmony_ci if (root_qdisc == mlxsw_sp_qdisc) { 1898c2ecf20Sopenharmony_ci struct mlxsw_sp_hdroom hdroom = orig_hdroom; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci hdroom.mode = MLXSW_SP_HDROOM_MODE_TC; 1928c2ecf20Sopenharmony_ci mlxsw_sp_hdroom_prios_reset_buf_idx(&hdroom); 1938c2ecf20Sopenharmony_ci mlxsw_sp_hdroom_bufs_reset_lossiness(&hdroom); 1948c2ecf20Sopenharmony_ci mlxsw_sp_hdroom_bufs_reset_sizes(mlxsw_sp_port, &hdroom); 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci err = mlxsw_sp_hdroom_configure(mlxsw_sp_port, &hdroom); 1978c2ecf20Sopenharmony_ci if (err) 1988c2ecf20Sopenharmony_ci goto err_hdroom_configure; 1998c2ecf20Sopenharmony_ci } 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci err = ops->check_params(mlxsw_sp_port, mlxsw_sp_qdisc, params); 2028c2ecf20Sopenharmony_ci if (err) 2038c2ecf20Sopenharmony_ci goto err_bad_param; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci err = ops->replace(mlxsw_sp_port, handle, mlxsw_sp_qdisc, params); 2068c2ecf20Sopenharmony_ci if (err) 2078c2ecf20Sopenharmony_ci goto err_config; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci /* Check if the Qdisc changed. That includes a situation where an 2108c2ecf20Sopenharmony_ci * invisible Qdisc replaces another one, or is being added for the 2118c2ecf20Sopenharmony_ci * first time. 2128c2ecf20Sopenharmony_ci */ 2138c2ecf20Sopenharmony_ci if (mlxsw_sp_qdisc->handle != handle || handle == TC_H_UNSPEC) { 2148c2ecf20Sopenharmony_ci mlxsw_sp_qdisc->ops = ops; 2158c2ecf20Sopenharmony_ci if (ops->clean_stats) 2168c2ecf20Sopenharmony_ci ops->clean_stats(mlxsw_sp_port, mlxsw_sp_qdisc); 2178c2ecf20Sopenharmony_ci } 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci mlxsw_sp_qdisc->handle = handle; 2208c2ecf20Sopenharmony_ci return 0; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_cierr_bad_param: 2238c2ecf20Sopenharmony_cierr_config: 2248c2ecf20Sopenharmony_ci mlxsw_sp_hdroom_configure(mlxsw_sp_port, &orig_hdroom); 2258c2ecf20Sopenharmony_cierr_hdroom_configure: 2268c2ecf20Sopenharmony_ci if (mlxsw_sp_qdisc->handle == handle && ops->unoffload) 2278c2ecf20Sopenharmony_ci ops->unoffload(mlxsw_sp_port, mlxsw_sp_qdisc, params); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc); 2308c2ecf20Sopenharmony_ci return err; 2318c2ecf20Sopenharmony_ci} 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_cistatic int 2348c2ecf20Sopenharmony_cimlxsw_sp_qdisc_get_stats(struct mlxsw_sp_port *mlxsw_sp_port, 2358c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 2368c2ecf20Sopenharmony_ci struct tc_qopt_offload_stats *stats_ptr) 2378c2ecf20Sopenharmony_ci{ 2388c2ecf20Sopenharmony_ci if (mlxsw_sp_qdisc && mlxsw_sp_qdisc->ops && 2398c2ecf20Sopenharmony_ci mlxsw_sp_qdisc->ops->get_stats) 2408c2ecf20Sopenharmony_ci return mlxsw_sp_qdisc->ops->get_stats(mlxsw_sp_port, 2418c2ecf20Sopenharmony_ci mlxsw_sp_qdisc, 2428c2ecf20Sopenharmony_ci stats_ptr); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 2458c2ecf20Sopenharmony_ci} 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_cistatic int 2488c2ecf20Sopenharmony_cimlxsw_sp_qdisc_get_xstats(struct mlxsw_sp_port *mlxsw_sp_port, 2498c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 2508c2ecf20Sopenharmony_ci void *xstats_ptr) 2518c2ecf20Sopenharmony_ci{ 2528c2ecf20Sopenharmony_ci if (mlxsw_sp_qdisc && mlxsw_sp_qdisc->ops && 2538c2ecf20Sopenharmony_ci mlxsw_sp_qdisc->ops->get_xstats) 2548c2ecf20Sopenharmony_ci return mlxsw_sp_qdisc->ops->get_xstats(mlxsw_sp_port, 2558c2ecf20Sopenharmony_ci mlxsw_sp_qdisc, 2568c2ecf20Sopenharmony_ci xstats_ptr); 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 2598c2ecf20Sopenharmony_ci} 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_cistatic u64 2628c2ecf20Sopenharmony_cimlxsw_sp_xstats_backlog(struct mlxsw_sp_port_xstats *xstats, int tclass_num) 2638c2ecf20Sopenharmony_ci{ 2648c2ecf20Sopenharmony_ci return xstats->backlog[tclass_num] + 2658c2ecf20Sopenharmony_ci xstats->backlog[tclass_num + 8]; 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_cistatic u64 2698c2ecf20Sopenharmony_cimlxsw_sp_xstats_tail_drop(struct mlxsw_sp_port_xstats *xstats, int tclass_num) 2708c2ecf20Sopenharmony_ci{ 2718c2ecf20Sopenharmony_ci return xstats->tail_drop[tclass_num] + 2728c2ecf20Sopenharmony_ci xstats->tail_drop[tclass_num + 8]; 2738c2ecf20Sopenharmony_ci} 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_cistatic void 2768c2ecf20Sopenharmony_cimlxsw_sp_qdisc_bstats_per_priority_get(struct mlxsw_sp_port_xstats *xstats, 2778c2ecf20Sopenharmony_ci u8 prio_bitmap, u64 *tx_packets, 2788c2ecf20Sopenharmony_ci u64 *tx_bytes) 2798c2ecf20Sopenharmony_ci{ 2808c2ecf20Sopenharmony_ci int i; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci *tx_packets = 0; 2838c2ecf20Sopenharmony_ci *tx_bytes = 0; 2848c2ecf20Sopenharmony_ci for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { 2858c2ecf20Sopenharmony_ci if (prio_bitmap & BIT(i)) { 2868c2ecf20Sopenharmony_ci *tx_packets += xstats->tx_packets[i]; 2878c2ecf20Sopenharmony_ci *tx_bytes += xstats->tx_bytes[i]; 2888c2ecf20Sopenharmony_ci } 2898c2ecf20Sopenharmony_ci } 2908c2ecf20Sopenharmony_ci} 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_cistatic void 2938c2ecf20Sopenharmony_cimlxsw_sp_qdisc_collect_tc_stats(struct mlxsw_sp_port *mlxsw_sp_port, 2948c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 2958c2ecf20Sopenharmony_ci u64 *p_tx_bytes, u64 *p_tx_packets, 2968c2ecf20Sopenharmony_ci u64 *p_drops, u64 *p_backlog) 2978c2ecf20Sopenharmony_ci{ 2988c2ecf20Sopenharmony_ci u8 tclass_num = mlxsw_sp_qdisc->tclass_num; 2998c2ecf20Sopenharmony_ci struct mlxsw_sp_port_xstats *xstats; 3008c2ecf20Sopenharmony_ci u64 tx_bytes, tx_packets; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci xstats = &mlxsw_sp_port->periodic_hw_stats.xstats; 3038c2ecf20Sopenharmony_ci mlxsw_sp_qdisc_bstats_per_priority_get(xstats, 3048c2ecf20Sopenharmony_ci mlxsw_sp_qdisc->prio_bitmap, 3058c2ecf20Sopenharmony_ci &tx_packets, &tx_bytes); 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci *p_tx_packets += tx_packets; 3088c2ecf20Sopenharmony_ci *p_tx_bytes += tx_bytes; 3098c2ecf20Sopenharmony_ci *p_drops += xstats->wred_drop[tclass_num] + 3108c2ecf20Sopenharmony_ci mlxsw_sp_xstats_tail_drop(xstats, tclass_num); 3118c2ecf20Sopenharmony_ci *p_backlog += mlxsw_sp_xstats_backlog(xstats, tclass_num); 3128c2ecf20Sopenharmony_ci} 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_cistatic void 3158c2ecf20Sopenharmony_cimlxsw_sp_qdisc_update_stats(struct mlxsw_sp *mlxsw_sp, 3168c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 3178c2ecf20Sopenharmony_ci u64 tx_bytes, u64 tx_packets, 3188c2ecf20Sopenharmony_ci u64 drops, u64 backlog, 3198c2ecf20Sopenharmony_ci struct tc_qopt_offload_stats *stats_ptr) 3208c2ecf20Sopenharmony_ci{ 3218c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc_stats *stats_base = &mlxsw_sp_qdisc->stats_base; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci tx_bytes -= stats_base->tx_bytes; 3248c2ecf20Sopenharmony_ci tx_packets -= stats_base->tx_packets; 3258c2ecf20Sopenharmony_ci drops -= stats_base->drops; 3268c2ecf20Sopenharmony_ci backlog -= stats_base->backlog; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci _bstats_update(stats_ptr->bstats, tx_bytes, tx_packets); 3298c2ecf20Sopenharmony_ci stats_ptr->qstats->drops += drops; 3308c2ecf20Sopenharmony_ci stats_ptr->qstats->backlog += mlxsw_sp_cells_bytes(mlxsw_sp, backlog); 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci stats_base->backlog += backlog; 3338c2ecf20Sopenharmony_ci stats_base->drops += drops; 3348c2ecf20Sopenharmony_ci stats_base->tx_bytes += tx_bytes; 3358c2ecf20Sopenharmony_ci stats_base->tx_packets += tx_packets; 3368c2ecf20Sopenharmony_ci} 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_cistatic void 3398c2ecf20Sopenharmony_cimlxsw_sp_qdisc_get_tc_stats(struct mlxsw_sp_port *mlxsw_sp_port, 3408c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 3418c2ecf20Sopenharmony_ci struct tc_qopt_offload_stats *stats_ptr) 3428c2ecf20Sopenharmony_ci{ 3438c2ecf20Sopenharmony_ci u64 tx_packets = 0; 3448c2ecf20Sopenharmony_ci u64 tx_bytes = 0; 3458c2ecf20Sopenharmony_ci u64 backlog = 0; 3468c2ecf20Sopenharmony_ci u64 drops = 0; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci mlxsw_sp_qdisc_collect_tc_stats(mlxsw_sp_port, mlxsw_sp_qdisc, 3498c2ecf20Sopenharmony_ci &tx_bytes, &tx_packets, 3508c2ecf20Sopenharmony_ci &drops, &backlog); 3518c2ecf20Sopenharmony_ci mlxsw_sp_qdisc_update_stats(mlxsw_sp_port->mlxsw_sp, mlxsw_sp_qdisc, 3528c2ecf20Sopenharmony_ci tx_bytes, tx_packets, drops, backlog, 3538c2ecf20Sopenharmony_ci stats_ptr); 3548c2ecf20Sopenharmony_ci} 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_cistatic int 3578c2ecf20Sopenharmony_cimlxsw_sp_tclass_congestion_enable(struct mlxsw_sp_port *mlxsw_sp_port, 3588c2ecf20Sopenharmony_ci int tclass_num, u32 min, u32 max, 3598c2ecf20Sopenharmony_ci u32 probability, bool is_wred, bool is_ecn) 3608c2ecf20Sopenharmony_ci{ 3618c2ecf20Sopenharmony_ci char cwtpm_cmd[MLXSW_REG_CWTPM_LEN]; 3628c2ecf20Sopenharmony_ci char cwtp_cmd[MLXSW_REG_CWTP_LEN]; 3638c2ecf20Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 3648c2ecf20Sopenharmony_ci int err; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci mlxsw_reg_cwtp_pack(cwtp_cmd, mlxsw_sp_port->local_port, tclass_num); 3678c2ecf20Sopenharmony_ci mlxsw_reg_cwtp_profile_pack(cwtp_cmd, MLXSW_REG_CWTP_DEFAULT_PROFILE, 3688c2ecf20Sopenharmony_ci roundup(min, MLXSW_REG_CWTP_MIN_VALUE), 3698c2ecf20Sopenharmony_ci roundup(max, MLXSW_REG_CWTP_MIN_VALUE), 3708c2ecf20Sopenharmony_ci probability); 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(cwtp), cwtp_cmd); 3738c2ecf20Sopenharmony_ci if (err) 3748c2ecf20Sopenharmony_ci return err; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci mlxsw_reg_cwtpm_pack(cwtpm_cmd, mlxsw_sp_port->local_port, tclass_num, 3778c2ecf20Sopenharmony_ci MLXSW_REG_CWTP_DEFAULT_PROFILE, is_wred, is_ecn); 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(cwtpm), cwtpm_cmd); 3808c2ecf20Sopenharmony_ci} 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_cistatic int 3838c2ecf20Sopenharmony_cimlxsw_sp_tclass_congestion_disable(struct mlxsw_sp_port *mlxsw_sp_port, 3848c2ecf20Sopenharmony_ci int tclass_num) 3858c2ecf20Sopenharmony_ci{ 3868c2ecf20Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 3878c2ecf20Sopenharmony_ci char cwtpm_cmd[MLXSW_REG_CWTPM_LEN]; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci mlxsw_reg_cwtpm_pack(cwtpm_cmd, mlxsw_sp_port->local_port, tclass_num, 3908c2ecf20Sopenharmony_ci MLXSW_REG_CWTPM_RESET_PROFILE, false, false); 3918c2ecf20Sopenharmony_ci return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(cwtpm), cwtpm_cmd); 3928c2ecf20Sopenharmony_ci} 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_cistatic void 3958c2ecf20Sopenharmony_cimlxsw_sp_setup_tc_qdisc_red_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port, 3968c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 3978c2ecf20Sopenharmony_ci{ 3988c2ecf20Sopenharmony_ci u8 tclass_num = mlxsw_sp_qdisc->tclass_num; 3998c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc_stats *stats_base; 4008c2ecf20Sopenharmony_ci struct mlxsw_sp_port_xstats *xstats; 4018c2ecf20Sopenharmony_ci struct red_stats *red_base; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci xstats = &mlxsw_sp_port->periodic_hw_stats.xstats; 4048c2ecf20Sopenharmony_ci stats_base = &mlxsw_sp_qdisc->stats_base; 4058c2ecf20Sopenharmony_ci red_base = &mlxsw_sp_qdisc->xstats_base.red; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci mlxsw_sp_qdisc_bstats_per_priority_get(xstats, 4088c2ecf20Sopenharmony_ci mlxsw_sp_qdisc->prio_bitmap, 4098c2ecf20Sopenharmony_ci &stats_base->tx_packets, 4108c2ecf20Sopenharmony_ci &stats_base->tx_bytes); 4118c2ecf20Sopenharmony_ci red_base->prob_drop = xstats->wred_drop[tclass_num]; 4128c2ecf20Sopenharmony_ci red_base->pdrop = mlxsw_sp_xstats_tail_drop(xstats, tclass_num); 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci stats_base->overlimits = red_base->prob_drop + red_base->prob_mark; 4158c2ecf20Sopenharmony_ci stats_base->drops = red_base->prob_drop + red_base->pdrop; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci stats_base->backlog = 0; 4188c2ecf20Sopenharmony_ci} 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_cistatic int 4218c2ecf20Sopenharmony_cimlxsw_sp_qdisc_red_destroy(struct mlxsw_sp_port *mlxsw_sp_port, 4228c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 4238c2ecf20Sopenharmony_ci{ 4248c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc; 4258c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc *root_qdisc = &qdisc_state->root_qdisc; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci if (root_qdisc != mlxsw_sp_qdisc) 4288c2ecf20Sopenharmony_ci root_qdisc->stats_base.backlog -= 4298c2ecf20Sopenharmony_ci mlxsw_sp_qdisc->stats_base.backlog; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci return mlxsw_sp_tclass_congestion_disable(mlxsw_sp_port, 4328c2ecf20Sopenharmony_ci mlxsw_sp_qdisc->tclass_num); 4338c2ecf20Sopenharmony_ci} 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_cistatic int 4368c2ecf20Sopenharmony_cimlxsw_sp_qdisc_red_check_params(struct mlxsw_sp_port *mlxsw_sp_port, 4378c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 4388c2ecf20Sopenharmony_ci void *params) 4398c2ecf20Sopenharmony_ci{ 4408c2ecf20Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 4418c2ecf20Sopenharmony_ci struct tc_red_qopt_offload_params *p = params; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci if (p->min > p->max) { 4448c2ecf20Sopenharmony_ci dev_err(mlxsw_sp->bus_info->dev, 4458c2ecf20Sopenharmony_ci "spectrum: RED: min %u is bigger then max %u\n", p->min, 4468c2ecf20Sopenharmony_ci p->max); 4478c2ecf20Sopenharmony_ci return -EINVAL; 4488c2ecf20Sopenharmony_ci } 4498c2ecf20Sopenharmony_ci if (p->max > MLXSW_CORE_RES_GET(mlxsw_sp->core, 4508c2ecf20Sopenharmony_ci GUARANTEED_SHARED_BUFFER)) { 4518c2ecf20Sopenharmony_ci dev_err(mlxsw_sp->bus_info->dev, 4528c2ecf20Sopenharmony_ci "spectrum: RED: max value %u is too big\n", p->max); 4538c2ecf20Sopenharmony_ci return -EINVAL; 4548c2ecf20Sopenharmony_ci } 4558c2ecf20Sopenharmony_ci if (p->min == 0 || p->max == 0) { 4568c2ecf20Sopenharmony_ci dev_err(mlxsw_sp->bus_info->dev, 4578c2ecf20Sopenharmony_ci "spectrum: RED: 0 value is illegal for min and max\n"); 4588c2ecf20Sopenharmony_ci return -EINVAL; 4598c2ecf20Sopenharmony_ci } 4608c2ecf20Sopenharmony_ci return 0; 4618c2ecf20Sopenharmony_ci} 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_cistatic int 4648c2ecf20Sopenharmony_cimlxsw_sp_qdisc_red_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, 4658c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 4668c2ecf20Sopenharmony_ci void *params) 4678c2ecf20Sopenharmony_ci{ 4688c2ecf20Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 4698c2ecf20Sopenharmony_ci struct tc_red_qopt_offload_params *p = params; 4708c2ecf20Sopenharmony_ci u8 tclass_num = mlxsw_sp_qdisc->tclass_num; 4718c2ecf20Sopenharmony_ci u32 min, max; 4728c2ecf20Sopenharmony_ci u64 prob; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci /* calculate probability in percentage */ 4758c2ecf20Sopenharmony_ci prob = p->probability; 4768c2ecf20Sopenharmony_ci prob *= 100; 4778c2ecf20Sopenharmony_ci prob = DIV_ROUND_UP(prob, 1 << 16); 4788c2ecf20Sopenharmony_ci prob = DIV_ROUND_UP(prob, 1 << 16); 4798c2ecf20Sopenharmony_ci min = mlxsw_sp_bytes_cells(mlxsw_sp, p->min); 4808c2ecf20Sopenharmony_ci max = mlxsw_sp_bytes_cells(mlxsw_sp, p->max); 4818c2ecf20Sopenharmony_ci return mlxsw_sp_tclass_congestion_enable(mlxsw_sp_port, tclass_num, 4828c2ecf20Sopenharmony_ci min, max, prob, 4838c2ecf20Sopenharmony_ci !p->is_nodrop, p->is_ecn); 4848c2ecf20Sopenharmony_ci} 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_cistatic void 4878c2ecf20Sopenharmony_cimlxsw_sp_qdisc_leaf_unoffload(struct mlxsw_sp_port *mlxsw_sp_port, 4888c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 4898c2ecf20Sopenharmony_ci struct gnet_stats_queue *qstats) 4908c2ecf20Sopenharmony_ci{ 4918c2ecf20Sopenharmony_ci u64 backlog; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci backlog = mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp, 4948c2ecf20Sopenharmony_ci mlxsw_sp_qdisc->stats_base.backlog); 4958c2ecf20Sopenharmony_ci qstats->backlog -= backlog; 4968c2ecf20Sopenharmony_ci mlxsw_sp_qdisc->stats_base.backlog = 0; 4978c2ecf20Sopenharmony_ci} 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_cistatic void 5008c2ecf20Sopenharmony_cimlxsw_sp_qdisc_red_unoffload(struct mlxsw_sp_port *mlxsw_sp_port, 5018c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 5028c2ecf20Sopenharmony_ci void *params) 5038c2ecf20Sopenharmony_ci{ 5048c2ecf20Sopenharmony_ci struct tc_red_qopt_offload_params *p = params; 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci mlxsw_sp_qdisc_leaf_unoffload(mlxsw_sp_port, mlxsw_sp_qdisc, p->qstats); 5078c2ecf20Sopenharmony_ci} 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_cistatic int 5108c2ecf20Sopenharmony_cimlxsw_sp_qdisc_get_red_xstats(struct mlxsw_sp_port *mlxsw_sp_port, 5118c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 5128c2ecf20Sopenharmony_ci void *xstats_ptr) 5138c2ecf20Sopenharmony_ci{ 5148c2ecf20Sopenharmony_ci struct red_stats *xstats_base = &mlxsw_sp_qdisc->xstats_base.red; 5158c2ecf20Sopenharmony_ci u8 tclass_num = mlxsw_sp_qdisc->tclass_num; 5168c2ecf20Sopenharmony_ci struct mlxsw_sp_port_xstats *xstats; 5178c2ecf20Sopenharmony_ci struct red_stats *res = xstats_ptr; 5188c2ecf20Sopenharmony_ci int early_drops, pdrops; 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci xstats = &mlxsw_sp_port->periodic_hw_stats.xstats; 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci early_drops = xstats->wred_drop[tclass_num] - xstats_base->prob_drop; 5238c2ecf20Sopenharmony_ci pdrops = mlxsw_sp_xstats_tail_drop(xstats, tclass_num) - 5248c2ecf20Sopenharmony_ci xstats_base->pdrop; 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci res->pdrop += pdrops; 5278c2ecf20Sopenharmony_ci res->prob_drop += early_drops; 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci xstats_base->pdrop += pdrops; 5308c2ecf20Sopenharmony_ci xstats_base->prob_drop += early_drops; 5318c2ecf20Sopenharmony_ci return 0; 5328c2ecf20Sopenharmony_ci} 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_cistatic int 5358c2ecf20Sopenharmony_cimlxsw_sp_qdisc_get_red_stats(struct mlxsw_sp_port *mlxsw_sp_port, 5368c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 5378c2ecf20Sopenharmony_ci struct tc_qopt_offload_stats *stats_ptr) 5388c2ecf20Sopenharmony_ci{ 5398c2ecf20Sopenharmony_ci u8 tclass_num = mlxsw_sp_qdisc->tclass_num; 5408c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc_stats *stats_base; 5418c2ecf20Sopenharmony_ci struct mlxsw_sp_port_xstats *xstats; 5428c2ecf20Sopenharmony_ci u64 overlimits; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci xstats = &mlxsw_sp_port->periodic_hw_stats.xstats; 5458c2ecf20Sopenharmony_ci stats_base = &mlxsw_sp_qdisc->stats_base; 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci mlxsw_sp_qdisc_get_tc_stats(mlxsw_sp_port, mlxsw_sp_qdisc, stats_ptr); 5488c2ecf20Sopenharmony_ci overlimits = xstats->wred_drop[tclass_num] - stats_base->overlimits; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci stats_ptr->qstats->overlimits += overlimits; 5518c2ecf20Sopenharmony_ci stats_base->overlimits += overlimits; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci return 0; 5548c2ecf20Sopenharmony_ci} 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci#define MLXSW_SP_PORT_DEFAULT_TCLASS 0 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_cistatic struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_red = { 5598c2ecf20Sopenharmony_ci .type = MLXSW_SP_QDISC_RED, 5608c2ecf20Sopenharmony_ci .check_params = mlxsw_sp_qdisc_red_check_params, 5618c2ecf20Sopenharmony_ci .replace = mlxsw_sp_qdisc_red_replace, 5628c2ecf20Sopenharmony_ci .unoffload = mlxsw_sp_qdisc_red_unoffload, 5638c2ecf20Sopenharmony_ci .destroy = mlxsw_sp_qdisc_red_destroy, 5648c2ecf20Sopenharmony_ci .get_stats = mlxsw_sp_qdisc_get_red_stats, 5658c2ecf20Sopenharmony_ci .get_xstats = mlxsw_sp_qdisc_get_red_xstats, 5668c2ecf20Sopenharmony_ci .clean_stats = mlxsw_sp_setup_tc_qdisc_red_clean_stats, 5678c2ecf20Sopenharmony_ci}; 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ciint mlxsw_sp_setup_tc_red(struct mlxsw_sp_port *mlxsw_sp_port, 5708c2ecf20Sopenharmony_ci struct tc_red_qopt_offload *p) 5718c2ecf20Sopenharmony_ci{ 5728c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc; 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent, false); 5758c2ecf20Sopenharmony_ci if (!mlxsw_sp_qdisc) 5768c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci if (p->command == TC_RED_REPLACE) 5798c2ecf20Sopenharmony_ci return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle, 5808c2ecf20Sopenharmony_ci mlxsw_sp_qdisc, 5818c2ecf20Sopenharmony_ci &mlxsw_sp_qdisc_ops_red, 5828c2ecf20Sopenharmony_ci &p->set); 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle, 5858c2ecf20Sopenharmony_ci MLXSW_SP_QDISC_RED)) 5868c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci switch (p->command) { 5898c2ecf20Sopenharmony_ci case TC_RED_DESTROY: 5908c2ecf20Sopenharmony_ci return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc); 5918c2ecf20Sopenharmony_ci case TC_RED_XSTATS: 5928c2ecf20Sopenharmony_ci return mlxsw_sp_qdisc_get_xstats(mlxsw_sp_port, mlxsw_sp_qdisc, 5938c2ecf20Sopenharmony_ci p->xstats); 5948c2ecf20Sopenharmony_ci case TC_RED_STATS: 5958c2ecf20Sopenharmony_ci return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc, 5968c2ecf20Sopenharmony_ci &p->stats); 5978c2ecf20Sopenharmony_ci default: 5988c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 5998c2ecf20Sopenharmony_ci } 6008c2ecf20Sopenharmony_ci} 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_cistatic void 6038c2ecf20Sopenharmony_cimlxsw_sp_setup_tc_qdisc_leaf_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port, 6048c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 6058c2ecf20Sopenharmony_ci{ 6068c2ecf20Sopenharmony_ci u64 backlog_cells = 0; 6078c2ecf20Sopenharmony_ci u64 tx_packets = 0; 6088c2ecf20Sopenharmony_ci u64 tx_bytes = 0; 6098c2ecf20Sopenharmony_ci u64 drops = 0; 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci mlxsw_sp_qdisc_collect_tc_stats(mlxsw_sp_port, mlxsw_sp_qdisc, 6128c2ecf20Sopenharmony_ci &tx_bytes, &tx_packets, 6138c2ecf20Sopenharmony_ci &drops, &backlog_cells); 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci mlxsw_sp_qdisc->stats_base.tx_packets = tx_packets; 6168c2ecf20Sopenharmony_ci mlxsw_sp_qdisc->stats_base.tx_bytes = tx_bytes; 6178c2ecf20Sopenharmony_ci mlxsw_sp_qdisc->stats_base.drops = drops; 6188c2ecf20Sopenharmony_ci mlxsw_sp_qdisc->stats_base.backlog = 0; 6198c2ecf20Sopenharmony_ci} 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_cistatic int 6228c2ecf20Sopenharmony_cimlxsw_sp_qdisc_tbf_destroy(struct mlxsw_sp_port *mlxsw_sp_port, 6238c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 6248c2ecf20Sopenharmony_ci{ 6258c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc; 6268c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc *root_qdisc = &qdisc_state->root_qdisc; 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci if (root_qdisc != mlxsw_sp_qdisc) 6298c2ecf20Sopenharmony_ci root_qdisc->stats_base.backlog -= 6308c2ecf20Sopenharmony_ci mlxsw_sp_qdisc->stats_base.backlog; 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci return mlxsw_sp_port_ets_maxrate_set(mlxsw_sp_port, 6338c2ecf20Sopenharmony_ci MLXSW_REG_QEEC_HR_SUBGROUP, 6348c2ecf20Sopenharmony_ci mlxsw_sp_qdisc->tclass_num, 0, 6358c2ecf20Sopenharmony_ci MLXSW_REG_QEEC_MAS_DIS, 0); 6368c2ecf20Sopenharmony_ci} 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_cistatic int 6398c2ecf20Sopenharmony_cimlxsw_sp_qdisc_tbf_bs(struct mlxsw_sp_port *mlxsw_sp_port, 6408c2ecf20Sopenharmony_ci u32 max_size, u8 *p_burst_size) 6418c2ecf20Sopenharmony_ci{ 6428c2ecf20Sopenharmony_ci /* TBF burst size is configured in bytes. The ASIC burst size value is 6438c2ecf20Sopenharmony_ci * ((2 ^ bs) * 512 bits. Convert the TBF bytes to 512-bit units. 6448c2ecf20Sopenharmony_ci */ 6458c2ecf20Sopenharmony_ci u32 bs512 = max_size / 64; 6468c2ecf20Sopenharmony_ci u8 bs = fls(bs512); 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci if (!bs) 6498c2ecf20Sopenharmony_ci return -EINVAL; 6508c2ecf20Sopenharmony_ci --bs; 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci /* Demand a power of two. */ 6538c2ecf20Sopenharmony_ci if ((1 << bs) != bs512) 6548c2ecf20Sopenharmony_ci return -EINVAL; 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci if (bs < mlxsw_sp_port->mlxsw_sp->lowest_shaper_bs || 6578c2ecf20Sopenharmony_ci bs > MLXSW_REG_QEEC_HIGHEST_SHAPER_BS) 6588c2ecf20Sopenharmony_ci return -EINVAL; 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci *p_burst_size = bs; 6618c2ecf20Sopenharmony_ci return 0; 6628c2ecf20Sopenharmony_ci} 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_cistatic u32 6658c2ecf20Sopenharmony_cimlxsw_sp_qdisc_tbf_max_size(u8 bs) 6668c2ecf20Sopenharmony_ci{ 6678c2ecf20Sopenharmony_ci return (1U << bs) * 64; 6688c2ecf20Sopenharmony_ci} 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_cistatic u64 6718c2ecf20Sopenharmony_cimlxsw_sp_qdisc_tbf_rate_kbps(struct tc_tbf_qopt_offload_replace_params *p) 6728c2ecf20Sopenharmony_ci{ 6738c2ecf20Sopenharmony_ci /* TBF interface is in bytes/s, whereas Spectrum ASIC is configured in 6748c2ecf20Sopenharmony_ci * Kbits/s. 6758c2ecf20Sopenharmony_ci */ 6768c2ecf20Sopenharmony_ci return div_u64(p->rate.rate_bytes_ps, 1000) * 8; 6778c2ecf20Sopenharmony_ci} 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_cistatic int 6808c2ecf20Sopenharmony_cimlxsw_sp_qdisc_tbf_check_params(struct mlxsw_sp_port *mlxsw_sp_port, 6818c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 6828c2ecf20Sopenharmony_ci void *params) 6838c2ecf20Sopenharmony_ci{ 6848c2ecf20Sopenharmony_ci struct tc_tbf_qopt_offload_replace_params *p = params; 6858c2ecf20Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 6868c2ecf20Sopenharmony_ci u64 rate_kbps = mlxsw_sp_qdisc_tbf_rate_kbps(p); 6878c2ecf20Sopenharmony_ci u8 burst_size; 6888c2ecf20Sopenharmony_ci int err; 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci if (rate_kbps >= MLXSW_REG_QEEC_MAS_DIS) { 6918c2ecf20Sopenharmony_ci dev_err(mlxsw_sp_port->mlxsw_sp->bus_info->dev, 6928c2ecf20Sopenharmony_ci "spectrum: TBF: rate of %lluKbps must be below %u\n", 6938c2ecf20Sopenharmony_ci rate_kbps, MLXSW_REG_QEEC_MAS_DIS); 6948c2ecf20Sopenharmony_ci return -EINVAL; 6958c2ecf20Sopenharmony_ci } 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci err = mlxsw_sp_qdisc_tbf_bs(mlxsw_sp_port, p->max_size, &burst_size); 6988c2ecf20Sopenharmony_ci if (err) { 6998c2ecf20Sopenharmony_ci u8 highest_shaper_bs = MLXSW_REG_QEEC_HIGHEST_SHAPER_BS; 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci dev_err(mlxsw_sp->bus_info->dev, 7028c2ecf20Sopenharmony_ci "spectrum: TBF: invalid burst size of %u, must be a power of two between %u and %u", 7038c2ecf20Sopenharmony_ci p->max_size, 7048c2ecf20Sopenharmony_ci mlxsw_sp_qdisc_tbf_max_size(mlxsw_sp->lowest_shaper_bs), 7058c2ecf20Sopenharmony_ci mlxsw_sp_qdisc_tbf_max_size(highest_shaper_bs)); 7068c2ecf20Sopenharmony_ci return -EINVAL; 7078c2ecf20Sopenharmony_ci } 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci return 0; 7108c2ecf20Sopenharmony_ci} 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_cistatic int 7138c2ecf20Sopenharmony_cimlxsw_sp_qdisc_tbf_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, 7148c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 7158c2ecf20Sopenharmony_ci void *params) 7168c2ecf20Sopenharmony_ci{ 7178c2ecf20Sopenharmony_ci struct tc_tbf_qopt_offload_replace_params *p = params; 7188c2ecf20Sopenharmony_ci u64 rate_kbps = mlxsw_sp_qdisc_tbf_rate_kbps(p); 7198c2ecf20Sopenharmony_ci u8 burst_size; 7208c2ecf20Sopenharmony_ci int err; 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci err = mlxsw_sp_qdisc_tbf_bs(mlxsw_sp_port, p->max_size, &burst_size); 7238c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(err)) 7248c2ecf20Sopenharmony_ci /* check_params above was supposed to reject this value. */ 7258c2ecf20Sopenharmony_ci return -EINVAL; 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci /* Configure subgroup shaper, so that both UC and MC traffic is subject 7288c2ecf20Sopenharmony_ci * to shaping. That is unlike RED, however UC queue lengths are going to 7298c2ecf20Sopenharmony_ci * be different than MC ones due to different pool and quota 7308c2ecf20Sopenharmony_ci * configurations, so the configuration is not applicable. For shaper on 7318c2ecf20Sopenharmony_ci * the other hand, subjecting the overall stream to the configured 7328c2ecf20Sopenharmony_ci * shaper makes sense. Also note that that is what we do for 7338c2ecf20Sopenharmony_ci * ieee_setmaxrate(). 7348c2ecf20Sopenharmony_ci */ 7358c2ecf20Sopenharmony_ci return mlxsw_sp_port_ets_maxrate_set(mlxsw_sp_port, 7368c2ecf20Sopenharmony_ci MLXSW_REG_QEEC_HR_SUBGROUP, 7378c2ecf20Sopenharmony_ci mlxsw_sp_qdisc->tclass_num, 0, 7388c2ecf20Sopenharmony_ci rate_kbps, burst_size); 7398c2ecf20Sopenharmony_ci} 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_cistatic void 7428c2ecf20Sopenharmony_cimlxsw_sp_qdisc_tbf_unoffload(struct mlxsw_sp_port *mlxsw_sp_port, 7438c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 7448c2ecf20Sopenharmony_ci void *params) 7458c2ecf20Sopenharmony_ci{ 7468c2ecf20Sopenharmony_ci struct tc_tbf_qopt_offload_replace_params *p = params; 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci mlxsw_sp_qdisc_leaf_unoffload(mlxsw_sp_port, mlxsw_sp_qdisc, p->qstats); 7498c2ecf20Sopenharmony_ci} 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_cistatic int 7528c2ecf20Sopenharmony_cimlxsw_sp_qdisc_get_tbf_stats(struct mlxsw_sp_port *mlxsw_sp_port, 7538c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 7548c2ecf20Sopenharmony_ci struct tc_qopt_offload_stats *stats_ptr) 7558c2ecf20Sopenharmony_ci{ 7568c2ecf20Sopenharmony_ci mlxsw_sp_qdisc_get_tc_stats(mlxsw_sp_port, mlxsw_sp_qdisc, 7578c2ecf20Sopenharmony_ci stats_ptr); 7588c2ecf20Sopenharmony_ci return 0; 7598c2ecf20Sopenharmony_ci} 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_cistatic struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_tbf = { 7628c2ecf20Sopenharmony_ci .type = MLXSW_SP_QDISC_TBF, 7638c2ecf20Sopenharmony_ci .check_params = mlxsw_sp_qdisc_tbf_check_params, 7648c2ecf20Sopenharmony_ci .replace = mlxsw_sp_qdisc_tbf_replace, 7658c2ecf20Sopenharmony_ci .unoffload = mlxsw_sp_qdisc_tbf_unoffload, 7668c2ecf20Sopenharmony_ci .destroy = mlxsw_sp_qdisc_tbf_destroy, 7678c2ecf20Sopenharmony_ci .get_stats = mlxsw_sp_qdisc_get_tbf_stats, 7688c2ecf20Sopenharmony_ci .clean_stats = mlxsw_sp_setup_tc_qdisc_leaf_clean_stats, 7698c2ecf20Sopenharmony_ci}; 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ciint mlxsw_sp_setup_tc_tbf(struct mlxsw_sp_port *mlxsw_sp_port, 7728c2ecf20Sopenharmony_ci struct tc_tbf_qopt_offload *p) 7738c2ecf20Sopenharmony_ci{ 7748c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc; 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent, false); 7778c2ecf20Sopenharmony_ci if (!mlxsw_sp_qdisc) 7788c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci if (p->command == TC_TBF_REPLACE) 7818c2ecf20Sopenharmony_ci return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle, 7828c2ecf20Sopenharmony_ci mlxsw_sp_qdisc, 7838c2ecf20Sopenharmony_ci &mlxsw_sp_qdisc_ops_tbf, 7848c2ecf20Sopenharmony_ci &p->replace_params); 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle, 7878c2ecf20Sopenharmony_ci MLXSW_SP_QDISC_TBF)) 7888c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci switch (p->command) { 7918c2ecf20Sopenharmony_ci case TC_TBF_DESTROY: 7928c2ecf20Sopenharmony_ci return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc); 7938c2ecf20Sopenharmony_ci case TC_TBF_STATS: 7948c2ecf20Sopenharmony_ci return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc, 7958c2ecf20Sopenharmony_ci &p->stats); 7968c2ecf20Sopenharmony_ci default: 7978c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 7988c2ecf20Sopenharmony_ci } 7998c2ecf20Sopenharmony_ci} 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_cistatic int 8028c2ecf20Sopenharmony_cimlxsw_sp_qdisc_fifo_destroy(struct mlxsw_sp_port *mlxsw_sp_port, 8038c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 8048c2ecf20Sopenharmony_ci{ 8058c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc; 8068c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc *root_qdisc = &qdisc_state->root_qdisc; 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci if (root_qdisc != mlxsw_sp_qdisc) 8098c2ecf20Sopenharmony_ci root_qdisc->stats_base.backlog -= 8108c2ecf20Sopenharmony_ci mlxsw_sp_qdisc->stats_base.backlog; 8118c2ecf20Sopenharmony_ci return 0; 8128c2ecf20Sopenharmony_ci} 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_cistatic int 8158c2ecf20Sopenharmony_cimlxsw_sp_qdisc_fifo_check_params(struct mlxsw_sp_port *mlxsw_sp_port, 8168c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 8178c2ecf20Sopenharmony_ci void *params) 8188c2ecf20Sopenharmony_ci{ 8198c2ecf20Sopenharmony_ci return 0; 8208c2ecf20Sopenharmony_ci} 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_cistatic int 8238c2ecf20Sopenharmony_cimlxsw_sp_qdisc_fifo_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, 8248c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 8258c2ecf20Sopenharmony_ci void *params) 8268c2ecf20Sopenharmony_ci{ 8278c2ecf20Sopenharmony_ci return 0; 8288c2ecf20Sopenharmony_ci} 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_cistatic int 8318c2ecf20Sopenharmony_cimlxsw_sp_qdisc_get_fifo_stats(struct mlxsw_sp_port *mlxsw_sp_port, 8328c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 8338c2ecf20Sopenharmony_ci struct tc_qopt_offload_stats *stats_ptr) 8348c2ecf20Sopenharmony_ci{ 8358c2ecf20Sopenharmony_ci mlxsw_sp_qdisc_get_tc_stats(mlxsw_sp_port, mlxsw_sp_qdisc, 8368c2ecf20Sopenharmony_ci stats_ptr); 8378c2ecf20Sopenharmony_ci return 0; 8388c2ecf20Sopenharmony_ci} 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_cistatic struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_fifo = { 8418c2ecf20Sopenharmony_ci .type = MLXSW_SP_QDISC_FIFO, 8428c2ecf20Sopenharmony_ci .check_params = mlxsw_sp_qdisc_fifo_check_params, 8438c2ecf20Sopenharmony_ci .replace = mlxsw_sp_qdisc_fifo_replace, 8448c2ecf20Sopenharmony_ci .destroy = mlxsw_sp_qdisc_fifo_destroy, 8458c2ecf20Sopenharmony_ci .get_stats = mlxsw_sp_qdisc_get_fifo_stats, 8468c2ecf20Sopenharmony_ci .clean_stats = mlxsw_sp_setup_tc_qdisc_leaf_clean_stats, 8478c2ecf20Sopenharmony_ci}; 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ciint mlxsw_sp_setup_tc_fifo(struct mlxsw_sp_port *mlxsw_sp_port, 8508c2ecf20Sopenharmony_ci struct tc_fifo_qopt_offload *p) 8518c2ecf20Sopenharmony_ci{ 8528c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc; 8538c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc; 8548c2ecf20Sopenharmony_ci int tclass, child_index; 8558c2ecf20Sopenharmony_ci u32 parent_handle; 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci /* Invisible FIFOs are tracked in future_handle and future_fifos. Make 8588c2ecf20Sopenharmony_ci * sure that not more than one qdisc is created for a port at a time. 8598c2ecf20Sopenharmony_ci * RTNL is a simple proxy for that. 8608c2ecf20Sopenharmony_ci */ 8618c2ecf20Sopenharmony_ci ASSERT_RTNL(); 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_ci mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent, false); 8648c2ecf20Sopenharmony_ci if (!mlxsw_sp_qdisc && p->handle == TC_H_UNSPEC) { 8658c2ecf20Sopenharmony_ci parent_handle = TC_H_MAJ(p->parent); 8668c2ecf20Sopenharmony_ci if (parent_handle != qdisc_state->future_handle) { 8678c2ecf20Sopenharmony_ci /* This notifications is for a different Qdisc than 8688c2ecf20Sopenharmony_ci * previously. Wipe the future cache. 8698c2ecf20Sopenharmony_ci */ 8708c2ecf20Sopenharmony_ci memset(qdisc_state->future_fifos, 0, 8718c2ecf20Sopenharmony_ci sizeof(qdisc_state->future_fifos)); 8728c2ecf20Sopenharmony_ci qdisc_state->future_handle = parent_handle; 8738c2ecf20Sopenharmony_ci } 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ci child_index = TC_H_MIN(p->parent); 8768c2ecf20Sopenharmony_ci tclass = MLXSW_SP_PRIO_CHILD_TO_TCLASS(child_index); 8778c2ecf20Sopenharmony_ci if (tclass < IEEE_8021QAZ_MAX_TCS) { 8788c2ecf20Sopenharmony_ci if (p->command == TC_FIFO_REPLACE) 8798c2ecf20Sopenharmony_ci qdisc_state->future_fifos[tclass] = true; 8808c2ecf20Sopenharmony_ci else if (p->command == TC_FIFO_DESTROY) 8818c2ecf20Sopenharmony_ci qdisc_state->future_fifos[tclass] = false; 8828c2ecf20Sopenharmony_ci } 8838c2ecf20Sopenharmony_ci } 8848c2ecf20Sopenharmony_ci if (!mlxsw_sp_qdisc) 8858c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci if (p->command == TC_FIFO_REPLACE) { 8888c2ecf20Sopenharmony_ci return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle, 8898c2ecf20Sopenharmony_ci mlxsw_sp_qdisc, 8908c2ecf20Sopenharmony_ci &mlxsw_sp_qdisc_ops_fifo, NULL); 8918c2ecf20Sopenharmony_ci } 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle, 8948c2ecf20Sopenharmony_ci MLXSW_SP_QDISC_FIFO)) 8958c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci switch (p->command) { 8988c2ecf20Sopenharmony_ci case TC_FIFO_DESTROY: 8998c2ecf20Sopenharmony_ci if (p->handle == mlxsw_sp_qdisc->handle) 9008c2ecf20Sopenharmony_ci return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, 9018c2ecf20Sopenharmony_ci mlxsw_sp_qdisc); 9028c2ecf20Sopenharmony_ci return 0; 9038c2ecf20Sopenharmony_ci case TC_FIFO_STATS: 9048c2ecf20Sopenharmony_ci return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc, 9058c2ecf20Sopenharmony_ci &p->stats); 9068c2ecf20Sopenharmony_ci case TC_FIFO_REPLACE: /* Handled above. */ 9078c2ecf20Sopenharmony_ci break; 9088c2ecf20Sopenharmony_ci } 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 9118c2ecf20Sopenharmony_ci} 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_cistatic int 9148c2ecf20Sopenharmony_ci__mlxsw_sp_qdisc_ets_destroy(struct mlxsw_sp_port *mlxsw_sp_port) 9158c2ecf20Sopenharmony_ci{ 9168c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc; 9178c2ecf20Sopenharmony_ci int i; 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { 9208c2ecf20Sopenharmony_ci mlxsw_sp_port_prio_tc_set(mlxsw_sp_port, i, 9218c2ecf20Sopenharmony_ci MLXSW_SP_PORT_DEFAULT_TCLASS); 9228c2ecf20Sopenharmony_ci mlxsw_sp_port_ets_set(mlxsw_sp_port, 9238c2ecf20Sopenharmony_ci MLXSW_REG_QEEC_HR_SUBGROUP, 9248c2ecf20Sopenharmony_ci i, 0, false, 0); 9258c2ecf20Sopenharmony_ci mlxsw_sp_qdisc_destroy(mlxsw_sp_port, 9268c2ecf20Sopenharmony_ci &qdisc_state->tclass_qdiscs[i]); 9278c2ecf20Sopenharmony_ci qdisc_state->tclass_qdiscs[i].prio_bitmap = 0; 9288c2ecf20Sopenharmony_ci } 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_ci return 0; 9318c2ecf20Sopenharmony_ci} 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_cistatic int 9348c2ecf20Sopenharmony_cimlxsw_sp_qdisc_prio_destroy(struct mlxsw_sp_port *mlxsw_sp_port, 9358c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 9368c2ecf20Sopenharmony_ci{ 9378c2ecf20Sopenharmony_ci return __mlxsw_sp_qdisc_ets_destroy(mlxsw_sp_port); 9388c2ecf20Sopenharmony_ci} 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_cistatic int 9418c2ecf20Sopenharmony_ci__mlxsw_sp_qdisc_ets_check_params(unsigned int nbands) 9428c2ecf20Sopenharmony_ci{ 9438c2ecf20Sopenharmony_ci if (nbands > IEEE_8021QAZ_MAX_TCS) 9448c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci return 0; 9478c2ecf20Sopenharmony_ci} 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_cistatic int 9508c2ecf20Sopenharmony_cimlxsw_sp_qdisc_prio_check_params(struct mlxsw_sp_port *mlxsw_sp_port, 9518c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 9528c2ecf20Sopenharmony_ci void *params) 9538c2ecf20Sopenharmony_ci{ 9548c2ecf20Sopenharmony_ci struct tc_prio_qopt_offload_params *p = params; 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci return __mlxsw_sp_qdisc_ets_check_params(p->bands); 9578c2ecf20Sopenharmony_ci} 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_cistatic int 9608c2ecf20Sopenharmony_ci__mlxsw_sp_qdisc_ets_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, 9618c2ecf20Sopenharmony_ci unsigned int nbands, 9628c2ecf20Sopenharmony_ci const unsigned int *quanta, 9638c2ecf20Sopenharmony_ci const unsigned int *weights, 9648c2ecf20Sopenharmony_ci const u8 *priomap) 9658c2ecf20Sopenharmony_ci{ 9668c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc; 9678c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc *child_qdisc; 9688c2ecf20Sopenharmony_ci int tclass, i, band, backlog; 9698c2ecf20Sopenharmony_ci u8 old_priomap; 9708c2ecf20Sopenharmony_ci int err; 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_ci for (band = 0; band < nbands; band++) { 9738c2ecf20Sopenharmony_ci tclass = MLXSW_SP_PRIO_BAND_TO_TCLASS(band); 9748c2ecf20Sopenharmony_ci child_qdisc = &qdisc_state->tclass_qdiscs[tclass]; 9758c2ecf20Sopenharmony_ci old_priomap = child_qdisc->prio_bitmap; 9768c2ecf20Sopenharmony_ci child_qdisc->prio_bitmap = 0; 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ci err = mlxsw_sp_port_ets_set(mlxsw_sp_port, 9798c2ecf20Sopenharmony_ci MLXSW_REG_QEEC_HR_SUBGROUP, 9808c2ecf20Sopenharmony_ci tclass, 0, !!quanta[band], 9818c2ecf20Sopenharmony_ci weights[band]); 9828c2ecf20Sopenharmony_ci if (err) 9838c2ecf20Sopenharmony_ci return err; 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ci for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { 9868c2ecf20Sopenharmony_ci if (priomap[i] == band) { 9878c2ecf20Sopenharmony_ci child_qdisc->prio_bitmap |= BIT(i); 9888c2ecf20Sopenharmony_ci if (BIT(i) & old_priomap) 9898c2ecf20Sopenharmony_ci continue; 9908c2ecf20Sopenharmony_ci err = mlxsw_sp_port_prio_tc_set(mlxsw_sp_port, 9918c2ecf20Sopenharmony_ci i, tclass); 9928c2ecf20Sopenharmony_ci if (err) 9938c2ecf20Sopenharmony_ci return err; 9948c2ecf20Sopenharmony_ci } 9958c2ecf20Sopenharmony_ci } 9968c2ecf20Sopenharmony_ci if (old_priomap != child_qdisc->prio_bitmap && 9978c2ecf20Sopenharmony_ci child_qdisc->ops && child_qdisc->ops->clean_stats) { 9988c2ecf20Sopenharmony_ci backlog = child_qdisc->stats_base.backlog; 9998c2ecf20Sopenharmony_ci child_qdisc->ops->clean_stats(mlxsw_sp_port, 10008c2ecf20Sopenharmony_ci child_qdisc); 10018c2ecf20Sopenharmony_ci child_qdisc->stats_base.backlog = backlog; 10028c2ecf20Sopenharmony_ci } 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_ci if (handle == qdisc_state->future_handle && 10058c2ecf20Sopenharmony_ci qdisc_state->future_fifos[tclass]) { 10068c2ecf20Sopenharmony_ci err = mlxsw_sp_qdisc_replace(mlxsw_sp_port, TC_H_UNSPEC, 10078c2ecf20Sopenharmony_ci child_qdisc, 10088c2ecf20Sopenharmony_ci &mlxsw_sp_qdisc_ops_fifo, 10098c2ecf20Sopenharmony_ci NULL); 10108c2ecf20Sopenharmony_ci if (err) 10118c2ecf20Sopenharmony_ci return err; 10128c2ecf20Sopenharmony_ci } 10138c2ecf20Sopenharmony_ci } 10148c2ecf20Sopenharmony_ci for (; band < IEEE_8021QAZ_MAX_TCS; band++) { 10158c2ecf20Sopenharmony_ci tclass = MLXSW_SP_PRIO_BAND_TO_TCLASS(band); 10168c2ecf20Sopenharmony_ci child_qdisc = &qdisc_state->tclass_qdiscs[tclass]; 10178c2ecf20Sopenharmony_ci child_qdisc->prio_bitmap = 0; 10188c2ecf20Sopenharmony_ci mlxsw_sp_qdisc_destroy(mlxsw_sp_port, child_qdisc); 10198c2ecf20Sopenharmony_ci mlxsw_sp_port_ets_set(mlxsw_sp_port, 10208c2ecf20Sopenharmony_ci MLXSW_REG_QEEC_HR_SUBGROUP, 10218c2ecf20Sopenharmony_ci tclass, 0, false, 0); 10228c2ecf20Sopenharmony_ci } 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_ci qdisc_state->future_handle = TC_H_UNSPEC; 10258c2ecf20Sopenharmony_ci memset(qdisc_state->future_fifos, 0, sizeof(qdisc_state->future_fifos)); 10268c2ecf20Sopenharmony_ci return 0; 10278c2ecf20Sopenharmony_ci} 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_cistatic int 10308c2ecf20Sopenharmony_cimlxsw_sp_qdisc_prio_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, 10318c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 10328c2ecf20Sopenharmony_ci void *params) 10338c2ecf20Sopenharmony_ci{ 10348c2ecf20Sopenharmony_ci struct tc_prio_qopt_offload_params *p = params; 10358c2ecf20Sopenharmony_ci unsigned int zeroes[TCQ_ETS_MAX_BANDS] = {0}; 10368c2ecf20Sopenharmony_ci 10378c2ecf20Sopenharmony_ci return __mlxsw_sp_qdisc_ets_replace(mlxsw_sp_port, handle, p->bands, 10388c2ecf20Sopenharmony_ci zeroes, zeroes, p->priomap); 10398c2ecf20Sopenharmony_ci} 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_cistatic void 10428c2ecf20Sopenharmony_ci__mlxsw_sp_qdisc_ets_unoffload(struct mlxsw_sp_port *mlxsw_sp_port, 10438c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 10448c2ecf20Sopenharmony_ci struct gnet_stats_queue *qstats) 10458c2ecf20Sopenharmony_ci{ 10468c2ecf20Sopenharmony_ci u64 backlog; 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci backlog = mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp, 10498c2ecf20Sopenharmony_ci mlxsw_sp_qdisc->stats_base.backlog); 10508c2ecf20Sopenharmony_ci qstats->backlog -= backlog; 10518c2ecf20Sopenharmony_ci} 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_cistatic void 10548c2ecf20Sopenharmony_cimlxsw_sp_qdisc_prio_unoffload(struct mlxsw_sp_port *mlxsw_sp_port, 10558c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 10568c2ecf20Sopenharmony_ci void *params) 10578c2ecf20Sopenharmony_ci{ 10588c2ecf20Sopenharmony_ci struct tc_prio_qopt_offload_params *p = params; 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci __mlxsw_sp_qdisc_ets_unoffload(mlxsw_sp_port, mlxsw_sp_qdisc, 10618c2ecf20Sopenharmony_ci p->qstats); 10628c2ecf20Sopenharmony_ci} 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_cistatic int 10658c2ecf20Sopenharmony_cimlxsw_sp_qdisc_get_prio_stats(struct mlxsw_sp_port *mlxsw_sp_port, 10668c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 10678c2ecf20Sopenharmony_ci struct tc_qopt_offload_stats *stats_ptr) 10688c2ecf20Sopenharmony_ci{ 10698c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc; 10708c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc *tc_qdisc; 10718c2ecf20Sopenharmony_ci u64 tx_packets = 0; 10728c2ecf20Sopenharmony_ci u64 tx_bytes = 0; 10738c2ecf20Sopenharmony_ci u64 backlog = 0; 10748c2ecf20Sopenharmony_ci u64 drops = 0; 10758c2ecf20Sopenharmony_ci int i; 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_ci for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { 10788c2ecf20Sopenharmony_ci tc_qdisc = &qdisc_state->tclass_qdiscs[i]; 10798c2ecf20Sopenharmony_ci mlxsw_sp_qdisc_collect_tc_stats(mlxsw_sp_port, tc_qdisc, 10808c2ecf20Sopenharmony_ci &tx_bytes, &tx_packets, 10818c2ecf20Sopenharmony_ci &drops, &backlog); 10828c2ecf20Sopenharmony_ci } 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_ci mlxsw_sp_qdisc_update_stats(mlxsw_sp_port->mlxsw_sp, mlxsw_sp_qdisc, 10858c2ecf20Sopenharmony_ci tx_bytes, tx_packets, drops, backlog, 10868c2ecf20Sopenharmony_ci stats_ptr); 10878c2ecf20Sopenharmony_ci return 0; 10888c2ecf20Sopenharmony_ci} 10898c2ecf20Sopenharmony_ci 10908c2ecf20Sopenharmony_cistatic void 10918c2ecf20Sopenharmony_cimlxsw_sp_setup_tc_qdisc_prio_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port, 10928c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 10938c2ecf20Sopenharmony_ci{ 10948c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc_stats *stats_base; 10958c2ecf20Sopenharmony_ci struct mlxsw_sp_port_xstats *xstats; 10968c2ecf20Sopenharmony_ci struct rtnl_link_stats64 *stats; 10978c2ecf20Sopenharmony_ci int i; 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_ci xstats = &mlxsw_sp_port->periodic_hw_stats.xstats; 11008c2ecf20Sopenharmony_ci stats = &mlxsw_sp_port->periodic_hw_stats.stats; 11018c2ecf20Sopenharmony_ci stats_base = &mlxsw_sp_qdisc->stats_base; 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_ci stats_base->tx_packets = stats->tx_packets; 11048c2ecf20Sopenharmony_ci stats_base->tx_bytes = stats->tx_bytes; 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_ci stats_base->drops = 0; 11078c2ecf20Sopenharmony_ci for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { 11088c2ecf20Sopenharmony_ci stats_base->drops += mlxsw_sp_xstats_tail_drop(xstats, i); 11098c2ecf20Sopenharmony_ci stats_base->drops += xstats->wred_drop[i]; 11108c2ecf20Sopenharmony_ci } 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci mlxsw_sp_qdisc->stats_base.backlog = 0; 11138c2ecf20Sopenharmony_ci} 11148c2ecf20Sopenharmony_ci 11158c2ecf20Sopenharmony_cistatic struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_prio = { 11168c2ecf20Sopenharmony_ci .type = MLXSW_SP_QDISC_PRIO, 11178c2ecf20Sopenharmony_ci .check_params = mlxsw_sp_qdisc_prio_check_params, 11188c2ecf20Sopenharmony_ci .replace = mlxsw_sp_qdisc_prio_replace, 11198c2ecf20Sopenharmony_ci .unoffload = mlxsw_sp_qdisc_prio_unoffload, 11208c2ecf20Sopenharmony_ci .destroy = mlxsw_sp_qdisc_prio_destroy, 11218c2ecf20Sopenharmony_ci .get_stats = mlxsw_sp_qdisc_get_prio_stats, 11228c2ecf20Sopenharmony_ci .clean_stats = mlxsw_sp_setup_tc_qdisc_prio_clean_stats, 11238c2ecf20Sopenharmony_ci}; 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_cistatic int 11268c2ecf20Sopenharmony_cimlxsw_sp_qdisc_ets_check_params(struct mlxsw_sp_port *mlxsw_sp_port, 11278c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 11288c2ecf20Sopenharmony_ci void *params) 11298c2ecf20Sopenharmony_ci{ 11308c2ecf20Sopenharmony_ci struct tc_ets_qopt_offload_replace_params *p = params; 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_ci return __mlxsw_sp_qdisc_ets_check_params(p->bands); 11338c2ecf20Sopenharmony_ci} 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_cistatic int 11368c2ecf20Sopenharmony_cimlxsw_sp_qdisc_ets_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, 11378c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 11388c2ecf20Sopenharmony_ci void *params) 11398c2ecf20Sopenharmony_ci{ 11408c2ecf20Sopenharmony_ci struct tc_ets_qopt_offload_replace_params *p = params; 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_ci return __mlxsw_sp_qdisc_ets_replace(mlxsw_sp_port, handle, p->bands, 11438c2ecf20Sopenharmony_ci p->quanta, p->weights, p->priomap); 11448c2ecf20Sopenharmony_ci} 11458c2ecf20Sopenharmony_ci 11468c2ecf20Sopenharmony_cistatic void 11478c2ecf20Sopenharmony_cimlxsw_sp_qdisc_ets_unoffload(struct mlxsw_sp_port *mlxsw_sp_port, 11488c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 11498c2ecf20Sopenharmony_ci void *params) 11508c2ecf20Sopenharmony_ci{ 11518c2ecf20Sopenharmony_ci struct tc_ets_qopt_offload_replace_params *p = params; 11528c2ecf20Sopenharmony_ci 11538c2ecf20Sopenharmony_ci __mlxsw_sp_qdisc_ets_unoffload(mlxsw_sp_port, mlxsw_sp_qdisc, 11548c2ecf20Sopenharmony_ci p->qstats); 11558c2ecf20Sopenharmony_ci} 11568c2ecf20Sopenharmony_ci 11578c2ecf20Sopenharmony_cistatic int 11588c2ecf20Sopenharmony_cimlxsw_sp_qdisc_ets_destroy(struct mlxsw_sp_port *mlxsw_sp_port, 11598c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 11608c2ecf20Sopenharmony_ci{ 11618c2ecf20Sopenharmony_ci return __mlxsw_sp_qdisc_ets_destroy(mlxsw_sp_port); 11628c2ecf20Sopenharmony_ci} 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_cistatic struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_ets = { 11658c2ecf20Sopenharmony_ci .type = MLXSW_SP_QDISC_ETS, 11668c2ecf20Sopenharmony_ci .check_params = mlxsw_sp_qdisc_ets_check_params, 11678c2ecf20Sopenharmony_ci .replace = mlxsw_sp_qdisc_ets_replace, 11688c2ecf20Sopenharmony_ci .unoffload = mlxsw_sp_qdisc_ets_unoffload, 11698c2ecf20Sopenharmony_ci .destroy = mlxsw_sp_qdisc_ets_destroy, 11708c2ecf20Sopenharmony_ci .get_stats = mlxsw_sp_qdisc_get_prio_stats, 11718c2ecf20Sopenharmony_ci .clean_stats = mlxsw_sp_setup_tc_qdisc_prio_clean_stats, 11728c2ecf20Sopenharmony_ci}; 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci/* Linux allows linking of Qdiscs to arbitrary classes (so long as the resulting 11758c2ecf20Sopenharmony_ci * graph is free of cycles). These operations do not change the parent handle 11768c2ecf20Sopenharmony_ci * though, which means it can be incomplete (if there is more than one class 11778c2ecf20Sopenharmony_ci * where the Qdisc in question is grafted) or outright wrong (if the Qdisc was 11788c2ecf20Sopenharmony_ci * linked to a different class and then removed from the original class). 11798c2ecf20Sopenharmony_ci * 11808c2ecf20Sopenharmony_ci * E.g. consider this sequence of operations: 11818c2ecf20Sopenharmony_ci * 11828c2ecf20Sopenharmony_ci * # tc qdisc add dev swp1 root handle 1: prio 11838c2ecf20Sopenharmony_ci * # tc qdisc add dev swp1 parent 1:3 handle 13: red limit 1000000 avpkt 10000 11848c2ecf20Sopenharmony_ci * RED: set bandwidth to 10Mbit 11858c2ecf20Sopenharmony_ci * # tc qdisc link dev swp1 handle 13: parent 1:2 11868c2ecf20Sopenharmony_ci * 11878c2ecf20Sopenharmony_ci * At this point, both 1:2 and 1:3 have the same RED Qdisc instance as their 11888c2ecf20Sopenharmony_ci * child. But RED will still only claim that 1:3 is its parent. If it's removed 11898c2ecf20Sopenharmony_ci * from that band, its only parent will be 1:2, but it will continue to claim 11908c2ecf20Sopenharmony_ci * that it is in fact 1:3. 11918c2ecf20Sopenharmony_ci * 11928c2ecf20Sopenharmony_ci * The notification for child Qdisc replace (e.g. TC_RED_REPLACE) comes before 11938c2ecf20Sopenharmony_ci * the notification for parent graft (e.g. TC_PRIO_GRAFT). We take the replace 11948c2ecf20Sopenharmony_ci * notification to offload the child Qdisc, based on its parent handle, and use 11958c2ecf20Sopenharmony_ci * the graft operation to validate that the class where the child is actually 11968c2ecf20Sopenharmony_ci * grafted corresponds to the parent handle. If the two don't match, we 11978c2ecf20Sopenharmony_ci * unoffload the child. 11988c2ecf20Sopenharmony_ci */ 11998c2ecf20Sopenharmony_cistatic int 12008c2ecf20Sopenharmony_ci__mlxsw_sp_qdisc_ets_graft(struct mlxsw_sp_port *mlxsw_sp_port, 12018c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 12028c2ecf20Sopenharmony_ci u8 band, u32 child_handle) 12038c2ecf20Sopenharmony_ci{ 12048c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc; 12058c2ecf20Sopenharmony_ci int tclass_num = MLXSW_SP_PRIO_BAND_TO_TCLASS(band); 12068c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc *old_qdisc; 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_ci if (band < IEEE_8021QAZ_MAX_TCS && 12098c2ecf20Sopenharmony_ci qdisc_state->tclass_qdiscs[tclass_num].handle == child_handle) 12108c2ecf20Sopenharmony_ci return 0; 12118c2ecf20Sopenharmony_ci 12128c2ecf20Sopenharmony_ci if (!child_handle) { 12138c2ecf20Sopenharmony_ci /* This is an invisible FIFO replacing the original Qdisc. 12148c2ecf20Sopenharmony_ci * Ignore it--the original Qdisc's destroy will follow. 12158c2ecf20Sopenharmony_ci */ 12168c2ecf20Sopenharmony_ci return 0; 12178c2ecf20Sopenharmony_ci } 12188c2ecf20Sopenharmony_ci 12198c2ecf20Sopenharmony_ci /* See if the grafted qdisc is already offloaded on any tclass. If so, 12208c2ecf20Sopenharmony_ci * unoffload it. 12218c2ecf20Sopenharmony_ci */ 12228c2ecf20Sopenharmony_ci old_qdisc = mlxsw_sp_qdisc_find_by_handle(mlxsw_sp_port, 12238c2ecf20Sopenharmony_ci child_handle); 12248c2ecf20Sopenharmony_ci if (old_qdisc) 12258c2ecf20Sopenharmony_ci mlxsw_sp_qdisc_destroy(mlxsw_sp_port, old_qdisc); 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_ci mlxsw_sp_qdisc_destroy(mlxsw_sp_port, 12288c2ecf20Sopenharmony_ci &qdisc_state->tclass_qdiscs[tclass_num]); 12298c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 12308c2ecf20Sopenharmony_ci} 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_cistatic int 12338c2ecf20Sopenharmony_cimlxsw_sp_qdisc_prio_graft(struct mlxsw_sp_port *mlxsw_sp_port, 12348c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 12358c2ecf20Sopenharmony_ci struct tc_prio_qopt_offload_graft_params *p) 12368c2ecf20Sopenharmony_ci{ 12378c2ecf20Sopenharmony_ci return __mlxsw_sp_qdisc_ets_graft(mlxsw_sp_port, mlxsw_sp_qdisc, 12388c2ecf20Sopenharmony_ci p->band, p->child_handle); 12398c2ecf20Sopenharmony_ci} 12408c2ecf20Sopenharmony_ci 12418c2ecf20Sopenharmony_ciint mlxsw_sp_setup_tc_prio(struct mlxsw_sp_port *mlxsw_sp_port, 12428c2ecf20Sopenharmony_ci struct tc_prio_qopt_offload *p) 12438c2ecf20Sopenharmony_ci{ 12448c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc; 12458c2ecf20Sopenharmony_ci 12468c2ecf20Sopenharmony_ci mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent, true); 12478c2ecf20Sopenharmony_ci if (!mlxsw_sp_qdisc) 12488c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_ci if (p->command == TC_PRIO_REPLACE) 12518c2ecf20Sopenharmony_ci return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle, 12528c2ecf20Sopenharmony_ci mlxsw_sp_qdisc, 12538c2ecf20Sopenharmony_ci &mlxsw_sp_qdisc_ops_prio, 12548c2ecf20Sopenharmony_ci &p->replace_params); 12558c2ecf20Sopenharmony_ci 12568c2ecf20Sopenharmony_ci if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle, 12578c2ecf20Sopenharmony_ci MLXSW_SP_QDISC_PRIO)) 12588c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 12598c2ecf20Sopenharmony_ci 12608c2ecf20Sopenharmony_ci switch (p->command) { 12618c2ecf20Sopenharmony_ci case TC_PRIO_DESTROY: 12628c2ecf20Sopenharmony_ci return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc); 12638c2ecf20Sopenharmony_ci case TC_PRIO_STATS: 12648c2ecf20Sopenharmony_ci return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc, 12658c2ecf20Sopenharmony_ci &p->stats); 12668c2ecf20Sopenharmony_ci case TC_PRIO_GRAFT: 12678c2ecf20Sopenharmony_ci return mlxsw_sp_qdisc_prio_graft(mlxsw_sp_port, mlxsw_sp_qdisc, 12688c2ecf20Sopenharmony_ci &p->graft_params); 12698c2ecf20Sopenharmony_ci default: 12708c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 12718c2ecf20Sopenharmony_ci } 12728c2ecf20Sopenharmony_ci} 12738c2ecf20Sopenharmony_ci 12748c2ecf20Sopenharmony_ciint mlxsw_sp_setup_tc_ets(struct mlxsw_sp_port *mlxsw_sp_port, 12758c2ecf20Sopenharmony_ci struct tc_ets_qopt_offload *p) 12768c2ecf20Sopenharmony_ci{ 12778c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc; 12788c2ecf20Sopenharmony_ci 12798c2ecf20Sopenharmony_ci mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent, true); 12808c2ecf20Sopenharmony_ci if (!mlxsw_sp_qdisc) 12818c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 12828c2ecf20Sopenharmony_ci 12838c2ecf20Sopenharmony_ci if (p->command == TC_ETS_REPLACE) 12848c2ecf20Sopenharmony_ci return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle, 12858c2ecf20Sopenharmony_ci mlxsw_sp_qdisc, 12868c2ecf20Sopenharmony_ci &mlxsw_sp_qdisc_ops_ets, 12878c2ecf20Sopenharmony_ci &p->replace_params); 12888c2ecf20Sopenharmony_ci 12898c2ecf20Sopenharmony_ci if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle, 12908c2ecf20Sopenharmony_ci MLXSW_SP_QDISC_ETS)) 12918c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_ci switch (p->command) { 12948c2ecf20Sopenharmony_ci case TC_ETS_DESTROY: 12958c2ecf20Sopenharmony_ci return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc); 12968c2ecf20Sopenharmony_ci case TC_ETS_STATS: 12978c2ecf20Sopenharmony_ci return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc, 12988c2ecf20Sopenharmony_ci &p->stats); 12998c2ecf20Sopenharmony_ci case TC_ETS_GRAFT: 13008c2ecf20Sopenharmony_ci return __mlxsw_sp_qdisc_ets_graft(mlxsw_sp_port, mlxsw_sp_qdisc, 13018c2ecf20Sopenharmony_ci p->graft_params.band, 13028c2ecf20Sopenharmony_ci p->graft_params.child_handle); 13038c2ecf20Sopenharmony_ci default: 13048c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 13058c2ecf20Sopenharmony_ci } 13068c2ecf20Sopenharmony_ci} 13078c2ecf20Sopenharmony_ci 13088c2ecf20Sopenharmony_cistruct mlxsw_sp_qevent_block { 13098c2ecf20Sopenharmony_ci struct list_head binding_list; 13108c2ecf20Sopenharmony_ci struct list_head mall_entry_list; 13118c2ecf20Sopenharmony_ci struct mlxsw_sp *mlxsw_sp; 13128c2ecf20Sopenharmony_ci}; 13138c2ecf20Sopenharmony_ci 13148c2ecf20Sopenharmony_cistruct mlxsw_sp_qevent_binding { 13158c2ecf20Sopenharmony_ci struct list_head list; 13168c2ecf20Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port; 13178c2ecf20Sopenharmony_ci u32 handle; 13188c2ecf20Sopenharmony_ci int tclass_num; 13198c2ecf20Sopenharmony_ci enum mlxsw_sp_span_trigger span_trigger; 13208c2ecf20Sopenharmony_ci}; 13218c2ecf20Sopenharmony_ci 13228c2ecf20Sopenharmony_cistatic LIST_HEAD(mlxsw_sp_qevent_block_cb_list); 13238c2ecf20Sopenharmony_ci 13248c2ecf20Sopenharmony_cistatic int mlxsw_sp_qevent_span_configure(struct mlxsw_sp *mlxsw_sp, 13258c2ecf20Sopenharmony_ci struct mlxsw_sp_mall_entry *mall_entry, 13268c2ecf20Sopenharmony_ci struct mlxsw_sp_qevent_binding *qevent_binding, 13278c2ecf20Sopenharmony_ci const struct mlxsw_sp_span_agent_parms *agent_parms, 13288c2ecf20Sopenharmony_ci int *p_span_id) 13298c2ecf20Sopenharmony_ci{ 13308c2ecf20Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port = qevent_binding->mlxsw_sp_port; 13318c2ecf20Sopenharmony_ci struct mlxsw_sp_span_trigger_parms trigger_parms = {}; 13328c2ecf20Sopenharmony_ci int span_id; 13338c2ecf20Sopenharmony_ci int err; 13348c2ecf20Sopenharmony_ci 13358c2ecf20Sopenharmony_ci err = mlxsw_sp_span_agent_get(mlxsw_sp, &span_id, agent_parms); 13368c2ecf20Sopenharmony_ci if (err) 13378c2ecf20Sopenharmony_ci return err; 13388c2ecf20Sopenharmony_ci 13398c2ecf20Sopenharmony_ci err = mlxsw_sp_span_analyzed_port_get(mlxsw_sp_port, true); 13408c2ecf20Sopenharmony_ci if (err) 13418c2ecf20Sopenharmony_ci goto err_analyzed_port_get; 13428c2ecf20Sopenharmony_ci 13438c2ecf20Sopenharmony_ci trigger_parms.span_id = span_id; 13448c2ecf20Sopenharmony_ci err = mlxsw_sp_span_agent_bind(mlxsw_sp, qevent_binding->span_trigger, mlxsw_sp_port, 13458c2ecf20Sopenharmony_ci &trigger_parms); 13468c2ecf20Sopenharmony_ci if (err) 13478c2ecf20Sopenharmony_ci goto err_agent_bind; 13488c2ecf20Sopenharmony_ci 13498c2ecf20Sopenharmony_ci err = mlxsw_sp_span_trigger_enable(mlxsw_sp_port, qevent_binding->span_trigger, 13508c2ecf20Sopenharmony_ci qevent_binding->tclass_num); 13518c2ecf20Sopenharmony_ci if (err) 13528c2ecf20Sopenharmony_ci goto err_trigger_enable; 13538c2ecf20Sopenharmony_ci 13548c2ecf20Sopenharmony_ci *p_span_id = span_id; 13558c2ecf20Sopenharmony_ci return 0; 13568c2ecf20Sopenharmony_ci 13578c2ecf20Sopenharmony_cierr_trigger_enable: 13588c2ecf20Sopenharmony_ci mlxsw_sp_span_agent_unbind(mlxsw_sp, qevent_binding->span_trigger, mlxsw_sp_port, 13598c2ecf20Sopenharmony_ci &trigger_parms); 13608c2ecf20Sopenharmony_cierr_agent_bind: 13618c2ecf20Sopenharmony_ci mlxsw_sp_span_analyzed_port_put(mlxsw_sp_port, true); 13628c2ecf20Sopenharmony_cierr_analyzed_port_get: 13638c2ecf20Sopenharmony_ci mlxsw_sp_span_agent_put(mlxsw_sp, span_id); 13648c2ecf20Sopenharmony_ci return err; 13658c2ecf20Sopenharmony_ci} 13668c2ecf20Sopenharmony_ci 13678c2ecf20Sopenharmony_cistatic void mlxsw_sp_qevent_span_deconfigure(struct mlxsw_sp *mlxsw_sp, 13688c2ecf20Sopenharmony_ci struct mlxsw_sp_qevent_binding *qevent_binding, 13698c2ecf20Sopenharmony_ci int span_id) 13708c2ecf20Sopenharmony_ci{ 13718c2ecf20Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port = qevent_binding->mlxsw_sp_port; 13728c2ecf20Sopenharmony_ci struct mlxsw_sp_span_trigger_parms trigger_parms = { 13738c2ecf20Sopenharmony_ci .span_id = span_id, 13748c2ecf20Sopenharmony_ci }; 13758c2ecf20Sopenharmony_ci 13768c2ecf20Sopenharmony_ci mlxsw_sp_span_trigger_disable(mlxsw_sp_port, qevent_binding->span_trigger, 13778c2ecf20Sopenharmony_ci qevent_binding->tclass_num); 13788c2ecf20Sopenharmony_ci mlxsw_sp_span_agent_unbind(mlxsw_sp, qevent_binding->span_trigger, mlxsw_sp_port, 13798c2ecf20Sopenharmony_ci &trigger_parms); 13808c2ecf20Sopenharmony_ci mlxsw_sp_span_analyzed_port_put(mlxsw_sp_port, true); 13818c2ecf20Sopenharmony_ci mlxsw_sp_span_agent_put(mlxsw_sp, span_id); 13828c2ecf20Sopenharmony_ci} 13838c2ecf20Sopenharmony_ci 13848c2ecf20Sopenharmony_cistatic int mlxsw_sp_qevent_mirror_configure(struct mlxsw_sp *mlxsw_sp, 13858c2ecf20Sopenharmony_ci struct mlxsw_sp_mall_entry *mall_entry, 13868c2ecf20Sopenharmony_ci struct mlxsw_sp_qevent_binding *qevent_binding) 13878c2ecf20Sopenharmony_ci{ 13888c2ecf20Sopenharmony_ci struct mlxsw_sp_span_agent_parms agent_parms = { 13898c2ecf20Sopenharmony_ci .to_dev = mall_entry->mirror.to_dev, 13908c2ecf20Sopenharmony_ci }; 13918c2ecf20Sopenharmony_ci 13928c2ecf20Sopenharmony_ci return mlxsw_sp_qevent_span_configure(mlxsw_sp, mall_entry, qevent_binding, 13938c2ecf20Sopenharmony_ci &agent_parms, &mall_entry->mirror.span_id); 13948c2ecf20Sopenharmony_ci} 13958c2ecf20Sopenharmony_ci 13968c2ecf20Sopenharmony_cistatic void mlxsw_sp_qevent_mirror_deconfigure(struct mlxsw_sp *mlxsw_sp, 13978c2ecf20Sopenharmony_ci struct mlxsw_sp_mall_entry *mall_entry, 13988c2ecf20Sopenharmony_ci struct mlxsw_sp_qevent_binding *qevent_binding) 13998c2ecf20Sopenharmony_ci{ 14008c2ecf20Sopenharmony_ci mlxsw_sp_qevent_span_deconfigure(mlxsw_sp, qevent_binding, mall_entry->mirror.span_id); 14018c2ecf20Sopenharmony_ci} 14028c2ecf20Sopenharmony_ci 14038c2ecf20Sopenharmony_cistatic int mlxsw_sp_qevent_trap_configure(struct mlxsw_sp *mlxsw_sp, 14048c2ecf20Sopenharmony_ci struct mlxsw_sp_mall_entry *mall_entry, 14058c2ecf20Sopenharmony_ci struct mlxsw_sp_qevent_binding *qevent_binding) 14068c2ecf20Sopenharmony_ci{ 14078c2ecf20Sopenharmony_ci struct mlxsw_sp_span_agent_parms agent_parms = {}; 14088c2ecf20Sopenharmony_ci int err; 14098c2ecf20Sopenharmony_ci 14108c2ecf20Sopenharmony_ci err = mlxsw_sp_trap_group_policer_hw_id_get(mlxsw_sp, 14118c2ecf20Sopenharmony_ci DEVLINK_TRAP_GROUP_GENERIC_ID_BUFFER_DROPS, 14128c2ecf20Sopenharmony_ci &agent_parms.policer_enable, 14138c2ecf20Sopenharmony_ci &agent_parms.policer_id); 14148c2ecf20Sopenharmony_ci if (err) 14158c2ecf20Sopenharmony_ci return err; 14168c2ecf20Sopenharmony_ci 14178c2ecf20Sopenharmony_ci return mlxsw_sp_qevent_span_configure(mlxsw_sp, mall_entry, qevent_binding, 14188c2ecf20Sopenharmony_ci &agent_parms, &mall_entry->trap.span_id); 14198c2ecf20Sopenharmony_ci} 14208c2ecf20Sopenharmony_ci 14218c2ecf20Sopenharmony_cistatic void mlxsw_sp_qevent_trap_deconfigure(struct mlxsw_sp *mlxsw_sp, 14228c2ecf20Sopenharmony_ci struct mlxsw_sp_mall_entry *mall_entry, 14238c2ecf20Sopenharmony_ci struct mlxsw_sp_qevent_binding *qevent_binding) 14248c2ecf20Sopenharmony_ci{ 14258c2ecf20Sopenharmony_ci mlxsw_sp_qevent_span_deconfigure(mlxsw_sp, qevent_binding, mall_entry->trap.span_id); 14268c2ecf20Sopenharmony_ci} 14278c2ecf20Sopenharmony_ci 14288c2ecf20Sopenharmony_cistatic int mlxsw_sp_qevent_entry_configure(struct mlxsw_sp *mlxsw_sp, 14298c2ecf20Sopenharmony_ci struct mlxsw_sp_mall_entry *mall_entry, 14308c2ecf20Sopenharmony_ci struct mlxsw_sp_qevent_binding *qevent_binding) 14318c2ecf20Sopenharmony_ci{ 14328c2ecf20Sopenharmony_ci switch (mall_entry->type) { 14338c2ecf20Sopenharmony_ci case MLXSW_SP_MALL_ACTION_TYPE_MIRROR: 14348c2ecf20Sopenharmony_ci return mlxsw_sp_qevent_mirror_configure(mlxsw_sp, mall_entry, qevent_binding); 14358c2ecf20Sopenharmony_ci case MLXSW_SP_MALL_ACTION_TYPE_TRAP: 14368c2ecf20Sopenharmony_ci return mlxsw_sp_qevent_trap_configure(mlxsw_sp, mall_entry, qevent_binding); 14378c2ecf20Sopenharmony_ci default: 14388c2ecf20Sopenharmony_ci /* This should have been validated away. */ 14398c2ecf20Sopenharmony_ci WARN_ON(1); 14408c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 14418c2ecf20Sopenharmony_ci } 14428c2ecf20Sopenharmony_ci} 14438c2ecf20Sopenharmony_ci 14448c2ecf20Sopenharmony_cistatic void mlxsw_sp_qevent_entry_deconfigure(struct mlxsw_sp *mlxsw_sp, 14458c2ecf20Sopenharmony_ci struct mlxsw_sp_mall_entry *mall_entry, 14468c2ecf20Sopenharmony_ci struct mlxsw_sp_qevent_binding *qevent_binding) 14478c2ecf20Sopenharmony_ci{ 14488c2ecf20Sopenharmony_ci switch (mall_entry->type) { 14498c2ecf20Sopenharmony_ci case MLXSW_SP_MALL_ACTION_TYPE_MIRROR: 14508c2ecf20Sopenharmony_ci return mlxsw_sp_qevent_mirror_deconfigure(mlxsw_sp, mall_entry, qevent_binding); 14518c2ecf20Sopenharmony_ci case MLXSW_SP_MALL_ACTION_TYPE_TRAP: 14528c2ecf20Sopenharmony_ci return mlxsw_sp_qevent_trap_deconfigure(mlxsw_sp, mall_entry, qevent_binding); 14538c2ecf20Sopenharmony_ci default: 14548c2ecf20Sopenharmony_ci WARN_ON(1); 14558c2ecf20Sopenharmony_ci return; 14568c2ecf20Sopenharmony_ci } 14578c2ecf20Sopenharmony_ci} 14588c2ecf20Sopenharmony_ci 14598c2ecf20Sopenharmony_cistatic int mlxsw_sp_qevent_binding_configure(struct mlxsw_sp_qevent_block *qevent_block, 14608c2ecf20Sopenharmony_ci struct mlxsw_sp_qevent_binding *qevent_binding) 14618c2ecf20Sopenharmony_ci{ 14628c2ecf20Sopenharmony_ci struct mlxsw_sp_mall_entry *mall_entry; 14638c2ecf20Sopenharmony_ci int err; 14648c2ecf20Sopenharmony_ci 14658c2ecf20Sopenharmony_ci list_for_each_entry(mall_entry, &qevent_block->mall_entry_list, list) { 14668c2ecf20Sopenharmony_ci err = mlxsw_sp_qevent_entry_configure(qevent_block->mlxsw_sp, mall_entry, 14678c2ecf20Sopenharmony_ci qevent_binding); 14688c2ecf20Sopenharmony_ci if (err) 14698c2ecf20Sopenharmony_ci goto err_entry_configure; 14708c2ecf20Sopenharmony_ci } 14718c2ecf20Sopenharmony_ci 14728c2ecf20Sopenharmony_ci return 0; 14738c2ecf20Sopenharmony_ci 14748c2ecf20Sopenharmony_cierr_entry_configure: 14758c2ecf20Sopenharmony_ci list_for_each_entry_continue_reverse(mall_entry, &qevent_block->mall_entry_list, list) 14768c2ecf20Sopenharmony_ci mlxsw_sp_qevent_entry_deconfigure(qevent_block->mlxsw_sp, mall_entry, 14778c2ecf20Sopenharmony_ci qevent_binding); 14788c2ecf20Sopenharmony_ci return err; 14798c2ecf20Sopenharmony_ci} 14808c2ecf20Sopenharmony_ci 14818c2ecf20Sopenharmony_cistatic void mlxsw_sp_qevent_binding_deconfigure(struct mlxsw_sp_qevent_block *qevent_block, 14828c2ecf20Sopenharmony_ci struct mlxsw_sp_qevent_binding *qevent_binding) 14838c2ecf20Sopenharmony_ci{ 14848c2ecf20Sopenharmony_ci struct mlxsw_sp_mall_entry *mall_entry; 14858c2ecf20Sopenharmony_ci 14868c2ecf20Sopenharmony_ci list_for_each_entry(mall_entry, &qevent_block->mall_entry_list, list) 14878c2ecf20Sopenharmony_ci mlxsw_sp_qevent_entry_deconfigure(qevent_block->mlxsw_sp, mall_entry, 14888c2ecf20Sopenharmony_ci qevent_binding); 14898c2ecf20Sopenharmony_ci} 14908c2ecf20Sopenharmony_ci 14918c2ecf20Sopenharmony_cistatic int mlxsw_sp_qevent_block_configure(struct mlxsw_sp_qevent_block *qevent_block) 14928c2ecf20Sopenharmony_ci{ 14938c2ecf20Sopenharmony_ci struct mlxsw_sp_qevent_binding *qevent_binding; 14948c2ecf20Sopenharmony_ci int err; 14958c2ecf20Sopenharmony_ci 14968c2ecf20Sopenharmony_ci list_for_each_entry(qevent_binding, &qevent_block->binding_list, list) { 14978c2ecf20Sopenharmony_ci err = mlxsw_sp_qevent_binding_configure(qevent_block, qevent_binding); 14988c2ecf20Sopenharmony_ci if (err) 14998c2ecf20Sopenharmony_ci goto err_binding_configure; 15008c2ecf20Sopenharmony_ci } 15018c2ecf20Sopenharmony_ci 15028c2ecf20Sopenharmony_ci return 0; 15038c2ecf20Sopenharmony_ci 15048c2ecf20Sopenharmony_cierr_binding_configure: 15058c2ecf20Sopenharmony_ci list_for_each_entry_continue_reverse(qevent_binding, &qevent_block->binding_list, list) 15068c2ecf20Sopenharmony_ci mlxsw_sp_qevent_binding_deconfigure(qevent_block, qevent_binding); 15078c2ecf20Sopenharmony_ci return err; 15088c2ecf20Sopenharmony_ci} 15098c2ecf20Sopenharmony_ci 15108c2ecf20Sopenharmony_cistatic void mlxsw_sp_qevent_block_deconfigure(struct mlxsw_sp_qevent_block *qevent_block) 15118c2ecf20Sopenharmony_ci{ 15128c2ecf20Sopenharmony_ci struct mlxsw_sp_qevent_binding *qevent_binding; 15138c2ecf20Sopenharmony_ci 15148c2ecf20Sopenharmony_ci list_for_each_entry(qevent_binding, &qevent_block->binding_list, list) 15158c2ecf20Sopenharmony_ci mlxsw_sp_qevent_binding_deconfigure(qevent_block, qevent_binding); 15168c2ecf20Sopenharmony_ci} 15178c2ecf20Sopenharmony_ci 15188c2ecf20Sopenharmony_cistatic struct mlxsw_sp_mall_entry * 15198c2ecf20Sopenharmony_cimlxsw_sp_qevent_mall_entry_find(struct mlxsw_sp_qevent_block *block, unsigned long cookie) 15208c2ecf20Sopenharmony_ci{ 15218c2ecf20Sopenharmony_ci struct mlxsw_sp_mall_entry *mall_entry; 15228c2ecf20Sopenharmony_ci 15238c2ecf20Sopenharmony_ci list_for_each_entry(mall_entry, &block->mall_entry_list, list) 15248c2ecf20Sopenharmony_ci if (mall_entry->cookie == cookie) 15258c2ecf20Sopenharmony_ci return mall_entry; 15268c2ecf20Sopenharmony_ci 15278c2ecf20Sopenharmony_ci return NULL; 15288c2ecf20Sopenharmony_ci} 15298c2ecf20Sopenharmony_ci 15308c2ecf20Sopenharmony_cistatic int mlxsw_sp_qevent_mall_replace(struct mlxsw_sp *mlxsw_sp, 15318c2ecf20Sopenharmony_ci struct mlxsw_sp_qevent_block *qevent_block, 15328c2ecf20Sopenharmony_ci struct tc_cls_matchall_offload *f) 15338c2ecf20Sopenharmony_ci{ 15348c2ecf20Sopenharmony_ci struct mlxsw_sp_mall_entry *mall_entry; 15358c2ecf20Sopenharmony_ci struct flow_action_entry *act; 15368c2ecf20Sopenharmony_ci int err; 15378c2ecf20Sopenharmony_ci 15388c2ecf20Sopenharmony_ci /* It should not currently be possible to replace a matchall rule. So 15398c2ecf20Sopenharmony_ci * this must be a new rule. 15408c2ecf20Sopenharmony_ci */ 15418c2ecf20Sopenharmony_ci if (!list_empty(&qevent_block->mall_entry_list)) { 15428c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(f->common.extack, "At most one filter supported"); 15438c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 15448c2ecf20Sopenharmony_ci } 15458c2ecf20Sopenharmony_ci if (f->rule->action.num_entries != 1) { 15468c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(f->common.extack, "Only singular actions supported"); 15478c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 15488c2ecf20Sopenharmony_ci } 15498c2ecf20Sopenharmony_ci if (f->common.chain_index) { 15508c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(f->common.extack, "Only chain 0 is supported"); 15518c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 15528c2ecf20Sopenharmony_ci } 15538c2ecf20Sopenharmony_ci if (f->common.protocol != htons(ETH_P_ALL)) { 15548c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(f->common.extack, "Protocol matching not supported"); 15558c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 15568c2ecf20Sopenharmony_ci } 15578c2ecf20Sopenharmony_ci 15588c2ecf20Sopenharmony_ci act = &f->rule->action.entries[0]; 15598c2ecf20Sopenharmony_ci if (!(act->hw_stats & FLOW_ACTION_HW_STATS_DISABLED)) { 15608c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(f->common.extack, "HW counters not supported on qevents"); 15618c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 15628c2ecf20Sopenharmony_ci } 15638c2ecf20Sopenharmony_ci 15648c2ecf20Sopenharmony_ci mall_entry = kzalloc(sizeof(*mall_entry), GFP_KERNEL); 15658c2ecf20Sopenharmony_ci if (!mall_entry) 15668c2ecf20Sopenharmony_ci return -ENOMEM; 15678c2ecf20Sopenharmony_ci mall_entry->cookie = f->cookie; 15688c2ecf20Sopenharmony_ci 15698c2ecf20Sopenharmony_ci if (act->id == FLOW_ACTION_MIRRED) { 15708c2ecf20Sopenharmony_ci mall_entry->type = MLXSW_SP_MALL_ACTION_TYPE_MIRROR; 15718c2ecf20Sopenharmony_ci mall_entry->mirror.to_dev = act->dev; 15728c2ecf20Sopenharmony_ci } else if (act->id == FLOW_ACTION_TRAP) { 15738c2ecf20Sopenharmony_ci mall_entry->type = MLXSW_SP_MALL_ACTION_TYPE_TRAP; 15748c2ecf20Sopenharmony_ci } else { 15758c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(f->common.extack, "Unsupported action"); 15768c2ecf20Sopenharmony_ci err = -EOPNOTSUPP; 15778c2ecf20Sopenharmony_ci goto err_unsupported_action; 15788c2ecf20Sopenharmony_ci } 15798c2ecf20Sopenharmony_ci 15808c2ecf20Sopenharmony_ci list_add_tail(&mall_entry->list, &qevent_block->mall_entry_list); 15818c2ecf20Sopenharmony_ci 15828c2ecf20Sopenharmony_ci err = mlxsw_sp_qevent_block_configure(qevent_block); 15838c2ecf20Sopenharmony_ci if (err) 15848c2ecf20Sopenharmony_ci goto err_block_configure; 15858c2ecf20Sopenharmony_ci 15868c2ecf20Sopenharmony_ci return 0; 15878c2ecf20Sopenharmony_ci 15888c2ecf20Sopenharmony_cierr_block_configure: 15898c2ecf20Sopenharmony_ci list_del(&mall_entry->list); 15908c2ecf20Sopenharmony_cierr_unsupported_action: 15918c2ecf20Sopenharmony_ci kfree(mall_entry); 15928c2ecf20Sopenharmony_ci return err; 15938c2ecf20Sopenharmony_ci} 15948c2ecf20Sopenharmony_ci 15958c2ecf20Sopenharmony_cistatic void mlxsw_sp_qevent_mall_destroy(struct mlxsw_sp_qevent_block *qevent_block, 15968c2ecf20Sopenharmony_ci struct tc_cls_matchall_offload *f) 15978c2ecf20Sopenharmony_ci{ 15988c2ecf20Sopenharmony_ci struct mlxsw_sp_mall_entry *mall_entry; 15998c2ecf20Sopenharmony_ci 16008c2ecf20Sopenharmony_ci mall_entry = mlxsw_sp_qevent_mall_entry_find(qevent_block, f->cookie); 16018c2ecf20Sopenharmony_ci if (!mall_entry) 16028c2ecf20Sopenharmony_ci return; 16038c2ecf20Sopenharmony_ci 16048c2ecf20Sopenharmony_ci mlxsw_sp_qevent_block_deconfigure(qevent_block); 16058c2ecf20Sopenharmony_ci 16068c2ecf20Sopenharmony_ci list_del(&mall_entry->list); 16078c2ecf20Sopenharmony_ci kfree(mall_entry); 16088c2ecf20Sopenharmony_ci} 16098c2ecf20Sopenharmony_ci 16108c2ecf20Sopenharmony_cistatic int mlxsw_sp_qevent_block_mall_cb(struct mlxsw_sp_qevent_block *qevent_block, 16118c2ecf20Sopenharmony_ci struct tc_cls_matchall_offload *f) 16128c2ecf20Sopenharmony_ci{ 16138c2ecf20Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = qevent_block->mlxsw_sp; 16148c2ecf20Sopenharmony_ci 16158c2ecf20Sopenharmony_ci switch (f->command) { 16168c2ecf20Sopenharmony_ci case TC_CLSMATCHALL_REPLACE: 16178c2ecf20Sopenharmony_ci return mlxsw_sp_qevent_mall_replace(mlxsw_sp, qevent_block, f); 16188c2ecf20Sopenharmony_ci case TC_CLSMATCHALL_DESTROY: 16198c2ecf20Sopenharmony_ci mlxsw_sp_qevent_mall_destroy(qevent_block, f); 16208c2ecf20Sopenharmony_ci return 0; 16218c2ecf20Sopenharmony_ci default: 16228c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 16238c2ecf20Sopenharmony_ci } 16248c2ecf20Sopenharmony_ci} 16258c2ecf20Sopenharmony_ci 16268c2ecf20Sopenharmony_cistatic int mlxsw_sp_qevent_block_cb(enum tc_setup_type type, void *type_data, void *cb_priv) 16278c2ecf20Sopenharmony_ci{ 16288c2ecf20Sopenharmony_ci struct mlxsw_sp_qevent_block *qevent_block = cb_priv; 16298c2ecf20Sopenharmony_ci 16308c2ecf20Sopenharmony_ci switch (type) { 16318c2ecf20Sopenharmony_ci case TC_SETUP_CLSMATCHALL: 16328c2ecf20Sopenharmony_ci return mlxsw_sp_qevent_block_mall_cb(qevent_block, type_data); 16338c2ecf20Sopenharmony_ci default: 16348c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 16358c2ecf20Sopenharmony_ci } 16368c2ecf20Sopenharmony_ci} 16378c2ecf20Sopenharmony_ci 16388c2ecf20Sopenharmony_cistatic struct mlxsw_sp_qevent_block *mlxsw_sp_qevent_block_create(struct mlxsw_sp *mlxsw_sp, 16398c2ecf20Sopenharmony_ci struct net *net) 16408c2ecf20Sopenharmony_ci{ 16418c2ecf20Sopenharmony_ci struct mlxsw_sp_qevent_block *qevent_block; 16428c2ecf20Sopenharmony_ci 16438c2ecf20Sopenharmony_ci qevent_block = kzalloc(sizeof(*qevent_block), GFP_KERNEL); 16448c2ecf20Sopenharmony_ci if (!qevent_block) 16458c2ecf20Sopenharmony_ci return NULL; 16468c2ecf20Sopenharmony_ci 16478c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&qevent_block->binding_list); 16488c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&qevent_block->mall_entry_list); 16498c2ecf20Sopenharmony_ci qevent_block->mlxsw_sp = mlxsw_sp; 16508c2ecf20Sopenharmony_ci return qevent_block; 16518c2ecf20Sopenharmony_ci} 16528c2ecf20Sopenharmony_ci 16538c2ecf20Sopenharmony_cistatic void 16548c2ecf20Sopenharmony_cimlxsw_sp_qevent_block_destroy(struct mlxsw_sp_qevent_block *qevent_block) 16558c2ecf20Sopenharmony_ci{ 16568c2ecf20Sopenharmony_ci WARN_ON(!list_empty(&qevent_block->binding_list)); 16578c2ecf20Sopenharmony_ci WARN_ON(!list_empty(&qevent_block->mall_entry_list)); 16588c2ecf20Sopenharmony_ci kfree(qevent_block); 16598c2ecf20Sopenharmony_ci} 16608c2ecf20Sopenharmony_ci 16618c2ecf20Sopenharmony_cistatic void mlxsw_sp_qevent_block_release(void *cb_priv) 16628c2ecf20Sopenharmony_ci{ 16638c2ecf20Sopenharmony_ci struct mlxsw_sp_qevent_block *qevent_block = cb_priv; 16648c2ecf20Sopenharmony_ci 16658c2ecf20Sopenharmony_ci mlxsw_sp_qevent_block_destroy(qevent_block); 16668c2ecf20Sopenharmony_ci} 16678c2ecf20Sopenharmony_ci 16688c2ecf20Sopenharmony_cistatic struct mlxsw_sp_qevent_binding * 16698c2ecf20Sopenharmony_cimlxsw_sp_qevent_binding_create(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, int tclass_num, 16708c2ecf20Sopenharmony_ci enum mlxsw_sp_span_trigger span_trigger) 16718c2ecf20Sopenharmony_ci{ 16728c2ecf20Sopenharmony_ci struct mlxsw_sp_qevent_binding *binding; 16738c2ecf20Sopenharmony_ci 16748c2ecf20Sopenharmony_ci binding = kzalloc(sizeof(*binding), GFP_KERNEL); 16758c2ecf20Sopenharmony_ci if (!binding) 16768c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 16778c2ecf20Sopenharmony_ci 16788c2ecf20Sopenharmony_ci binding->mlxsw_sp_port = mlxsw_sp_port; 16798c2ecf20Sopenharmony_ci binding->handle = handle; 16808c2ecf20Sopenharmony_ci binding->tclass_num = tclass_num; 16818c2ecf20Sopenharmony_ci binding->span_trigger = span_trigger; 16828c2ecf20Sopenharmony_ci return binding; 16838c2ecf20Sopenharmony_ci} 16848c2ecf20Sopenharmony_ci 16858c2ecf20Sopenharmony_cistatic void 16868c2ecf20Sopenharmony_cimlxsw_sp_qevent_binding_destroy(struct mlxsw_sp_qevent_binding *binding) 16878c2ecf20Sopenharmony_ci{ 16888c2ecf20Sopenharmony_ci kfree(binding); 16898c2ecf20Sopenharmony_ci} 16908c2ecf20Sopenharmony_ci 16918c2ecf20Sopenharmony_cistatic struct mlxsw_sp_qevent_binding * 16928c2ecf20Sopenharmony_cimlxsw_sp_qevent_binding_lookup(struct mlxsw_sp_qevent_block *block, 16938c2ecf20Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port, 16948c2ecf20Sopenharmony_ci u32 handle, 16958c2ecf20Sopenharmony_ci enum mlxsw_sp_span_trigger span_trigger) 16968c2ecf20Sopenharmony_ci{ 16978c2ecf20Sopenharmony_ci struct mlxsw_sp_qevent_binding *qevent_binding; 16988c2ecf20Sopenharmony_ci 16998c2ecf20Sopenharmony_ci list_for_each_entry(qevent_binding, &block->binding_list, list) 17008c2ecf20Sopenharmony_ci if (qevent_binding->mlxsw_sp_port == mlxsw_sp_port && 17018c2ecf20Sopenharmony_ci qevent_binding->handle == handle && 17028c2ecf20Sopenharmony_ci qevent_binding->span_trigger == span_trigger) 17038c2ecf20Sopenharmony_ci return qevent_binding; 17048c2ecf20Sopenharmony_ci return NULL; 17058c2ecf20Sopenharmony_ci} 17068c2ecf20Sopenharmony_ci 17078c2ecf20Sopenharmony_cistatic int mlxsw_sp_setup_tc_block_qevent_bind(struct mlxsw_sp_port *mlxsw_sp_port, 17088c2ecf20Sopenharmony_ci struct flow_block_offload *f, 17098c2ecf20Sopenharmony_ci enum mlxsw_sp_span_trigger span_trigger) 17108c2ecf20Sopenharmony_ci{ 17118c2ecf20Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 17128c2ecf20Sopenharmony_ci struct mlxsw_sp_qevent_binding *qevent_binding; 17138c2ecf20Sopenharmony_ci struct mlxsw_sp_qevent_block *qevent_block; 17148c2ecf20Sopenharmony_ci struct flow_block_cb *block_cb; 17158c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc *qdisc; 17168c2ecf20Sopenharmony_ci bool register_block = false; 17178c2ecf20Sopenharmony_ci int err; 17188c2ecf20Sopenharmony_ci 17198c2ecf20Sopenharmony_ci block_cb = flow_block_cb_lookup(f->block, mlxsw_sp_qevent_block_cb, mlxsw_sp); 17208c2ecf20Sopenharmony_ci if (!block_cb) { 17218c2ecf20Sopenharmony_ci qevent_block = mlxsw_sp_qevent_block_create(mlxsw_sp, f->net); 17228c2ecf20Sopenharmony_ci if (!qevent_block) 17238c2ecf20Sopenharmony_ci return -ENOMEM; 17248c2ecf20Sopenharmony_ci block_cb = flow_block_cb_alloc(mlxsw_sp_qevent_block_cb, mlxsw_sp, qevent_block, 17258c2ecf20Sopenharmony_ci mlxsw_sp_qevent_block_release); 17268c2ecf20Sopenharmony_ci if (IS_ERR(block_cb)) { 17278c2ecf20Sopenharmony_ci mlxsw_sp_qevent_block_destroy(qevent_block); 17288c2ecf20Sopenharmony_ci return PTR_ERR(block_cb); 17298c2ecf20Sopenharmony_ci } 17308c2ecf20Sopenharmony_ci register_block = true; 17318c2ecf20Sopenharmony_ci } else { 17328c2ecf20Sopenharmony_ci qevent_block = flow_block_cb_priv(block_cb); 17338c2ecf20Sopenharmony_ci } 17348c2ecf20Sopenharmony_ci flow_block_cb_incref(block_cb); 17358c2ecf20Sopenharmony_ci 17368c2ecf20Sopenharmony_ci qdisc = mlxsw_sp_qdisc_find_by_handle(mlxsw_sp_port, f->sch->handle); 17378c2ecf20Sopenharmony_ci if (!qdisc) { 17388c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(f->extack, "Qdisc not offloaded"); 17398c2ecf20Sopenharmony_ci err = -ENOENT; 17408c2ecf20Sopenharmony_ci goto err_find_qdisc; 17418c2ecf20Sopenharmony_ci } 17428c2ecf20Sopenharmony_ci 17438c2ecf20Sopenharmony_ci if (WARN_ON(mlxsw_sp_qevent_binding_lookup(qevent_block, mlxsw_sp_port, f->sch->handle, 17448c2ecf20Sopenharmony_ci span_trigger))) { 17458c2ecf20Sopenharmony_ci err = -EEXIST; 17468c2ecf20Sopenharmony_ci goto err_binding_exists; 17478c2ecf20Sopenharmony_ci } 17488c2ecf20Sopenharmony_ci 17498c2ecf20Sopenharmony_ci qevent_binding = mlxsw_sp_qevent_binding_create(mlxsw_sp_port, f->sch->handle, 17508c2ecf20Sopenharmony_ci qdisc->tclass_num, span_trigger); 17518c2ecf20Sopenharmony_ci if (IS_ERR(qevent_binding)) { 17528c2ecf20Sopenharmony_ci err = PTR_ERR(qevent_binding); 17538c2ecf20Sopenharmony_ci goto err_binding_create; 17548c2ecf20Sopenharmony_ci } 17558c2ecf20Sopenharmony_ci 17568c2ecf20Sopenharmony_ci err = mlxsw_sp_qevent_binding_configure(qevent_block, qevent_binding); 17578c2ecf20Sopenharmony_ci if (err) 17588c2ecf20Sopenharmony_ci goto err_binding_configure; 17598c2ecf20Sopenharmony_ci 17608c2ecf20Sopenharmony_ci list_add(&qevent_binding->list, &qevent_block->binding_list); 17618c2ecf20Sopenharmony_ci 17628c2ecf20Sopenharmony_ci if (register_block) { 17638c2ecf20Sopenharmony_ci flow_block_cb_add(block_cb, f); 17648c2ecf20Sopenharmony_ci list_add_tail(&block_cb->driver_list, &mlxsw_sp_qevent_block_cb_list); 17658c2ecf20Sopenharmony_ci } 17668c2ecf20Sopenharmony_ci 17678c2ecf20Sopenharmony_ci return 0; 17688c2ecf20Sopenharmony_ci 17698c2ecf20Sopenharmony_cierr_binding_configure: 17708c2ecf20Sopenharmony_ci mlxsw_sp_qevent_binding_destroy(qevent_binding); 17718c2ecf20Sopenharmony_cierr_binding_create: 17728c2ecf20Sopenharmony_cierr_binding_exists: 17738c2ecf20Sopenharmony_cierr_find_qdisc: 17748c2ecf20Sopenharmony_ci if (!flow_block_cb_decref(block_cb)) 17758c2ecf20Sopenharmony_ci flow_block_cb_free(block_cb); 17768c2ecf20Sopenharmony_ci return err; 17778c2ecf20Sopenharmony_ci} 17788c2ecf20Sopenharmony_ci 17798c2ecf20Sopenharmony_cistatic void mlxsw_sp_setup_tc_block_qevent_unbind(struct mlxsw_sp_port *mlxsw_sp_port, 17808c2ecf20Sopenharmony_ci struct flow_block_offload *f, 17818c2ecf20Sopenharmony_ci enum mlxsw_sp_span_trigger span_trigger) 17828c2ecf20Sopenharmony_ci{ 17838c2ecf20Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 17848c2ecf20Sopenharmony_ci struct mlxsw_sp_qevent_binding *qevent_binding; 17858c2ecf20Sopenharmony_ci struct mlxsw_sp_qevent_block *qevent_block; 17868c2ecf20Sopenharmony_ci struct flow_block_cb *block_cb; 17878c2ecf20Sopenharmony_ci 17888c2ecf20Sopenharmony_ci block_cb = flow_block_cb_lookup(f->block, mlxsw_sp_qevent_block_cb, mlxsw_sp); 17898c2ecf20Sopenharmony_ci if (!block_cb) 17908c2ecf20Sopenharmony_ci return; 17918c2ecf20Sopenharmony_ci qevent_block = flow_block_cb_priv(block_cb); 17928c2ecf20Sopenharmony_ci 17938c2ecf20Sopenharmony_ci qevent_binding = mlxsw_sp_qevent_binding_lookup(qevent_block, mlxsw_sp_port, f->sch->handle, 17948c2ecf20Sopenharmony_ci span_trigger); 17958c2ecf20Sopenharmony_ci if (!qevent_binding) 17968c2ecf20Sopenharmony_ci return; 17978c2ecf20Sopenharmony_ci 17988c2ecf20Sopenharmony_ci list_del(&qevent_binding->list); 17998c2ecf20Sopenharmony_ci mlxsw_sp_qevent_binding_deconfigure(qevent_block, qevent_binding); 18008c2ecf20Sopenharmony_ci mlxsw_sp_qevent_binding_destroy(qevent_binding); 18018c2ecf20Sopenharmony_ci 18028c2ecf20Sopenharmony_ci if (!flow_block_cb_decref(block_cb)) { 18038c2ecf20Sopenharmony_ci flow_block_cb_remove(block_cb, f); 18048c2ecf20Sopenharmony_ci list_del(&block_cb->driver_list); 18058c2ecf20Sopenharmony_ci } 18068c2ecf20Sopenharmony_ci} 18078c2ecf20Sopenharmony_ci 18088c2ecf20Sopenharmony_cistatic int mlxsw_sp_setup_tc_block_qevent(struct mlxsw_sp_port *mlxsw_sp_port, 18098c2ecf20Sopenharmony_ci struct flow_block_offload *f, 18108c2ecf20Sopenharmony_ci enum mlxsw_sp_span_trigger span_trigger) 18118c2ecf20Sopenharmony_ci{ 18128c2ecf20Sopenharmony_ci f->driver_block_list = &mlxsw_sp_qevent_block_cb_list; 18138c2ecf20Sopenharmony_ci 18148c2ecf20Sopenharmony_ci switch (f->command) { 18158c2ecf20Sopenharmony_ci case FLOW_BLOCK_BIND: 18168c2ecf20Sopenharmony_ci return mlxsw_sp_setup_tc_block_qevent_bind(mlxsw_sp_port, f, span_trigger); 18178c2ecf20Sopenharmony_ci case FLOW_BLOCK_UNBIND: 18188c2ecf20Sopenharmony_ci mlxsw_sp_setup_tc_block_qevent_unbind(mlxsw_sp_port, f, span_trigger); 18198c2ecf20Sopenharmony_ci return 0; 18208c2ecf20Sopenharmony_ci default: 18218c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 18228c2ecf20Sopenharmony_ci } 18238c2ecf20Sopenharmony_ci} 18248c2ecf20Sopenharmony_ci 18258c2ecf20Sopenharmony_ciint mlxsw_sp_setup_tc_block_qevent_early_drop(struct mlxsw_sp_port *mlxsw_sp_port, 18268c2ecf20Sopenharmony_ci struct flow_block_offload *f) 18278c2ecf20Sopenharmony_ci{ 18288c2ecf20Sopenharmony_ci return mlxsw_sp_setup_tc_block_qevent(mlxsw_sp_port, f, MLXSW_SP_SPAN_TRIGGER_EARLY_DROP); 18298c2ecf20Sopenharmony_ci} 18308c2ecf20Sopenharmony_ci 18318c2ecf20Sopenharmony_ciint mlxsw_sp_tc_qdisc_init(struct mlxsw_sp_port *mlxsw_sp_port) 18328c2ecf20Sopenharmony_ci{ 18338c2ecf20Sopenharmony_ci struct mlxsw_sp_qdisc_state *qdisc_state; 18348c2ecf20Sopenharmony_ci int i; 18358c2ecf20Sopenharmony_ci 18368c2ecf20Sopenharmony_ci qdisc_state = kzalloc(sizeof(*qdisc_state), GFP_KERNEL); 18378c2ecf20Sopenharmony_ci if (!qdisc_state) 18388c2ecf20Sopenharmony_ci return -ENOMEM; 18398c2ecf20Sopenharmony_ci 18408c2ecf20Sopenharmony_ci qdisc_state->root_qdisc.prio_bitmap = 0xff; 18418c2ecf20Sopenharmony_ci qdisc_state->root_qdisc.tclass_num = MLXSW_SP_PORT_DEFAULT_TCLASS; 18428c2ecf20Sopenharmony_ci for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) 18438c2ecf20Sopenharmony_ci qdisc_state->tclass_qdiscs[i].tclass_num = i; 18448c2ecf20Sopenharmony_ci 18458c2ecf20Sopenharmony_ci mlxsw_sp_port->qdisc = qdisc_state; 18468c2ecf20Sopenharmony_ci return 0; 18478c2ecf20Sopenharmony_ci} 18488c2ecf20Sopenharmony_ci 18498c2ecf20Sopenharmony_civoid mlxsw_sp_tc_qdisc_fini(struct mlxsw_sp_port *mlxsw_sp_port) 18508c2ecf20Sopenharmony_ci{ 18518c2ecf20Sopenharmony_ci kfree(mlxsw_sp_port->qdisc); 18528c2ecf20Sopenharmony_ci} 1853