brintos

brintos / linux-shallow public Read only

0
0
Text · 65.2 KiB · 4243d3b Raw
2338 lines · c
1// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.02/* Copyright (c) 2017-2018 Mellanox Technologies. All rights reserved */3 4#include <linux/kernel.h>5#include <linux/errno.h>6#include <linux/netdevice.h>7#include <net/pkt_cls.h>8#include <net/red.h>9 10#include "spectrum.h"11#include "spectrum_span.h"12#include "reg.h"13 14#define MLXSW_SP_PRIO_BAND_TO_TCLASS(band) (IEEE_8021QAZ_MAX_TCS - band - 1)15#define MLXSW_SP_PRIO_CHILD_TO_TCLASS(child) \16	MLXSW_SP_PRIO_BAND_TO_TCLASS((child - 1))17 18enum mlxsw_sp_qdisc_type {19	MLXSW_SP_QDISC_NO_QDISC,20	MLXSW_SP_QDISC_RED,21	MLXSW_SP_QDISC_PRIO,22	MLXSW_SP_QDISC_ETS,23	MLXSW_SP_QDISC_TBF,24	MLXSW_SP_QDISC_FIFO,25};26 27struct mlxsw_sp_qdisc;28 29struct mlxsw_sp_qdisc_ops {30	enum mlxsw_sp_qdisc_type type;31	int (*check_params)(struct mlxsw_sp_port *mlxsw_sp_port,32			    void *params);33	int (*replace)(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,34		       struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, void *params);35	int (*destroy)(struct mlxsw_sp_port *mlxsw_sp_port,36		       struct mlxsw_sp_qdisc *mlxsw_sp_qdisc);37	int (*get_stats)(struct mlxsw_sp_port *mlxsw_sp_port,38			 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,39			 struct tc_qopt_offload_stats *stats_ptr);40	int (*get_xstats)(struct mlxsw_sp_port *mlxsw_sp_port,41			  struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,42			  void *xstats_ptr);43	void (*clean_stats)(struct mlxsw_sp_port *mlxsw_sp_port,44			    struct mlxsw_sp_qdisc *mlxsw_sp_qdisc);45	/* unoffload - to be used for a qdisc that stops being offloaded without46	 * being destroyed.47	 */48	void (*unoffload)(struct mlxsw_sp_port *mlxsw_sp_port,49			  struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, void *params);50	struct mlxsw_sp_qdisc *(*find_class)(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,51					     u32 parent);52	unsigned int num_classes;53 54	u8 (*get_prio_bitmap)(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,55			      struct mlxsw_sp_qdisc *child);56	int (*get_tclass_num)(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,57			      struct mlxsw_sp_qdisc *child);58};59 60struct mlxsw_sp_qdisc_ets_band {61	u8 prio_bitmap;62	int tclass_num;63};64 65struct mlxsw_sp_qdisc_ets_data {66	struct mlxsw_sp_qdisc_ets_band bands[IEEE_8021QAZ_MAX_TCS];67};68 69struct mlxsw_sp_qdisc {70	u32 handle;71	union {72		struct red_stats red;73	} xstats_base;74	struct mlxsw_sp_qdisc_stats {75		u64 tx_bytes;76		u64 tx_packets;77		u64 drops;78		u64 overlimits;79		u64 backlog;80	} stats_base;81 82	union {83		struct mlxsw_sp_qdisc_ets_data *ets_data;84	};85 86	struct mlxsw_sp_qdisc_ops *ops;87	struct mlxsw_sp_qdisc *parent;88	struct mlxsw_sp_qdisc *qdiscs;89	unsigned int num_classes;90};91 92struct mlxsw_sp_qdisc_state {93	struct mlxsw_sp_qdisc root_qdisc;94 95	/* When a PRIO or ETS are added, the invisible FIFOs in their bands are96	 * created first. When notifications for these FIFOs arrive, it is not97	 * known what qdisc their parent handle refers to. It could be a98	 * newly-created PRIO that will replace the currently-offloaded one, or99	 * it could be e.g. a RED that will be attached below it.100	 *101	 * As the notifications start to arrive, use them to note what the102	 * future parent handle is, and keep track of which child FIFOs were103	 * seen. Then when the parent is known, retroactively offload those104	 * FIFOs.105	 */106	u32 future_handle;107	bool future_fifos[IEEE_8021QAZ_MAX_TCS];108	struct mutex lock; /* Protects qdisc state. */109};110 111static bool112mlxsw_sp_qdisc_compare(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, u32 handle)113{114	return mlxsw_sp_qdisc->ops && mlxsw_sp_qdisc->handle == handle;115}116 117static struct mlxsw_sp_qdisc *118mlxsw_sp_qdisc_walk(struct mlxsw_sp_qdisc *qdisc,119		    struct mlxsw_sp_qdisc *(*pre)(struct mlxsw_sp_qdisc *,120						  void *),121		    void *data)122{123	struct mlxsw_sp_qdisc *tmp;124	unsigned int i;125 126	if (pre) {127		tmp = pre(qdisc, data);128		if (tmp)129			return tmp;130	}131 132	if (qdisc->ops) {133		for (i = 0; i < qdisc->num_classes; i++) {134			tmp = &qdisc->qdiscs[i];135			if (qdisc->ops) {136				tmp = mlxsw_sp_qdisc_walk(tmp, pre, data);137				if (tmp)138					return tmp;139			}140		}141	}142 143	return NULL;144}145 146static struct mlxsw_sp_qdisc *147mlxsw_sp_qdisc_walk_cb_find(struct mlxsw_sp_qdisc *qdisc, void *data)148{149	u32 parent = *(u32 *)data;150 151	if (qdisc->ops && TC_H_MAJ(qdisc->handle) == TC_H_MAJ(parent)) {152		if (qdisc->ops->find_class)153			return qdisc->ops->find_class(qdisc, parent);154	}155 156	return NULL;157}158 159static struct mlxsw_sp_qdisc *160mlxsw_sp_qdisc_find(struct mlxsw_sp_port *mlxsw_sp_port, u32 parent)161{162	struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc;163 164	if (!qdisc_state)165		return NULL;166	if (parent == TC_H_ROOT)167		return &qdisc_state->root_qdisc;168	return mlxsw_sp_qdisc_walk(&qdisc_state->root_qdisc,169				   mlxsw_sp_qdisc_walk_cb_find, &parent);170}171 172static struct mlxsw_sp_qdisc *173mlxsw_sp_qdisc_walk_cb_find_by_handle(struct mlxsw_sp_qdisc *qdisc, void *data)174{175	u32 handle = *(u32 *)data;176 177	if (qdisc->ops && qdisc->handle == handle)178		return qdisc;179	return NULL;180}181 182static struct mlxsw_sp_qdisc *183mlxsw_sp_qdisc_find_by_handle(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle)184{185	struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc;186 187	if (!qdisc_state)188		return NULL;189	return mlxsw_sp_qdisc_walk(&qdisc_state->root_qdisc,190				   mlxsw_sp_qdisc_walk_cb_find_by_handle,191				   &handle);192}193 194static void195mlxsw_sp_qdisc_reduce_parent_backlog(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)196{197	struct mlxsw_sp_qdisc *tmp;198 199	for (tmp = mlxsw_sp_qdisc->parent; tmp; tmp = tmp->parent)200		tmp->stats_base.backlog -= mlxsw_sp_qdisc->stats_base.backlog;201}202 203static u8 mlxsw_sp_qdisc_get_prio_bitmap(struct mlxsw_sp_port *mlxsw_sp_port,204					 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)205{206	struct mlxsw_sp_qdisc *parent = mlxsw_sp_qdisc->parent;207 208	if (!parent)209		return 0xff;210	if (!parent->ops->get_prio_bitmap)211		return mlxsw_sp_qdisc_get_prio_bitmap(mlxsw_sp_port, parent);212	return parent->ops->get_prio_bitmap(parent, mlxsw_sp_qdisc);213}214 215#define MLXSW_SP_PORT_DEFAULT_TCLASS 0216 217static int mlxsw_sp_qdisc_get_tclass_num(struct mlxsw_sp_port *mlxsw_sp_port,218					 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)219{220	struct mlxsw_sp_qdisc *parent = mlxsw_sp_qdisc->parent;221 222	if (!parent)223		return MLXSW_SP_PORT_DEFAULT_TCLASS;224	if (!parent->ops->get_tclass_num)225		return mlxsw_sp_qdisc_get_tclass_num(mlxsw_sp_port, parent);226	return parent->ops->get_tclass_num(parent, mlxsw_sp_qdisc);227}228 229static int230mlxsw_sp_qdisc_destroy(struct mlxsw_sp_port *mlxsw_sp_port,231		       struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)232{233	struct mlxsw_sp_qdisc *root_qdisc = &mlxsw_sp_port->qdisc->root_qdisc;234	int err_hdroom = 0;235	int err = 0;236	int i;237 238	if (!mlxsw_sp_qdisc)239		return 0;240 241	if (root_qdisc == mlxsw_sp_qdisc) {242		struct mlxsw_sp_hdroom hdroom = *mlxsw_sp_port->hdroom;243 244		hdroom.mode = MLXSW_SP_HDROOM_MODE_DCB;245		mlxsw_sp_hdroom_prios_reset_buf_idx(&hdroom);246		mlxsw_sp_hdroom_bufs_reset_lossiness(&hdroom);247		mlxsw_sp_hdroom_bufs_reset_sizes(mlxsw_sp_port, &hdroom);248		err_hdroom = mlxsw_sp_hdroom_configure(mlxsw_sp_port, &hdroom);249	}250 251	if (!mlxsw_sp_qdisc->ops)252		return 0;253 254	for (i = 0; i < mlxsw_sp_qdisc->num_classes; i++)255		mlxsw_sp_qdisc_destroy(mlxsw_sp_port,256				       &mlxsw_sp_qdisc->qdiscs[i]);257	mlxsw_sp_qdisc_reduce_parent_backlog(mlxsw_sp_qdisc);258	if (mlxsw_sp_qdisc->ops->destroy)259		err = mlxsw_sp_qdisc->ops->destroy(mlxsw_sp_port,260						   mlxsw_sp_qdisc);261	if (mlxsw_sp_qdisc->ops->clean_stats)262		mlxsw_sp_qdisc->ops->clean_stats(mlxsw_sp_port, mlxsw_sp_qdisc);263 264	mlxsw_sp_qdisc->handle = TC_H_UNSPEC;265	mlxsw_sp_qdisc->ops = NULL;266	mlxsw_sp_qdisc->num_classes = 0;267	kfree(mlxsw_sp_qdisc->qdiscs);268	mlxsw_sp_qdisc->qdiscs = NULL;269	return err_hdroom ?: err;270}271 272struct mlxsw_sp_qdisc_tree_validate {273	bool forbid_ets;274	bool forbid_root_tbf;275	bool forbid_tbf;276	bool forbid_red;277};278 279static int280__mlxsw_sp_qdisc_tree_validate(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,281			       struct mlxsw_sp_qdisc_tree_validate validate);282 283static int284mlxsw_sp_qdisc_tree_validate_children(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,285				      struct mlxsw_sp_qdisc_tree_validate validate)286{287	unsigned int i;288	int err;289 290	for (i = 0; i < mlxsw_sp_qdisc->num_classes; i++) {291		err = __mlxsw_sp_qdisc_tree_validate(&mlxsw_sp_qdisc->qdiscs[i],292						     validate);293		if (err)294			return err;295	}296 297	return 0;298}299 300static int301__mlxsw_sp_qdisc_tree_validate(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,302			       struct mlxsw_sp_qdisc_tree_validate validate)303{304	if (!mlxsw_sp_qdisc->ops)305		return 0;306 307	switch (mlxsw_sp_qdisc->ops->type) {308	case MLXSW_SP_QDISC_FIFO:309		break;310	case MLXSW_SP_QDISC_RED:311		if (validate.forbid_red)312			return -EINVAL;313		validate.forbid_red = true;314		validate.forbid_root_tbf = true;315		validate.forbid_ets = true;316		break;317	case MLXSW_SP_QDISC_TBF:318		if (validate.forbid_root_tbf) {319			if (validate.forbid_tbf)320				return -EINVAL;321			/* This is a TC TBF. */322			validate.forbid_tbf = true;323			validate.forbid_ets = true;324		} else {325			/* This is root TBF. */326			validate.forbid_root_tbf = true;327		}328		break;329	case MLXSW_SP_QDISC_PRIO:330	case MLXSW_SP_QDISC_ETS:331		if (validate.forbid_ets)332			return -EINVAL;333		validate.forbid_root_tbf = true;334		validate.forbid_ets = true;335		break;336	default:337		WARN_ON(1);338		return -EINVAL;339	}340 341	return mlxsw_sp_qdisc_tree_validate_children(mlxsw_sp_qdisc, validate);342}343 344static int mlxsw_sp_qdisc_tree_validate(struct mlxsw_sp_port *mlxsw_sp_port)345{346	struct mlxsw_sp_qdisc_tree_validate validate = {};347	struct mlxsw_sp_qdisc *mlxsw_sp_qdisc;348 349	mlxsw_sp_qdisc = &mlxsw_sp_port->qdisc->root_qdisc;350	return __mlxsw_sp_qdisc_tree_validate(mlxsw_sp_qdisc, validate);351}352 353static int mlxsw_sp_qdisc_create(struct mlxsw_sp_port *mlxsw_sp_port,354				 u32 handle,355				 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,356				 struct mlxsw_sp_qdisc_ops *ops, void *params)357{358	struct mlxsw_sp_qdisc *root_qdisc = &mlxsw_sp_port->qdisc->root_qdisc;359	struct mlxsw_sp_hdroom orig_hdroom;360	unsigned int i;361	int err;362 363	err = ops->check_params(mlxsw_sp_port, params);364	if (err)365		return err;366 367	if (ops->num_classes) {368		mlxsw_sp_qdisc->qdiscs = kcalloc(ops->num_classes,369						 sizeof(*mlxsw_sp_qdisc->qdiscs),370						 GFP_KERNEL);371		if (!mlxsw_sp_qdisc->qdiscs)372			return -ENOMEM;373 374		for (i = 0; i < ops->num_classes; i++)375			mlxsw_sp_qdisc->qdiscs[i].parent = mlxsw_sp_qdisc;376	}377 378	orig_hdroom = *mlxsw_sp_port->hdroom;379	if (root_qdisc == mlxsw_sp_qdisc) {380		struct mlxsw_sp_hdroom hdroom = orig_hdroom;381 382		hdroom.mode = MLXSW_SP_HDROOM_MODE_TC;383		mlxsw_sp_hdroom_prios_reset_buf_idx(&hdroom);384		mlxsw_sp_hdroom_bufs_reset_lossiness(&hdroom);385		mlxsw_sp_hdroom_bufs_reset_sizes(mlxsw_sp_port, &hdroom);386 387		err = mlxsw_sp_hdroom_configure(mlxsw_sp_port, &hdroom);388		if (err)389			goto err_hdroom_configure;390	}391 392	mlxsw_sp_qdisc->num_classes = ops->num_classes;393	mlxsw_sp_qdisc->ops = ops;394	mlxsw_sp_qdisc->handle = handle;395	err = mlxsw_sp_qdisc_tree_validate(mlxsw_sp_port);396	if (err)397		goto err_replace;398 399	err = ops->replace(mlxsw_sp_port, handle, mlxsw_sp_qdisc, params);400	if (err)401		goto err_replace;402 403	return 0;404 405err_replace:406	mlxsw_sp_qdisc->handle = TC_H_UNSPEC;407	mlxsw_sp_qdisc->ops = NULL;408	mlxsw_sp_qdisc->num_classes = 0;409	mlxsw_sp_hdroom_configure(mlxsw_sp_port, &orig_hdroom);410err_hdroom_configure:411	kfree(mlxsw_sp_qdisc->qdiscs);412	mlxsw_sp_qdisc->qdiscs = NULL;413	return err;414}415 416static int417mlxsw_sp_qdisc_change(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,418		      struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, void *params)419{420	struct mlxsw_sp_qdisc_ops *ops = mlxsw_sp_qdisc->ops;421	int err;422 423	err = ops->check_params(mlxsw_sp_port, params);424	if (err)425		goto unoffload;426 427	err = ops->replace(mlxsw_sp_port, handle, mlxsw_sp_qdisc, params);428	if (err)429		goto unoffload;430 431	/* Check if the Qdisc changed. That includes a situation where an432	 * invisible Qdisc replaces another one, or is being added for the433	 * first time.434	 */435	if (mlxsw_sp_qdisc->handle != handle) {436		if (ops->clean_stats)437			ops->clean_stats(mlxsw_sp_port, mlxsw_sp_qdisc);438	}439 440	mlxsw_sp_qdisc->handle = handle;441	return 0;442 443unoffload:444	if (ops->unoffload)445		ops->unoffload(mlxsw_sp_port, mlxsw_sp_qdisc, params);446 447	mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);448	return err;449}450 451static int452mlxsw_sp_qdisc_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,453		       struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,454		       struct mlxsw_sp_qdisc_ops *ops, void *params)455{456	if (mlxsw_sp_qdisc->ops && mlxsw_sp_qdisc->ops->type != ops->type)457		/* In case this location contained a different qdisc of the458		 * same type we can override the old qdisc configuration.459		 * Otherwise, we need to remove the old qdisc before setting the460		 * new one.461		 */462		mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);463 464	if (!mlxsw_sp_qdisc->ops)465		return mlxsw_sp_qdisc_create(mlxsw_sp_port, handle,466					     mlxsw_sp_qdisc, ops, params);467	else468		return mlxsw_sp_qdisc_change(mlxsw_sp_port, handle,469					     mlxsw_sp_qdisc, params);470}471 472static int473mlxsw_sp_qdisc_get_stats(struct mlxsw_sp_port *mlxsw_sp_port,474			 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,475			 struct tc_qopt_offload_stats *stats_ptr)476{477	if (mlxsw_sp_qdisc && mlxsw_sp_qdisc->ops &&478	    mlxsw_sp_qdisc->ops->get_stats)479		return mlxsw_sp_qdisc->ops->get_stats(mlxsw_sp_port,480						      mlxsw_sp_qdisc,481						      stats_ptr);482 483	return -EOPNOTSUPP;484}485 486static int487mlxsw_sp_qdisc_get_xstats(struct mlxsw_sp_port *mlxsw_sp_port,488			  struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,489			  void *xstats_ptr)490{491	if (mlxsw_sp_qdisc && mlxsw_sp_qdisc->ops &&492	    mlxsw_sp_qdisc->ops->get_xstats)493		return mlxsw_sp_qdisc->ops->get_xstats(mlxsw_sp_port,494						      mlxsw_sp_qdisc,495						      xstats_ptr);496 497	return -EOPNOTSUPP;498}499 500static u64501mlxsw_sp_xstats_backlog(struct mlxsw_sp_port_xstats *xstats, int tclass_num)502{503	return xstats->backlog[tclass_num] +504	       xstats->backlog[tclass_num + 8];505}506 507static u64508mlxsw_sp_xstats_tail_drop(struct mlxsw_sp_port_xstats *xstats, int tclass_num)509{510	return xstats->tail_drop[tclass_num] +511	       xstats->tail_drop[tclass_num + 8];512}513 514static void515mlxsw_sp_qdisc_bstats_per_priority_get(struct mlxsw_sp_port_xstats *xstats,516				       u8 prio_bitmap, u64 *tx_packets,517				       u64 *tx_bytes)518{519	int i;520 521	*tx_packets = 0;522	*tx_bytes = 0;523	for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {524		if (prio_bitmap & BIT(i)) {525			*tx_packets += xstats->tx_packets[i];526			*tx_bytes += xstats->tx_bytes[i];527		}528	}529}530 531static void532mlxsw_sp_qdisc_collect_tc_stats(struct mlxsw_sp_port *mlxsw_sp_port,533				struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,534				u64 *p_tx_bytes, u64 *p_tx_packets,535				u64 *p_drops, u64 *p_backlog)536{537	struct mlxsw_sp_port_xstats *xstats;538	u64 tx_bytes, tx_packets;539	u8 prio_bitmap;540	int tclass_num;541 542	prio_bitmap = mlxsw_sp_qdisc_get_prio_bitmap(mlxsw_sp_port,543						     mlxsw_sp_qdisc);544	tclass_num = mlxsw_sp_qdisc_get_tclass_num(mlxsw_sp_port,545						   mlxsw_sp_qdisc);546	xstats = &mlxsw_sp_port->periodic_hw_stats.xstats;547	mlxsw_sp_qdisc_bstats_per_priority_get(xstats, prio_bitmap,548					       &tx_packets, &tx_bytes);549 550	*p_tx_packets += tx_packets;551	*p_tx_bytes += tx_bytes;552	*p_drops += xstats->wred_drop[tclass_num] +553		    mlxsw_sp_xstats_tail_drop(xstats, tclass_num);554	*p_backlog += mlxsw_sp_xstats_backlog(xstats, tclass_num);555}556 557static void558mlxsw_sp_qdisc_update_stats(struct mlxsw_sp *mlxsw_sp,559			    struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,560			    u64 tx_bytes, u64 tx_packets,561			    u64 drops, u64 backlog,562			    struct tc_qopt_offload_stats *stats_ptr)563{564	struct mlxsw_sp_qdisc_stats *stats_base = &mlxsw_sp_qdisc->stats_base;565 566	tx_bytes -= stats_base->tx_bytes;567	tx_packets -= stats_base->tx_packets;568	drops -= stats_base->drops;569	backlog -= stats_base->backlog;570 571	_bstats_update(stats_ptr->bstats, tx_bytes, tx_packets);572	stats_ptr->qstats->drops += drops;573	stats_ptr->qstats->backlog += mlxsw_sp_cells_bytes(mlxsw_sp, backlog);574 575	stats_base->backlog += backlog;576	stats_base->drops += drops;577	stats_base->tx_bytes += tx_bytes;578	stats_base->tx_packets += tx_packets;579}580 581static void582mlxsw_sp_qdisc_get_tc_stats(struct mlxsw_sp_port *mlxsw_sp_port,583			    struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,584			    struct tc_qopt_offload_stats *stats_ptr)585{586	u64 tx_packets = 0;587	u64 tx_bytes = 0;588	u64 backlog = 0;589	u64 drops = 0;590 591	mlxsw_sp_qdisc_collect_tc_stats(mlxsw_sp_port, mlxsw_sp_qdisc,592					&tx_bytes, &tx_packets,593					&drops, &backlog);594	mlxsw_sp_qdisc_update_stats(mlxsw_sp_port->mlxsw_sp, mlxsw_sp_qdisc,595				    tx_bytes, tx_packets, drops, backlog,596				    stats_ptr);597}598 599static int600mlxsw_sp_tclass_congestion_enable(struct mlxsw_sp_port *mlxsw_sp_port,601				  int tclass_num, u32 min, u32 max,602				  u32 probability, bool is_wred, bool is_ecn)603{604	char cwtpm_cmd[MLXSW_REG_CWTPM_LEN];605	char cwtp_cmd[MLXSW_REG_CWTP_LEN];606	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;607	int err;608 609	mlxsw_reg_cwtp_pack(cwtp_cmd, mlxsw_sp_port->local_port, tclass_num);610	mlxsw_reg_cwtp_profile_pack(cwtp_cmd, MLXSW_REG_CWTP_DEFAULT_PROFILE,611				    roundup(min, MLXSW_REG_CWTP_MIN_VALUE),612				    roundup(max, MLXSW_REG_CWTP_MIN_VALUE),613				    probability);614 615	err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(cwtp), cwtp_cmd);616	if (err)617		return err;618 619	mlxsw_reg_cwtpm_pack(cwtpm_cmd, mlxsw_sp_port->local_port, tclass_num,620			     MLXSW_REG_CWTP_DEFAULT_PROFILE, is_wred, is_ecn);621 622	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(cwtpm), cwtpm_cmd);623}624 625static int626mlxsw_sp_tclass_congestion_disable(struct mlxsw_sp_port *mlxsw_sp_port,627				   int tclass_num)628{629	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;630	char cwtpm_cmd[MLXSW_REG_CWTPM_LEN];631 632	mlxsw_reg_cwtpm_pack(cwtpm_cmd, mlxsw_sp_port->local_port, tclass_num,633			     MLXSW_REG_CWTPM_RESET_PROFILE, false, false);634	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(cwtpm), cwtpm_cmd);635}636 637static void638mlxsw_sp_setup_tc_qdisc_red_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port,639					struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)640{641	struct mlxsw_sp_qdisc_stats *stats_base;642	struct mlxsw_sp_port_xstats *xstats;643	struct red_stats *red_base;644	u8 prio_bitmap;645	int tclass_num;646 647	prio_bitmap = mlxsw_sp_qdisc_get_prio_bitmap(mlxsw_sp_port,648						     mlxsw_sp_qdisc);649	tclass_num = mlxsw_sp_qdisc_get_tclass_num(mlxsw_sp_port,650						   mlxsw_sp_qdisc);651	xstats = &mlxsw_sp_port->periodic_hw_stats.xstats;652	stats_base = &mlxsw_sp_qdisc->stats_base;653	red_base = &mlxsw_sp_qdisc->xstats_base.red;654 655	mlxsw_sp_qdisc_bstats_per_priority_get(xstats, prio_bitmap,656					       &stats_base->tx_packets,657					       &stats_base->tx_bytes);658	red_base->prob_mark = xstats->tc_ecn[tclass_num];659	red_base->prob_drop = xstats->wred_drop[tclass_num];660	red_base->pdrop = mlxsw_sp_xstats_tail_drop(xstats, tclass_num);661 662	stats_base->overlimits = red_base->prob_drop + red_base->prob_mark;663	stats_base->drops = red_base->prob_drop + red_base->pdrop;664 665	stats_base->backlog = 0;666}667 668static int669mlxsw_sp_qdisc_red_destroy(struct mlxsw_sp_port *mlxsw_sp_port,670			   struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)671{672	int tclass_num = mlxsw_sp_qdisc_get_tclass_num(mlxsw_sp_port,673						       mlxsw_sp_qdisc);674 675	return mlxsw_sp_tclass_congestion_disable(mlxsw_sp_port, tclass_num);676}677 678static int679mlxsw_sp_qdisc_red_check_params(struct mlxsw_sp_port *mlxsw_sp_port,680				void *params)681{682	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;683	struct tc_red_qopt_offload_params *p = params;684 685	if (p->min > p->max) {686		dev_err(mlxsw_sp->bus_info->dev,687			"spectrum: RED: min %u is bigger then max %u\n", p->min,688			p->max);689		return -EINVAL;690	}691	if (p->max > MLXSW_CORE_RES_GET(mlxsw_sp->core,692					GUARANTEED_SHARED_BUFFER)) {693		dev_err(mlxsw_sp->bus_info->dev,694			"spectrum: RED: max value %u is too big\n", p->max);695		return -EINVAL;696	}697	if (p->min == 0 || p->max == 0) {698		dev_err(mlxsw_sp->bus_info->dev,699			"spectrum: RED: 0 value is illegal for min and max\n");700		return -EINVAL;701	}702	return 0;703}704 705static int706mlxsw_sp_qdisc_future_fifo_replace(struct mlxsw_sp_port *mlxsw_sp_port,707				   u32 handle, unsigned int band,708				   struct mlxsw_sp_qdisc *child_qdisc);709static void710mlxsw_sp_qdisc_future_fifos_init(struct mlxsw_sp_port *mlxsw_sp_port,711				 u32 handle);712 713static int714mlxsw_sp_qdisc_red_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,715			   struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,716			   void *params)717{718	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;719	struct tc_red_qopt_offload_params *p = params;720	int tclass_num;721	u32 min, max;722	u64 prob;723	int err;724 725	err = mlxsw_sp_qdisc_future_fifo_replace(mlxsw_sp_port, handle, 0,726						 &mlxsw_sp_qdisc->qdiscs[0]);727	if (err)728		return err;729	mlxsw_sp_qdisc_future_fifos_init(mlxsw_sp_port, TC_H_UNSPEC);730 731	tclass_num = mlxsw_sp_qdisc_get_tclass_num(mlxsw_sp_port,732						   mlxsw_sp_qdisc);733 734	/* calculate probability in percentage */735	prob = p->probability;736	prob *= 100;737	prob = DIV_ROUND_UP(prob, 1 << 16);738	prob = DIV_ROUND_UP(prob, 1 << 16);739	min = mlxsw_sp_bytes_cells(mlxsw_sp, p->min);740	max = mlxsw_sp_bytes_cells(mlxsw_sp, p->max);741	return mlxsw_sp_tclass_congestion_enable(mlxsw_sp_port, tclass_num,742						 min, max, prob,743						 !p->is_nodrop, p->is_ecn);744}745 746static void747mlxsw_sp_qdisc_leaf_unoffload(struct mlxsw_sp_port *mlxsw_sp_port,748			      struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,749			      struct gnet_stats_queue *qstats)750{751	u64 backlog;752 753	backlog = mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp,754				       mlxsw_sp_qdisc->stats_base.backlog);755	qstats->backlog -= backlog;756	mlxsw_sp_qdisc->stats_base.backlog = 0;757}758 759static void760mlxsw_sp_qdisc_red_unoffload(struct mlxsw_sp_port *mlxsw_sp_port,761			     struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,762			     void *params)763{764	struct tc_red_qopt_offload_params *p = params;765 766	mlxsw_sp_qdisc_leaf_unoffload(mlxsw_sp_port, mlxsw_sp_qdisc, p->qstats);767}768 769static int770mlxsw_sp_qdisc_get_red_xstats(struct mlxsw_sp_port *mlxsw_sp_port,771			      struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,772			      void *xstats_ptr)773{774	struct red_stats *xstats_base = &mlxsw_sp_qdisc->xstats_base.red;775	struct mlxsw_sp_port_xstats *xstats;776	struct red_stats *res = xstats_ptr;777	int early_drops, marks, pdrops;778	int tclass_num;779 780	tclass_num = mlxsw_sp_qdisc_get_tclass_num(mlxsw_sp_port,781						   mlxsw_sp_qdisc);782	xstats = &mlxsw_sp_port->periodic_hw_stats.xstats;783 784	early_drops = xstats->wred_drop[tclass_num] - xstats_base->prob_drop;785	marks = xstats->tc_ecn[tclass_num] - xstats_base->prob_mark;786	pdrops = mlxsw_sp_xstats_tail_drop(xstats, tclass_num) -787		 xstats_base->pdrop;788 789	res->pdrop += pdrops;790	res->prob_drop += early_drops;791	res->prob_mark += marks;792 793	xstats_base->pdrop += pdrops;794	xstats_base->prob_drop += early_drops;795	xstats_base->prob_mark += marks;796	return 0;797}798 799static int800mlxsw_sp_qdisc_get_red_stats(struct mlxsw_sp_port *mlxsw_sp_port,801			     struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,802			     struct tc_qopt_offload_stats *stats_ptr)803{804	struct mlxsw_sp_qdisc_stats *stats_base;805	struct mlxsw_sp_port_xstats *xstats;806	u64 overlimits;807	int tclass_num;808 809	tclass_num = mlxsw_sp_qdisc_get_tclass_num(mlxsw_sp_port,810						   mlxsw_sp_qdisc);811	xstats = &mlxsw_sp_port->periodic_hw_stats.xstats;812	stats_base = &mlxsw_sp_qdisc->stats_base;813 814	mlxsw_sp_qdisc_get_tc_stats(mlxsw_sp_port, mlxsw_sp_qdisc, stats_ptr);815	overlimits = xstats->wred_drop[tclass_num] +816		     xstats->tc_ecn[tclass_num] - stats_base->overlimits;817 818	stats_ptr->qstats->overlimits += overlimits;819	stats_base->overlimits += overlimits;820 821	return 0;822}823 824static struct mlxsw_sp_qdisc *825mlxsw_sp_qdisc_leaf_find_class(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,826			       u32 parent)827{828	/* RED and TBF are formally classful qdiscs, but all class references,829	 * including X:0, just refer to the same one class.830	 */831	return &mlxsw_sp_qdisc->qdiscs[0];832}833 834static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_red = {835	.type = MLXSW_SP_QDISC_RED,836	.check_params = mlxsw_sp_qdisc_red_check_params,837	.replace = mlxsw_sp_qdisc_red_replace,838	.unoffload = mlxsw_sp_qdisc_red_unoffload,839	.destroy = mlxsw_sp_qdisc_red_destroy,840	.get_stats = mlxsw_sp_qdisc_get_red_stats,841	.get_xstats = mlxsw_sp_qdisc_get_red_xstats,842	.clean_stats = mlxsw_sp_setup_tc_qdisc_red_clean_stats,843	.find_class = mlxsw_sp_qdisc_leaf_find_class,844	.num_classes = 1,845};846 847static int mlxsw_sp_qdisc_graft(struct mlxsw_sp_port *mlxsw_sp_port,848				struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,849				u8 band, u32 child_handle);850 851static int __mlxsw_sp_setup_tc_red(struct mlxsw_sp_port *mlxsw_sp_port,852				   struct tc_red_qopt_offload *p)853{854	struct mlxsw_sp_qdisc *mlxsw_sp_qdisc;855 856	mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent);857	if (!mlxsw_sp_qdisc)858		return -EOPNOTSUPP;859 860	if (p->command == TC_RED_REPLACE)861		return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle,862					      mlxsw_sp_qdisc,863					      &mlxsw_sp_qdisc_ops_red,864					      &p->set);865 866	if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle))867		return -EOPNOTSUPP;868 869	switch (p->command) {870	case TC_RED_DESTROY:871		return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);872	case TC_RED_XSTATS:873		return mlxsw_sp_qdisc_get_xstats(mlxsw_sp_port, mlxsw_sp_qdisc,874						 p->xstats);875	case TC_RED_STATS:876		return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc,877						&p->stats);878	case TC_RED_GRAFT:879		return mlxsw_sp_qdisc_graft(mlxsw_sp_port, mlxsw_sp_qdisc, 0,880					    p->child_handle);881	default:882		return -EOPNOTSUPP;883	}884}885 886int mlxsw_sp_setup_tc_red(struct mlxsw_sp_port *mlxsw_sp_port,887			  struct tc_red_qopt_offload *p)888{889	int err;890 891	mutex_lock(&mlxsw_sp_port->qdisc->lock);892	err = __mlxsw_sp_setup_tc_red(mlxsw_sp_port, p);893	mutex_unlock(&mlxsw_sp_port->qdisc->lock);894 895	return err;896}897 898static void899mlxsw_sp_setup_tc_qdisc_leaf_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port,900					 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)901{902	u64 backlog_cells = 0;903	u64 tx_packets = 0;904	u64 tx_bytes = 0;905	u64 drops = 0;906 907	mlxsw_sp_qdisc_collect_tc_stats(mlxsw_sp_port, mlxsw_sp_qdisc,908					&tx_bytes, &tx_packets,909					&drops, &backlog_cells);910 911	mlxsw_sp_qdisc->stats_base.tx_packets = tx_packets;912	mlxsw_sp_qdisc->stats_base.tx_bytes = tx_bytes;913	mlxsw_sp_qdisc->stats_base.drops = drops;914	mlxsw_sp_qdisc->stats_base.backlog = 0;915}916 917static enum mlxsw_reg_qeec_hr918mlxsw_sp_qdisc_tbf_hr(struct mlxsw_sp_port *mlxsw_sp_port,919		      struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)920{921	if (mlxsw_sp_qdisc == &mlxsw_sp_port->qdisc->root_qdisc)922		return MLXSW_REG_QEEC_HR_PORT;923 924	/* Configure subgroup shaper, so that both UC and MC traffic is subject925	 * to shaping. That is unlike RED, however UC queue lengths are going to926	 * be different than MC ones due to different pool and quota927	 * configurations, so the configuration is not applicable. For shaper on928	 * the other hand, subjecting the overall stream to the configured929	 * shaper makes sense. Also note that that is what we do for930	 * ieee_setmaxrate().931	 */932	return MLXSW_REG_QEEC_HR_SUBGROUP;933}934 935static int936mlxsw_sp_qdisc_tbf_destroy(struct mlxsw_sp_port *mlxsw_sp_port,937			   struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)938{939	enum mlxsw_reg_qeec_hr hr = mlxsw_sp_qdisc_tbf_hr(mlxsw_sp_port,940							  mlxsw_sp_qdisc);941	int tclass_num = mlxsw_sp_qdisc_get_tclass_num(mlxsw_sp_port,942						       mlxsw_sp_qdisc);943 944	return mlxsw_sp_port_ets_maxrate_set(mlxsw_sp_port, hr, tclass_num, 0,945					     MLXSW_REG_QEEC_MAS_DIS, 0);946}947 948static int949mlxsw_sp_qdisc_tbf_bs(struct mlxsw_sp_port *mlxsw_sp_port,950		      u32 max_size, u8 *p_burst_size)951{952	/* TBF burst size is configured in bytes. The ASIC burst size value is953	 * ((2 ^ bs) * 512 bits. Convert the TBF bytes to 512-bit units.954	 */955	u32 bs512 = max_size / 64;956	u8 bs = fls(bs512);957 958	if (!bs)959		return -EINVAL;960	--bs;961 962	/* Demand a power of two. */963	if ((1 << bs) != bs512)964		return -EINVAL;965 966	if (bs < mlxsw_sp_port->mlxsw_sp->lowest_shaper_bs ||967	    bs > MLXSW_REG_QEEC_HIGHEST_SHAPER_BS)968		return -EINVAL;969 970	*p_burst_size = bs;971	return 0;972}973 974static u32975mlxsw_sp_qdisc_tbf_max_size(u8 bs)976{977	return (1U << bs) * 64;978}979 980static u64981mlxsw_sp_qdisc_tbf_rate_kbps(struct tc_tbf_qopt_offload_replace_params *p)982{983	/* TBF interface is in bytes/s, whereas Spectrum ASIC is configured in984	 * Kbits/s.985	 */986	return div_u64(p->rate.rate_bytes_ps, 1000) * 8;987}988 989static int990mlxsw_sp_qdisc_tbf_check_params(struct mlxsw_sp_port *mlxsw_sp_port,991				void *params)992{993	struct tc_tbf_qopt_offload_replace_params *p = params;994	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;995	u64 rate_kbps = mlxsw_sp_qdisc_tbf_rate_kbps(p);996	u8 burst_size;997	int err;998 999	if (rate_kbps >= MLXSW_REG_QEEC_MAS_DIS) {1000		dev_err(mlxsw_sp_port->mlxsw_sp->bus_info->dev,1001			"spectrum: TBF: rate of %lluKbps must be below %u\n",1002			rate_kbps, MLXSW_REG_QEEC_MAS_DIS);1003		return -EINVAL;1004	}1005 1006	err = mlxsw_sp_qdisc_tbf_bs(mlxsw_sp_port, p->max_size, &burst_size);1007	if (err) {1008		u8 highest_shaper_bs = MLXSW_REG_QEEC_HIGHEST_SHAPER_BS;1009 1010		dev_err(mlxsw_sp->bus_info->dev,1011			"spectrum: TBF: invalid burst size of %u, must be a power of two between %u and %u",1012			p->max_size,1013			mlxsw_sp_qdisc_tbf_max_size(mlxsw_sp->lowest_shaper_bs),1014			mlxsw_sp_qdisc_tbf_max_size(highest_shaper_bs));1015		return -EINVAL;1016	}1017 1018	return 0;1019}1020 1021static int1022mlxsw_sp_qdisc_tbf_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,1023			   struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,1024			   void *params)1025{1026	enum mlxsw_reg_qeec_hr hr = mlxsw_sp_qdisc_tbf_hr(mlxsw_sp_port,1027							  mlxsw_sp_qdisc);1028	struct tc_tbf_qopt_offload_replace_params *p = params;1029	u64 rate_kbps = mlxsw_sp_qdisc_tbf_rate_kbps(p);1030	int tclass_num;1031	u8 burst_size;1032	int err;1033 1034	err = mlxsw_sp_qdisc_future_fifo_replace(mlxsw_sp_port, handle, 0,1035						 &mlxsw_sp_qdisc->qdiscs[0]);1036	if (err)1037		return err;1038	mlxsw_sp_qdisc_future_fifos_init(mlxsw_sp_port, TC_H_UNSPEC);1039 1040	tclass_num = mlxsw_sp_qdisc_get_tclass_num(mlxsw_sp_port,1041						   mlxsw_sp_qdisc);1042 1043	err = mlxsw_sp_qdisc_tbf_bs(mlxsw_sp_port, p->max_size, &burst_size);1044	if (WARN_ON_ONCE(err))1045		/* check_params above was supposed to reject this value. */1046		return -EINVAL;1047 1048	return mlxsw_sp_port_ets_maxrate_set(mlxsw_sp_port, hr, tclass_num, 0,1049					     rate_kbps, burst_size);1050}1051 1052static void1053mlxsw_sp_qdisc_tbf_unoffload(struct mlxsw_sp_port *mlxsw_sp_port,1054			     struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,1055			     void *params)1056{1057	struct tc_tbf_qopt_offload_replace_params *p = params;1058 1059	mlxsw_sp_qdisc_leaf_unoffload(mlxsw_sp_port, mlxsw_sp_qdisc, p->qstats);1060}1061 1062static int1063mlxsw_sp_qdisc_get_tbf_stats(struct mlxsw_sp_port *mlxsw_sp_port,1064			     struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,1065			     struct tc_qopt_offload_stats *stats_ptr)1066{1067	mlxsw_sp_qdisc_get_tc_stats(mlxsw_sp_port, mlxsw_sp_qdisc,1068				    stats_ptr);1069	return 0;1070}1071 1072static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_tbf = {1073	.type = MLXSW_SP_QDISC_TBF,1074	.check_params = mlxsw_sp_qdisc_tbf_check_params,1075	.replace = mlxsw_sp_qdisc_tbf_replace,1076	.unoffload = mlxsw_sp_qdisc_tbf_unoffload,1077	.destroy = mlxsw_sp_qdisc_tbf_destroy,1078	.get_stats = mlxsw_sp_qdisc_get_tbf_stats,1079	.clean_stats = mlxsw_sp_setup_tc_qdisc_leaf_clean_stats,1080	.find_class = mlxsw_sp_qdisc_leaf_find_class,1081	.num_classes = 1,1082};1083 1084static int __mlxsw_sp_setup_tc_tbf(struct mlxsw_sp_port *mlxsw_sp_port,1085				   struct tc_tbf_qopt_offload *p)1086{1087	struct mlxsw_sp_qdisc *mlxsw_sp_qdisc;1088 1089	mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent);1090	if (!mlxsw_sp_qdisc)1091		return -EOPNOTSUPP;1092 1093	if (p->command == TC_TBF_REPLACE)1094		return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle,1095					      mlxsw_sp_qdisc,1096					      &mlxsw_sp_qdisc_ops_tbf,1097					      &p->replace_params);1098 1099	if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle))1100		return -EOPNOTSUPP;1101 1102	switch (p->command) {1103	case TC_TBF_DESTROY:1104		return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);1105	case TC_TBF_STATS:1106		return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc,1107						&p->stats);1108	case TC_TBF_GRAFT:1109		return mlxsw_sp_qdisc_graft(mlxsw_sp_port, mlxsw_sp_qdisc, 0,1110					    p->child_handle);1111	default:1112		return -EOPNOTSUPP;1113	}1114}1115 1116int mlxsw_sp_setup_tc_tbf(struct mlxsw_sp_port *mlxsw_sp_port,1117			  struct tc_tbf_qopt_offload *p)1118{1119	int err;1120 1121	mutex_lock(&mlxsw_sp_port->qdisc->lock);1122	err = __mlxsw_sp_setup_tc_tbf(mlxsw_sp_port, p);1123	mutex_unlock(&mlxsw_sp_port->qdisc->lock);1124 1125	return err;1126}1127 1128static int1129mlxsw_sp_qdisc_fifo_check_params(struct mlxsw_sp_port *mlxsw_sp_port,1130				 void *params)1131{1132	return 0;1133}1134 1135static int1136mlxsw_sp_qdisc_fifo_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,1137			    struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,1138			    void *params)1139{1140	return 0;1141}1142 1143static int1144mlxsw_sp_qdisc_get_fifo_stats(struct mlxsw_sp_port *mlxsw_sp_port,1145			      struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,1146			      struct tc_qopt_offload_stats *stats_ptr)1147{1148	mlxsw_sp_qdisc_get_tc_stats(mlxsw_sp_port, mlxsw_sp_qdisc,1149				    stats_ptr);1150	return 0;1151}1152 1153static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_fifo = {1154	.type = MLXSW_SP_QDISC_FIFO,1155	.check_params = mlxsw_sp_qdisc_fifo_check_params,1156	.replace = mlxsw_sp_qdisc_fifo_replace,1157	.get_stats = mlxsw_sp_qdisc_get_fifo_stats,1158	.clean_stats = mlxsw_sp_setup_tc_qdisc_leaf_clean_stats,1159};1160 1161static int1162mlxsw_sp_qdisc_future_fifo_replace(struct mlxsw_sp_port *mlxsw_sp_port,1163				   u32 handle, unsigned int band,1164				   struct mlxsw_sp_qdisc *child_qdisc)1165{1166	struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc;1167 1168	if (handle == qdisc_state->future_handle &&1169	    qdisc_state->future_fifos[band])1170		return mlxsw_sp_qdisc_replace(mlxsw_sp_port, TC_H_UNSPEC,1171					      child_qdisc,1172					      &mlxsw_sp_qdisc_ops_fifo,1173					      NULL);1174	return 0;1175}1176 1177static void1178mlxsw_sp_qdisc_future_fifos_init(struct mlxsw_sp_port *mlxsw_sp_port,1179				 u32 handle)1180{1181	struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc;1182 1183	qdisc_state->future_handle = handle;1184	memset(qdisc_state->future_fifos, 0, sizeof(qdisc_state->future_fifos));1185}1186 1187static int __mlxsw_sp_setup_tc_fifo(struct mlxsw_sp_port *mlxsw_sp_port,1188				    struct tc_fifo_qopt_offload *p)1189{1190	struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc;1191	struct mlxsw_sp_qdisc *mlxsw_sp_qdisc;1192	unsigned int band;1193	u32 parent_handle;1194 1195	mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent);1196	if (!mlxsw_sp_qdisc && p->handle == TC_H_UNSPEC) {1197		parent_handle = TC_H_MAJ(p->parent);1198		if (parent_handle != qdisc_state->future_handle) {1199			/* This notifications is for a different Qdisc than1200			 * previously. Wipe the future cache.1201			 */1202			mlxsw_sp_qdisc_future_fifos_init(mlxsw_sp_port,1203							 parent_handle);1204		}1205 1206		band = TC_H_MIN(p->parent) - 1;1207		if (band < IEEE_8021QAZ_MAX_TCS) {1208			if (p->command == TC_FIFO_REPLACE)1209				qdisc_state->future_fifos[band] = true;1210			else if (p->command == TC_FIFO_DESTROY)1211				qdisc_state->future_fifos[band] = false;1212		}1213	}1214	if (!mlxsw_sp_qdisc)1215		return -EOPNOTSUPP;1216 1217	if (p->command == TC_FIFO_REPLACE) {1218		return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle,1219					      mlxsw_sp_qdisc,1220					      &mlxsw_sp_qdisc_ops_fifo, NULL);1221	}1222 1223	if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle))1224		return -EOPNOTSUPP;1225 1226	switch (p->command) {1227	case TC_FIFO_DESTROY:1228		return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);1229	case TC_FIFO_STATS:1230		return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc,1231						&p->stats);1232	case TC_FIFO_REPLACE: /* Handled above. */1233		break;1234	}1235 1236	return -EOPNOTSUPP;1237}1238 1239int mlxsw_sp_setup_tc_fifo(struct mlxsw_sp_port *mlxsw_sp_port,1240			   struct tc_fifo_qopt_offload *p)1241{1242	int err;1243 1244	mutex_lock(&mlxsw_sp_port->qdisc->lock);1245	err = __mlxsw_sp_setup_tc_fifo(mlxsw_sp_port, p);1246	mutex_unlock(&mlxsw_sp_port->qdisc->lock);1247 1248	return err;1249}1250 1251static int __mlxsw_sp_qdisc_ets_destroy(struct mlxsw_sp_port *mlxsw_sp_port,1252					struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)1253{1254	int i;1255 1256	for (i = 0; i < mlxsw_sp_qdisc->num_classes; i++) {1257		mlxsw_sp_port_prio_tc_set(mlxsw_sp_port, i,1258					  MLXSW_SP_PORT_DEFAULT_TCLASS);1259		mlxsw_sp_port_ets_set(mlxsw_sp_port,1260				      MLXSW_REG_QEEC_HR_SUBGROUP,1261				      i, 0, false, 0);1262	}1263 1264	kfree(mlxsw_sp_qdisc->ets_data);1265	mlxsw_sp_qdisc->ets_data = NULL;1266	return 0;1267}1268 1269static int1270mlxsw_sp_qdisc_prio_destroy(struct mlxsw_sp_port *mlxsw_sp_port,1271			    struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)1272{1273	return __mlxsw_sp_qdisc_ets_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);1274}1275 1276static int1277__mlxsw_sp_qdisc_ets_check_params(unsigned int nbands)1278{1279	if (nbands > IEEE_8021QAZ_MAX_TCS)1280		return -EOPNOTSUPP;1281 1282	return 0;1283}1284 1285static int1286mlxsw_sp_qdisc_prio_check_params(struct mlxsw_sp_port *mlxsw_sp_port,1287				 void *params)1288{1289	struct tc_prio_qopt_offload_params *p = params;1290 1291	return __mlxsw_sp_qdisc_ets_check_params(p->bands);1292}1293 1294static struct mlxsw_sp_qdisc *1295mlxsw_sp_qdisc_walk_cb_clean_stats(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,1296				   void *mlxsw_sp_port)1297{1298	u64 backlog;1299 1300	if (mlxsw_sp_qdisc->ops) {1301		backlog = mlxsw_sp_qdisc->stats_base.backlog;1302		if (mlxsw_sp_qdisc->ops->clean_stats)1303			mlxsw_sp_qdisc->ops->clean_stats(mlxsw_sp_port,1304							 mlxsw_sp_qdisc);1305		mlxsw_sp_qdisc->stats_base.backlog = backlog;1306	}1307 1308	return NULL;1309}1310 1311static void1312mlxsw_sp_qdisc_tree_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port,1313				struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)1314{1315	mlxsw_sp_qdisc_walk(mlxsw_sp_qdisc, mlxsw_sp_qdisc_walk_cb_clean_stats,1316			    mlxsw_sp_port);1317}1318 1319static int1320__mlxsw_sp_qdisc_ets_replace(struct mlxsw_sp_port *mlxsw_sp_port,1321			     struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,1322			     u32 handle, unsigned int nbands,1323			     const unsigned int *quanta,1324			     const unsigned int *weights,1325			     const u8 *priomap)1326{1327	struct mlxsw_sp_qdisc_ets_data *ets_data = mlxsw_sp_qdisc->ets_data;1328	struct mlxsw_sp_qdisc_ets_band *ets_band;1329	struct mlxsw_sp_qdisc *child_qdisc;1330	u8 old_priomap, new_priomap;1331	int i, band;1332	int err;1333 1334	if (!ets_data) {1335		ets_data = kzalloc(sizeof(*ets_data), GFP_KERNEL);1336		if (!ets_data)1337			return -ENOMEM;1338		mlxsw_sp_qdisc->ets_data = ets_data;1339 1340		for (band = 0; band < mlxsw_sp_qdisc->num_classes; band++) {1341			int tclass_num = MLXSW_SP_PRIO_BAND_TO_TCLASS(band);1342 1343			ets_band = &ets_data->bands[band];1344			ets_band->tclass_num = tclass_num;1345		}1346	}1347 1348	for (band = 0; band < nbands; band++) {1349		int tclass_num;1350 1351		child_qdisc = &mlxsw_sp_qdisc->qdiscs[band];1352		ets_band = &ets_data->bands[band];1353 1354		tclass_num = ets_band->tclass_num;1355		old_priomap = ets_band->prio_bitmap;1356		new_priomap = 0;1357 1358		err = mlxsw_sp_port_ets_set(mlxsw_sp_port,1359					    MLXSW_REG_QEEC_HR_SUBGROUP,1360					    tclass_num, 0, !!quanta[band],1361					    weights[band]);1362		if (err)1363			return err;1364 1365		for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {1366			if (priomap[i] == band) {1367				new_priomap |= BIT(i);1368				if (BIT(i) & old_priomap)1369					continue;1370				err = mlxsw_sp_port_prio_tc_set(mlxsw_sp_port,1371								i, tclass_num);1372				if (err)1373					return err;1374			}1375		}1376 1377		ets_band->prio_bitmap = new_priomap;1378 1379		if (old_priomap != new_priomap)1380			mlxsw_sp_qdisc_tree_clean_stats(mlxsw_sp_port,1381							child_qdisc);1382 1383		err = mlxsw_sp_qdisc_future_fifo_replace(mlxsw_sp_port, handle,1384							 band, child_qdisc);1385		if (err)1386			return err;1387	}1388	for (; band < IEEE_8021QAZ_MAX_TCS; band++) {1389		ets_band = &ets_data->bands[band];1390		ets_band->prio_bitmap = 0;1391 1392		child_qdisc = &mlxsw_sp_qdisc->qdiscs[band];1393		mlxsw_sp_qdisc_destroy(mlxsw_sp_port, child_qdisc);1394 1395		mlxsw_sp_port_ets_set(mlxsw_sp_port,1396				      MLXSW_REG_QEEC_HR_SUBGROUP,1397				      ets_band->tclass_num, 0, false, 0);1398	}1399 1400	mlxsw_sp_qdisc_future_fifos_init(mlxsw_sp_port, TC_H_UNSPEC);1401	return 0;1402}1403 1404static int1405mlxsw_sp_qdisc_prio_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,1406			    struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,1407			    void *params)1408{1409	struct tc_prio_qopt_offload_params *p = params;1410	unsigned int zeroes[TCQ_ETS_MAX_BANDS] = {0};1411 1412	return __mlxsw_sp_qdisc_ets_replace(mlxsw_sp_port, mlxsw_sp_qdisc,1413					    handle, p->bands, zeroes,1414					    zeroes, p->priomap);1415}1416 1417static void1418__mlxsw_sp_qdisc_ets_unoffload(struct mlxsw_sp_port *mlxsw_sp_port,1419			       struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,1420			       struct gnet_stats_queue *qstats)1421{1422	u64 backlog;1423 1424	backlog = mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp,1425				       mlxsw_sp_qdisc->stats_base.backlog);1426	qstats->backlog -= backlog;1427}1428 1429static void1430mlxsw_sp_qdisc_prio_unoffload(struct mlxsw_sp_port *mlxsw_sp_port,1431			      struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,1432			      void *params)1433{1434	struct tc_prio_qopt_offload_params *p = params;1435 1436	__mlxsw_sp_qdisc_ets_unoffload(mlxsw_sp_port, mlxsw_sp_qdisc,1437				       p->qstats);1438}1439 1440static int1441mlxsw_sp_qdisc_get_prio_stats(struct mlxsw_sp_port *mlxsw_sp_port,1442			      struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,1443			      struct tc_qopt_offload_stats *stats_ptr)1444{1445	struct mlxsw_sp_qdisc *tc_qdisc;1446	u64 tx_packets = 0;1447	u64 tx_bytes = 0;1448	u64 backlog = 0;1449	u64 drops = 0;1450	int i;1451 1452	for (i = 0; i < mlxsw_sp_qdisc->num_classes; i++) {1453		tc_qdisc = &mlxsw_sp_qdisc->qdiscs[i];1454		mlxsw_sp_qdisc_collect_tc_stats(mlxsw_sp_port, tc_qdisc,1455						&tx_bytes, &tx_packets,1456						&drops, &backlog);1457	}1458 1459	mlxsw_sp_qdisc_update_stats(mlxsw_sp_port->mlxsw_sp, mlxsw_sp_qdisc,1460				    tx_bytes, tx_packets, drops, backlog,1461				    stats_ptr);1462	return 0;1463}1464 1465static void1466mlxsw_sp_setup_tc_qdisc_prio_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port,1467					 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)1468{1469	struct mlxsw_sp_qdisc_stats *stats_base;1470	struct mlxsw_sp_port_xstats *xstats;1471	struct rtnl_link_stats64 *stats;1472	int i;1473 1474	xstats = &mlxsw_sp_port->periodic_hw_stats.xstats;1475	stats = &mlxsw_sp_port->periodic_hw_stats.stats;1476	stats_base = &mlxsw_sp_qdisc->stats_base;1477 1478	stats_base->tx_packets = stats->tx_packets;1479	stats_base->tx_bytes = stats->tx_bytes;1480 1481	stats_base->drops = 0;1482	for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {1483		stats_base->drops += mlxsw_sp_xstats_tail_drop(xstats, i);1484		stats_base->drops += xstats->wred_drop[i];1485	}1486 1487	mlxsw_sp_qdisc->stats_base.backlog = 0;1488}1489 1490static struct mlxsw_sp_qdisc *1491mlxsw_sp_qdisc_prio_find_class(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,1492			       u32 parent)1493{1494	int child_index = TC_H_MIN(parent);1495	int band = child_index - 1;1496 1497	if (band < 0 || band >= mlxsw_sp_qdisc->num_classes)1498		return NULL;1499	return &mlxsw_sp_qdisc->qdiscs[band];1500}1501 1502static struct mlxsw_sp_qdisc_ets_band *1503mlxsw_sp_qdisc_ets_get_band(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,1504			    struct mlxsw_sp_qdisc *child)1505{1506	unsigned int band = child - mlxsw_sp_qdisc->qdiscs;1507 1508	if (WARN_ON(band >= IEEE_8021QAZ_MAX_TCS))1509		band = 0;1510	return &mlxsw_sp_qdisc->ets_data->bands[band];1511}1512 1513static u81514mlxsw_sp_qdisc_ets_get_prio_bitmap(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,1515				   struct mlxsw_sp_qdisc *child)1516{1517	return mlxsw_sp_qdisc_ets_get_band(mlxsw_sp_qdisc, child)->prio_bitmap;1518}1519 1520static int1521mlxsw_sp_qdisc_ets_get_tclass_num(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,1522				  struct mlxsw_sp_qdisc *child)1523{1524	return mlxsw_sp_qdisc_ets_get_band(mlxsw_sp_qdisc, child)->tclass_num;1525}1526 1527static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_prio = {1528	.type = MLXSW_SP_QDISC_PRIO,1529	.check_params = mlxsw_sp_qdisc_prio_check_params,1530	.replace = mlxsw_sp_qdisc_prio_replace,1531	.unoffload = mlxsw_sp_qdisc_prio_unoffload,1532	.destroy = mlxsw_sp_qdisc_prio_destroy,1533	.get_stats = mlxsw_sp_qdisc_get_prio_stats,1534	.clean_stats = mlxsw_sp_setup_tc_qdisc_prio_clean_stats,1535	.find_class = mlxsw_sp_qdisc_prio_find_class,1536	.num_classes = IEEE_8021QAZ_MAX_TCS,1537	.get_prio_bitmap = mlxsw_sp_qdisc_ets_get_prio_bitmap,1538	.get_tclass_num = mlxsw_sp_qdisc_ets_get_tclass_num,1539};1540 1541static int1542mlxsw_sp_qdisc_ets_check_params(struct mlxsw_sp_port *mlxsw_sp_port,1543				void *params)1544{1545	struct tc_ets_qopt_offload_replace_params *p = params;1546 1547	return __mlxsw_sp_qdisc_ets_check_params(p->bands);1548}1549 1550static int1551mlxsw_sp_qdisc_ets_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,1552			   struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,1553			   void *params)1554{1555	struct tc_ets_qopt_offload_replace_params *p = params;1556 1557	return __mlxsw_sp_qdisc_ets_replace(mlxsw_sp_port, mlxsw_sp_qdisc,1558					    handle, p->bands, p->quanta,1559					    p->weights, p->priomap);1560}1561 1562static void1563mlxsw_sp_qdisc_ets_unoffload(struct mlxsw_sp_port *mlxsw_sp_port,1564			     struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,1565			     void *params)1566{1567	struct tc_ets_qopt_offload_replace_params *p = params;1568 1569	__mlxsw_sp_qdisc_ets_unoffload(mlxsw_sp_port, mlxsw_sp_qdisc,1570				       p->qstats);1571}1572 1573static int1574mlxsw_sp_qdisc_ets_destroy(struct mlxsw_sp_port *mlxsw_sp_port,1575			   struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)1576{1577	return __mlxsw_sp_qdisc_ets_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);1578}1579 1580static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_ets = {1581	.type = MLXSW_SP_QDISC_ETS,1582	.check_params = mlxsw_sp_qdisc_ets_check_params,1583	.replace = mlxsw_sp_qdisc_ets_replace,1584	.unoffload = mlxsw_sp_qdisc_ets_unoffload,1585	.destroy = mlxsw_sp_qdisc_ets_destroy,1586	.get_stats = mlxsw_sp_qdisc_get_prio_stats,1587	.clean_stats = mlxsw_sp_setup_tc_qdisc_prio_clean_stats,1588	.find_class = mlxsw_sp_qdisc_prio_find_class,1589	.num_classes = IEEE_8021QAZ_MAX_TCS,1590	.get_prio_bitmap = mlxsw_sp_qdisc_ets_get_prio_bitmap,1591	.get_tclass_num = mlxsw_sp_qdisc_ets_get_tclass_num,1592};1593 1594/* Linux allows linking of Qdiscs to arbitrary classes (so long as the resulting1595 * graph is free of cycles). These operations do not change the parent handle1596 * though, which means it can be incomplete (if there is more than one class1597 * where the Qdisc in question is grafted) or outright wrong (if the Qdisc was1598 * linked to a different class and then removed from the original class).1599 *1600 * E.g. consider this sequence of operations:1601 *1602 *  # tc qdisc add dev swp1 root handle 1: prio1603 *  # tc qdisc add dev swp1 parent 1:3 handle 13: red limit 1000000 avpkt 100001604 *  RED: set bandwidth to 10Mbit1605 *  # tc qdisc link dev swp1 handle 13: parent 1:21606 *1607 * At this point, both 1:2 and 1:3 have the same RED Qdisc instance as their1608 * child. But RED will still only claim that 1:3 is its parent. If it's removed1609 * from that band, its only parent will be 1:2, but it will continue to claim1610 * that it is in fact 1:3.1611 *1612 * The notification for child Qdisc replace (e.g. TC_RED_REPLACE) comes before1613 * the notification for parent graft (e.g. TC_PRIO_GRAFT). We take the replace1614 * notification to offload the child Qdisc, based on its parent handle, and use1615 * the graft operation to validate that the class where the child is actually1616 * grafted corresponds to the parent handle. If the two don't match, we1617 * unoffload the child.1618 */1619static int mlxsw_sp_qdisc_graft(struct mlxsw_sp_port *mlxsw_sp_port,1620				struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,1621				u8 band, u32 child_handle)1622{1623	struct mlxsw_sp_qdisc *old_qdisc;1624	u32 parent;1625 1626	if (band < mlxsw_sp_qdisc->num_classes &&1627	    mlxsw_sp_qdisc->qdiscs[band].handle == child_handle)1628		return 0;1629 1630	if (!child_handle) {1631		/* This is an invisible FIFO replacing the original Qdisc.1632		 * Ignore it--the original Qdisc's destroy will follow.1633		 */1634		return 0;1635	}1636 1637	/* See if the grafted qdisc is already offloaded on any tclass. If so,1638	 * unoffload it.1639	 */1640	old_qdisc = mlxsw_sp_qdisc_find_by_handle(mlxsw_sp_port,1641						  child_handle);1642	if (old_qdisc)1643		mlxsw_sp_qdisc_destroy(mlxsw_sp_port, old_qdisc);1644 1645	parent = TC_H_MAKE(mlxsw_sp_qdisc->handle, band + 1);1646	mlxsw_sp_qdisc = mlxsw_sp_qdisc->ops->find_class(mlxsw_sp_qdisc,1647							 parent);1648	if (!WARN_ON(!mlxsw_sp_qdisc))1649		mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);1650 1651	return -EOPNOTSUPP;1652}1653 1654static int __mlxsw_sp_setup_tc_prio(struct mlxsw_sp_port *mlxsw_sp_port,1655				    struct tc_prio_qopt_offload *p)1656{1657	struct mlxsw_sp_qdisc *mlxsw_sp_qdisc;1658 1659	mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent);1660	if (!mlxsw_sp_qdisc)1661		return -EOPNOTSUPP;1662 1663	if (p->command == TC_PRIO_REPLACE)1664		return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle,1665					      mlxsw_sp_qdisc,1666					      &mlxsw_sp_qdisc_ops_prio,1667					      &p->replace_params);1668 1669	if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle))1670		return -EOPNOTSUPP;1671 1672	switch (p->command) {1673	case TC_PRIO_DESTROY:1674		return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);1675	case TC_PRIO_STATS:1676		return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc,1677						&p->stats);1678	case TC_PRIO_GRAFT:1679		return mlxsw_sp_qdisc_graft(mlxsw_sp_port, mlxsw_sp_qdisc,1680					    p->graft_params.band,1681					    p->graft_params.child_handle);1682	default:1683		return -EOPNOTSUPP;1684	}1685}1686 1687int mlxsw_sp_setup_tc_prio(struct mlxsw_sp_port *mlxsw_sp_port,1688			   struct tc_prio_qopt_offload *p)1689{1690	int err;1691 1692	mutex_lock(&mlxsw_sp_port->qdisc->lock);1693	err = __mlxsw_sp_setup_tc_prio(mlxsw_sp_port, p);1694	mutex_unlock(&mlxsw_sp_port->qdisc->lock);1695 1696	return err;1697}1698 1699static int __mlxsw_sp_setup_tc_ets(struct mlxsw_sp_port *mlxsw_sp_port,1700				   struct tc_ets_qopt_offload *p)1701{1702	struct mlxsw_sp_qdisc *mlxsw_sp_qdisc;1703 1704	mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent);1705	if (!mlxsw_sp_qdisc)1706		return -EOPNOTSUPP;1707 1708	if (p->command == TC_ETS_REPLACE)1709		return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle,1710					      mlxsw_sp_qdisc,1711					      &mlxsw_sp_qdisc_ops_ets,1712					      &p->replace_params);1713 1714	if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle))1715		return -EOPNOTSUPP;1716 1717	switch (p->command) {1718	case TC_ETS_DESTROY:1719		return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);1720	case TC_ETS_STATS:1721		return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc,1722						&p->stats);1723	case TC_ETS_GRAFT:1724		return mlxsw_sp_qdisc_graft(mlxsw_sp_port, mlxsw_sp_qdisc,1725					    p->graft_params.band,1726					    p->graft_params.child_handle);1727	default:1728		return -EOPNOTSUPP;1729	}1730}1731 1732int mlxsw_sp_setup_tc_ets(struct mlxsw_sp_port *mlxsw_sp_port,1733			  struct tc_ets_qopt_offload *p)1734{1735	int err;1736 1737	mutex_lock(&mlxsw_sp_port->qdisc->lock);1738	err = __mlxsw_sp_setup_tc_ets(mlxsw_sp_port, p);1739	mutex_unlock(&mlxsw_sp_port->qdisc->lock);1740 1741	return err;1742}1743 1744struct mlxsw_sp_qevent_block {1745	struct list_head binding_list;1746	struct list_head mall_entry_list;1747	struct mlxsw_sp *mlxsw_sp;1748};1749 1750struct mlxsw_sp_qevent_binding {1751	struct list_head list;1752	struct mlxsw_sp_port *mlxsw_sp_port;1753	u32 handle;1754	int tclass_num;1755	enum mlxsw_sp_span_trigger span_trigger;1756	unsigned int action_mask;1757};1758 1759static LIST_HEAD(mlxsw_sp_qevent_block_cb_list);1760 1761static int mlxsw_sp_qevent_span_configure(struct mlxsw_sp *mlxsw_sp,1762					  struct mlxsw_sp_mall_entry *mall_entry,1763					  struct mlxsw_sp_qevent_binding *qevent_binding,1764					  const struct mlxsw_sp_span_agent_parms *agent_parms,1765					  int *p_span_id)1766{1767	enum mlxsw_sp_span_trigger span_trigger = qevent_binding->span_trigger;1768	struct mlxsw_sp_port *mlxsw_sp_port = qevent_binding->mlxsw_sp_port;1769	struct mlxsw_sp_span_trigger_parms trigger_parms = {};1770	bool ingress;1771	int span_id;1772	int err;1773 1774	err = mlxsw_sp_span_agent_get(mlxsw_sp, &span_id, agent_parms);1775	if (err)1776		return err;1777 1778	ingress = mlxsw_sp_span_trigger_is_ingress(span_trigger);1779	err = mlxsw_sp_span_analyzed_port_get(mlxsw_sp_port, ingress);1780	if (err)1781		goto err_analyzed_port_get;1782 1783	trigger_parms.span_id = span_id;1784	trigger_parms.probability_rate = 1;1785	err = mlxsw_sp_span_agent_bind(mlxsw_sp, span_trigger, mlxsw_sp_port,1786				       &trigger_parms);1787	if (err)1788		goto err_agent_bind;1789 1790	err = mlxsw_sp_span_trigger_enable(mlxsw_sp_port, span_trigger,1791					   qevent_binding->tclass_num);1792	if (err)1793		goto err_trigger_enable;1794 1795	*p_span_id = span_id;1796	return 0;1797 1798err_trigger_enable:1799	mlxsw_sp_span_agent_unbind(mlxsw_sp, span_trigger, mlxsw_sp_port,1800				   &trigger_parms);1801err_agent_bind:1802	mlxsw_sp_span_analyzed_port_put(mlxsw_sp_port, ingress);1803err_analyzed_port_get:1804	mlxsw_sp_span_agent_put(mlxsw_sp, span_id);1805	return err;1806}1807 1808static void mlxsw_sp_qevent_span_deconfigure(struct mlxsw_sp *mlxsw_sp,1809					     struct mlxsw_sp_qevent_binding *qevent_binding,1810					     int span_id)1811{1812	enum mlxsw_sp_span_trigger span_trigger = qevent_binding->span_trigger;1813	struct mlxsw_sp_port *mlxsw_sp_port = qevent_binding->mlxsw_sp_port;1814	struct mlxsw_sp_span_trigger_parms trigger_parms = {1815		.span_id = span_id,1816	};1817	bool ingress;1818 1819	ingress = mlxsw_sp_span_trigger_is_ingress(span_trigger);1820 1821	mlxsw_sp_span_trigger_disable(mlxsw_sp_port, span_trigger,1822				      qevent_binding->tclass_num);1823	mlxsw_sp_span_agent_unbind(mlxsw_sp, span_trigger, mlxsw_sp_port,1824				   &trigger_parms);1825	mlxsw_sp_span_analyzed_port_put(mlxsw_sp_port, ingress);1826	mlxsw_sp_span_agent_put(mlxsw_sp, span_id);1827}1828 1829static int mlxsw_sp_qevent_mirror_configure(struct mlxsw_sp *mlxsw_sp,1830					    struct mlxsw_sp_mall_entry *mall_entry,1831					    struct mlxsw_sp_qevent_binding *qevent_binding)1832{1833	struct mlxsw_sp_span_agent_parms agent_parms = {1834		.to_dev = mall_entry->mirror.to_dev,1835	};1836 1837	return mlxsw_sp_qevent_span_configure(mlxsw_sp, mall_entry, qevent_binding,1838					      &agent_parms, &mall_entry->mirror.span_id);1839}1840 1841static void mlxsw_sp_qevent_mirror_deconfigure(struct mlxsw_sp *mlxsw_sp,1842					       struct mlxsw_sp_mall_entry *mall_entry,1843					       struct mlxsw_sp_qevent_binding *qevent_binding)1844{1845	mlxsw_sp_qevent_span_deconfigure(mlxsw_sp, qevent_binding, mall_entry->mirror.span_id);1846}1847 1848static int mlxsw_sp_qevent_trap_configure(struct mlxsw_sp *mlxsw_sp,1849					  struct mlxsw_sp_mall_entry *mall_entry,1850					  struct mlxsw_sp_qevent_binding *qevent_binding)1851{1852	struct mlxsw_sp_span_agent_parms agent_parms = {1853		.session_id = MLXSW_SP_SPAN_SESSION_ID_BUFFER,1854	};1855	int err;1856 1857	err = mlxsw_sp_trap_group_policer_hw_id_get(mlxsw_sp,1858						    DEVLINK_TRAP_GROUP_GENERIC_ID_BUFFER_DROPS,1859						    &agent_parms.policer_enable,1860						    &agent_parms.policer_id);1861	if (err)1862		return err;1863 1864	return mlxsw_sp_qevent_span_configure(mlxsw_sp, mall_entry, qevent_binding,1865					      &agent_parms, &mall_entry->trap.span_id);1866}1867 1868static void mlxsw_sp_qevent_trap_deconfigure(struct mlxsw_sp *mlxsw_sp,1869					     struct mlxsw_sp_mall_entry *mall_entry,1870					     struct mlxsw_sp_qevent_binding *qevent_binding)1871{1872	mlxsw_sp_qevent_span_deconfigure(mlxsw_sp, qevent_binding, mall_entry->trap.span_id);1873}1874 1875static int1876mlxsw_sp_qevent_entry_configure(struct mlxsw_sp *mlxsw_sp,1877				struct mlxsw_sp_mall_entry *mall_entry,1878				struct mlxsw_sp_qevent_binding *qevent_binding,1879				struct netlink_ext_ack *extack)1880{1881	if (!(BIT(mall_entry->type) & qevent_binding->action_mask)) {1882		NL_SET_ERR_MSG(extack, "Action not supported at this qevent");1883		return -EOPNOTSUPP;1884	}1885 1886	switch (mall_entry->type) {1887	case MLXSW_SP_MALL_ACTION_TYPE_MIRROR:1888		return mlxsw_sp_qevent_mirror_configure(mlxsw_sp, mall_entry, qevent_binding);1889	case MLXSW_SP_MALL_ACTION_TYPE_TRAP:1890		return mlxsw_sp_qevent_trap_configure(mlxsw_sp, mall_entry, qevent_binding);1891	default:1892		/* This should have been validated away. */1893		WARN_ON(1);1894		return -EOPNOTSUPP;1895	}1896}1897 1898static void mlxsw_sp_qevent_entry_deconfigure(struct mlxsw_sp *mlxsw_sp,1899					      struct mlxsw_sp_mall_entry *mall_entry,1900					      struct mlxsw_sp_qevent_binding *qevent_binding)1901{1902	switch (mall_entry->type) {1903	case MLXSW_SP_MALL_ACTION_TYPE_MIRROR:1904		return mlxsw_sp_qevent_mirror_deconfigure(mlxsw_sp, mall_entry, qevent_binding);1905	case MLXSW_SP_MALL_ACTION_TYPE_TRAP:1906		return mlxsw_sp_qevent_trap_deconfigure(mlxsw_sp, mall_entry, qevent_binding);1907	default:1908		WARN_ON(1);1909		return;1910	}1911}1912 1913static int1914mlxsw_sp_qevent_binding_configure(struct mlxsw_sp_qevent_block *qevent_block,1915				  struct mlxsw_sp_qevent_binding *qevent_binding,1916				  struct netlink_ext_ack *extack)1917{1918	struct mlxsw_sp_mall_entry *mall_entry;1919	int err;1920 1921	list_for_each_entry(mall_entry, &qevent_block->mall_entry_list, list) {1922		err = mlxsw_sp_qevent_entry_configure(qevent_block->mlxsw_sp, mall_entry,1923						      qevent_binding, extack);1924		if (err)1925			goto err_entry_configure;1926	}1927 1928	return 0;1929 1930err_entry_configure:1931	list_for_each_entry_continue_reverse(mall_entry, &qevent_block->mall_entry_list, list)1932		mlxsw_sp_qevent_entry_deconfigure(qevent_block->mlxsw_sp, mall_entry,1933						  qevent_binding);1934	return err;1935}1936 1937static void mlxsw_sp_qevent_binding_deconfigure(struct mlxsw_sp_qevent_block *qevent_block,1938						struct mlxsw_sp_qevent_binding *qevent_binding)1939{1940	struct mlxsw_sp_mall_entry *mall_entry;1941 1942	list_for_each_entry(mall_entry, &qevent_block->mall_entry_list, list)1943		mlxsw_sp_qevent_entry_deconfigure(qevent_block->mlxsw_sp, mall_entry,1944						  qevent_binding);1945}1946 1947static int1948mlxsw_sp_qevent_block_configure(struct mlxsw_sp_qevent_block *qevent_block,1949				struct netlink_ext_ack *extack)1950{1951	struct mlxsw_sp_qevent_binding *qevent_binding;1952	int err;1953 1954	list_for_each_entry(qevent_binding, &qevent_block->binding_list, list) {1955		err = mlxsw_sp_qevent_binding_configure(qevent_block,1956							qevent_binding,1957							extack);1958		if (err)1959			goto err_binding_configure;1960	}1961 1962	return 0;1963 1964err_binding_configure:1965	list_for_each_entry_continue_reverse(qevent_binding, &qevent_block->binding_list, list)1966		mlxsw_sp_qevent_binding_deconfigure(qevent_block, qevent_binding);1967	return err;1968}1969 1970static void mlxsw_sp_qevent_block_deconfigure(struct mlxsw_sp_qevent_block *qevent_block)1971{1972	struct mlxsw_sp_qevent_binding *qevent_binding;1973 1974	list_for_each_entry(qevent_binding, &qevent_block->binding_list, list)1975		mlxsw_sp_qevent_binding_deconfigure(qevent_block, qevent_binding);1976}1977 1978static struct mlxsw_sp_mall_entry *1979mlxsw_sp_qevent_mall_entry_find(struct mlxsw_sp_qevent_block *block, unsigned long cookie)1980{1981	struct mlxsw_sp_mall_entry *mall_entry;1982 1983	list_for_each_entry(mall_entry, &block->mall_entry_list, list)1984		if (mall_entry->cookie == cookie)1985			return mall_entry;1986 1987	return NULL;1988}1989 1990static int mlxsw_sp_qevent_mall_replace(struct mlxsw_sp *mlxsw_sp,1991					struct mlxsw_sp_qevent_block *qevent_block,1992					struct tc_cls_matchall_offload *f)1993{1994	struct mlxsw_sp_mall_entry *mall_entry;1995	struct flow_action_entry *act;1996	int err;1997 1998	/* It should not currently be possible to replace a matchall rule. So1999	 * this must be a new rule.2000	 */2001	if (!list_empty(&qevent_block->mall_entry_list)) {2002		NL_SET_ERR_MSG(f->common.extack, "At most one filter supported");2003		return -EOPNOTSUPP;2004	}2005	if (f->rule->action.num_entries != 1) {2006		NL_SET_ERR_MSG(f->common.extack, "Only singular actions supported");2007		return -EOPNOTSUPP;2008	}2009	if (f->common.chain_index) {2010		NL_SET_ERR_MSG(f->common.extack, "Only chain 0 is supported");2011		return -EOPNOTSUPP;2012	}2013	if (f->common.protocol != htons(ETH_P_ALL)) {2014		NL_SET_ERR_MSG(f->common.extack, "Protocol matching not supported");2015		return -EOPNOTSUPP;2016	}2017 2018	act = &f->rule->action.entries[0];2019	if (!(act->hw_stats & FLOW_ACTION_HW_STATS_DISABLED)) {2020		NL_SET_ERR_MSG(f->common.extack, "HW counters not supported on qevents");2021		return -EOPNOTSUPP;2022	}2023 2024	mall_entry = kzalloc(sizeof(*mall_entry), GFP_KERNEL);2025	if (!mall_entry)2026		return -ENOMEM;2027	mall_entry->cookie = f->cookie;2028 2029	if (act->id == FLOW_ACTION_MIRRED) {2030		mall_entry->type = MLXSW_SP_MALL_ACTION_TYPE_MIRROR;2031		mall_entry->mirror.to_dev = act->dev;2032	} else if (act->id == FLOW_ACTION_TRAP) {2033		mall_entry->type = MLXSW_SP_MALL_ACTION_TYPE_TRAP;2034	} else {2035		NL_SET_ERR_MSG(f->common.extack, "Unsupported action");2036		err = -EOPNOTSUPP;2037		goto err_unsupported_action;2038	}2039 2040	list_add_tail(&mall_entry->list, &qevent_block->mall_entry_list);2041 2042	err = mlxsw_sp_qevent_block_configure(qevent_block, f->common.extack);2043	if (err)2044		goto err_block_configure;2045 2046	return 0;2047 2048err_block_configure:2049	list_del(&mall_entry->list);2050err_unsupported_action:2051	kfree(mall_entry);2052	return err;2053}2054 2055static void mlxsw_sp_qevent_mall_destroy(struct mlxsw_sp_qevent_block *qevent_block,2056					 struct tc_cls_matchall_offload *f)2057{2058	struct mlxsw_sp_mall_entry *mall_entry;2059 2060	mall_entry = mlxsw_sp_qevent_mall_entry_find(qevent_block, f->cookie);2061	if (!mall_entry)2062		return;2063 2064	mlxsw_sp_qevent_block_deconfigure(qevent_block);2065 2066	list_del(&mall_entry->list);2067	kfree(mall_entry);2068}2069 2070static int mlxsw_sp_qevent_block_mall_cb(struct mlxsw_sp_qevent_block *qevent_block,2071					 struct tc_cls_matchall_offload *f)2072{2073	struct mlxsw_sp *mlxsw_sp = qevent_block->mlxsw_sp;2074 2075	switch (f->command) {2076	case TC_CLSMATCHALL_REPLACE:2077		return mlxsw_sp_qevent_mall_replace(mlxsw_sp, qevent_block, f);2078	case TC_CLSMATCHALL_DESTROY:2079		mlxsw_sp_qevent_mall_destroy(qevent_block, f);2080		return 0;2081	default:2082		return -EOPNOTSUPP;2083	}2084}2085 2086static int mlxsw_sp_qevent_block_cb(enum tc_setup_type type, void *type_data, void *cb_priv)2087{2088	struct mlxsw_sp_qevent_block *qevent_block = cb_priv;2089 2090	switch (type) {2091	case TC_SETUP_CLSMATCHALL:2092		return mlxsw_sp_qevent_block_mall_cb(qevent_block, type_data);2093	default:2094		return -EOPNOTSUPP;2095	}2096}2097 2098static struct mlxsw_sp_qevent_block *mlxsw_sp_qevent_block_create(struct mlxsw_sp *mlxsw_sp,2099								  struct net *net)2100{2101	struct mlxsw_sp_qevent_block *qevent_block;2102 2103	qevent_block = kzalloc(sizeof(*qevent_block), GFP_KERNEL);2104	if (!qevent_block)2105		return NULL;2106 2107	INIT_LIST_HEAD(&qevent_block->binding_list);2108	INIT_LIST_HEAD(&qevent_block->mall_entry_list);2109	qevent_block->mlxsw_sp = mlxsw_sp;2110	return qevent_block;2111}2112 2113static void2114mlxsw_sp_qevent_block_destroy(struct mlxsw_sp_qevent_block *qevent_block)2115{2116	WARN_ON(!list_empty(&qevent_block->binding_list));2117	WARN_ON(!list_empty(&qevent_block->mall_entry_list));2118	kfree(qevent_block);2119}2120 2121static void mlxsw_sp_qevent_block_release(void *cb_priv)2122{2123	struct mlxsw_sp_qevent_block *qevent_block = cb_priv;2124 2125	mlxsw_sp_qevent_block_destroy(qevent_block);2126}2127 2128static struct mlxsw_sp_qevent_binding *2129mlxsw_sp_qevent_binding_create(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, int tclass_num,2130			       enum mlxsw_sp_span_trigger span_trigger,2131			       unsigned int action_mask)2132{2133	struct mlxsw_sp_qevent_binding *binding;2134 2135	binding = kzalloc(sizeof(*binding), GFP_KERNEL);2136	if (!binding)2137		return ERR_PTR(-ENOMEM);2138 2139	binding->mlxsw_sp_port = mlxsw_sp_port;2140	binding->handle = handle;2141	binding->tclass_num = tclass_num;2142	binding->span_trigger = span_trigger;2143	binding->action_mask = action_mask;2144	return binding;2145}2146 2147static void2148mlxsw_sp_qevent_binding_destroy(struct mlxsw_sp_qevent_binding *binding)2149{2150	kfree(binding);2151}2152 2153static struct mlxsw_sp_qevent_binding *2154mlxsw_sp_qevent_binding_lookup(struct mlxsw_sp_qevent_block *block,2155			       struct mlxsw_sp_port *mlxsw_sp_port,2156			       u32 handle,2157			       enum mlxsw_sp_span_trigger span_trigger)2158{2159	struct mlxsw_sp_qevent_binding *qevent_binding;2160 2161	list_for_each_entry(qevent_binding, &block->binding_list, list)2162		if (qevent_binding->mlxsw_sp_port == mlxsw_sp_port &&2163		    qevent_binding->handle == handle &&2164		    qevent_binding->span_trigger == span_trigger)2165			return qevent_binding;2166	return NULL;2167}2168 2169static int2170mlxsw_sp_setup_tc_block_qevent_bind(struct mlxsw_sp_port *mlxsw_sp_port,2171				    struct flow_block_offload *f,2172				    enum mlxsw_sp_span_trigger span_trigger,2173				    unsigned int action_mask)2174{2175	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;2176	struct mlxsw_sp_qevent_binding *qevent_binding;2177	struct mlxsw_sp_qevent_block *qevent_block;2178	struct flow_block_cb *block_cb;2179	struct mlxsw_sp_qdisc *qdisc;2180	bool register_block = false;2181	int tclass_num;2182	int err;2183 2184	block_cb = flow_block_cb_lookup(f->block, mlxsw_sp_qevent_block_cb, mlxsw_sp);2185	if (!block_cb) {2186		qevent_block = mlxsw_sp_qevent_block_create(mlxsw_sp, f->net);2187		if (!qevent_block)2188			return -ENOMEM;2189		block_cb = flow_block_cb_alloc(mlxsw_sp_qevent_block_cb, mlxsw_sp, qevent_block,2190					       mlxsw_sp_qevent_block_release);2191		if (IS_ERR(block_cb)) {2192			mlxsw_sp_qevent_block_destroy(qevent_block);2193			return PTR_ERR(block_cb);2194		}2195		register_block = true;2196	} else {2197		qevent_block = flow_block_cb_priv(block_cb);2198	}2199	flow_block_cb_incref(block_cb);2200 2201	qdisc = mlxsw_sp_qdisc_find_by_handle(mlxsw_sp_port, f->sch->handle);2202	if (!qdisc) {2203		NL_SET_ERR_MSG(f->extack, "Qdisc not offloaded");2204		err = -ENOENT;2205		goto err_find_qdisc;2206	}2207 2208	if (WARN_ON(mlxsw_sp_qevent_binding_lookup(qevent_block, mlxsw_sp_port, f->sch->handle,2209						   span_trigger))) {2210		err = -EEXIST;2211		goto err_binding_exists;2212	}2213 2214	tclass_num = mlxsw_sp_qdisc_get_tclass_num(mlxsw_sp_port, qdisc);2215	qevent_binding = mlxsw_sp_qevent_binding_create(mlxsw_sp_port,2216							f->sch->handle,2217							tclass_num,2218							span_trigger,2219							action_mask);2220	if (IS_ERR(qevent_binding)) {2221		err = PTR_ERR(qevent_binding);2222		goto err_binding_create;2223	}2224 2225	err = mlxsw_sp_qevent_binding_configure(qevent_block, qevent_binding,2226						f->extack);2227	if (err)2228		goto err_binding_configure;2229 2230	list_add(&qevent_binding->list, &qevent_block->binding_list);2231 2232	if (register_block) {2233		flow_block_cb_add(block_cb, f);2234		list_add_tail(&block_cb->driver_list, &mlxsw_sp_qevent_block_cb_list);2235	}2236 2237	return 0;2238 2239err_binding_configure:2240	mlxsw_sp_qevent_binding_destroy(qevent_binding);2241err_binding_create:2242err_binding_exists:2243err_find_qdisc:2244	if (!flow_block_cb_decref(block_cb))2245		flow_block_cb_free(block_cb);2246	return err;2247}2248 2249static void mlxsw_sp_setup_tc_block_qevent_unbind(struct mlxsw_sp_port *mlxsw_sp_port,2250						  struct flow_block_offload *f,2251						  enum mlxsw_sp_span_trigger span_trigger)2252{2253	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;2254	struct mlxsw_sp_qevent_binding *qevent_binding;2255	struct mlxsw_sp_qevent_block *qevent_block;2256	struct flow_block_cb *block_cb;2257 2258	block_cb = flow_block_cb_lookup(f->block, mlxsw_sp_qevent_block_cb, mlxsw_sp);2259	if (!block_cb)2260		return;2261	qevent_block = flow_block_cb_priv(block_cb);2262 2263	qevent_binding = mlxsw_sp_qevent_binding_lookup(qevent_block, mlxsw_sp_port, f->sch->handle,2264							span_trigger);2265	if (!qevent_binding)2266		return;2267 2268	list_del(&qevent_binding->list);2269	mlxsw_sp_qevent_binding_deconfigure(qevent_block, qevent_binding);2270	mlxsw_sp_qevent_binding_destroy(qevent_binding);2271 2272	if (!flow_block_cb_decref(block_cb)) {2273		flow_block_cb_remove(block_cb, f);2274		list_del(&block_cb->driver_list);2275	}2276}2277 2278static int2279mlxsw_sp_setup_tc_block_qevent(struct mlxsw_sp_port *mlxsw_sp_port,2280			       struct flow_block_offload *f,2281			       enum mlxsw_sp_span_trigger span_trigger,2282			       unsigned int action_mask)2283{2284	f->driver_block_list = &mlxsw_sp_qevent_block_cb_list;2285 2286	switch (f->command) {2287	case FLOW_BLOCK_BIND:2288		return mlxsw_sp_setup_tc_block_qevent_bind(mlxsw_sp_port, f,2289							   span_trigger,2290							   action_mask);2291	case FLOW_BLOCK_UNBIND:2292		mlxsw_sp_setup_tc_block_qevent_unbind(mlxsw_sp_port, f, span_trigger);2293		return 0;2294	default:2295		return -EOPNOTSUPP;2296	}2297}2298 2299int mlxsw_sp_setup_tc_block_qevent_early_drop(struct mlxsw_sp_port *mlxsw_sp_port,2300					      struct flow_block_offload *f)2301{2302	unsigned int action_mask = BIT(MLXSW_SP_MALL_ACTION_TYPE_MIRROR) |2303				   BIT(MLXSW_SP_MALL_ACTION_TYPE_TRAP);2304 2305	return mlxsw_sp_setup_tc_block_qevent(mlxsw_sp_port, f,2306					      MLXSW_SP_SPAN_TRIGGER_EARLY_DROP,2307					      action_mask);2308}2309 2310int mlxsw_sp_setup_tc_block_qevent_mark(struct mlxsw_sp_port *mlxsw_sp_port,2311					struct flow_block_offload *f)2312{2313	unsigned int action_mask = BIT(MLXSW_SP_MALL_ACTION_TYPE_MIRROR);2314 2315	return mlxsw_sp_setup_tc_block_qevent(mlxsw_sp_port, f,2316					      MLXSW_SP_SPAN_TRIGGER_ECN,2317					      action_mask);2318}2319 2320int mlxsw_sp_tc_qdisc_init(struct mlxsw_sp_port *mlxsw_sp_port)2321{2322	struct mlxsw_sp_qdisc_state *qdisc_state;2323 2324	qdisc_state = kzalloc(sizeof(*qdisc_state), GFP_KERNEL);2325	if (!qdisc_state)2326		return -ENOMEM;2327 2328	mutex_init(&qdisc_state->lock);2329	mlxsw_sp_port->qdisc = qdisc_state;2330	return 0;2331}2332 2333void mlxsw_sp_tc_qdisc_fini(struct mlxsw_sp_port *mlxsw_sp_port)2334{2335	mutex_destroy(&mlxsw_sp_port->qdisc->lock);2336	kfree(mlxsw_sp_port->qdisc);2337}2338