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