162306a36Sopenharmony_ci// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 262306a36Sopenharmony_ci/* Copyright (c) 2017-2018 Mellanox Technologies. All rights reserved */ 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include <linux/kernel.h> 562306a36Sopenharmony_ci#include <linux/errno.h> 662306a36Sopenharmony_ci#include <linux/netdevice.h> 762306a36Sopenharmony_ci#include <net/pkt_cls.h> 862306a36Sopenharmony_ci#include <net/red.h> 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include "spectrum.h" 1162306a36Sopenharmony_ci#include "spectrum_span.h" 1262306a36Sopenharmony_ci#include "reg.h" 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#define MLXSW_SP_PRIO_BAND_TO_TCLASS(band) (IEEE_8021QAZ_MAX_TCS - band - 1) 1562306a36Sopenharmony_ci#define MLXSW_SP_PRIO_CHILD_TO_TCLASS(child) \ 1662306a36Sopenharmony_ci MLXSW_SP_PRIO_BAND_TO_TCLASS((child - 1)) 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_cienum mlxsw_sp_qdisc_type { 1962306a36Sopenharmony_ci MLXSW_SP_QDISC_NO_QDISC, 2062306a36Sopenharmony_ci MLXSW_SP_QDISC_RED, 2162306a36Sopenharmony_ci MLXSW_SP_QDISC_PRIO, 2262306a36Sopenharmony_ci MLXSW_SP_QDISC_ETS, 2362306a36Sopenharmony_ci MLXSW_SP_QDISC_TBF, 2462306a36Sopenharmony_ci MLXSW_SP_QDISC_FIFO, 2562306a36Sopenharmony_ci}; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistruct mlxsw_sp_qdisc; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistruct mlxsw_sp_qdisc_ops { 3062306a36Sopenharmony_ci enum mlxsw_sp_qdisc_type type; 3162306a36Sopenharmony_ci int (*check_params)(struct mlxsw_sp_port *mlxsw_sp_port, 3262306a36Sopenharmony_ci void *params); 3362306a36Sopenharmony_ci int (*replace)(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, 3462306a36Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, void *params); 3562306a36Sopenharmony_ci int (*destroy)(struct mlxsw_sp_port *mlxsw_sp_port, 3662306a36Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc); 3762306a36Sopenharmony_ci int (*get_stats)(struct mlxsw_sp_port *mlxsw_sp_port, 3862306a36Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 3962306a36Sopenharmony_ci struct tc_qopt_offload_stats *stats_ptr); 4062306a36Sopenharmony_ci int (*get_xstats)(struct mlxsw_sp_port *mlxsw_sp_port, 4162306a36Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 4262306a36Sopenharmony_ci void *xstats_ptr); 4362306a36Sopenharmony_ci void (*clean_stats)(struct mlxsw_sp_port *mlxsw_sp_port, 4462306a36Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc); 4562306a36Sopenharmony_ci /* unoffload - to be used for a qdisc that stops being offloaded without 4662306a36Sopenharmony_ci * being destroyed. 4762306a36Sopenharmony_ci */ 4862306a36Sopenharmony_ci void (*unoffload)(struct mlxsw_sp_port *mlxsw_sp_port, 4962306a36Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, void *params); 5062306a36Sopenharmony_ci struct mlxsw_sp_qdisc *(*find_class)(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 5162306a36Sopenharmony_ci u32 parent); 5262306a36Sopenharmony_ci unsigned int num_classes; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci u8 (*get_prio_bitmap)(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 5562306a36Sopenharmony_ci struct mlxsw_sp_qdisc *child); 5662306a36Sopenharmony_ci int (*get_tclass_num)(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 5762306a36Sopenharmony_ci struct mlxsw_sp_qdisc *child); 5862306a36Sopenharmony_ci}; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistruct mlxsw_sp_qdisc_ets_band { 6162306a36Sopenharmony_ci u8 prio_bitmap; 6262306a36Sopenharmony_ci int tclass_num; 6362306a36Sopenharmony_ci}; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistruct mlxsw_sp_qdisc_ets_data { 6662306a36Sopenharmony_ci struct mlxsw_sp_qdisc_ets_band bands[IEEE_8021QAZ_MAX_TCS]; 6762306a36Sopenharmony_ci}; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistruct mlxsw_sp_qdisc { 7062306a36Sopenharmony_ci u32 handle; 7162306a36Sopenharmony_ci union { 7262306a36Sopenharmony_ci struct red_stats red; 7362306a36Sopenharmony_ci } xstats_base; 7462306a36Sopenharmony_ci struct mlxsw_sp_qdisc_stats { 7562306a36Sopenharmony_ci u64 tx_bytes; 7662306a36Sopenharmony_ci u64 tx_packets; 7762306a36Sopenharmony_ci u64 drops; 7862306a36Sopenharmony_ci u64 overlimits; 7962306a36Sopenharmony_ci u64 backlog; 8062306a36Sopenharmony_ci } stats_base; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci union { 8362306a36Sopenharmony_ci struct mlxsw_sp_qdisc_ets_data *ets_data; 8462306a36Sopenharmony_ci }; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci struct mlxsw_sp_qdisc_ops *ops; 8762306a36Sopenharmony_ci struct mlxsw_sp_qdisc *parent; 8862306a36Sopenharmony_ci struct mlxsw_sp_qdisc *qdiscs; 8962306a36Sopenharmony_ci unsigned int num_classes; 9062306a36Sopenharmony_ci}; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistruct mlxsw_sp_qdisc_state { 9362306a36Sopenharmony_ci struct mlxsw_sp_qdisc root_qdisc; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci /* When a PRIO or ETS are added, the invisible FIFOs in their bands are 9662306a36Sopenharmony_ci * created first. When notifications for these FIFOs arrive, it is not 9762306a36Sopenharmony_ci * known what qdisc their parent handle refers to. It could be a 9862306a36Sopenharmony_ci * newly-created PRIO that will replace the currently-offloaded one, or 9962306a36Sopenharmony_ci * it could be e.g. a RED that will be attached below it. 10062306a36Sopenharmony_ci * 10162306a36Sopenharmony_ci * As the notifications start to arrive, use them to note what the 10262306a36Sopenharmony_ci * future parent handle is, and keep track of which child FIFOs were 10362306a36Sopenharmony_ci * seen. Then when the parent is known, retroactively offload those 10462306a36Sopenharmony_ci * FIFOs. 10562306a36Sopenharmony_ci */ 10662306a36Sopenharmony_ci u32 future_handle; 10762306a36Sopenharmony_ci bool future_fifos[IEEE_8021QAZ_MAX_TCS]; 10862306a36Sopenharmony_ci struct mutex lock; /* Protects qdisc state. */ 10962306a36Sopenharmony_ci}; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistatic bool 11262306a36Sopenharmony_cimlxsw_sp_qdisc_compare(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, u32 handle) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci return mlxsw_sp_qdisc->ops && mlxsw_sp_qdisc->handle == handle; 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic struct mlxsw_sp_qdisc * 11862306a36Sopenharmony_cimlxsw_sp_qdisc_walk(struct mlxsw_sp_qdisc *qdisc, 11962306a36Sopenharmony_ci struct mlxsw_sp_qdisc *(*pre)(struct mlxsw_sp_qdisc *, 12062306a36Sopenharmony_ci void *), 12162306a36Sopenharmony_ci void *data) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci struct mlxsw_sp_qdisc *tmp; 12462306a36Sopenharmony_ci unsigned int i; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci if (pre) { 12762306a36Sopenharmony_ci tmp = pre(qdisc, data); 12862306a36Sopenharmony_ci if (tmp) 12962306a36Sopenharmony_ci return tmp; 13062306a36Sopenharmony_ci } 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci if (qdisc->ops) { 13362306a36Sopenharmony_ci for (i = 0; i < qdisc->num_classes; i++) { 13462306a36Sopenharmony_ci tmp = &qdisc->qdiscs[i]; 13562306a36Sopenharmony_ci if (qdisc->ops) { 13662306a36Sopenharmony_ci tmp = mlxsw_sp_qdisc_walk(tmp, pre, data); 13762306a36Sopenharmony_ci if (tmp) 13862306a36Sopenharmony_ci return tmp; 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci return NULL; 14462306a36Sopenharmony_ci} 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cistatic struct mlxsw_sp_qdisc * 14762306a36Sopenharmony_cimlxsw_sp_qdisc_walk_cb_find(struct mlxsw_sp_qdisc *qdisc, void *data) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci u32 parent = *(u32 *)data; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci if (qdisc->ops && TC_H_MAJ(qdisc->handle) == TC_H_MAJ(parent)) { 15262306a36Sopenharmony_ci if (qdisc->ops->find_class) 15362306a36Sopenharmony_ci return qdisc->ops->find_class(qdisc, parent); 15462306a36Sopenharmony_ci } 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci return NULL; 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cistatic struct mlxsw_sp_qdisc * 16062306a36Sopenharmony_cimlxsw_sp_qdisc_find(struct mlxsw_sp_port *mlxsw_sp_port, u32 parent) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci if (!qdisc_state) 16562306a36Sopenharmony_ci return NULL; 16662306a36Sopenharmony_ci if (parent == TC_H_ROOT) 16762306a36Sopenharmony_ci return &qdisc_state->root_qdisc; 16862306a36Sopenharmony_ci return mlxsw_sp_qdisc_walk(&qdisc_state->root_qdisc, 16962306a36Sopenharmony_ci mlxsw_sp_qdisc_walk_cb_find, &parent); 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cistatic struct mlxsw_sp_qdisc * 17362306a36Sopenharmony_cimlxsw_sp_qdisc_walk_cb_find_by_handle(struct mlxsw_sp_qdisc *qdisc, void *data) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci u32 handle = *(u32 *)data; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci if (qdisc->ops && qdisc->handle == handle) 17862306a36Sopenharmony_ci return qdisc; 17962306a36Sopenharmony_ci return NULL; 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic struct mlxsw_sp_qdisc * 18362306a36Sopenharmony_cimlxsw_sp_qdisc_find_by_handle(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci if (!qdisc_state) 18862306a36Sopenharmony_ci return NULL; 18962306a36Sopenharmony_ci return mlxsw_sp_qdisc_walk(&qdisc_state->root_qdisc, 19062306a36Sopenharmony_ci mlxsw_sp_qdisc_walk_cb_find_by_handle, 19162306a36Sopenharmony_ci &handle); 19262306a36Sopenharmony_ci} 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_cistatic void 19562306a36Sopenharmony_cimlxsw_sp_qdisc_reduce_parent_backlog(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci struct mlxsw_sp_qdisc *tmp; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci for (tmp = mlxsw_sp_qdisc->parent; tmp; tmp = tmp->parent) 20062306a36Sopenharmony_ci tmp->stats_base.backlog -= mlxsw_sp_qdisc->stats_base.backlog; 20162306a36Sopenharmony_ci} 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_cistatic u8 mlxsw_sp_qdisc_get_prio_bitmap(struct mlxsw_sp_port *mlxsw_sp_port, 20462306a36Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 20562306a36Sopenharmony_ci{ 20662306a36Sopenharmony_ci struct mlxsw_sp_qdisc *parent = mlxsw_sp_qdisc->parent; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci if (!parent) 20962306a36Sopenharmony_ci return 0xff; 21062306a36Sopenharmony_ci if (!parent->ops->get_prio_bitmap) 21162306a36Sopenharmony_ci return mlxsw_sp_qdisc_get_prio_bitmap(mlxsw_sp_port, parent); 21262306a36Sopenharmony_ci return parent->ops->get_prio_bitmap(parent, mlxsw_sp_qdisc); 21362306a36Sopenharmony_ci} 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci#define MLXSW_SP_PORT_DEFAULT_TCLASS 0 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_cistatic int mlxsw_sp_qdisc_get_tclass_num(struct mlxsw_sp_port *mlxsw_sp_port, 21862306a36Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 21962306a36Sopenharmony_ci{ 22062306a36Sopenharmony_ci struct mlxsw_sp_qdisc *parent = mlxsw_sp_qdisc->parent; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci if (!parent) 22362306a36Sopenharmony_ci return MLXSW_SP_PORT_DEFAULT_TCLASS; 22462306a36Sopenharmony_ci if (!parent->ops->get_tclass_num) 22562306a36Sopenharmony_ci return mlxsw_sp_qdisc_get_tclass_num(mlxsw_sp_port, parent); 22662306a36Sopenharmony_ci return parent->ops->get_tclass_num(parent, mlxsw_sp_qdisc); 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_cistatic int 23062306a36Sopenharmony_cimlxsw_sp_qdisc_destroy(struct mlxsw_sp_port *mlxsw_sp_port, 23162306a36Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 23262306a36Sopenharmony_ci{ 23362306a36Sopenharmony_ci struct mlxsw_sp_qdisc *root_qdisc = &mlxsw_sp_port->qdisc->root_qdisc; 23462306a36Sopenharmony_ci int err_hdroom = 0; 23562306a36Sopenharmony_ci int err = 0; 23662306a36Sopenharmony_ci int i; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci if (!mlxsw_sp_qdisc) 23962306a36Sopenharmony_ci return 0; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci if (root_qdisc == mlxsw_sp_qdisc) { 24262306a36Sopenharmony_ci struct mlxsw_sp_hdroom hdroom = *mlxsw_sp_port->hdroom; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci hdroom.mode = MLXSW_SP_HDROOM_MODE_DCB; 24562306a36Sopenharmony_ci mlxsw_sp_hdroom_prios_reset_buf_idx(&hdroom); 24662306a36Sopenharmony_ci mlxsw_sp_hdroom_bufs_reset_lossiness(&hdroom); 24762306a36Sopenharmony_ci mlxsw_sp_hdroom_bufs_reset_sizes(mlxsw_sp_port, &hdroom); 24862306a36Sopenharmony_ci err_hdroom = mlxsw_sp_hdroom_configure(mlxsw_sp_port, &hdroom); 24962306a36Sopenharmony_ci } 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci if (!mlxsw_sp_qdisc->ops) 25262306a36Sopenharmony_ci return 0; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci for (i = 0; i < mlxsw_sp_qdisc->num_classes; i++) 25562306a36Sopenharmony_ci mlxsw_sp_qdisc_destroy(mlxsw_sp_port, 25662306a36Sopenharmony_ci &mlxsw_sp_qdisc->qdiscs[i]); 25762306a36Sopenharmony_ci mlxsw_sp_qdisc_reduce_parent_backlog(mlxsw_sp_qdisc); 25862306a36Sopenharmony_ci if (mlxsw_sp_qdisc->ops->destroy) 25962306a36Sopenharmony_ci err = mlxsw_sp_qdisc->ops->destroy(mlxsw_sp_port, 26062306a36Sopenharmony_ci mlxsw_sp_qdisc); 26162306a36Sopenharmony_ci if (mlxsw_sp_qdisc->ops->clean_stats) 26262306a36Sopenharmony_ci mlxsw_sp_qdisc->ops->clean_stats(mlxsw_sp_port, mlxsw_sp_qdisc); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci mlxsw_sp_qdisc->handle = TC_H_UNSPEC; 26562306a36Sopenharmony_ci mlxsw_sp_qdisc->ops = NULL; 26662306a36Sopenharmony_ci mlxsw_sp_qdisc->num_classes = 0; 26762306a36Sopenharmony_ci kfree(mlxsw_sp_qdisc->qdiscs); 26862306a36Sopenharmony_ci mlxsw_sp_qdisc->qdiscs = NULL; 26962306a36Sopenharmony_ci return err_hdroom ?: err; 27062306a36Sopenharmony_ci} 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_cistruct mlxsw_sp_qdisc_tree_validate { 27362306a36Sopenharmony_ci bool forbid_ets; 27462306a36Sopenharmony_ci bool forbid_root_tbf; 27562306a36Sopenharmony_ci bool forbid_tbf; 27662306a36Sopenharmony_ci bool forbid_red; 27762306a36Sopenharmony_ci}; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_cistatic int 28062306a36Sopenharmony_ci__mlxsw_sp_qdisc_tree_validate(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 28162306a36Sopenharmony_ci struct mlxsw_sp_qdisc_tree_validate validate); 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_cistatic int 28462306a36Sopenharmony_cimlxsw_sp_qdisc_tree_validate_children(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 28562306a36Sopenharmony_ci struct mlxsw_sp_qdisc_tree_validate validate) 28662306a36Sopenharmony_ci{ 28762306a36Sopenharmony_ci unsigned int i; 28862306a36Sopenharmony_ci int err; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci for (i = 0; i < mlxsw_sp_qdisc->num_classes; i++) { 29162306a36Sopenharmony_ci err = __mlxsw_sp_qdisc_tree_validate(&mlxsw_sp_qdisc->qdiscs[i], 29262306a36Sopenharmony_ci validate); 29362306a36Sopenharmony_ci if (err) 29462306a36Sopenharmony_ci return err; 29562306a36Sopenharmony_ci } 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci return 0; 29862306a36Sopenharmony_ci} 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_cistatic int 30162306a36Sopenharmony_ci__mlxsw_sp_qdisc_tree_validate(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 30262306a36Sopenharmony_ci struct mlxsw_sp_qdisc_tree_validate validate) 30362306a36Sopenharmony_ci{ 30462306a36Sopenharmony_ci if (!mlxsw_sp_qdisc->ops) 30562306a36Sopenharmony_ci return 0; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci switch (mlxsw_sp_qdisc->ops->type) { 30862306a36Sopenharmony_ci case MLXSW_SP_QDISC_FIFO: 30962306a36Sopenharmony_ci break; 31062306a36Sopenharmony_ci case MLXSW_SP_QDISC_RED: 31162306a36Sopenharmony_ci if (validate.forbid_red) 31262306a36Sopenharmony_ci return -EINVAL; 31362306a36Sopenharmony_ci validate.forbid_red = true; 31462306a36Sopenharmony_ci validate.forbid_root_tbf = true; 31562306a36Sopenharmony_ci validate.forbid_ets = true; 31662306a36Sopenharmony_ci break; 31762306a36Sopenharmony_ci case MLXSW_SP_QDISC_TBF: 31862306a36Sopenharmony_ci if (validate.forbid_root_tbf) { 31962306a36Sopenharmony_ci if (validate.forbid_tbf) 32062306a36Sopenharmony_ci return -EINVAL; 32162306a36Sopenharmony_ci /* This is a TC TBF. */ 32262306a36Sopenharmony_ci validate.forbid_tbf = true; 32362306a36Sopenharmony_ci validate.forbid_ets = true; 32462306a36Sopenharmony_ci } else { 32562306a36Sopenharmony_ci /* This is root TBF. */ 32662306a36Sopenharmony_ci validate.forbid_root_tbf = true; 32762306a36Sopenharmony_ci } 32862306a36Sopenharmony_ci break; 32962306a36Sopenharmony_ci case MLXSW_SP_QDISC_PRIO: 33062306a36Sopenharmony_ci case MLXSW_SP_QDISC_ETS: 33162306a36Sopenharmony_ci if (validate.forbid_ets) 33262306a36Sopenharmony_ci return -EINVAL; 33362306a36Sopenharmony_ci validate.forbid_root_tbf = true; 33462306a36Sopenharmony_ci validate.forbid_ets = true; 33562306a36Sopenharmony_ci break; 33662306a36Sopenharmony_ci default: 33762306a36Sopenharmony_ci WARN_ON(1); 33862306a36Sopenharmony_ci return -EINVAL; 33962306a36Sopenharmony_ci } 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci return mlxsw_sp_qdisc_tree_validate_children(mlxsw_sp_qdisc, validate); 34262306a36Sopenharmony_ci} 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_cistatic int mlxsw_sp_qdisc_tree_validate(struct mlxsw_sp_port *mlxsw_sp_port) 34562306a36Sopenharmony_ci{ 34662306a36Sopenharmony_ci struct mlxsw_sp_qdisc_tree_validate validate = {}; 34762306a36Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci mlxsw_sp_qdisc = &mlxsw_sp_port->qdisc->root_qdisc; 35062306a36Sopenharmony_ci return __mlxsw_sp_qdisc_tree_validate(mlxsw_sp_qdisc, validate); 35162306a36Sopenharmony_ci} 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_cistatic int mlxsw_sp_qdisc_create(struct mlxsw_sp_port *mlxsw_sp_port, 35462306a36Sopenharmony_ci u32 handle, 35562306a36Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 35662306a36Sopenharmony_ci struct mlxsw_sp_qdisc_ops *ops, void *params) 35762306a36Sopenharmony_ci{ 35862306a36Sopenharmony_ci struct mlxsw_sp_qdisc *root_qdisc = &mlxsw_sp_port->qdisc->root_qdisc; 35962306a36Sopenharmony_ci struct mlxsw_sp_hdroom orig_hdroom; 36062306a36Sopenharmony_ci unsigned int i; 36162306a36Sopenharmony_ci int err; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci err = ops->check_params(mlxsw_sp_port, params); 36462306a36Sopenharmony_ci if (err) 36562306a36Sopenharmony_ci return err; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci if (ops->num_classes) { 36862306a36Sopenharmony_ci mlxsw_sp_qdisc->qdiscs = kcalloc(ops->num_classes, 36962306a36Sopenharmony_ci sizeof(*mlxsw_sp_qdisc->qdiscs), 37062306a36Sopenharmony_ci GFP_KERNEL); 37162306a36Sopenharmony_ci if (!mlxsw_sp_qdisc->qdiscs) 37262306a36Sopenharmony_ci return -ENOMEM; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci for (i = 0; i < ops->num_classes; i++) 37562306a36Sopenharmony_ci mlxsw_sp_qdisc->qdiscs[i].parent = mlxsw_sp_qdisc; 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci orig_hdroom = *mlxsw_sp_port->hdroom; 37962306a36Sopenharmony_ci if (root_qdisc == mlxsw_sp_qdisc) { 38062306a36Sopenharmony_ci struct mlxsw_sp_hdroom hdroom = orig_hdroom; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci hdroom.mode = MLXSW_SP_HDROOM_MODE_TC; 38362306a36Sopenharmony_ci mlxsw_sp_hdroom_prios_reset_buf_idx(&hdroom); 38462306a36Sopenharmony_ci mlxsw_sp_hdroom_bufs_reset_lossiness(&hdroom); 38562306a36Sopenharmony_ci mlxsw_sp_hdroom_bufs_reset_sizes(mlxsw_sp_port, &hdroom); 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci err = mlxsw_sp_hdroom_configure(mlxsw_sp_port, &hdroom); 38862306a36Sopenharmony_ci if (err) 38962306a36Sopenharmony_ci goto err_hdroom_configure; 39062306a36Sopenharmony_ci } 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci mlxsw_sp_qdisc->num_classes = ops->num_classes; 39362306a36Sopenharmony_ci mlxsw_sp_qdisc->ops = ops; 39462306a36Sopenharmony_ci mlxsw_sp_qdisc->handle = handle; 39562306a36Sopenharmony_ci err = mlxsw_sp_qdisc_tree_validate(mlxsw_sp_port); 39662306a36Sopenharmony_ci if (err) 39762306a36Sopenharmony_ci goto err_replace; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci err = ops->replace(mlxsw_sp_port, handle, mlxsw_sp_qdisc, params); 40062306a36Sopenharmony_ci if (err) 40162306a36Sopenharmony_ci goto err_replace; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci return 0; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_cierr_replace: 40662306a36Sopenharmony_ci mlxsw_sp_qdisc->handle = TC_H_UNSPEC; 40762306a36Sopenharmony_ci mlxsw_sp_qdisc->ops = NULL; 40862306a36Sopenharmony_ci mlxsw_sp_qdisc->num_classes = 0; 40962306a36Sopenharmony_ci mlxsw_sp_hdroom_configure(mlxsw_sp_port, &orig_hdroom); 41062306a36Sopenharmony_cierr_hdroom_configure: 41162306a36Sopenharmony_ci kfree(mlxsw_sp_qdisc->qdiscs); 41262306a36Sopenharmony_ci mlxsw_sp_qdisc->qdiscs = NULL; 41362306a36Sopenharmony_ci return err; 41462306a36Sopenharmony_ci} 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_cistatic int 41762306a36Sopenharmony_cimlxsw_sp_qdisc_change(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, 41862306a36Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, void *params) 41962306a36Sopenharmony_ci{ 42062306a36Sopenharmony_ci struct mlxsw_sp_qdisc_ops *ops = mlxsw_sp_qdisc->ops; 42162306a36Sopenharmony_ci int err; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci err = ops->check_params(mlxsw_sp_port, params); 42462306a36Sopenharmony_ci if (err) 42562306a36Sopenharmony_ci goto unoffload; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci err = ops->replace(mlxsw_sp_port, handle, mlxsw_sp_qdisc, params); 42862306a36Sopenharmony_ci if (err) 42962306a36Sopenharmony_ci goto unoffload; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci /* Check if the Qdisc changed. That includes a situation where an 43262306a36Sopenharmony_ci * invisible Qdisc replaces another one, or is being added for the 43362306a36Sopenharmony_ci * first time. 43462306a36Sopenharmony_ci */ 43562306a36Sopenharmony_ci if (mlxsw_sp_qdisc->handle != handle) { 43662306a36Sopenharmony_ci if (ops->clean_stats) 43762306a36Sopenharmony_ci ops->clean_stats(mlxsw_sp_port, mlxsw_sp_qdisc); 43862306a36Sopenharmony_ci } 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci mlxsw_sp_qdisc->handle = handle; 44162306a36Sopenharmony_ci return 0; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ciunoffload: 44462306a36Sopenharmony_ci if (ops->unoffload) 44562306a36Sopenharmony_ci ops->unoffload(mlxsw_sp_port, mlxsw_sp_qdisc, params); 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc); 44862306a36Sopenharmony_ci return err; 44962306a36Sopenharmony_ci} 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_cistatic int 45262306a36Sopenharmony_cimlxsw_sp_qdisc_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, 45362306a36Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 45462306a36Sopenharmony_ci struct mlxsw_sp_qdisc_ops *ops, void *params) 45562306a36Sopenharmony_ci{ 45662306a36Sopenharmony_ci if (mlxsw_sp_qdisc->ops && mlxsw_sp_qdisc->ops->type != ops->type) 45762306a36Sopenharmony_ci /* In case this location contained a different qdisc of the 45862306a36Sopenharmony_ci * same type we can override the old qdisc configuration. 45962306a36Sopenharmony_ci * Otherwise, we need to remove the old qdisc before setting the 46062306a36Sopenharmony_ci * new one. 46162306a36Sopenharmony_ci */ 46262306a36Sopenharmony_ci mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc); 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci if (!mlxsw_sp_qdisc->ops) 46562306a36Sopenharmony_ci return mlxsw_sp_qdisc_create(mlxsw_sp_port, handle, 46662306a36Sopenharmony_ci mlxsw_sp_qdisc, ops, params); 46762306a36Sopenharmony_ci else 46862306a36Sopenharmony_ci return mlxsw_sp_qdisc_change(mlxsw_sp_port, handle, 46962306a36Sopenharmony_ci mlxsw_sp_qdisc, params); 47062306a36Sopenharmony_ci} 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_cistatic int 47362306a36Sopenharmony_cimlxsw_sp_qdisc_get_stats(struct mlxsw_sp_port *mlxsw_sp_port, 47462306a36Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 47562306a36Sopenharmony_ci struct tc_qopt_offload_stats *stats_ptr) 47662306a36Sopenharmony_ci{ 47762306a36Sopenharmony_ci if (mlxsw_sp_qdisc && mlxsw_sp_qdisc->ops && 47862306a36Sopenharmony_ci mlxsw_sp_qdisc->ops->get_stats) 47962306a36Sopenharmony_ci return mlxsw_sp_qdisc->ops->get_stats(mlxsw_sp_port, 48062306a36Sopenharmony_ci mlxsw_sp_qdisc, 48162306a36Sopenharmony_ci stats_ptr); 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci return -EOPNOTSUPP; 48462306a36Sopenharmony_ci} 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_cistatic int 48762306a36Sopenharmony_cimlxsw_sp_qdisc_get_xstats(struct mlxsw_sp_port *mlxsw_sp_port, 48862306a36Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 48962306a36Sopenharmony_ci void *xstats_ptr) 49062306a36Sopenharmony_ci{ 49162306a36Sopenharmony_ci if (mlxsw_sp_qdisc && mlxsw_sp_qdisc->ops && 49262306a36Sopenharmony_ci mlxsw_sp_qdisc->ops->get_xstats) 49362306a36Sopenharmony_ci return mlxsw_sp_qdisc->ops->get_xstats(mlxsw_sp_port, 49462306a36Sopenharmony_ci mlxsw_sp_qdisc, 49562306a36Sopenharmony_ci xstats_ptr); 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci return -EOPNOTSUPP; 49862306a36Sopenharmony_ci} 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_cistatic u64 50162306a36Sopenharmony_cimlxsw_sp_xstats_backlog(struct mlxsw_sp_port_xstats *xstats, int tclass_num) 50262306a36Sopenharmony_ci{ 50362306a36Sopenharmony_ci return xstats->backlog[tclass_num] + 50462306a36Sopenharmony_ci xstats->backlog[tclass_num + 8]; 50562306a36Sopenharmony_ci} 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_cistatic u64 50862306a36Sopenharmony_cimlxsw_sp_xstats_tail_drop(struct mlxsw_sp_port_xstats *xstats, int tclass_num) 50962306a36Sopenharmony_ci{ 51062306a36Sopenharmony_ci return xstats->tail_drop[tclass_num] + 51162306a36Sopenharmony_ci xstats->tail_drop[tclass_num + 8]; 51262306a36Sopenharmony_ci} 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_cistatic void 51562306a36Sopenharmony_cimlxsw_sp_qdisc_bstats_per_priority_get(struct mlxsw_sp_port_xstats *xstats, 51662306a36Sopenharmony_ci u8 prio_bitmap, u64 *tx_packets, 51762306a36Sopenharmony_ci u64 *tx_bytes) 51862306a36Sopenharmony_ci{ 51962306a36Sopenharmony_ci int i; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci *tx_packets = 0; 52262306a36Sopenharmony_ci *tx_bytes = 0; 52362306a36Sopenharmony_ci for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { 52462306a36Sopenharmony_ci if (prio_bitmap & BIT(i)) { 52562306a36Sopenharmony_ci *tx_packets += xstats->tx_packets[i]; 52662306a36Sopenharmony_ci *tx_bytes += xstats->tx_bytes[i]; 52762306a36Sopenharmony_ci } 52862306a36Sopenharmony_ci } 52962306a36Sopenharmony_ci} 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_cistatic void 53262306a36Sopenharmony_cimlxsw_sp_qdisc_collect_tc_stats(struct mlxsw_sp_port *mlxsw_sp_port, 53362306a36Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 53462306a36Sopenharmony_ci u64 *p_tx_bytes, u64 *p_tx_packets, 53562306a36Sopenharmony_ci u64 *p_drops, u64 *p_backlog) 53662306a36Sopenharmony_ci{ 53762306a36Sopenharmony_ci struct mlxsw_sp_port_xstats *xstats; 53862306a36Sopenharmony_ci u64 tx_bytes, tx_packets; 53962306a36Sopenharmony_ci u8 prio_bitmap; 54062306a36Sopenharmony_ci int tclass_num; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci prio_bitmap = mlxsw_sp_qdisc_get_prio_bitmap(mlxsw_sp_port, 54362306a36Sopenharmony_ci mlxsw_sp_qdisc); 54462306a36Sopenharmony_ci tclass_num = mlxsw_sp_qdisc_get_tclass_num(mlxsw_sp_port, 54562306a36Sopenharmony_ci mlxsw_sp_qdisc); 54662306a36Sopenharmony_ci xstats = &mlxsw_sp_port->periodic_hw_stats.xstats; 54762306a36Sopenharmony_ci mlxsw_sp_qdisc_bstats_per_priority_get(xstats, prio_bitmap, 54862306a36Sopenharmony_ci &tx_packets, &tx_bytes); 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci *p_tx_packets += tx_packets; 55162306a36Sopenharmony_ci *p_tx_bytes += tx_bytes; 55262306a36Sopenharmony_ci *p_drops += xstats->wred_drop[tclass_num] + 55362306a36Sopenharmony_ci mlxsw_sp_xstats_tail_drop(xstats, tclass_num); 55462306a36Sopenharmony_ci *p_backlog += mlxsw_sp_xstats_backlog(xstats, tclass_num); 55562306a36Sopenharmony_ci} 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_cistatic void 55862306a36Sopenharmony_cimlxsw_sp_qdisc_update_stats(struct mlxsw_sp *mlxsw_sp, 55962306a36Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 56062306a36Sopenharmony_ci u64 tx_bytes, u64 tx_packets, 56162306a36Sopenharmony_ci u64 drops, u64 backlog, 56262306a36Sopenharmony_ci struct tc_qopt_offload_stats *stats_ptr) 56362306a36Sopenharmony_ci{ 56462306a36Sopenharmony_ci struct mlxsw_sp_qdisc_stats *stats_base = &mlxsw_sp_qdisc->stats_base; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci tx_bytes -= stats_base->tx_bytes; 56762306a36Sopenharmony_ci tx_packets -= stats_base->tx_packets; 56862306a36Sopenharmony_ci drops -= stats_base->drops; 56962306a36Sopenharmony_ci backlog -= stats_base->backlog; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci _bstats_update(stats_ptr->bstats, tx_bytes, tx_packets); 57262306a36Sopenharmony_ci stats_ptr->qstats->drops += drops; 57362306a36Sopenharmony_ci stats_ptr->qstats->backlog += mlxsw_sp_cells_bytes(mlxsw_sp, backlog); 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci stats_base->backlog += backlog; 57662306a36Sopenharmony_ci stats_base->drops += drops; 57762306a36Sopenharmony_ci stats_base->tx_bytes += tx_bytes; 57862306a36Sopenharmony_ci stats_base->tx_packets += tx_packets; 57962306a36Sopenharmony_ci} 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_cistatic void 58262306a36Sopenharmony_cimlxsw_sp_qdisc_get_tc_stats(struct mlxsw_sp_port *mlxsw_sp_port, 58362306a36Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 58462306a36Sopenharmony_ci struct tc_qopt_offload_stats *stats_ptr) 58562306a36Sopenharmony_ci{ 58662306a36Sopenharmony_ci u64 tx_packets = 0; 58762306a36Sopenharmony_ci u64 tx_bytes = 0; 58862306a36Sopenharmony_ci u64 backlog = 0; 58962306a36Sopenharmony_ci u64 drops = 0; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci mlxsw_sp_qdisc_collect_tc_stats(mlxsw_sp_port, mlxsw_sp_qdisc, 59262306a36Sopenharmony_ci &tx_bytes, &tx_packets, 59362306a36Sopenharmony_ci &drops, &backlog); 59462306a36Sopenharmony_ci mlxsw_sp_qdisc_update_stats(mlxsw_sp_port->mlxsw_sp, mlxsw_sp_qdisc, 59562306a36Sopenharmony_ci tx_bytes, tx_packets, drops, backlog, 59662306a36Sopenharmony_ci stats_ptr); 59762306a36Sopenharmony_ci} 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_cistatic int 60062306a36Sopenharmony_cimlxsw_sp_tclass_congestion_enable(struct mlxsw_sp_port *mlxsw_sp_port, 60162306a36Sopenharmony_ci int tclass_num, u32 min, u32 max, 60262306a36Sopenharmony_ci u32 probability, bool is_wred, bool is_ecn) 60362306a36Sopenharmony_ci{ 60462306a36Sopenharmony_ci char cwtpm_cmd[MLXSW_REG_CWTPM_LEN]; 60562306a36Sopenharmony_ci char cwtp_cmd[MLXSW_REG_CWTP_LEN]; 60662306a36Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 60762306a36Sopenharmony_ci int err; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci mlxsw_reg_cwtp_pack(cwtp_cmd, mlxsw_sp_port->local_port, tclass_num); 61062306a36Sopenharmony_ci mlxsw_reg_cwtp_profile_pack(cwtp_cmd, MLXSW_REG_CWTP_DEFAULT_PROFILE, 61162306a36Sopenharmony_ci roundup(min, MLXSW_REG_CWTP_MIN_VALUE), 61262306a36Sopenharmony_ci roundup(max, MLXSW_REG_CWTP_MIN_VALUE), 61362306a36Sopenharmony_ci probability); 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(cwtp), cwtp_cmd); 61662306a36Sopenharmony_ci if (err) 61762306a36Sopenharmony_ci return err; 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci mlxsw_reg_cwtpm_pack(cwtpm_cmd, mlxsw_sp_port->local_port, tclass_num, 62062306a36Sopenharmony_ci MLXSW_REG_CWTP_DEFAULT_PROFILE, is_wred, is_ecn); 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(cwtpm), cwtpm_cmd); 62362306a36Sopenharmony_ci} 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_cistatic int 62662306a36Sopenharmony_cimlxsw_sp_tclass_congestion_disable(struct mlxsw_sp_port *mlxsw_sp_port, 62762306a36Sopenharmony_ci int tclass_num) 62862306a36Sopenharmony_ci{ 62962306a36Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 63062306a36Sopenharmony_ci char cwtpm_cmd[MLXSW_REG_CWTPM_LEN]; 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci mlxsw_reg_cwtpm_pack(cwtpm_cmd, mlxsw_sp_port->local_port, tclass_num, 63362306a36Sopenharmony_ci MLXSW_REG_CWTPM_RESET_PROFILE, false, false); 63462306a36Sopenharmony_ci return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(cwtpm), cwtpm_cmd); 63562306a36Sopenharmony_ci} 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_cistatic void 63862306a36Sopenharmony_cimlxsw_sp_setup_tc_qdisc_red_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port, 63962306a36Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 64062306a36Sopenharmony_ci{ 64162306a36Sopenharmony_ci struct mlxsw_sp_qdisc_stats *stats_base; 64262306a36Sopenharmony_ci struct mlxsw_sp_port_xstats *xstats; 64362306a36Sopenharmony_ci struct red_stats *red_base; 64462306a36Sopenharmony_ci u8 prio_bitmap; 64562306a36Sopenharmony_ci int tclass_num; 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci prio_bitmap = mlxsw_sp_qdisc_get_prio_bitmap(mlxsw_sp_port, 64862306a36Sopenharmony_ci mlxsw_sp_qdisc); 64962306a36Sopenharmony_ci tclass_num = mlxsw_sp_qdisc_get_tclass_num(mlxsw_sp_port, 65062306a36Sopenharmony_ci mlxsw_sp_qdisc); 65162306a36Sopenharmony_ci xstats = &mlxsw_sp_port->periodic_hw_stats.xstats; 65262306a36Sopenharmony_ci stats_base = &mlxsw_sp_qdisc->stats_base; 65362306a36Sopenharmony_ci red_base = &mlxsw_sp_qdisc->xstats_base.red; 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci mlxsw_sp_qdisc_bstats_per_priority_get(xstats, prio_bitmap, 65662306a36Sopenharmony_ci &stats_base->tx_packets, 65762306a36Sopenharmony_ci &stats_base->tx_bytes); 65862306a36Sopenharmony_ci red_base->prob_mark = xstats->tc_ecn[tclass_num]; 65962306a36Sopenharmony_ci red_base->prob_drop = xstats->wred_drop[tclass_num]; 66062306a36Sopenharmony_ci red_base->pdrop = mlxsw_sp_xstats_tail_drop(xstats, tclass_num); 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci stats_base->overlimits = red_base->prob_drop + red_base->prob_mark; 66362306a36Sopenharmony_ci stats_base->drops = red_base->prob_drop + red_base->pdrop; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci stats_base->backlog = 0; 66662306a36Sopenharmony_ci} 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_cistatic int 66962306a36Sopenharmony_cimlxsw_sp_qdisc_red_destroy(struct mlxsw_sp_port *mlxsw_sp_port, 67062306a36Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 67162306a36Sopenharmony_ci{ 67262306a36Sopenharmony_ci int tclass_num = mlxsw_sp_qdisc_get_tclass_num(mlxsw_sp_port, 67362306a36Sopenharmony_ci mlxsw_sp_qdisc); 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci return mlxsw_sp_tclass_congestion_disable(mlxsw_sp_port, tclass_num); 67662306a36Sopenharmony_ci} 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_cistatic int 67962306a36Sopenharmony_cimlxsw_sp_qdisc_red_check_params(struct mlxsw_sp_port *mlxsw_sp_port, 68062306a36Sopenharmony_ci void *params) 68162306a36Sopenharmony_ci{ 68262306a36Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 68362306a36Sopenharmony_ci struct tc_red_qopt_offload_params *p = params; 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci if (p->min > p->max) { 68662306a36Sopenharmony_ci dev_err(mlxsw_sp->bus_info->dev, 68762306a36Sopenharmony_ci "spectrum: RED: min %u is bigger then max %u\n", p->min, 68862306a36Sopenharmony_ci p->max); 68962306a36Sopenharmony_ci return -EINVAL; 69062306a36Sopenharmony_ci } 69162306a36Sopenharmony_ci if (p->max > MLXSW_CORE_RES_GET(mlxsw_sp->core, 69262306a36Sopenharmony_ci GUARANTEED_SHARED_BUFFER)) { 69362306a36Sopenharmony_ci dev_err(mlxsw_sp->bus_info->dev, 69462306a36Sopenharmony_ci "spectrum: RED: max value %u is too big\n", p->max); 69562306a36Sopenharmony_ci return -EINVAL; 69662306a36Sopenharmony_ci } 69762306a36Sopenharmony_ci if (p->min == 0 || p->max == 0) { 69862306a36Sopenharmony_ci dev_err(mlxsw_sp->bus_info->dev, 69962306a36Sopenharmony_ci "spectrum: RED: 0 value is illegal for min and max\n"); 70062306a36Sopenharmony_ci return -EINVAL; 70162306a36Sopenharmony_ci } 70262306a36Sopenharmony_ci return 0; 70362306a36Sopenharmony_ci} 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_cistatic int 70662306a36Sopenharmony_cimlxsw_sp_qdisc_future_fifo_replace(struct mlxsw_sp_port *mlxsw_sp_port, 70762306a36Sopenharmony_ci u32 handle, unsigned int band, 70862306a36Sopenharmony_ci struct mlxsw_sp_qdisc *child_qdisc); 70962306a36Sopenharmony_cistatic void 71062306a36Sopenharmony_cimlxsw_sp_qdisc_future_fifos_init(struct mlxsw_sp_port *mlxsw_sp_port, 71162306a36Sopenharmony_ci u32 handle); 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_cistatic int 71462306a36Sopenharmony_cimlxsw_sp_qdisc_red_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, 71562306a36Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 71662306a36Sopenharmony_ci void *params) 71762306a36Sopenharmony_ci{ 71862306a36Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 71962306a36Sopenharmony_ci struct tc_red_qopt_offload_params *p = params; 72062306a36Sopenharmony_ci int tclass_num; 72162306a36Sopenharmony_ci u32 min, max; 72262306a36Sopenharmony_ci u64 prob; 72362306a36Sopenharmony_ci int err; 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci err = mlxsw_sp_qdisc_future_fifo_replace(mlxsw_sp_port, handle, 0, 72662306a36Sopenharmony_ci &mlxsw_sp_qdisc->qdiscs[0]); 72762306a36Sopenharmony_ci if (err) 72862306a36Sopenharmony_ci return err; 72962306a36Sopenharmony_ci mlxsw_sp_qdisc_future_fifos_init(mlxsw_sp_port, TC_H_UNSPEC); 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci tclass_num = mlxsw_sp_qdisc_get_tclass_num(mlxsw_sp_port, 73262306a36Sopenharmony_ci mlxsw_sp_qdisc); 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci /* calculate probability in percentage */ 73562306a36Sopenharmony_ci prob = p->probability; 73662306a36Sopenharmony_ci prob *= 100; 73762306a36Sopenharmony_ci prob = DIV_ROUND_UP(prob, 1 << 16); 73862306a36Sopenharmony_ci prob = DIV_ROUND_UP(prob, 1 << 16); 73962306a36Sopenharmony_ci min = mlxsw_sp_bytes_cells(mlxsw_sp, p->min); 74062306a36Sopenharmony_ci max = mlxsw_sp_bytes_cells(mlxsw_sp, p->max); 74162306a36Sopenharmony_ci return mlxsw_sp_tclass_congestion_enable(mlxsw_sp_port, tclass_num, 74262306a36Sopenharmony_ci min, max, prob, 74362306a36Sopenharmony_ci !p->is_nodrop, p->is_ecn); 74462306a36Sopenharmony_ci} 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_cistatic void 74762306a36Sopenharmony_cimlxsw_sp_qdisc_leaf_unoffload(struct mlxsw_sp_port *mlxsw_sp_port, 74862306a36Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 74962306a36Sopenharmony_ci struct gnet_stats_queue *qstats) 75062306a36Sopenharmony_ci{ 75162306a36Sopenharmony_ci u64 backlog; 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci backlog = mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp, 75462306a36Sopenharmony_ci mlxsw_sp_qdisc->stats_base.backlog); 75562306a36Sopenharmony_ci qstats->backlog -= backlog; 75662306a36Sopenharmony_ci mlxsw_sp_qdisc->stats_base.backlog = 0; 75762306a36Sopenharmony_ci} 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_cistatic void 76062306a36Sopenharmony_cimlxsw_sp_qdisc_red_unoffload(struct mlxsw_sp_port *mlxsw_sp_port, 76162306a36Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 76262306a36Sopenharmony_ci void *params) 76362306a36Sopenharmony_ci{ 76462306a36Sopenharmony_ci struct tc_red_qopt_offload_params *p = params; 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci mlxsw_sp_qdisc_leaf_unoffload(mlxsw_sp_port, mlxsw_sp_qdisc, p->qstats); 76762306a36Sopenharmony_ci} 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_cistatic int 77062306a36Sopenharmony_cimlxsw_sp_qdisc_get_red_xstats(struct mlxsw_sp_port *mlxsw_sp_port, 77162306a36Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 77262306a36Sopenharmony_ci void *xstats_ptr) 77362306a36Sopenharmony_ci{ 77462306a36Sopenharmony_ci struct red_stats *xstats_base = &mlxsw_sp_qdisc->xstats_base.red; 77562306a36Sopenharmony_ci struct mlxsw_sp_port_xstats *xstats; 77662306a36Sopenharmony_ci struct red_stats *res = xstats_ptr; 77762306a36Sopenharmony_ci int early_drops, marks, pdrops; 77862306a36Sopenharmony_ci int tclass_num; 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci tclass_num = mlxsw_sp_qdisc_get_tclass_num(mlxsw_sp_port, 78162306a36Sopenharmony_ci mlxsw_sp_qdisc); 78262306a36Sopenharmony_ci xstats = &mlxsw_sp_port->periodic_hw_stats.xstats; 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci early_drops = xstats->wred_drop[tclass_num] - xstats_base->prob_drop; 78562306a36Sopenharmony_ci marks = xstats->tc_ecn[tclass_num] - xstats_base->prob_mark; 78662306a36Sopenharmony_ci pdrops = mlxsw_sp_xstats_tail_drop(xstats, tclass_num) - 78762306a36Sopenharmony_ci xstats_base->pdrop; 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci res->pdrop += pdrops; 79062306a36Sopenharmony_ci res->prob_drop += early_drops; 79162306a36Sopenharmony_ci res->prob_mark += marks; 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci xstats_base->pdrop += pdrops; 79462306a36Sopenharmony_ci xstats_base->prob_drop += early_drops; 79562306a36Sopenharmony_ci xstats_base->prob_mark += marks; 79662306a36Sopenharmony_ci return 0; 79762306a36Sopenharmony_ci} 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_cistatic int 80062306a36Sopenharmony_cimlxsw_sp_qdisc_get_red_stats(struct mlxsw_sp_port *mlxsw_sp_port, 80162306a36Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 80262306a36Sopenharmony_ci struct tc_qopt_offload_stats *stats_ptr) 80362306a36Sopenharmony_ci{ 80462306a36Sopenharmony_ci struct mlxsw_sp_qdisc_stats *stats_base; 80562306a36Sopenharmony_ci struct mlxsw_sp_port_xstats *xstats; 80662306a36Sopenharmony_ci u64 overlimits; 80762306a36Sopenharmony_ci int tclass_num; 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci tclass_num = mlxsw_sp_qdisc_get_tclass_num(mlxsw_sp_port, 81062306a36Sopenharmony_ci mlxsw_sp_qdisc); 81162306a36Sopenharmony_ci xstats = &mlxsw_sp_port->periodic_hw_stats.xstats; 81262306a36Sopenharmony_ci stats_base = &mlxsw_sp_qdisc->stats_base; 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci mlxsw_sp_qdisc_get_tc_stats(mlxsw_sp_port, mlxsw_sp_qdisc, stats_ptr); 81562306a36Sopenharmony_ci overlimits = xstats->wred_drop[tclass_num] + 81662306a36Sopenharmony_ci xstats->tc_ecn[tclass_num] - stats_base->overlimits; 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci stats_ptr->qstats->overlimits += overlimits; 81962306a36Sopenharmony_ci stats_base->overlimits += overlimits; 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci return 0; 82262306a36Sopenharmony_ci} 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_cistatic struct mlxsw_sp_qdisc * 82562306a36Sopenharmony_cimlxsw_sp_qdisc_leaf_find_class(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 82662306a36Sopenharmony_ci u32 parent) 82762306a36Sopenharmony_ci{ 82862306a36Sopenharmony_ci /* RED and TBF are formally classful qdiscs, but all class references, 82962306a36Sopenharmony_ci * including X:0, just refer to the same one class. 83062306a36Sopenharmony_ci */ 83162306a36Sopenharmony_ci return &mlxsw_sp_qdisc->qdiscs[0]; 83262306a36Sopenharmony_ci} 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_cistatic struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_red = { 83562306a36Sopenharmony_ci .type = MLXSW_SP_QDISC_RED, 83662306a36Sopenharmony_ci .check_params = mlxsw_sp_qdisc_red_check_params, 83762306a36Sopenharmony_ci .replace = mlxsw_sp_qdisc_red_replace, 83862306a36Sopenharmony_ci .unoffload = mlxsw_sp_qdisc_red_unoffload, 83962306a36Sopenharmony_ci .destroy = mlxsw_sp_qdisc_red_destroy, 84062306a36Sopenharmony_ci .get_stats = mlxsw_sp_qdisc_get_red_stats, 84162306a36Sopenharmony_ci .get_xstats = mlxsw_sp_qdisc_get_red_xstats, 84262306a36Sopenharmony_ci .clean_stats = mlxsw_sp_setup_tc_qdisc_red_clean_stats, 84362306a36Sopenharmony_ci .find_class = mlxsw_sp_qdisc_leaf_find_class, 84462306a36Sopenharmony_ci .num_classes = 1, 84562306a36Sopenharmony_ci}; 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_cistatic int mlxsw_sp_qdisc_graft(struct mlxsw_sp_port *mlxsw_sp_port, 84862306a36Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 84962306a36Sopenharmony_ci u8 band, u32 child_handle); 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_cistatic int __mlxsw_sp_setup_tc_red(struct mlxsw_sp_port *mlxsw_sp_port, 85262306a36Sopenharmony_ci struct tc_red_qopt_offload *p) 85362306a36Sopenharmony_ci{ 85462306a36Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc; 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent); 85762306a36Sopenharmony_ci if (!mlxsw_sp_qdisc) 85862306a36Sopenharmony_ci return -EOPNOTSUPP; 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci if (p->command == TC_RED_REPLACE) 86162306a36Sopenharmony_ci return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle, 86262306a36Sopenharmony_ci mlxsw_sp_qdisc, 86362306a36Sopenharmony_ci &mlxsw_sp_qdisc_ops_red, 86462306a36Sopenharmony_ci &p->set); 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle)) 86762306a36Sopenharmony_ci return -EOPNOTSUPP; 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci switch (p->command) { 87062306a36Sopenharmony_ci case TC_RED_DESTROY: 87162306a36Sopenharmony_ci return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc); 87262306a36Sopenharmony_ci case TC_RED_XSTATS: 87362306a36Sopenharmony_ci return mlxsw_sp_qdisc_get_xstats(mlxsw_sp_port, mlxsw_sp_qdisc, 87462306a36Sopenharmony_ci p->xstats); 87562306a36Sopenharmony_ci case TC_RED_STATS: 87662306a36Sopenharmony_ci return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc, 87762306a36Sopenharmony_ci &p->stats); 87862306a36Sopenharmony_ci case TC_RED_GRAFT: 87962306a36Sopenharmony_ci return mlxsw_sp_qdisc_graft(mlxsw_sp_port, mlxsw_sp_qdisc, 0, 88062306a36Sopenharmony_ci p->child_handle); 88162306a36Sopenharmony_ci default: 88262306a36Sopenharmony_ci return -EOPNOTSUPP; 88362306a36Sopenharmony_ci } 88462306a36Sopenharmony_ci} 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ciint mlxsw_sp_setup_tc_red(struct mlxsw_sp_port *mlxsw_sp_port, 88762306a36Sopenharmony_ci struct tc_red_qopt_offload *p) 88862306a36Sopenharmony_ci{ 88962306a36Sopenharmony_ci int err; 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci mutex_lock(&mlxsw_sp_port->qdisc->lock); 89262306a36Sopenharmony_ci err = __mlxsw_sp_setup_tc_red(mlxsw_sp_port, p); 89362306a36Sopenharmony_ci mutex_unlock(&mlxsw_sp_port->qdisc->lock); 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci return err; 89662306a36Sopenharmony_ci} 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_cistatic void 89962306a36Sopenharmony_cimlxsw_sp_setup_tc_qdisc_leaf_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port, 90062306a36Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 90162306a36Sopenharmony_ci{ 90262306a36Sopenharmony_ci u64 backlog_cells = 0; 90362306a36Sopenharmony_ci u64 tx_packets = 0; 90462306a36Sopenharmony_ci u64 tx_bytes = 0; 90562306a36Sopenharmony_ci u64 drops = 0; 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci mlxsw_sp_qdisc_collect_tc_stats(mlxsw_sp_port, mlxsw_sp_qdisc, 90862306a36Sopenharmony_ci &tx_bytes, &tx_packets, 90962306a36Sopenharmony_ci &drops, &backlog_cells); 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci mlxsw_sp_qdisc->stats_base.tx_packets = tx_packets; 91262306a36Sopenharmony_ci mlxsw_sp_qdisc->stats_base.tx_bytes = tx_bytes; 91362306a36Sopenharmony_ci mlxsw_sp_qdisc->stats_base.drops = drops; 91462306a36Sopenharmony_ci mlxsw_sp_qdisc->stats_base.backlog = 0; 91562306a36Sopenharmony_ci} 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_cistatic enum mlxsw_reg_qeec_hr 91862306a36Sopenharmony_cimlxsw_sp_qdisc_tbf_hr(struct mlxsw_sp_port *mlxsw_sp_port, 91962306a36Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 92062306a36Sopenharmony_ci{ 92162306a36Sopenharmony_ci if (mlxsw_sp_qdisc == &mlxsw_sp_port->qdisc->root_qdisc) 92262306a36Sopenharmony_ci return MLXSW_REG_QEEC_HR_PORT; 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci /* Configure subgroup shaper, so that both UC and MC traffic is subject 92562306a36Sopenharmony_ci * to shaping. That is unlike RED, however UC queue lengths are going to 92662306a36Sopenharmony_ci * be different than MC ones due to different pool and quota 92762306a36Sopenharmony_ci * configurations, so the configuration is not applicable. For shaper on 92862306a36Sopenharmony_ci * the other hand, subjecting the overall stream to the configured 92962306a36Sopenharmony_ci * shaper makes sense. Also note that that is what we do for 93062306a36Sopenharmony_ci * ieee_setmaxrate(). 93162306a36Sopenharmony_ci */ 93262306a36Sopenharmony_ci return MLXSW_REG_QEEC_HR_SUBGROUP; 93362306a36Sopenharmony_ci} 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_cistatic int 93662306a36Sopenharmony_cimlxsw_sp_qdisc_tbf_destroy(struct mlxsw_sp_port *mlxsw_sp_port, 93762306a36Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 93862306a36Sopenharmony_ci{ 93962306a36Sopenharmony_ci enum mlxsw_reg_qeec_hr hr = mlxsw_sp_qdisc_tbf_hr(mlxsw_sp_port, 94062306a36Sopenharmony_ci mlxsw_sp_qdisc); 94162306a36Sopenharmony_ci int tclass_num = mlxsw_sp_qdisc_get_tclass_num(mlxsw_sp_port, 94262306a36Sopenharmony_ci mlxsw_sp_qdisc); 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_ci return mlxsw_sp_port_ets_maxrate_set(mlxsw_sp_port, hr, tclass_num, 0, 94562306a36Sopenharmony_ci MLXSW_REG_QEEC_MAS_DIS, 0); 94662306a36Sopenharmony_ci} 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_cistatic int 94962306a36Sopenharmony_cimlxsw_sp_qdisc_tbf_bs(struct mlxsw_sp_port *mlxsw_sp_port, 95062306a36Sopenharmony_ci u32 max_size, u8 *p_burst_size) 95162306a36Sopenharmony_ci{ 95262306a36Sopenharmony_ci /* TBF burst size is configured in bytes. The ASIC burst size value is 95362306a36Sopenharmony_ci * ((2 ^ bs) * 512 bits. Convert the TBF bytes to 512-bit units. 95462306a36Sopenharmony_ci */ 95562306a36Sopenharmony_ci u32 bs512 = max_size / 64; 95662306a36Sopenharmony_ci u8 bs = fls(bs512); 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci if (!bs) 95962306a36Sopenharmony_ci return -EINVAL; 96062306a36Sopenharmony_ci --bs; 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci /* Demand a power of two. */ 96362306a36Sopenharmony_ci if ((1 << bs) != bs512) 96462306a36Sopenharmony_ci return -EINVAL; 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci if (bs < mlxsw_sp_port->mlxsw_sp->lowest_shaper_bs || 96762306a36Sopenharmony_ci bs > MLXSW_REG_QEEC_HIGHEST_SHAPER_BS) 96862306a36Sopenharmony_ci return -EINVAL; 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci *p_burst_size = bs; 97162306a36Sopenharmony_ci return 0; 97262306a36Sopenharmony_ci} 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_cistatic u32 97562306a36Sopenharmony_cimlxsw_sp_qdisc_tbf_max_size(u8 bs) 97662306a36Sopenharmony_ci{ 97762306a36Sopenharmony_ci return (1U << bs) * 64; 97862306a36Sopenharmony_ci} 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_cistatic u64 98162306a36Sopenharmony_cimlxsw_sp_qdisc_tbf_rate_kbps(struct tc_tbf_qopt_offload_replace_params *p) 98262306a36Sopenharmony_ci{ 98362306a36Sopenharmony_ci /* TBF interface is in bytes/s, whereas Spectrum ASIC is configured in 98462306a36Sopenharmony_ci * Kbits/s. 98562306a36Sopenharmony_ci */ 98662306a36Sopenharmony_ci return div_u64(p->rate.rate_bytes_ps, 1000) * 8; 98762306a36Sopenharmony_ci} 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_cistatic int 99062306a36Sopenharmony_cimlxsw_sp_qdisc_tbf_check_params(struct mlxsw_sp_port *mlxsw_sp_port, 99162306a36Sopenharmony_ci void *params) 99262306a36Sopenharmony_ci{ 99362306a36Sopenharmony_ci struct tc_tbf_qopt_offload_replace_params *p = params; 99462306a36Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 99562306a36Sopenharmony_ci u64 rate_kbps = mlxsw_sp_qdisc_tbf_rate_kbps(p); 99662306a36Sopenharmony_ci u8 burst_size; 99762306a36Sopenharmony_ci int err; 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci if (rate_kbps >= MLXSW_REG_QEEC_MAS_DIS) { 100062306a36Sopenharmony_ci dev_err(mlxsw_sp_port->mlxsw_sp->bus_info->dev, 100162306a36Sopenharmony_ci "spectrum: TBF: rate of %lluKbps must be below %u\n", 100262306a36Sopenharmony_ci rate_kbps, MLXSW_REG_QEEC_MAS_DIS); 100362306a36Sopenharmony_ci return -EINVAL; 100462306a36Sopenharmony_ci } 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_ci err = mlxsw_sp_qdisc_tbf_bs(mlxsw_sp_port, p->max_size, &burst_size); 100762306a36Sopenharmony_ci if (err) { 100862306a36Sopenharmony_ci u8 highest_shaper_bs = MLXSW_REG_QEEC_HIGHEST_SHAPER_BS; 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_ci dev_err(mlxsw_sp->bus_info->dev, 101162306a36Sopenharmony_ci "spectrum: TBF: invalid burst size of %u, must be a power of two between %u and %u", 101262306a36Sopenharmony_ci p->max_size, 101362306a36Sopenharmony_ci mlxsw_sp_qdisc_tbf_max_size(mlxsw_sp->lowest_shaper_bs), 101462306a36Sopenharmony_ci mlxsw_sp_qdisc_tbf_max_size(highest_shaper_bs)); 101562306a36Sopenharmony_ci return -EINVAL; 101662306a36Sopenharmony_ci } 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_ci return 0; 101962306a36Sopenharmony_ci} 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_cistatic int 102262306a36Sopenharmony_cimlxsw_sp_qdisc_tbf_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, 102362306a36Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 102462306a36Sopenharmony_ci void *params) 102562306a36Sopenharmony_ci{ 102662306a36Sopenharmony_ci enum mlxsw_reg_qeec_hr hr = mlxsw_sp_qdisc_tbf_hr(mlxsw_sp_port, 102762306a36Sopenharmony_ci mlxsw_sp_qdisc); 102862306a36Sopenharmony_ci struct tc_tbf_qopt_offload_replace_params *p = params; 102962306a36Sopenharmony_ci u64 rate_kbps = mlxsw_sp_qdisc_tbf_rate_kbps(p); 103062306a36Sopenharmony_ci int tclass_num; 103162306a36Sopenharmony_ci u8 burst_size; 103262306a36Sopenharmony_ci int err; 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci err = mlxsw_sp_qdisc_future_fifo_replace(mlxsw_sp_port, handle, 0, 103562306a36Sopenharmony_ci &mlxsw_sp_qdisc->qdiscs[0]); 103662306a36Sopenharmony_ci if (err) 103762306a36Sopenharmony_ci return err; 103862306a36Sopenharmony_ci mlxsw_sp_qdisc_future_fifos_init(mlxsw_sp_port, TC_H_UNSPEC); 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci tclass_num = mlxsw_sp_qdisc_get_tclass_num(mlxsw_sp_port, 104162306a36Sopenharmony_ci mlxsw_sp_qdisc); 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_ci err = mlxsw_sp_qdisc_tbf_bs(mlxsw_sp_port, p->max_size, &burst_size); 104462306a36Sopenharmony_ci if (WARN_ON_ONCE(err)) 104562306a36Sopenharmony_ci /* check_params above was supposed to reject this value. */ 104662306a36Sopenharmony_ci return -EINVAL; 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_ci return mlxsw_sp_port_ets_maxrate_set(mlxsw_sp_port, hr, tclass_num, 0, 104962306a36Sopenharmony_ci rate_kbps, burst_size); 105062306a36Sopenharmony_ci} 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_cistatic void 105362306a36Sopenharmony_cimlxsw_sp_qdisc_tbf_unoffload(struct mlxsw_sp_port *mlxsw_sp_port, 105462306a36Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 105562306a36Sopenharmony_ci void *params) 105662306a36Sopenharmony_ci{ 105762306a36Sopenharmony_ci struct tc_tbf_qopt_offload_replace_params *p = params; 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci mlxsw_sp_qdisc_leaf_unoffload(mlxsw_sp_port, mlxsw_sp_qdisc, p->qstats); 106062306a36Sopenharmony_ci} 106162306a36Sopenharmony_ci 106262306a36Sopenharmony_cistatic int 106362306a36Sopenharmony_cimlxsw_sp_qdisc_get_tbf_stats(struct mlxsw_sp_port *mlxsw_sp_port, 106462306a36Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 106562306a36Sopenharmony_ci struct tc_qopt_offload_stats *stats_ptr) 106662306a36Sopenharmony_ci{ 106762306a36Sopenharmony_ci mlxsw_sp_qdisc_get_tc_stats(mlxsw_sp_port, mlxsw_sp_qdisc, 106862306a36Sopenharmony_ci stats_ptr); 106962306a36Sopenharmony_ci return 0; 107062306a36Sopenharmony_ci} 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_cistatic struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_tbf = { 107362306a36Sopenharmony_ci .type = MLXSW_SP_QDISC_TBF, 107462306a36Sopenharmony_ci .check_params = mlxsw_sp_qdisc_tbf_check_params, 107562306a36Sopenharmony_ci .replace = mlxsw_sp_qdisc_tbf_replace, 107662306a36Sopenharmony_ci .unoffload = mlxsw_sp_qdisc_tbf_unoffload, 107762306a36Sopenharmony_ci .destroy = mlxsw_sp_qdisc_tbf_destroy, 107862306a36Sopenharmony_ci .get_stats = mlxsw_sp_qdisc_get_tbf_stats, 107962306a36Sopenharmony_ci .clean_stats = mlxsw_sp_setup_tc_qdisc_leaf_clean_stats, 108062306a36Sopenharmony_ci .find_class = mlxsw_sp_qdisc_leaf_find_class, 108162306a36Sopenharmony_ci .num_classes = 1, 108262306a36Sopenharmony_ci}; 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_cistatic int __mlxsw_sp_setup_tc_tbf(struct mlxsw_sp_port *mlxsw_sp_port, 108562306a36Sopenharmony_ci struct tc_tbf_qopt_offload *p) 108662306a36Sopenharmony_ci{ 108762306a36Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc; 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent); 109062306a36Sopenharmony_ci if (!mlxsw_sp_qdisc) 109162306a36Sopenharmony_ci return -EOPNOTSUPP; 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci if (p->command == TC_TBF_REPLACE) 109462306a36Sopenharmony_ci return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle, 109562306a36Sopenharmony_ci mlxsw_sp_qdisc, 109662306a36Sopenharmony_ci &mlxsw_sp_qdisc_ops_tbf, 109762306a36Sopenharmony_ci &p->replace_params); 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_ci if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle)) 110062306a36Sopenharmony_ci return -EOPNOTSUPP; 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_ci switch (p->command) { 110362306a36Sopenharmony_ci case TC_TBF_DESTROY: 110462306a36Sopenharmony_ci return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc); 110562306a36Sopenharmony_ci case TC_TBF_STATS: 110662306a36Sopenharmony_ci return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc, 110762306a36Sopenharmony_ci &p->stats); 110862306a36Sopenharmony_ci case TC_TBF_GRAFT: 110962306a36Sopenharmony_ci return mlxsw_sp_qdisc_graft(mlxsw_sp_port, mlxsw_sp_qdisc, 0, 111062306a36Sopenharmony_ci p->child_handle); 111162306a36Sopenharmony_ci default: 111262306a36Sopenharmony_ci return -EOPNOTSUPP; 111362306a36Sopenharmony_ci } 111462306a36Sopenharmony_ci} 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ciint mlxsw_sp_setup_tc_tbf(struct mlxsw_sp_port *mlxsw_sp_port, 111762306a36Sopenharmony_ci struct tc_tbf_qopt_offload *p) 111862306a36Sopenharmony_ci{ 111962306a36Sopenharmony_ci int err; 112062306a36Sopenharmony_ci 112162306a36Sopenharmony_ci mutex_lock(&mlxsw_sp_port->qdisc->lock); 112262306a36Sopenharmony_ci err = __mlxsw_sp_setup_tc_tbf(mlxsw_sp_port, p); 112362306a36Sopenharmony_ci mutex_unlock(&mlxsw_sp_port->qdisc->lock); 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_ci return err; 112662306a36Sopenharmony_ci} 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_cistatic int 112962306a36Sopenharmony_cimlxsw_sp_qdisc_fifo_check_params(struct mlxsw_sp_port *mlxsw_sp_port, 113062306a36Sopenharmony_ci void *params) 113162306a36Sopenharmony_ci{ 113262306a36Sopenharmony_ci return 0; 113362306a36Sopenharmony_ci} 113462306a36Sopenharmony_ci 113562306a36Sopenharmony_cistatic int 113662306a36Sopenharmony_cimlxsw_sp_qdisc_fifo_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, 113762306a36Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 113862306a36Sopenharmony_ci void *params) 113962306a36Sopenharmony_ci{ 114062306a36Sopenharmony_ci return 0; 114162306a36Sopenharmony_ci} 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_cistatic int 114462306a36Sopenharmony_cimlxsw_sp_qdisc_get_fifo_stats(struct mlxsw_sp_port *mlxsw_sp_port, 114562306a36Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 114662306a36Sopenharmony_ci struct tc_qopt_offload_stats *stats_ptr) 114762306a36Sopenharmony_ci{ 114862306a36Sopenharmony_ci mlxsw_sp_qdisc_get_tc_stats(mlxsw_sp_port, mlxsw_sp_qdisc, 114962306a36Sopenharmony_ci stats_ptr); 115062306a36Sopenharmony_ci return 0; 115162306a36Sopenharmony_ci} 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_cistatic struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_fifo = { 115462306a36Sopenharmony_ci .type = MLXSW_SP_QDISC_FIFO, 115562306a36Sopenharmony_ci .check_params = mlxsw_sp_qdisc_fifo_check_params, 115662306a36Sopenharmony_ci .replace = mlxsw_sp_qdisc_fifo_replace, 115762306a36Sopenharmony_ci .get_stats = mlxsw_sp_qdisc_get_fifo_stats, 115862306a36Sopenharmony_ci .clean_stats = mlxsw_sp_setup_tc_qdisc_leaf_clean_stats, 115962306a36Sopenharmony_ci}; 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_cistatic int 116262306a36Sopenharmony_cimlxsw_sp_qdisc_future_fifo_replace(struct mlxsw_sp_port *mlxsw_sp_port, 116362306a36Sopenharmony_ci u32 handle, unsigned int band, 116462306a36Sopenharmony_ci struct mlxsw_sp_qdisc *child_qdisc) 116562306a36Sopenharmony_ci{ 116662306a36Sopenharmony_ci struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc; 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_ci if (handle == qdisc_state->future_handle && 116962306a36Sopenharmony_ci qdisc_state->future_fifos[band]) 117062306a36Sopenharmony_ci return mlxsw_sp_qdisc_replace(mlxsw_sp_port, TC_H_UNSPEC, 117162306a36Sopenharmony_ci child_qdisc, 117262306a36Sopenharmony_ci &mlxsw_sp_qdisc_ops_fifo, 117362306a36Sopenharmony_ci NULL); 117462306a36Sopenharmony_ci return 0; 117562306a36Sopenharmony_ci} 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_cistatic void 117862306a36Sopenharmony_cimlxsw_sp_qdisc_future_fifos_init(struct mlxsw_sp_port *mlxsw_sp_port, 117962306a36Sopenharmony_ci u32 handle) 118062306a36Sopenharmony_ci{ 118162306a36Sopenharmony_ci struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc; 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_ci qdisc_state->future_handle = handle; 118462306a36Sopenharmony_ci memset(qdisc_state->future_fifos, 0, sizeof(qdisc_state->future_fifos)); 118562306a36Sopenharmony_ci} 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_cistatic int __mlxsw_sp_setup_tc_fifo(struct mlxsw_sp_port *mlxsw_sp_port, 118862306a36Sopenharmony_ci struct tc_fifo_qopt_offload *p) 118962306a36Sopenharmony_ci{ 119062306a36Sopenharmony_ci struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc; 119162306a36Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc; 119262306a36Sopenharmony_ci unsigned int band; 119362306a36Sopenharmony_ci u32 parent_handle; 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_ci mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent); 119662306a36Sopenharmony_ci if (!mlxsw_sp_qdisc && p->handle == TC_H_UNSPEC) { 119762306a36Sopenharmony_ci parent_handle = TC_H_MAJ(p->parent); 119862306a36Sopenharmony_ci if (parent_handle != qdisc_state->future_handle) { 119962306a36Sopenharmony_ci /* This notifications is for a different Qdisc than 120062306a36Sopenharmony_ci * previously. Wipe the future cache. 120162306a36Sopenharmony_ci */ 120262306a36Sopenharmony_ci mlxsw_sp_qdisc_future_fifos_init(mlxsw_sp_port, 120362306a36Sopenharmony_ci parent_handle); 120462306a36Sopenharmony_ci } 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_ci band = TC_H_MIN(p->parent) - 1; 120762306a36Sopenharmony_ci if (band < IEEE_8021QAZ_MAX_TCS) { 120862306a36Sopenharmony_ci if (p->command == TC_FIFO_REPLACE) 120962306a36Sopenharmony_ci qdisc_state->future_fifos[band] = true; 121062306a36Sopenharmony_ci else if (p->command == TC_FIFO_DESTROY) 121162306a36Sopenharmony_ci qdisc_state->future_fifos[band] = false; 121262306a36Sopenharmony_ci } 121362306a36Sopenharmony_ci } 121462306a36Sopenharmony_ci if (!mlxsw_sp_qdisc) 121562306a36Sopenharmony_ci return -EOPNOTSUPP; 121662306a36Sopenharmony_ci 121762306a36Sopenharmony_ci if (p->command == TC_FIFO_REPLACE) { 121862306a36Sopenharmony_ci return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle, 121962306a36Sopenharmony_ci mlxsw_sp_qdisc, 122062306a36Sopenharmony_ci &mlxsw_sp_qdisc_ops_fifo, NULL); 122162306a36Sopenharmony_ci } 122262306a36Sopenharmony_ci 122362306a36Sopenharmony_ci if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle)) 122462306a36Sopenharmony_ci return -EOPNOTSUPP; 122562306a36Sopenharmony_ci 122662306a36Sopenharmony_ci switch (p->command) { 122762306a36Sopenharmony_ci case TC_FIFO_DESTROY: 122862306a36Sopenharmony_ci return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc); 122962306a36Sopenharmony_ci case TC_FIFO_STATS: 123062306a36Sopenharmony_ci return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc, 123162306a36Sopenharmony_ci &p->stats); 123262306a36Sopenharmony_ci case TC_FIFO_REPLACE: /* Handled above. */ 123362306a36Sopenharmony_ci break; 123462306a36Sopenharmony_ci } 123562306a36Sopenharmony_ci 123662306a36Sopenharmony_ci return -EOPNOTSUPP; 123762306a36Sopenharmony_ci} 123862306a36Sopenharmony_ci 123962306a36Sopenharmony_ciint mlxsw_sp_setup_tc_fifo(struct mlxsw_sp_port *mlxsw_sp_port, 124062306a36Sopenharmony_ci struct tc_fifo_qopt_offload *p) 124162306a36Sopenharmony_ci{ 124262306a36Sopenharmony_ci int err; 124362306a36Sopenharmony_ci 124462306a36Sopenharmony_ci mutex_lock(&mlxsw_sp_port->qdisc->lock); 124562306a36Sopenharmony_ci err = __mlxsw_sp_setup_tc_fifo(mlxsw_sp_port, p); 124662306a36Sopenharmony_ci mutex_unlock(&mlxsw_sp_port->qdisc->lock); 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_ci return err; 124962306a36Sopenharmony_ci} 125062306a36Sopenharmony_ci 125162306a36Sopenharmony_cistatic int __mlxsw_sp_qdisc_ets_destroy(struct mlxsw_sp_port *mlxsw_sp_port, 125262306a36Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 125362306a36Sopenharmony_ci{ 125462306a36Sopenharmony_ci int i; 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_ci for (i = 0; i < mlxsw_sp_qdisc->num_classes; i++) { 125762306a36Sopenharmony_ci mlxsw_sp_port_prio_tc_set(mlxsw_sp_port, i, 125862306a36Sopenharmony_ci MLXSW_SP_PORT_DEFAULT_TCLASS); 125962306a36Sopenharmony_ci mlxsw_sp_port_ets_set(mlxsw_sp_port, 126062306a36Sopenharmony_ci MLXSW_REG_QEEC_HR_SUBGROUP, 126162306a36Sopenharmony_ci i, 0, false, 0); 126262306a36Sopenharmony_ci } 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_ci kfree(mlxsw_sp_qdisc->ets_data); 126562306a36Sopenharmony_ci mlxsw_sp_qdisc->ets_data = NULL; 126662306a36Sopenharmony_ci return 0; 126762306a36Sopenharmony_ci} 126862306a36Sopenharmony_ci 126962306a36Sopenharmony_cistatic int 127062306a36Sopenharmony_cimlxsw_sp_qdisc_prio_destroy(struct mlxsw_sp_port *mlxsw_sp_port, 127162306a36Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 127262306a36Sopenharmony_ci{ 127362306a36Sopenharmony_ci return __mlxsw_sp_qdisc_ets_destroy(mlxsw_sp_port, mlxsw_sp_qdisc); 127462306a36Sopenharmony_ci} 127562306a36Sopenharmony_ci 127662306a36Sopenharmony_cistatic int 127762306a36Sopenharmony_ci__mlxsw_sp_qdisc_ets_check_params(unsigned int nbands) 127862306a36Sopenharmony_ci{ 127962306a36Sopenharmony_ci if (nbands > IEEE_8021QAZ_MAX_TCS) 128062306a36Sopenharmony_ci return -EOPNOTSUPP; 128162306a36Sopenharmony_ci 128262306a36Sopenharmony_ci return 0; 128362306a36Sopenharmony_ci} 128462306a36Sopenharmony_ci 128562306a36Sopenharmony_cistatic int 128662306a36Sopenharmony_cimlxsw_sp_qdisc_prio_check_params(struct mlxsw_sp_port *mlxsw_sp_port, 128762306a36Sopenharmony_ci void *params) 128862306a36Sopenharmony_ci{ 128962306a36Sopenharmony_ci struct tc_prio_qopt_offload_params *p = params; 129062306a36Sopenharmony_ci 129162306a36Sopenharmony_ci return __mlxsw_sp_qdisc_ets_check_params(p->bands); 129262306a36Sopenharmony_ci} 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_cistatic struct mlxsw_sp_qdisc * 129562306a36Sopenharmony_cimlxsw_sp_qdisc_walk_cb_clean_stats(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 129662306a36Sopenharmony_ci void *mlxsw_sp_port) 129762306a36Sopenharmony_ci{ 129862306a36Sopenharmony_ci u64 backlog; 129962306a36Sopenharmony_ci 130062306a36Sopenharmony_ci if (mlxsw_sp_qdisc->ops) { 130162306a36Sopenharmony_ci backlog = mlxsw_sp_qdisc->stats_base.backlog; 130262306a36Sopenharmony_ci if (mlxsw_sp_qdisc->ops->clean_stats) 130362306a36Sopenharmony_ci mlxsw_sp_qdisc->ops->clean_stats(mlxsw_sp_port, 130462306a36Sopenharmony_ci mlxsw_sp_qdisc); 130562306a36Sopenharmony_ci mlxsw_sp_qdisc->stats_base.backlog = backlog; 130662306a36Sopenharmony_ci } 130762306a36Sopenharmony_ci 130862306a36Sopenharmony_ci return NULL; 130962306a36Sopenharmony_ci} 131062306a36Sopenharmony_ci 131162306a36Sopenharmony_cistatic void 131262306a36Sopenharmony_cimlxsw_sp_qdisc_tree_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port, 131362306a36Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 131462306a36Sopenharmony_ci{ 131562306a36Sopenharmony_ci mlxsw_sp_qdisc_walk(mlxsw_sp_qdisc, mlxsw_sp_qdisc_walk_cb_clean_stats, 131662306a36Sopenharmony_ci mlxsw_sp_port); 131762306a36Sopenharmony_ci} 131862306a36Sopenharmony_ci 131962306a36Sopenharmony_cistatic int 132062306a36Sopenharmony_ci__mlxsw_sp_qdisc_ets_replace(struct mlxsw_sp_port *mlxsw_sp_port, 132162306a36Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 132262306a36Sopenharmony_ci u32 handle, unsigned int nbands, 132362306a36Sopenharmony_ci const unsigned int *quanta, 132462306a36Sopenharmony_ci const unsigned int *weights, 132562306a36Sopenharmony_ci const u8 *priomap) 132662306a36Sopenharmony_ci{ 132762306a36Sopenharmony_ci struct mlxsw_sp_qdisc_ets_data *ets_data = mlxsw_sp_qdisc->ets_data; 132862306a36Sopenharmony_ci struct mlxsw_sp_qdisc_ets_band *ets_band; 132962306a36Sopenharmony_ci struct mlxsw_sp_qdisc *child_qdisc; 133062306a36Sopenharmony_ci u8 old_priomap, new_priomap; 133162306a36Sopenharmony_ci int i, band; 133262306a36Sopenharmony_ci int err; 133362306a36Sopenharmony_ci 133462306a36Sopenharmony_ci if (!ets_data) { 133562306a36Sopenharmony_ci ets_data = kzalloc(sizeof(*ets_data), GFP_KERNEL); 133662306a36Sopenharmony_ci if (!ets_data) 133762306a36Sopenharmony_ci return -ENOMEM; 133862306a36Sopenharmony_ci mlxsw_sp_qdisc->ets_data = ets_data; 133962306a36Sopenharmony_ci 134062306a36Sopenharmony_ci for (band = 0; band < mlxsw_sp_qdisc->num_classes; band++) { 134162306a36Sopenharmony_ci int tclass_num = MLXSW_SP_PRIO_BAND_TO_TCLASS(band); 134262306a36Sopenharmony_ci 134362306a36Sopenharmony_ci ets_band = &ets_data->bands[band]; 134462306a36Sopenharmony_ci ets_band->tclass_num = tclass_num; 134562306a36Sopenharmony_ci } 134662306a36Sopenharmony_ci } 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_ci for (band = 0; band < nbands; band++) { 134962306a36Sopenharmony_ci int tclass_num; 135062306a36Sopenharmony_ci 135162306a36Sopenharmony_ci child_qdisc = &mlxsw_sp_qdisc->qdiscs[band]; 135262306a36Sopenharmony_ci ets_band = &ets_data->bands[band]; 135362306a36Sopenharmony_ci 135462306a36Sopenharmony_ci tclass_num = ets_band->tclass_num; 135562306a36Sopenharmony_ci old_priomap = ets_band->prio_bitmap; 135662306a36Sopenharmony_ci new_priomap = 0; 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_ci err = mlxsw_sp_port_ets_set(mlxsw_sp_port, 135962306a36Sopenharmony_ci MLXSW_REG_QEEC_HR_SUBGROUP, 136062306a36Sopenharmony_ci tclass_num, 0, !!quanta[band], 136162306a36Sopenharmony_ci weights[band]); 136262306a36Sopenharmony_ci if (err) 136362306a36Sopenharmony_ci return err; 136462306a36Sopenharmony_ci 136562306a36Sopenharmony_ci for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { 136662306a36Sopenharmony_ci if (priomap[i] == band) { 136762306a36Sopenharmony_ci new_priomap |= BIT(i); 136862306a36Sopenharmony_ci if (BIT(i) & old_priomap) 136962306a36Sopenharmony_ci continue; 137062306a36Sopenharmony_ci err = mlxsw_sp_port_prio_tc_set(mlxsw_sp_port, 137162306a36Sopenharmony_ci i, tclass_num); 137262306a36Sopenharmony_ci if (err) 137362306a36Sopenharmony_ci return err; 137462306a36Sopenharmony_ci } 137562306a36Sopenharmony_ci } 137662306a36Sopenharmony_ci 137762306a36Sopenharmony_ci ets_band->prio_bitmap = new_priomap; 137862306a36Sopenharmony_ci 137962306a36Sopenharmony_ci if (old_priomap != new_priomap) 138062306a36Sopenharmony_ci mlxsw_sp_qdisc_tree_clean_stats(mlxsw_sp_port, 138162306a36Sopenharmony_ci child_qdisc); 138262306a36Sopenharmony_ci 138362306a36Sopenharmony_ci err = mlxsw_sp_qdisc_future_fifo_replace(mlxsw_sp_port, handle, 138462306a36Sopenharmony_ci band, child_qdisc); 138562306a36Sopenharmony_ci if (err) 138662306a36Sopenharmony_ci return err; 138762306a36Sopenharmony_ci } 138862306a36Sopenharmony_ci for (; band < IEEE_8021QAZ_MAX_TCS; band++) { 138962306a36Sopenharmony_ci ets_band = &ets_data->bands[band]; 139062306a36Sopenharmony_ci ets_band->prio_bitmap = 0; 139162306a36Sopenharmony_ci 139262306a36Sopenharmony_ci child_qdisc = &mlxsw_sp_qdisc->qdiscs[band]; 139362306a36Sopenharmony_ci mlxsw_sp_qdisc_destroy(mlxsw_sp_port, child_qdisc); 139462306a36Sopenharmony_ci 139562306a36Sopenharmony_ci mlxsw_sp_port_ets_set(mlxsw_sp_port, 139662306a36Sopenharmony_ci MLXSW_REG_QEEC_HR_SUBGROUP, 139762306a36Sopenharmony_ci ets_band->tclass_num, 0, false, 0); 139862306a36Sopenharmony_ci } 139962306a36Sopenharmony_ci 140062306a36Sopenharmony_ci mlxsw_sp_qdisc_future_fifos_init(mlxsw_sp_port, TC_H_UNSPEC); 140162306a36Sopenharmony_ci return 0; 140262306a36Sopenharmony_ci} 140362306a36Sopenharmony_ci 140462306a36Sopenharmony_cistatic int 140562306a36Sopenharmony_cimlxsw_sp_qdisc_prio_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, 140662306a36Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 140762306a36Sopenharmony_ci void *params) 140862306a36Sopenharmony_ci{ 140962306a36Sopenharmony_ci struct tc_prio_qopt_offload_params *p = params; 141062306a36Sopenharmony_ci unsigned int zeroes[TCQ_ETS_MAX_BANDS] = {0}; 141162306a36Sopenharmony_ci 141262306a36Sopenharmony_ci return __mlxsw_sp_qdisc_ets_replace(mlxsw_sp_port, mlxsw_sp_qdisc, 141362306a36Sopenharmony_ci handle, p->bands, zeroes, 141462306a36Sopenharmony_ci zeroes, p->priomap); 141562306a36Sopenharmony_ci} 141662306a36Sopenharmony_ci 141762306a36Sopenharmony_cistatic void 141862306a36Sopenharmony_ci__mlxsw_sp_qdisc_ets_unoffload(struct mlxsw_sp_port *mlxsw_sp_port, 141962306a36Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 142062306a36Sopenharmony_ci struct gnet_stats_queue *qstats) 142162306a36Sopenharmony_ci{ 142262306a36Sopenharmony_ci u64 backlog; 142362306a36Sopenharmony_ci 142462306a36Sopenharmony_ci backlog = mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp, 142562306a36Sopenharmony_ci mlxsw_sp_qdisc->stats_base.backlog); 142662306a36Sopenharmony_ci qstats->backlog -= backlog; 142762306a36Sopenharmony_ci} 142862306a36Sopenharmony_ci 142962306a36Sopenharmony_cistatic void 143062306a36Sopenharmony_cimlxsw_sp_qdisc_prio_unoffload(struct mlxsw_sp_port *mlxsw_sp_port, 143162306a36Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 143262306a36Sopenharmony_ci void *params) 143362306a36Sopenharmony_ci{ 143462306a36Sopenharmony_ci struct tc_prio_qopt_offload_params *p = params; 143562306a36Sopenharmony_ci 143662306a36Sopenharmony_ci __mlxsw_sp_qdisc_ets_unoffload(mlxsw_sp_port, mlxsw_sp_qdisc, 143762306a36Sopenharmony_ci p->qstats); 143862306a36Sopenharmony_ci} 143962306a36Sopenharmony_ci 144062306a36Sopenharmony_cistatic int 144162306a36Sopenharmony_cimlxsw_sp_qdisc_get_prio_stats(struct mlxsw_sp_port *mlxsw_sp_port, 144262306a36Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 144362306a36Sopenharmony_ci struct tc_qopt_offload_stats *stats_ptr) 144462306a36Sopenharmony_ci{ 144562306a36Sopenharmony_ci struct mlxsw_sp_qdisc *tc_qdisc; 144662306a36Sopenharmony_ci u64 tx_packets = 0; 144762306a36Sopenharmony_ci u64 tx_bytes = 0; 144862306a36Sopenharmony_ci u64 backlog = 0; 144962306a36Sopenharmony_ci u64 drops = 0; 145062306a36Sopenharmony_ci int i; 145162306a36Sopenharmony_ci 145262306a36Sopenharmony_ci for (i = 0; i < mlxsw_sp_qdisc->num_classes; i++) { 145362306a36Sopenharmony_ci tc_qdisc = &mlxsw_sp_qdisc->qdiscs[i]; 145462306a36Sopenharmony_ci mlxsw_sp_qdisc_collect_tc_stats(mlxsw_sp_port, tc_qdisc, 145562306a36Sopenharmony_ci &tx_bytes, &tx_packets, 145662306a36Sopenharmony_ci &drops, &backlog); 145762306a36Sopenharmony_ci } 145862306a36Sopenharmony_ci 145962306a36Sopenharmony_ci mlxsw_sp_qdisc_update_stats(mlxsw_sp_port->mlxsw_sp, mlxsw_sp_qdisc, 146062306a36Sopenharmony_ci tx_bytes, tx_packets, drops, backlog, 146162306a36Sopenharmony_ci stats_ptr); 146262306a36Sopenharmony_ci return 0; 146362306a36Sopenharmony_ci} 146462306a36Sopenharmony_ci 146562306a36Sopenharmony_cistatic void 146662306a36Sopenharmony_cimlxsw_sp_setup_tc_qdisc_prio_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port, 146762306a36Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 146862306a36Sopenharmony_ci{ 146962306a36Sopenharmony_ci struct mlxsw_sp_qdisc_stats *stats_base; 147062306a36Sopenharmony_ci struct mlxsw_sp_port_xstats *xstats; 147162306a36Sopenharmony_ci struct rtnl_link_stats64 *stats; 147262306a36Sopenharmony_ci int i; 147362306a36Sopenharmony_ci 147462306a36Sopenharmony_ci xstats = &mlxsw_sp_port->periodic_hw_stats.xstats; 147562306a36Sopenharmony_ci stats = &mlxsw_sp_port->periodic_hw_stats.stats; 147662306a36Sopenharmony_ci stats_base = &mlxsw_sp_qdisc->stats_base; 147762306a36Sopenharmony_ci 147862306a36Sopenharmony_ci stats_base->tx_packets = stats->tx_packets; 147962306a36Sopenharmony_ci stats_base->tx_bytes = stats->tx_bytes; 148062306a36Sopenharmony_ci 148162306a36Sopenharmony_ci stats_base->drops = 0; 148262306a36Sopenharmony_ci for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { 148362306a36Sopenharmony_ci stats_base->drops += mlxsw_sp_xstats_tail_drop(xstats, i); 148462306a36Sopenharmony_ci stats_base->drops += xstats->wred_drop[i]; 148562306a36Sopenharmony_ci } 148662306a36Sopenharmony_ci 148762306a36Sopenharmony_ci mlxsw_sp_qdisc->stats_base.backlog = 0; 148862306a36Sopenharmony_ci} 148962306a36Sopenharmony_ci 149062306a36Sopenharmony_cistatic struct mlxsw_sp_qdisc * 149162306a36Sopenharmony_cimlxsw_sp_qdisc_prio_find_class(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 149262306a36Sopenharmony_ci u32 parent) 149362306a36Sopenharmony_ci{ 149462306a36Sopenharmony_ci int child_index = TC_H_MIN(parent); 149562306a36Sopenharmony_ci int band = child_index - 1; 149662306a36Sopenharmony_ci 149762306a36Sopenharmony_ci if (band < 0 || band >= mlxsw_sp_qdisc->num_classes) 149862306a36Sopenharmony_ci return NULL; 149962306a36Sopenharmony_ci return &mlxsw_sp_qdisc->qdiscs[band]; 150062306a36Sopenharmony_ci} 150162306a36Sopenharmony_ci 150262306a36Sopenharmony_cistatic struct mlxsw_sp_qdisc_ets_band * 150362306a36Sopenharmony_cimlxsw_sp_qdisc_ets_get_band(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 150462306a36Sopenharmony_ci struct mlxsw_sp_qdisc *child) 150562306a36Sopenharmony_ci{ 150662306a36Sopenharmony_ci unsigned int band = child - mlxsw_sp_qdisc->qdiscs; 150762306a36Sopenharmony_ci 150862306a36Sopenharmony_ci if (WARN_ON(band >= IEEE_8021QAZ_MAX_TCS)) 150962306a36Sopenharmony_ci band = 0; 151062306a36Sopenharmony_ci return &mlxsw_sp_qdisc->ets_data->bands[band]; 151162306a36Sopenharmony_ci} 151262306a36Sopenharmony_ci 151362306a36Sopenharmony_cistatic u8 151462306a36Sopenharmony_cimlxsw_sp_qdisc_ets_get_prio_bitmap(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 151562306a36Sopenharmony_ci struct mlxsw_sp_qdisc *child) 151662306a36Sopenharmony_ci{ 151762306a36Sopenharmony_ci return mlxsw_sp_qdisc_ets_get_band(mlxsw_sp_qdisc, child)->prio_bitmap; 151862306a36Sopenharmony_ci} 151962306a36Sopenharmony_ci 152062306a36Sopenharmony_cistatic int 152162306a36Sopenharmony_cimlxsw_sp_qdisc_ets_get_tclass_num(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 152262306a36Sopenharmony_ci struct mlxsw_sp_qdisc *child) 152362306a36Sopenharmony_ci{ 152462306a36Sopenharmony_ci return mlxsw_sp_qdisc_ets_get_band(mlxsw_sp_qdisc, child)->tclass_num; 152562306a36Sopenharmony_ci} 152662306a36Sopenharmony_ci 152762306a36Sopenharmony_cistatic struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_prio = { 152862306a36Sopenharmony_ci .type = MLXSW_SP_QDISC_PRIO, 152962306a36Sopenharmony_ci .check_params = mlxsw_sp_qdisc_prio_check_params, 153062306a36Sopenharmony_ci .replace = mlxsw_sp_qdisc_prio_replace, 153162306a36Sopenharmony_ci .unoffload = mlxsw_sp_qdisc_prio_unoffload, 153262306a36Sopenharmony_ci .destroy = mlxsw_sp_qdisc_prio_destroy, 153362306a36Sopenharmony_ci .get_stats = mlxsw_sp_qdisc_get_prio_stats, 153462306a36Sopenharmony_ci .clean_stats = mlxsw_sp_setup_tc_qdisc_prio_clean_stats, 153562306a36Sopenharmony_ci .find_class = mlxsw_sp_qdisc_prio_find_class, 153662306a36Sopenharmony_ci .num_classes = IEEE_8021QAZ_MAX_TCS, 153762306a36Sopenharmony_ci .get_prio_bitmap = mlxsw_sp_qdisc_ets_get_prio_bitmap, 153862306a36Sopenharmony_ci .get_tclass_num = mlxsw_sp_qdisc_ets_get_tclass_num, 153962306a36Sopenharmony_ci}; 154062306a36Sopenharmony_ci 154162306a36Sopenharmony_cistatic int 154262306a36Sopenharmony_cimlxsw_sp_qdisc_ets_check_params(struct mlxsw_sp_port *mlxsw_sp_port, 154362306a36Sopenharmony_ci void *params) 154462306a36Sopenharmony_ci{ 154562306a36Sopenharmony_ci struct tc_ets_qopt_offload_replace_params *p = params; 154662306a36Sopenharmony_ci 154762306a36Sopenharmony_ci return __mlxsw_sp_qdisc_ets_check_params(p->bands); 154862306a36Sopenharmony_ci} 154962306a36Sopenharmony_ci 155062306a36Sopenharmony_cistatic int 155162306a36Sopenharmony_cimlxsw_sp_qdisc_ets_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, 155262306a36Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 155362306a36Sopenharmony_ci void *params) 155462306a36Sopenharmony_ci{ 155562306a36Sopenharmony_ci struct tc_ets_qopt_offload_replace_params *p = params; 155662306a36Sopenharmony_ci 155762306a36Sopenharmony_ci return __mlxsw_sp_qdisc_ets_replace(mlxsw_sp_port, mlxsw_sp_qdisc, 155862306a36Sopenharmony_ci handle, p->bands, p->quanta, 155962306a36Sopenharmony_ci p->weights, p->priomap); 156062306a36Sopenharmony_ci} 156162306a36Sopenharmony_ci 156262306a36Sopenharmony_cistatic void 156362306a36Sopenharmony_cimlxsw_sp_qdisc_ets_unoffload(struct mlxsw_sp_port *mlxsw_sp_port, 156462306a36Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 156562306a36Sopenharmony_ci void *params) 156662306a36Sopenharmony_ci{ 156762306a36Sopenharmony_ci struct tc_ets_qopt_offload_replace_params *p = params; 156862306a36Sopenharmony_ci 156962306a36Sopenharmony_ci __mlxsw_sp_qdisc_ets_unoffload(mlxsw_sp_port, mlxsw_sp_qdisc, 157062306a36Sopenharmony_ci p->qstats); 157162306a36Sopenharmony_ci} 157262306a36Sopenharmony_ci 157362306a36Sopenharmony_cistatic int 157462306a36Sopenharmony_cimlxsw_sp_qdisc_ets_destroy(struct mlxsw_sp_port *mlxsw_sp_port, 157562306a36Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 157662306a36Sopenharmony_ci{ 157762306a36Sopenharmony_ci return __mlxsw_sp_qdisc_ets_destroy(mlxsw_sp_port, mlxsw_sp_qdisc); 157862306a36Sopenharmony_ci} 157962306a36Sopenharmony_ci 158062306a36Sopenharmony_cistatic struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_ets = { 158162306a36Sopenharmony_ci .type = MLXSW_SP_QDISC_ETS, 158262306a36Sopenharmony_ci .check_params = mlxsw_sp_qdisc_ets_check_params, 158362306a36Sopenharmony_ci .replace = mlxsw_sp_qdisc_ets_replace, 158462306a36Sopenharmony_ci .unoffload = mlxsw_sp_qdisc_ets_unoffload, 158562306a36Sopenharmony_ci .destroy = mlxsw_sp_qdisc_ets_destroy, 158662306a36Sopenharmony_ci .get_stats = mlxsw_sp_qdisc_get_prio_stats, 158762306a36Sopenharmony_ci .clean_stats = mlxsw_sp_setup_tc_qdisc_prio_clean_stats, 158862306a36Sopenharmony_ci .find_class = mlxsw_sp_qdisc_prio_find_class, 158962306a36Sopenharmony_ci .num_classes = IEEE_8021QAZ_MAX_TCS, 159062306a36Sopenharmony_ci .get_prio_bitmap = mlxsw_sp_qdisc_ets_get_prio_bitmap, 159162306a36Sopenharmony_ci .get_tclass_num = mlxsw_sp_qdisc_ets_get_tclass_num, 159262306a36Sopenharmony_ci}; 159362306a36Sopenharmony_ci 159462306a36Sopenharmony_ci/* Linux allows linking of Qdiscs to arbitrary classes (so long as the resulting 159562306a36Sopenharmony_ci * graph is free of cycles). These operations do not change the parent handle 159662306a36Sopenharmony_ci * though, which means it can be incomplete (if there is more than one class 159762306a36Sopenharmony_ci * where the Qdisc in question is grafted) or outright wrong (if the Qdisc was 159862306a36Sopenharmony_ci * linked to a different class and then removed from the original class). 159962306a36Sopenharmony_ci * 160062306a36Sopenharmony_ci * E.g. consider this sequence of operations: 160162306a36Sopenharmony_ci * 160262306a36Sopenharmony_ci * # tc qdisc add dev swp1 root handle 1: prio 160362306a36Sopenharmony_ci * # tc qdisc add dev swp1 parent 1:3 handle 13: red limit 1000000 avpkt 10000 160462306a36Sopenharmony_ci * RED: set bandwidth to 10Mbit 160562306a36Sopenharmony_ci * # tc qdisc link dev swp1 handle 13: parent 1:2 160662306a36Sopenharmony_ci * 160762306a36Sopenharmony_ci * At this point, both 1:2 and 1:3 have the same RED Qdisc instance as their 160862306a36Sopenharmony_ci * child. But RED will still only claim that 1:3 is its parent. If it's removed 160962306a36Sopenharmony_ci * from that band, its only parent will be 1:2, but it will continue to claim 161062306a36Sopenharmony_ci * that it is in fact 1:3. 161162306a36Sopenharmony_ci * 161262306a36Sopenharmony_ci * The notification for child Qdisc replace (e.g. TC_RED_REPLACE) comes before 161362306a36Sopenharmony_ci * the notification for parent graft (e.g. TC_PRIO_GRAFT). We take the replace 161462306a36Sopenharmony_ci * notification to offload the child Qdisc, based on its parent handle, and use 161562306a36Sopenharmony_ci * the graft operation to validate that the class where the child is actually 161662306a36Sopenharmony_ci * grafted corresponds to the parent handle. If the two don't match, we 161762306a36Sopenharmony_ci * unoffload the child. 161862306a36Sopenharmony_ci */ 161962306a36Sopenharmony_cistatic int mlxsw_sp_qdisc_graft(struct mlxsw_sp_port *mlxsw_sp_port, 162062306a36Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 162162306a36Sopenharmony_ci u8 band, u32 child_handle) 162262306a36Sopenharmony_ci{ 162362306a36Sopenharmony_ci struct mlxsw_sp_qdisc *old_qdisc; 162462306a36Sopenharmony_ci u32 parent; 162562306a36Sopenharmony_ci 162662306a36Sopenharmony_ci if (band < mlxsw_sp_qdisc->num_classes && 162762306a36Sopenharmony_ci mlxsw_sp_qdisc->qdiscs[band].handle == child_handle) 162862306a36Sopenharmony_ci return 0; 162962306a36Sopenharmony_ci 163062306a36Sopenharmony_ci if (!child_handle) { 163162306a36Sopenharmony_ci /* This is an invisible FIFO replacing the original Qdisc. 163262306a36Sopenharmony_ci * Ignore it--the original Qdisc's destroy will follow. 163362306a36Sopenharmony_ci */ 163462306a36Sopenharmony_ci return 0; 163562306a36Sopenharmony_ci } 163662306a36Sopenharmony_ci 163762306a36Sopenharmony_ci /* See if the grafted qdisc is already offloaded on any tclass. If so, 163862306a36Sopenharmony_ci * unoffload it. 163962306a36Sopenharmony_ci */ 164062306a36Sopenharmony_ci old_qdisc = mlxsw_sp_qdisc_find_by_handle(mlxsw_sp_port, 164162306a36Sopenharmony_ci child_handle); 164262306a36Sopenharmony_ci if (old_qdisc) 164362306a36Sopenharmony_ci mlxsw_sp_qdisc_destroy(mlxsw_sp_port, old_qdisc); 164462306a36Sopenharmony_ci 164562306a36Sopenharmony_ci parent = TC_H_MAKE(mlxsw_sp_qdisc->handle, band + 1); 164662306a36Sopenharmony_ci mlxsw_sp_qdisc = mlxsw_sp_qdisc->ops->find_class(mlxsw_sp_qdisc, 164762306a36Sopenharmony_ci parent); 164862306a36Sopenharmony_ci if (!WARN_ON(!mlxsw_sp_qdisc)) 164962306a36Sopenharmony_ci mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc); 165062306a36Sopenharmony_ci 165162306a36Sopenharmony_ci return -EOPNOTSUPP; 165262306a36Sopenharmony_ci} 165362306a36Sopenharmony_ci 165462306a36Sopenharmony_cistatic int __mlxsw_sp_setup_tc_prio(struct mlxsw_sp_port *mlxsw_sp_port, 165562306a36Sopenharmony_ci struct tc_prio_qopt_offload *p) 165662306a36Sopenharmony_ci{ 165762306a36Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc; 165862306a36Sopenharmony_ci 165962306a36Sopenharmony_ci mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent); 166062306a36Sopenharmony_ci if (!mlxsw_sp_qdisc) 166162306a36Sopenharmony_ci return -EOPNOTSUPP; 166262306a36Sopenharmony_ci 166362306a36Sopenharmony_ci if (p->command == TC_PRIO_REPLACE) 166462306a36Sopenharmony_ci return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle, 166562306a36Sopenharmony_ci mlxsw_sp_qdisc, 166662306a36Sopenharmony_ci &mlxsw_sp_qdisc_ops_prio, 166762306a36Sopenharmony_ci &p->replace_params); 166862306a36Sopenharmony_ci 166962306a36Sopenharmony_ci if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle)) 167062306a36Sopenharmony_ci return -EOPNOTSUPP; 167162306a36Sopenharmony_ci 167262306a36Sopenharmony_ci switch (p->command) { 167362306a36Sopenharmony_ci case TC_PRIO_DESTROY: 167462306a36Sopenharmony_ci return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc); 167562306a36Sopenharmony_ci case TC_PRIO_STATS: 167662306a36Sopenharmony_ci return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc, 167762306a36Sopenharmony_ci &p->stats); 167862306a36Sopenharmony_ci case TC_PRIO_GRAFT: 167962306a36Sopenharmony_ci return mlxsw_sp_qdisc_graft(mlxsw_sp_port, mlxsw_sp_qdisc, 168062306a36Sopenharmony_ci p->graft_params.band, 168162306a36Sopenharmony_ci p->graft_params.child_handle); 168262306a36Sopenharmony_ci default: 168362306a36Sopenharmony_ci return -EOPNOTSUPP; 168462306a36Sopenharmony_ci } 168562306a36Sopenharmony_ci} 168662306a36Sopenharmony_ci 168762306a36Sopenharmony_ciint mlxsw_sp_setup_tc_prio(struct mlxsw_sp_port *mlxsw_sp_port, 168862306a36Sopenharmony_ci struct tc_prio_qopt_offload *p) 168962306a36Sopenharmony_ci{ 169062306a36Sopenharmony_ci int err; 169162306a36Sopenharmony_ci 169262306a36Sopenharmony_ci mutex_lock(&mlxsw_sp_port->qdisc->lock); 169362306a36Sopenharmony_ci err = __mlxsw_sp_setup_tc_prio(mlxsw_sp_port, p); 169462306a36Sopenharmony_ci mutex_unlock(&mlxsw_sp_port->qdisc->lock); 169562306a36Sopenharmony_ci 169662306a36Sopenharmony_ci return err; 169762306a36Sopenharmony_ci} 169862306a36Sopenharmony_ci 169962306a36Sopenharmony_cistatic int __mlxsw_sp_setup_tc_ets(struct mlxsw_sp_port *mlxsw_sp_port, 170062306a36Sopenharmony_ci struct tc_ets_qopt_offload *p) 170162306a36Sopenharmony_ci{ 170262306a36Sopenharmony_ci struct mlxsw_sp_qdisc *mlxsw_sp_qdisc; 170362306a36Sopenharmony_ci 170462306a36Sopenharmony_ci mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent); 170562306a36Sopenharmony_ci if (!mlxsw_sp_qdisc) 170662306a36Sopenharmony_ci return -EOPNOTSUPP; 170762306a36Sopenharmony_ci 170862306a36Sopenharmony_ci if (p->command == TC_ETS_REPLACE) 170962306a36Sopenharmony_ci return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle, 171062306a36Sopenharmony_ci mlxsw_sp_qdisc, 171162306a36Sopenharmony_ci &mlxsw_sp_qdisc_ops_ets, 171262306a36Sopenharmony_ci &p->replace_params); 171362306a36Sopenharmony_ci 171462306a36Sopenharmony_ci if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle)) 171562306a36Sopenharmony_ci return -EOPNOTSUPP; 171662306a36Sopenharmony_ci 171762306a36Sopenharmony_ci switch (p->command) { 171862306a36Sopenharmony_ci case TC_ETS_DESTROY: 171962306a36Sopenharmony_ci return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc); 172062306a36Sopenharmony_ci case TC_ETS_STATS: 172162306a36Sopenharmony_ci return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc, 172262306a36Sopenharmony_ci &p->stats); 172362306a36Sopenharmony_ci case TC_ETS_GRAFT: 172462306a36Sopenharmony_ci return mlxsw_sp_qdisc_graft(mlxsw_sp_port, mlxsw_sp_qdisc, 172562306a36Sopenharmony_ci p->graft_params.band, 172662306a36Sopenharmony_ci p->graft_params.child_handle); 172762306a36Sopenharmony_ci default: 172862306a36Sopenharmony_ci return -EOPNOTSUPP; 172962306a36Sopenharmony_ci } 173062306a36Sopenharmony_ci} 173162306a36Sopenharmony_ci 173262306a36Sopenharmony_ciint mlxsw_sp_setup_tc_ets(struct mlxsw_sp_port *mlxsw_sp_port, 173362306a36Sopenharmony_ci struct tc_ets_qopt_offload *p) 173462306a36Sopenharmony_ci{ 173562306a36Sopenharmony_ci int err; 173662306a36Sopenharmony_ci 173762306a36Sopenharmony_ci mutex_lock(&mlxsw_sp_port->qdisc->lock); 173862306a36Sopenharmony_ci err = __mlxsw_sp_setup_tc_ets(mlxsw_sp_port, p); 173962306a36Sopenharmony_ci mutex_unlock(&mlxsw_sp_port->qdisc->lock); 174062306a36Sopenharmony_ci 174162306a36Sopenharmony_ci return err; 174262306a36Sopenharmony_ci} 174362306a36Sopenharmony_ci 174462306a36Sopenharmony_cistruct mlxsw_sp_qevent_block { 174562306a36Sopenharmony_ci struct list_head binding_list; 174662306a36Sopenharmony_ci struct list_head mall_entry_list; 174762306a36Sopenharmony_ci struct mlxsw_sp *mlxsw_sp; 174862306a36Sopenharmony_ci}; 174962306a36Sopenharmony_ci 175062306a36Sopenharmony_cistruct mlxsw_sp_qevent_binding { 175162306a36Sopenharmony_ci struct list_head list; 175262306a36Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port; 175362306a36Sopenharmony_ci u32 handle; 175462306a36Sopenharmony_ci int tclass_num; 175562306a36Sopenharmony_ci enum mlxsw_sp_span_trigger span_trigger; 175662306a36Sopenharmony_ci unsigned int action_mask; 175762306a36Sopenharmony_ci}; 175862306a36Sopenharmony_ci 175962306a36Sopenharmony_cistatic LIST_HEAD(mlxsw_sp_qevent_block_cb_list); 176062306a36Sopenharmony_ci 176162306a36Sopenharmony_cistatic int mlxsw_sp_qevent_span_configure(struct mlxsw_sp *mlxsw_sp, 176262306a36Sopenharmony_ci struct mlxsw_sp_mall_entry *mall_entry, 176362306a36Sopenharmony_ci struct mlxsw_sp_qevent_binding *qevent_binding, 176462306a36Sopenharmony_ci const struct mlxsw_sp_span_agent_parms *agent_parms, 176562306a36Sopenharmony_ci int *p_span_id) 176662306a36Sopenharmony_ci{ 176762306a36Sopenharmony_ci enum mlxsw_sp_span_trigger span_trigger = qevent_binding->span_trigger; 176862306a36Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port = qevent_binding->mlxsw_sp_port; 176962306a36Sopenharmony_ci struct mlxsw_sp_span_trigger_parms trigger_parms = {}; 177062306a36Sopenharmony_ci bool ingress; 177162306a36Sopenharmony_ci int span_id; 177262306a36Sopenharmony_ci int err; 177362306a36Sopenharmony_ci 177462306a36Sopenharmony_ci err = mlxsw_sp_span_agent_get(mlxsw_sp, &span_id, agent_parms); 177562306a36Sopenharmony_ci if (err) 177662306a36Sopenharmony_ci return err; 177762306a36Sopenharmony_ci 177862306a36Sopenharmony_ci ingress = mlxsw_sp_span_trigger_is_ingress(span_trigger); 177962306a36Sopenharmony_ci err = mlxsw_sp_span_analyzed_port_get(mlxsw_sp_port, ingress); 178062306a36Sopenharmony_ci if (err) 178162306a36Sopenharmony_ci goto err_analyzed_port_get; 178262306a36Sopenharmony_ci 178362306a36Sopenharmony_ci trigger_parms.span_id = span_id; 178462306a36Sopenharmony_ci trigger_parms.probability_rate = 1; 178562306a36Sopenharmony_ci err = mlxsw_sp_span_agent_bind(mlxsw_sp, span_trigger, mlxsw_sp_port, 178662306a36Sopenharmony_ci &trigger_parms); 178762306a36Sopenharmony_ci if (err) 178862306a36Sopenharmony_ci goto err_agent_bind; 178962306a36Sopenharmony_ci 179062306a36Sopenharmony_ci err = mlxsw_sp_span_trigger_enable(mlxsw_sp_port, span_trigger, 179162306a36Sopenharmony_ci qevent_binding->tclass_num); 179262306a36Sopenharmony_ci if (err) 179362306a36Sopenharmony_ci goto err_trigger_enable; 179462306a36Sopenharmony_ci 179562306a36Sopenharmony_ci *p_span_id = span_id; 179662306a36Sopenharmony_ci return 0; 179762306a36Sopenharmony_ci 179862306a36Sopenharmony_cierr_trigger_enable: 179962306a36Sopenharmony_ci mlxsw_sp_span_agent_unbind(mlxsw_sp, span_trigger, mlxsw_sp_port, 180062306a36Sopenharmony_ci &trigger_parms); 180162306a36Sopenharmony_cierr_agent_bind: 180262306a36Sopenharmony_ci mlxsw_sp_span_analyzed_port_put(mlxsw_sp_port, ingress); 180362306a36Sopenharmony_cierr_analyzed_port_get: 180462306a36Sopenharmony_ci mlxsw_sp_span_agent_put(mlxsw_sp, span_id); 180562306a36Sopenharmony_ci return err; 180662306a36Sopenharmony_ci} 180762306a36Sopenharmony_ci 180862306a36Sopenharmony_cistatic void mlxsw_sp_qevent_span_deconfigure(struct mlxsw_sp *mlxsw_sp, 180962306a36Sopenharmony_ci struct mlxsw_sp_qevent_binding *qevent_binding, 181062306a36Sopenharmony_ci int span_id) 181162306a36Sopenharmony_ci{ 181262306a36Sopenharmony_ci enum mlxsw_sp_span_trigger span_trigger = qevent_binding->span_trigger; 181362306a36Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port = qevent_binding->mlxsw_sp_port; 181462306a36Sopenharmony_ci struct mlxsw_sp_span_trigger_parms trigger_parms = { 181562306a36Sopenharmony_ci .span_id = span_id, 181662306a36Sopenharmony_ci }; 181762306a36Sopenharmony_ci bool ingress; 181862306a36Sopenharmony_ci 181962306a36Sopenharmony_ci ingress = mlxsw_sp_span_trigger_is_ingress(span_trigger); 182062306a36Sopenharmony_ci 182162306a36Sopenharmony_ci mlxsw_sp_span_trigger_disable(mlxsw_sp_port, span_trigger, 182262306a36Sopenharmony_ci qevent_binding->tclass_num); 182362306a36Sopenharmony_ci mlxsw_sp_span_agent_unbind(mlxsw_sp, span_trigger, mlxsw_sp_port, 182462306a36Sopenharmony_ci &trigger_parms); 182562306a36Sopenharmony_ci mlxsw_sp_span_analyzed_port_put(mlxsw_sp_port, ingress); 182662306a36Sopenharmony_ci mlxsw_sp_span_agent_put(mlxsw_sp, span_id); 182762306a36Sopenharmony_ci} 182862306a36Sopenharmony_ci 182962306a36Sopenharmony_cistatic int mlxsw_sp_qevent_mirror_configure(struct mlxsw_sp *mlxsw_sp, 183062306a36Sopenharmony_ci struct mlxsw_sp_mall_entry *mall_entry, 183162306a36Sopenharmony_ci struct mlxsw_sp_qevent_binding *qevent_binding) 183262306a36Sopenharmony_ci{ 183362306a36Sopenharmony_ci struct mlxsw_sp_span_agent_parms agent_parms = { 183462306a36Sopenharmony_ci .to_dev = mall_entry->mirror.to_dev, 183562306a36Sopenharmony_ci }; 183662306a36Sopenharmony_ci 183762306a36Sopenharmony_ci return mlxsw_sp_qevent_span_configure(mlxsw_sp, mall_entry, qevent_binding, 183862306a36Sopenharmony_ci &agent_parms, &mall_entry->mirror.span_id); 183962306a36Sopenharmony_ci} 184062306a36Sopenharmony_ci 184162306a36Sopenharmony_cistatic void mlxsw_sp_qevent_mirror_deconfigure(struct mlxsw_sp *mlxsw_sp, 184262306a36Sopenharmony_ci struct mlxsw_sp_mall_entry *mall_entry, 184362306a36Sopenharmony_ci struct mlxsw_sp_qevent_binding *qevent_binding) 184462306a36Sopenharmony_ci{ 184562306a36Sopenharmony_ci mlxsw_sp_qevent_span_deconfigure(mlxsw_sp, qevent_binding, mall_entry->mirror.span_id); 184662306a36Sopenharmony_ci} 184762306a36Sopenharmony_ci 184862306a36Sopenharmony_cistatic int mlxsw_sp_qevent_trap_configure(struct mlxsw_sp *mlxsw_sp, 184962306a36Sopenharmony_ci struct mlxsw_sp_mall_entry *mall_entry, 185062306a36Sopenharmony_ci struct mlxsw_sp_qevent_binding *qevent_binding) 185162306a36Sopenharmony_ci{ 185262306a36Sopenharmony_ci struct mlxsw_sp_span_agent_parms agent_parms = { 185362306a36Sopenharmony_ci .session_id = MLXSW_SP_SPAN_SESSION_ID_BUFFER, 185462306a36Sopenharmony_ci }; 185562306a36Sopenharmony_ci int err; 185662306a36Sopenharmony_ci 185762306a36Sopenharmony_ci err = mlxsw_sp_trap_group_policer_hw_id_get(mlxsw_sp, 185862306a36Sopenharmony_ci DEVLINK_TRAP_GROUP_GENERIC_ID_BUFFER_DROPS, 185962306a36Sopenharmony_ci &agent_parms.policer_enable, 186062306a36Sopenharmony_ci &agent_parms.policer_id); 186162306a36Sopenharmony_ci if (err) 186262306a36Sopenharmony_ci return err; 186362306a36Sopenharmony_ci 186462306a36Sopenharmony_ci return mlxsw_sp_qevent_span_configure(mlxsw_sp, mall_entry, qevent_binding, 186562306a36Sopenharmony_ci &agent_parms, &mall_entry->trap.span_id); 186662306a36Sopenharmony_ci} 186762306a36Sopenharmony_ci 186862306a36Sopenharmony_cistatic void mlxsw_sp_qevent_trap_deconfigure(struct mlxsw_sp *mlxsw_sp, 186962306a36Sopenharmony_ci struct mlxsw_sp_mall_entry *mall_entry, 187062306a36Sopenharmony_ci struct mlxsw_sp_qevent_binding *qevent_binding) 187162306a36Sopenharmony_ci{ 187262306a36Sopenharmony_ci mlxsw_sp_qevent_span_deconfigure(mlxsw_sp, qevent_binding, mall_entry->trap.span_id); 187362306a36Sopenharmony_ci} 187462306a36Sopenharmony_ci 187562306a36Sopenharmony_cistatic int 187662306a36Sopenharmony_cimlxsw_sp_qevent_entry_configure(struct mlxsw_sp *mlxsw_sp, 187762306a36Sopenharmony_ci struct mlxsw_sp_mall_entry *mall_entry, 187862306a36Sopenharmony_ci struct mlxsw_sp_qevent_binding *qevent_binding, 187962306a36Sopenharmony_ci struct netlink_ext_ack *extack) 188062306a36Sopenharmony_ci{ 188162306a36Sopenharmony_ci if (!(BIT(mall_entry->type) & qevent_binding->action_mask)) { 188262306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Action not supported at this qevent"); 188362306a36Sopenharmony_ci return -EOPNOTSUPP; 188462306a36Sopenharmony_ci } 188562306a36Sopenharmony_ci 188662306a36Sopenharmony_ci switch (mall_entry->type) { 188762306a36Sopenharmony_ci case MLXSW_SP_MALL_ACTION_TYPE_MIRROR: 188862306a36Sopenharmony_ci return mlxsw_sp_qevent_mirror_configure(mlxsw_sp, mall_entry, qevent_binding); 188962306a36Sopenharmony_ci case MLXSW_SP_MALL_ACTION_TYPE_TRAP: 189062306a36Sopenharmony_ci return mlxsw_sp_qevent_trap_configure(mlxsw_sp, mall_entry, qevent_binding); 189162306a36Sopenharmony_ci default: 189262306a36Sopenharmony_ci /* This should have been validated away. */ 189362306a36Sopenharmony_ci WARN_ON(1); 189462306a36Sopenharmony_ci return -EOPNOTSUPP; 189562306a36Sopenharmony_ci } 189662306a36Sopenharmony_ci} 189762306a36Sopenharmony_ci 189862306a36Sopenharmony_cistatic void mlxsw_sp_qevent_entry_deconfigure(struct mlxsw_sp *mlxsw_sp, 189962306a36Sopenharmony_ci struct mlxsw_sp_mall_entry *mall_entry, 190062306a36Sopenharmony_ci struct mlxsw_sp_qevent_binding *qevent_binding) 190162306a36Sopenharmony_ci{ 190262306a36Sopenharmony_ci switch (mall_entry->type) { 190362306a36Sopenharmony_ci case MLXSW_SP_MALL_ACTION_TYPE_MIRROR: 190462306a36Sopenharmony_ci return mlxsw_sp_qevent_mirror_deconfigure(mlxsw_sp, mall_entry, qevent_binding); 190562306a36Sopenharmony_ci case MLXSW_SP_MALL_ACTION_TYPE_TRAP: 190662306a36Sopenharmony_ci return mlxsw_sp_qevent_trap_deconfigure(mlxsw_sp, mall_entry, qevent_binding); 190762306a36Sopenharmony_ci default: 190862306a36Sopenharmony_ci WARN_ON(1); 190962306a36Sopenharmony_ci return; 191062306a36Sopenharmony_ci } 191162306a36Sopenharmony_ci} 191262306a36Sopenharmony_ci 191362306a36Sopenharmony_cistatic int 191462306a36Sopenharmony_cimlxsw_sp_qevent_binding_configure(struct mlxsw_sp_qevent_block *qevent_block, 191562306a36Sopenharmony_ci struct mlxsw_sp_qevent_binding *qevent_binding, 191662306a36Sopenharmony_ci struct netlink_ext_ack *extack) 191762306a36Sopenharmony_ci{ 191862306a36Sopenharmony_ci struct mlxsw_sp_mall_entry *mall_entry; 191962306a36Sopenharmony_ci int err; 192062306a36Sopenharmony_ci 192162306a36Sopenharmony_ci list_for_each_entry(mall_entry, &qevent_block->mall_entry_list, list) { 192262306a36Sopenharmony_ci err = mlxsw_sp_qevent_entry_configure(qevent_block->mlxsw_sp, mall_entry, 192362306a36Sopenharmony_ci qevent_binding, extack); 192462306a36Sopenharmony_ci if (err) 192562306a36Sopenharmony_ci goto err_entry_configure; 192662306a36Sopenharmony_ci } 192762306a36Sopenharmony_ci 192862306a36Sopenharmony_ci return 0; 192962306a36Sopenharmony_ci 193062306a36Sopenharmony_cierr_entry_configure: 193162306a36Sopenharmony_ci list_for_each_entry_continue_reverse(mall_entry, &qevent_block->mall_entry_list, list) 193262306a36Sopenharmony_ci mlxsw_sp_qevent_entry_deconfigure(qevent_block->mlxsw_sp, mall_entry, 193362306a36Sopenharmony_ci qevent_binding); 193462306a36Sopenharmony_ci return err; 193562306a36Sopenharmony_ci} 193662306a36Sopenharmony_ci 193762306a36Sopenharmony_cistatic void mlxsw_sp_qevent_binding_deconfigure(struct mlxsw_sp_qevent_block *qevent_block, 193862306a36Sopenharmony_ci struct mlxsw_sp_qevent_binding *qevent_binding) 193962306a36Sopenharmony_ci{ 194062306a36Sopenharmony_ci struct mlxsw_sp_mall_entry *mall_entry; 194162306a36Sopenharmony_ci 194262306a36Sopenharmony_ci list_for_each_entry(mall_entry, &qevent_block->mall_entry_list, list) 194362306a36Sopenharmony_ci mlxsw_sp_qevent_entry_deconfigure(qevent_block->mlxsw_sp, mall_entry, 194462306a36Sopenharmony_ci qevent_binding); 194562306a36Sopenharmony_ci} 194662306a36Sopenharmony_ci 194762306a36Sopenharmony_cistatic int 194862306a36Sopenharmony_cimlxsw_sp_qevent_block_configure(struct mlxsw_sp_qevent_block *qevent_block, 194962306a36Sopenharmony_ci struct netlink_ext_ack *extack) 195062306a36Sopenharmony_ci{ 195162306a36Sopenharmony_ci struct mlxsw_sp_qevent_binding *qevent_binding; 195262306a36Sopenharmony_ci int err; 195362306a36Sopenharmony_ci 195462306a36Sopenharmony_ci list_for_each_entry(qevent_binding, &qevent_block->binding_list, list) { 195562306a36Sopenharmony_ci err = mlxsw_sp_qevent_binding_configure(qevent_block, 195662306a36Sopenharmony_ci qevent_binding, 195762306a36Sopenharmony_ci extack); 195862306a36Sopenharmony_ci if (err) 195962306a36Sopenharmony_ci goto err_binding_configure; 196062306a36Sopenharmony_ci } 196162306a36Sopenharmony_ci 196262306a36Sopenharmony_ci return 0; 196362306a36Sopenharmony_ci 196462306a36Sopenharmony_cierr_binding_configure: 196562306a36Sopenharmony_ci list_for_each_entry_continue_reverse(qevent_binding, &qevent_block->binding_list, list) 196662306a36Sopenharmony_ci mlxsw_sp_qevent_binding_deconfigure(qevent_block, qevent_binding); 196762306a36Sopenharmony_ci return err; 196862306a36Sopenharmony_ci} 196962306a36Sopenharmony_ci 197062306a36Sopenharmony_cistatic void mlxsw_sp_qevent_block_deconfigure(struct mlxsw_sp_qevent_block *qevent_block) 197162306a36Sopenharmony_ci{ 197262306a36Sopenharmony_ci struct mlxsw_sp_qevent_binding *qevent_binding; 197362306a36Sopenharmony_ci 197462306a36Sopenharmony_ci list_for_each_entry(qevent_binding, &qevent_block->binding_list, list) 197562306a36Sopenharmony_ci mlxsw_sp_qevent_binding_deconfigure(qevent_block, qevent_binding); 197662306a36Sopenharmony_ci} 197762306a36Sopenharmony_ci 197862306a36Sopenharmony_cistatic struct mlxsw_sp_mall_entry * 197962306a36Sopenharmony_cimlxsw_sp_qevent_mall_entry_find(struct mlxsw_sp_qevent_block *block, unsigned long cookie) 198062306a36Sopenharmony_ci{ 198162306a36Sopenharmony_ci struct mlxsw_sp_mall_entry *mall_entry; 198262306a36Sopenharmony_ci 198362306a36Sopenharmony_ci list_for_each_entry(mall_entry, &block->mall_entry_list, list) 198462306a36Sopenharmony_ci if (mall_entry->cookie == cookie) 198562306a36Sopenharmony_ci return mall_entry; 198662306a36Sopenharmony_ci 198762306a36Sopenharmony_ci return NULL; 198862306a36Sopenharmony_ci} 198962306a36Sopenharmony_ci 199062306a36Sopenharmony_cistatic int mlxsw_sp_qevent_mall_replace(struct mlxsw_sp *mlxsw_sp, 199162306a36Sopenharmony_ci struct mlxsw_sp_qevent_block *qevent_block, 199262306a36Sopenharmony_ci struct tc_cls_matchall_offload *f) 199362306a36Sopenharmony_ci{ 199462306a36Sopenharmony_ci struct mlxsw_sp_mall_entry *mall_entry; 199562306a36Sopenharmony_ci struct flow_action_entry *act; 199662306a36Sopenharmony_ci int err; 199762306a36Sopenharmony_ci 199862306a36Sopenharmony_ci /* It should not currently be possible to replace a matchall rule. So 199962306a36Sopenharmony_ci * this must be a new rule. 200062306a36Sopenharmony_ci */ 200162306a36Sopenharmony_ci if (!list_empty(&qevent_block->mall_entry_list)) { 200262306a36Sopenharmony_ci NL_SET_ERR_MSG(f->common.extack, "At most one filter supported"); 200362306a36Sopenharmony_ci return -EOPNOTSUPP; 200462306a36Sopenharmony_ci } 200562306a36Sopenharmony_ci if (f->rule->action.num_entries != 1) { 200662306a36Sopenharmony_ci NL_SET_ERR_MSG(f->common.extack, "Only singular actions supported"); 200762306a36Sopenharmony_ci return -EOPNOTSUPP; 200862306a36Sopenharmony_ci } 200962306a36Sopenharmony_ci if (f->common.chain_index) { 201062306a36Sopenharmony_ci NL_SET_ERR_MSG(f->common.extack, "Only chain 0 is supported"); 201162306a36Sopenharmony_ci return -EOPNOTSUPP; 201262306a36Sopenharmony_ci } 201362306a36Sopenharmony_ci if (f->common.protocol != htons(ETH_P_ALL)) { 201462306a36Sopenharmony_ci NL_SET_ERR_MSG(f->common.extack, "Protocol matching not supported"); 201562306a36Sopenharmony_ci return -EOPNOTSUPP; 201662306a36Sopenharmony_ci } 201762306a36Sopenharmony_ci 201862306a36Sopenharmony_ci act = &f->rule->action.entries[0]; 201962306a36Sopenharmony_ci if (!(act->hw_stats & FLOW_ACTION_HW_STATS_DISABLED)) { 202062306a36Sopenharmony_ci NL_SET_ERR_MSG(f->common.extack, "HW counters not supported on qevents"); 202162306a36Sopenharmony_ci return -EOPNOTSUPP; 202262306a36Sopenharmony_ci } 202362306a36Sopenharmony_ci 202462306a36Sopenharmony_ci mall_entry = kzalloc(sizeof(*mall_entry), GFP_KERNEL); 202562306a36Sopenharmony_ci if (!mall_entry) 202662306a36Sopenharmony_ci return -ENOMEM; 202762306a36Sopenharmony_ci mall_entry->cookie = f->cookie; 202862306a36Sopenharmony_ci 202962306a36Sopenharmony_ci if (act->id == FLOW_ACTION_MIRRED) { 203062306a36Sopenharmony_ci mall_entry->type = MLXSW_SP_MALL_ACTION_TYPE_MIRROR; 203162306a36Sopenharmony_ci mall_entry->mirror.to_dev = act->dev; 203262306a36Sopenharmony_ci } else if (act->id == FLOW_ACTION_TRAP) { 203362306a36Sopenharmony_ci mall_entry->type = MLXSW_SP_MALL_ACTION_TYPE_TRAP; 203462306a36Sopenharmony_ci } else { 203562306a36Sopenharmony_ci NL_SET_ERR_MSG(f->common.extack, "Unsupported action"); 203662306a36Sopenharmony_ci err = -EOPNOTSUPP; 203762306a36Sopenharmony_ci goto err_unsupported_action; 203862306a36Sopenharmony_ci } 203962306a36Sopenharmony_ci 204062306a36Sopenharmony_ci list_add_tail(&mall_entry->list, &qevent_block->mall_entry_list); 204162306a36Sopenharmony_ci 204262306a36Sopenharmony_ci err = mlxsw_sp_qevent_block_configure(qevent_block, f->common.extack); 204362306a36Sopenharmony_ci if (err) 204462306a36Sopenharmony_ci goto err_block_configure; 204562306a36Sopenharmony_ci 204662306a36Sopenharmony_ci return 0; 204762306a36Sopenharmony_ci 204862306a36Sopenharmony_cierr_block_configure: 204962306a36Sopenharmony_ci list_del(&mall_entry->list); 205062306a36Sopenharmony_cierr_unsupported_action: 205162306a36Sopenharmony_ci kfree(mall_entry); 205262306a36Sopenharmony_ci return err; 205362306a36Sopenharmony_ci} 205462306a36Sopenharmony_ci 205562306a36Sopenharmony_cistatic void mlxsw_sp_qevent_mall_destroy(struct mlxsw_sp_qevent_block *qevent_block, 205662306a36Sopenharmony_ci struct tc_cls_matchall_offload *f) 205762306a36Sopenharmony_ci{ 205862306a36Sopenharmony_ci struct mlxsw_sp_mall_entry *mall_entry; 205962306a36Sopenharmony_ci 206062306a36Sopenharmony_ci mall_entry = mlxsw_sp_qevent_mall_entry_find(qevent_block, f->cookie); 206162306a36Sopenharmony_ci if (!mall_entry) 206262306a36Sopenharmony_ci return; 206362306a36Sopenharmony_ci 206462306a36Sopenharmony_ci mlxsw_sp_qevent_block_deconfigure(qevent_block); 206562306a36Sopenharmony_ci 206662306a36Sopenharmony_ci list_del(&mall_entry->list); 206762306a36Sopenharmony_ci kfree(mall_entry); 206862306a36Sopenharmony_ci} 206962306a36Sopenharmony_ci 207062306a36Sopenharmony_cistatic int mlxsw_sp_qevent_block_mall_cb(struct mlxsw_sp_qevent_block *qevent_block, 207162306a36Sopenharmony_ci struct tc_cls_matchall_offload *f) 207262306a36Sopenharmony_ci{ 207362306a36Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = qevent_block->mlxsw_sp; 207462306a36Sopenharmony_ci 207562306a36Sopenharmony_ci switch (f->command) { 207662306a36Sopenharmony_ci case TC_CLSMATCHALL_REPLACE: 207762306a36Sopenharmony_ci return mlxsw_sp_qevent_mall_replace(mlxsw_sp, qevent_block, f); 207862306a36Sopenharmony_ci case TC_CLSMATCHALL_DESTROY: 207962306a36Sopenharmony_ci mlxsw_sp_qevent_mall_destroy(qevent_block, f); 208062306a36Sopenharmony_ci return 0; 208162306a36Sopenharmony_ci default: 208262306a36Sopenharmony_ci return -EOPNOTSUPP; 208362306a36Sopenharmony_ci } 208462306a36Sopenharmony_ci} 208562306a36Sopenharmony_ci 208662306a36Sopenharmony_cistatic int mlxsw_sp_qevent_block_cb(enum tc_setup_type type, void *type_data, void *cb_priv) 208762306a36Sopenharmony_ci{ 208862306a36Sopenharmony_ci struct mlxsw_sp_qevent_block *qevent_block = cb_priv; 208962306a36Sopenharmony_ci 209062306a36Sopenharmony_ci switch (type) { 209162306a36Sopenharmony_ci case TC_SETUP_CLSMATCHALL: 209262306a36Sopenharmony_ci return mlxsw_sp_qevent_block_mall_cb(qevent_block, type_data); 209362306a36Sopenharmony_ci default: 209462306a36Sopenharmony_ci return -EOPNOTSUPP; 209562306a36Sopenharmony_ci } 209662306a36Sopenharmony_ci} 209762306a36Sopenharmony_ci 209862306a36Sopenharmony_cistatic struct mlxsw_sp_qevent_block *mlxsw_sp_qevent_block_create(struct mlxsw_sp *mlxsw_sp, 209962306a36Sopenharmony_ci struct net *net) 210062306a36Sopenharmony_ci{ 210162306a36Sopenharmony_ci struct mlxsw_sp_qevent_block *qevent_block; 210262306a36Sopenharmony_ci 210362306a36Sopenharmony_ci qevent_block = kzalloc(sizeof(*qevent_block), GFP_KERNEL); 210462306a36Sopenharmony_ci if (!qevent_block) 210562306a36Sopenharmony_ci return NULL; 210662306a36Sopenharmony_ci 210762306a36Sopenharmony_ci INIT_LIST_HEAD(&qevent_block->binding_list); 210862306a36Sopenharmony_ci INIT_LIST_HEAD(&qevent_block->mall_entry_list); 210962306a36Sopenharmony_ci qevent_block->mlxsw_sp = mlxsw_sp; 211062306a36Sopenharmony_ci return qevent_block; 211162306a36Sopenharmony_ci} 211262306a36Sopenharmony_ci 211362306a36Sopenharmony_cistatic void 211462306a36Sopenharmony_cimlxsw_sp_qevent_block_destroy(struct mlxsw_sp_qevent_block *qevent_block) 211562306a36Sopenharmony_ci{ 211662306a36Sopenharmony_ci WARN_ON(!list_empty(&qevent_block->binding_list)); 211762306a36Sopenharmony_ci WARN_ON(!list_empty(&qevent_block->mall_entry_list)); 211862306a36Sopenharmony_ci kfree(qevent_block); 211962306a36Sopenharmony_ci} 212062306a36Sopenharmony_ci 212162306a36Sopenharmony_cistatic void mlxsw_sp_qevent_block_release(void *cb_priv) 212262306a36Sopenharmony_ci{ 212362306a36Sopenharmony_ci struct mlxsw_sp_qevent_block *qevent_block = cb_priv; 212462306a36Sopenharmony_ci 212562306a36Sopenharmony_ci mlxsw_sp_qevent_block_destroy(qevent_block); 212662306a36Sopenharmony_ci} 212762306a36Sopenharmony_ci 212862306a36Sopenharmony_cistatic struct mlxsw_sp_qevent_binding * 212962306a36Sopenharmony_cimlxsw_sp_qevent_binding_create(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, int tclass_num, 213062306a36Sopenharmony_ci enum mlxsw_sp_span_trigger span_trigger, 213162306a36Sopenharmony_ci unsigned int action_mask) 213262306a36Sopenharmony_ci{ 213362306a36Sopenharmony_ci struct mlxsw_sp_qevent_binding *binding; 213462306a36Sopenharmony_ci 213562306a36Sopenharmony_ci binding = kzalloc(sizeof(*binding), GFP_KERNEL); 213662306a36Sopenharmony_ci if (!binding) 213762306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 213862306a36Sopenharmony_ci 213962306a36Sopenharmony_ci binding->mlxsw_sp_port = mlxsw_sp_port; 214062306a36Sopenharmony_ci binding->handle = handle; 214162306a36Sopenharmony_ci binding->tclass_num = tclass_num; 214262306a36Sopenharmony_ci binding->span_trigger = span_trigger; 214362306a36Sopenharmony_ci binding->action_mask = action_mask; 214462306a36Sopenharmony_ci return binding; 214562306a36Sopenharmony_ci} 214662306a36Sopenharmony_ci 214762306a36Sopenharmony_cistatic void 214862306a36Sopenharmony_cimlxsw_sp_qevent_binding_destroy(struct mlxsw_sp_qevent_binding *binding) 214962306a36Sopenharmony_ci{ 215062306a36Sopenharmony_ci kfree(binding); 215162306a36Sopenharmony_ci} 215262306a36Sopenharmony_ci 215362306a36Sopenharmony_cistatic struct mlxsw_sp_qevent_binding * 215462306a36Sopenharmony_cimlxsw_sp_qevent_binding_lookup(struct mlxsw_sp_qevent_block *block, 215562306a36Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port, 215662306a36Sopenharmony_ci u32 handle, 215762306a36Sopenharmony_ci enum mlxsw_sp_span_trigger span_trigger) 215862306a36Sopenharmony_ci{ 215962306a36Sopenharmony_ci struct mlxsw_sp_qevent_binding *qevent_binding; 216062306a36Sopenharmony_ci 216162306a36Sopenharmony_ci list_for_each_entry(qevent_binding, &block->binding_list, list) 216262306a36Sopenharmony_ci if (qevent_binding->mlxsw_sp_port == mlxsw_sp_port && 216362306a36Sopenharmony_ci qevent_binding->handle == handle && 216462306a36Sopenharmony_ci qevent_binding->span_trigger == span_trigger) 216562306a36Sopenharmony_ci return qevent_binding; 216662306a36Sopenharmony_ci return NULL; 216762306a36Sopenharmony_ci} 216862306a36Sopenharmony_ci 216962306a36Sopenharmony_cistatic int 217062306a36Sopenharmony_cimlxsw_sp_setup_tc_block_qevent_bind(struct mlxsw_sp_port *mlxsw_sp_port, 217162306a36Sopenharmony_ci struct flow_block_offload *f, 217262306a36Sopenharmony_ci enum mlxsw_sp_span_trigger span_trigger, 217362306a36Sopenharmony_ci unsigned int action_mask) 217462306a36Sopenharmony_ci{ 217562306a36Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 217662306a36Sopenharmony_ci struct mlxsw_sp_qevent_binding *qevent_binding; 217762306a36Sopenharmony_ci struct mlxsw_sp_qevent_block *qevent_block; 217862306a36Sopenharmony_ci struct flow_block_cb *block_cb; 217962306a36Sopenharmony_ci struct mlxsw_sp_qdisc *qdisc; 218062306a36Sopenharmony_ci bool register_block = false; 218162306a36Sopenharmony_ci int tclass_num; 218262306a36Sopenharmony_ci int err; 218362306a36Sopenharmony_ci 218462306a36Sopenharmony_ci block_cb = flow_block_cb_lookup(f->block, mlxsw_sp_qevent_block_cb, mlxsw_sp); 218562306a36Sopenharmony_ci if (!block_cb) { 218662306a36Sopenharmony_ci qevent_block = mlxsw_sp_qevent_block_create(mlxsw_sp, f->net); 218762306a36Sopenharmony_ci if (!qevent_block) 218862306a36Sopenharmony_ci return -ENOMEM; 218962306a36Sopenharmony_ci block_cb = flow_block_cb_alloc(mlxsw_sp_qevent_block_cb, mlxsw_sp, qevent_block, 219062306a36Sopenharmony_ci mlxsw_sp_qevent_block_release); 219162306a36Sopenharmony_ci if (IS_ERR(block_cb)) { 219262306a36Sopenharmony_ci mlxsw_sp_qevent_block_destroy(qevent_block); 219362306a36Sopenharmony_ci return PTR_ERR(block_cb); 219462306a36Sopenharmony_ci } 219562306a36Sopenharmony_ci register_block = true; 219662306a36Sopenharmony_ci } else { 219762306a36Sopenharmony_ci qevent_block = flow_block_cb_priv(block_cb); 219862306a36Sopenharmony_ci } 219962306a36Sopenharmony_ci flow_block_cb_incref(block_cb); 220062306a36Sopenharmony_ci 220162306a36Sopenharmony_ci qdisc = mlxsw_sp_qdisc_find_by_handle(mlxsw_sp_port, f->sch->handle); 220262306a36Sopenharmony_ci if (!qdisc) { 220362306a36Sopenharmony_ci NL_SET_ERR_MSG(f->extack, "Qdisc not offloaded"); 220462306a36Sopenharmony_ci err = -ENOENT; 220562306a36Sopenharmony_ci goto err_find_qdisc; 220662306a36Sopenharmony_ci } 220762306a36Sopenharmony_ci 220862306a36Sopenharmony_ci if (WARN_ON(mlxsw_sp_qevent_binding_lookup(qevent_block, mlxsw_sp_port, f->sch->handle, 220962306a36Sopenharmony_ci span_trigger))) { 221062306a36Sopenharmony_ci err = -EEXIST; 221162306a36Sopenharmony_ci goto err_binding_exists; 221262306a36Sopenharmony_ci } 221362306a36Sopenharmony_ci 221462306a36Sopenharmony_ci tclass_num = mlxsw_sp_qdisc_get_tclass_num(mlxsw_sp_port, qdisc); 221562306a36Sopenharmony_ci qevent_binding = mlxsw_sp_qevent_binding_create(mlxsw_sp_port, 221662306a36Sopenharmony_ci f->sch->handle, 221762306a36Sopenharmony_ci tclass_num, 221862306a36Sopenharmony_ci span_trigger, 221962306a36Sopenharmony_ci action_mask); 222062306a36Sopenharmony_ci if (IS_ERR(qevent_binding)) { 222162306a36Sopenharmony_ci err = PTR_ERR(qevent_binding); 222262306a36Sopenharmony_ci goto err_binding_create; 222362306a36Sopenharmony_ci } 222462306a36Sopenharmony_ci 222562306a36Sopenharmony_ci err = mlxsw_sp_qevent_binding_configure(qevent_block, qevent_binding, 222662306a36Sopenharmony_ci f->extack); 222762306a36Sopenharmony_ci if (err) 222862306a36Sopenharmony_ci goto err_binding_configure; 222962306a36Sopenharmony_ci 223062306a36Sopenharmony_ci list_add(&qevent_binding->list, &qevent_block->binding_list); 223162306a36Sopenharmony_ci 223262306a36Sopenharmony_ci if (register_block) { 223362306a36Sopenharmony_ci flow_block_cb_add(block_cb, f); 223462306a36Sopenharmony_ci list_add_tail(&block_cb->driver_list, &mlxsw_sp_qevent_block_cb_list); 223562306a36Sopenharmony_ci } 223662306a36Sopenharmony_ci 223762306a36Sopenharmony_ci return 0; 223862306a36Sopenharmony_ci 223962306a36Sopenharmony_cierr_binding_configure: 224062306a36Sopenharmony_ci mlxsw_sp_qevent_binding_destroy(qevent_binding); 224162306a36Sopenharmony_cierr_binding_create: 224262306a36Sopenharmony_cierr_binding_exists: 224362306a36Sopenharmony_cierr_find_qdisc: 224462306a36Sopenharmony_ci if (!flow_block_cb_decref(block_cb)) 224562306a36Sopenharmony_ci flow_block_cb_free(block_cb); 224662306a36Sopenharmony_ci return err; 224762306a36Sopenharmony_ci} 224862306a36Sopenharmony_ci 224962306a36Sopenharmony_cistatic void mlxsw_sp_setup_tc_block_qevent_unbind(struct mlxsw_sp_port *mlxsw_sp_port, 225062306a36Sopenharmony_ci struct flow_block_offload *f, 225162306a36Sopenharmony_ci enum mlxsw_sp_span_trigger span_trigger) 225262306a36Sopenharmony_ci{ 225362306a36Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 225462306a36Sopenharmony_ci struct mlxsw_sp_qevent_binding *qevent_binding; 225562306a36Sopenharmony_ci struct mlxsw_sp_qevent_block *qevent_block; 225662306a36Sopenharmony_ci struct flow_block_cb *block_cb; 225762306a36Sopenharmony_ci 225862306a36Sopenharmony_ci block_cb = flow_block_cb_lookup(f->block, mlxsw_sp_qevent_block_cb, mlxsw_sp); 225962306a36Sopenharmony_ci if (!block_cb) 226062306a36Sopenharmony_ci return; 226162306a36Sopenharmony_ci qevent_block = flow_block_cb_priv(block_cb); 226262306a36Sopenharmony_ci 226362306a36Sopenharmony_ci qevent_binding = mlxsw_sp_qevent_binding_lookup(qevent_block, mlxsw_sp_port, f->sch->handle, 226462306a36Sopenharmony_ci span_trigger); 226562306a36Sopenharmony_ci if (!qevent_binding) 226662306a36Sopenharmony_ci return; 226762306a36Sopenharmony_ci 226862306a36Sopenharmony_ci list_del(&qevent_binding->list); 226962306a36Sopenharmony_ci mlxsw_sp_qevent_binding_deconfigure(qevent_block, qevent_binding); 227062306a36Sopenharmony_ci mlxsw_sp_qevent_binding_destroy(qevent_binding); 227162306a36Sopenharmony_ci 227262306a36Sopenharmony_ci if (!flow_block_cb_decref(block_cb)) { 227362306a36Sopenharmony_ci flow_block_cb_remove(block_cb, f); 227462306a36Sopenharmony_ci list_del(&block_cb->driver_list); 227562306a36Sopenharmony_ci } 227662306a36Sopenharmony_ci} 227762306a36Sopenharmony_ci 227862306a36Sopenharmony_cistatic int 227962306a36Sopenharmony_cimlxsw_sp_setup_tc_block_qevent(struct mlxsw_sp_port *mlxsw_sp_port, 228062306a36Sopenharmony_ci struct flow_block_offload *f, 228162306a36Sopenharmony_ci enum mlxsw_sp_span_trigger span_trigger, 228262306a36Sopenharmony_ci unsigned int action_mask) 228362306a36Sopenharmony_ci{ 228462306a36Sopenharmony_ci f->driver_block_list = &mlxsw_sp_qevent_block_cb_list; 228562306a36Sopenharmony_ci 228662306a36Sopenharmony_ci switch (f->command) { 228762306a36Sopenharmony_ci case FLOW_BLOCK_BIND: 228862306a36Sopenharmony_ci return mlxsw_sp_setup_tc_block_qevent_bind(mlxsw_sp_port, f, 228962306a36Sopenharmony_ci span_trigger, 229062306a36Sopenharmony_ci action_mask); 229162306a36Sopenharmony_ci case FLOW_BLOCK_UNBIND: 229262306a36Sopenharmony_ci mlxsw_sp_setup_tc_block_qevent_unbind(mlxsw_sp_port, f, span_trigger); 229362306a36Sopenharmony_ci return 0; 229462306a36Sopenharmony_ci default: 229562306a36Sopenharmony_ci return -EOPNOTSUPP; 229662306a36Sopenharmony_ci } 229762306a36Sopenharmony_ci} 229862306a36Sopenharmony_ci 229962306a36Sopenharmony_ciint mlxsw_sp_setup_tc_block_qevent_early_drop(struct mlxsw_sp_port *mlxsw_sp_port, 230062306a36Sopenharmony_ci struct flow_block_offload *f) 230162306a36Sopenharmony_ci{ 230262306a36Sopenharmony_ci unsigned int action_mask = BIT(MLXSW_SP_MALL_ACTION_TYPE_MIRROR) | 230362306a36Sopenharmony_ci BIT(MLXSW_SP_MALL_ACTION_TYPE_TRAP); 230462306a36Sopenharmony_ci 230562306a36Sopenharmony_ci return mlxsw_sp_setup_tc_block_qevent(mlxsw_sp_port, f, 230662306a36Sopenharmony_ci MLXSW_SP_SPAN_TRIGGER_EARLY_DROP, 230762306a36Sopenharmony_ci action_mask); 230862306a36Sopenharmony_ci} 230962306a36Sopenharmony_ci 231062306a36Sopenharmony_ciint mlxsw_sp_setup_tc_block_qevent_mark(struct mlxsw_sp_port *mlxsw_sp_port, 231162306a36Sopenharmony_ci struct flow_block_offload *f) 231262306a36Sopenharmony_ci{ 231362306a36Sopenharmony_ci unsigned int action_mask = BIT(MLXSW_SP_MALL_ACTION_TYPE_MIRROR); 231462306a36Sopenharmony_ci 231562306a36Sopenharmony_ci return mlxsw_sp_setup_tc_block_qevent(mlxsw_sp_port, f, 231662306a36Sopenharmony_ci MLXSW_SP_SPAN_TRIGGER_ECN, 231762306a36Sopenharmony_ci action_mask); 231862306a36Sopenharmony_ci} 231962306a36Sopenharmony_ci 232062306a36Sopenharmony_ciint mlxsw_sp_tc_qdisc_init(struct mlxsw_sp_port *mlxsw_sp_port) 232162306a36Sopenharmony_ci{ 232262306a36Sopenharmony_ci struct mlxsw_sp_qdisc_state *qdisc_state; 232362306a36Sopenharmony_ci 232462306a36Sopenharmony_ci qdisc_state = kzalloc(sizeof(*qdisc_state), GFP_KERNEL); 232562306a36Sopenharmony_ci if (!qdisc_state) 232662306a36Sopenharmony_ci return -ENOMEM; 232762306a36Sopenharmony_ci 232862306a36Sopenharmony_ci mutex_init(&qdisc_state->lock); 232962306a36Sopenharmony_ci mlxsw_sp_port->qdisc = qdisc_state; 233062306a36Sopenharmony_ci return 0; 233162306a36Sopenharmony_ci} 233262306a36Sopenharmony_ci 233362306a36Sopenharmony_civoid mlxsw_sp_tc_qdisc_fini(struct mlxsw_sp_port *mlxsw_sp_port) 233462306a36Sopenharmony_ci{ 233562306a36Sopenharmony_ci mutex_destroy(&mlxsw_sp_port->qdisc->lock); 233662306a36Sopenharmony_ci kfree(mlxsw_sp_port->qdisc); 233762306a36Sopenharmony_ci} 2338