xref: /openbmc/linux/drivers/net/ethernet/mellanox/mlxsw/spectrum_policer.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
18d3fbae7SIdo Schimmel // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
28d3fbae7SIdo Schimmel /* Copyright (c) 2020 Mellanox Technologies. All rights reserved */
38d3fbae7SIdo Schimmel 
48d3fbae7SIdo Schimmel #include <linux/idr.h>
58d3fbae7SIdo Schimmel #include <linux/log2.h>
68d3fbae7SIdo Schimmel #include <linux/mutex.h>
78d3fbae7SIdo Schimmel #include <linux/netlink.h>
8bf038f03SIdo Schimmel #include <net/devlink.h>
98d3fbae7SIdo Schimmel 
108d3fbae7SIdo Schimmel #include "spectrum.h"
118d3fbae7SIdo Schimmel 
128d3fbae7SIdo Schimmel struct mlxsw_sp_policer_family {
138d3fbae7SIdo Schimmel 	enum mlxsw_sp_policer_type type;
148d3fbae7SIdo Schimmel 	enum mlxsw_reg_qpcr_g qpcr_type;
158d3fbae7SIdo Schimmel 	struct mlxsw_sp *mlxsw_sp;
168d3fbae7SIdo Schimmel 	u16 start_index; /* Inclusive */
178d3fbae7SIdo Schimmel 	u16 end_index; /* Exclusive */
188d3fbae7SIdo Schimmel 	struct idr policer_idr;
198d3fbae7SIdo Schimmel 	struct mutex lock; /* Protects policer_idr */
20bf038f03SIdo Schimmel 	atomic_t policers_count;
218d3fbae7SIdo Schimmel 	const struct mlxsw_sp_policer_family_ops *ops;
228d3fbae7SIdo Schimmel };
238d3fbae7SIdo Schimmel 
248d3fbae7SIdo Schimmel struct mlxsw_sp_policer {
258d3fbae7SIdo Schimmel 	struct mlxsw_sp_policer_params params;
268d3fbae7SIdo Schimmel 	u16 index;
278d3fbae7SIdo Schimmel };
288d3fbae7SIdo Schimmel 
298d3fbae7SIdo Schimmel struct mlxsw_sp_policer_family_ops {
308d3fbae7SIdo Schimmel 	int (*init)(struct mlxsw_sp_policer_family *family);
318d3fbae7SIdo Schimmel 	void (*fini)(struct mlxsw_sp_policer_family *family);
328d3fbae7SIdo Schimmel 	int (*policer_index_alloc)(struct mlxsw_sp_policer_family *family,
338d3fbae7SIdo Schimmel 				   struct mlxsw_sp_policer *policer);
348d3fbae7SIdo Schimmel 	struct mlxsw_sp_policer * (*policer_index_free)(struct mlxsw_sp_policer_family *family,
358d3fbae7SIdo Schimmel 							u16 policer_index);
368d3fbae7SIdo Schimmel 	int (*policer_init)(struct mlxsw_sp_policer_family *family,
378d3fbae7SIdo Schimmel 			    const struct mlxsw_sp_policer *policer);
388d3fbae7SIdo Schimmel 	int (*policer_params_check)(const struct mlxsw_sp_policer_family *family,
398d3fbae7SIdo Schimmel 				    const struct mlxsw_sp_policer_params *params,
408d3fbae7SIdo Schimmel 				    struct netlink_ext_ack *extack);
418d3fbae7SIdo Schimmel };
428d3fbae7SIdo Schimmel 
438d3fbae7SIdo Schimmel struct mlxsw_sp_policer_core {
448d3fbae7SIdo Schimmel 	struct mlxsw_sp_policer_family *family_arr[MLXSW_SP_POLICER_TYPE_MAX + 1];
458d3fbae7SIdo Schimmel 	const struct mlxsw_sp_policer_core_ops *ops;
468d3fbae7SIdo Schimmel 	u8 lowest_bs_bits;
478d3fbae7SIdo Schimmel 	u8 highest_bs_bits;
488d3fbae7SIdo Schimmel };
498d3fbae7SIdo Schimmel 
508d3fbae7SIdo Schimmel struct mlxsw_sp_policer_core_ops {
518d3fbae7SIdo Schimmel 	int (*init)(struct mlxsw_sp_policer_core *policer_core);
528d3fbae7SIdo Schimmel };
538d3fbae7SIdo Schimmel 
mlxsw_sp_policer_rate_bytes_ps_kbps(u64 rate_bytes_ps)548d3fbae7SIdo Schimmel static u64 mlxsw_sp_policer_rate_bytes_ps_kbps(u64 rate_bytes_ps)
558d3fbae7SIdo Schimmel {
568d3fbae7SIdo Schimmel 	return div_u64(rate_bytes_ps, 1000) * BITS_PER_BYTE;
578d3fbae7SIdo Schimmel }
588d3fbae7SIdo Schimmel 
mlxsw_sp_policer_burst_bytes_hw_units(u64 burst_bytes)598d3fbae7SIdo Schimmel static u8 mlxsw_sp_policer_burst_bytes_hw_units(u64 burst_bytes)
608d3fbae7SIdo Schimmel {
618d3fbae7SIdo Schimmel 	/* Provided burst size is in bytes. The ASIC burst size value is
628d3fbae7SIdo Schimmel 	 * (2 ^ bs) * 512 bits. Convert the provided size to 512-bit units.
638d3fbae7SIdo Schimmel 	 */
648d3fbae7SIdo Schimmel 	u64 bs512 = div_u64(burst_bytes, 64);
658d3fbae7SIdo Schimmel 
668d3fbae7SIdo Schimmel 	if (!bs512)
678d3fbae7SIdo Schimmel 		return 0;
688d3fbae7SIdo Schimmel 
698d3fbae7SIdo Schimmel 	return fls64(bs512) - 1;
708d3fbae7SIdo Schimmel }
718d3fbae7SIdo Schimmel 
mlxsw_sp_policer_single_rate_occ_get(void * priv)72bf038f03SIdo Schimmel static u64 mlxsw_sp_policer_single_rate_occ_get(void *priv)
73bf038f03SIdo Schimmel {
74bf038f03SIdo Schimmel 	struct mlxsw_sp_policer_family *family = priv;
75bf038f03SIdo Schimmel 
76bf038f03SIdo Schimmel 	return atomic_read(&family->policers_count);
77bf038f03SIdo Schimmel }
78bf038f03SIdo Schimmel 
798d3fbae7SIdo Schimmel static int
mlxsw_sp_policer_single_rate_family_init(struct mlxsw_sp_policer_family * family)808d3fbae7SIdo Schimmel mlxsw_sp_policer_single_rate_family_init(struct mlxsw_sp_policer_family *family)
818d3fbae7SIdo Schimmel {
828d3fbae7SIdo Schimmel 	struct mlxsw_core *core = family->mlxsw_sp->core;
83bf038f03SIdo Schimmel 	struct devlink *devlink;
848d3fbae7SIdo Schimmel 
858d3fbae7SIdo Schimmel 	/* CPU policers are allocated from the first N policers in the global
868d3fbae7SIdo Schimmel 	 * range, so skip them.
878d3fbae7SIdo Schimmel 	 */
888d3fbae7SIdo Schimmel 	if (!MLXSW_CORE_RES_VALID(core, MAX_GLOBAL_POLICERS) ||
898d3fbae7SIdo Schimmel 	    !MLXSW_CORE_RES_VALID(core, MAX_CPU_POLICERS))
908d3fbae7SIdo Schimmel 		return -EIO;
918d3fbae7SIdo Schimmel 
928d3fbae7SIdo Schimmel 	family->start_index = MLXSW_CORE_RES_GET(core, MAX_CPU_POLICERS);
938d3fbae7SIdo Schimmel 	family->end_index = MLXSW_CORE_RES_GET(core, MAX_GLOBAL_POLICERS);
948d3fbae7SIdo Schimmel 
95bf038f03SIdo Schimmel 	atomic_set(&family->policers_count, 0);
96bf038f03SIdo Schimmel 	devlink = priv_to_devlink(core);
97*72a4c8c9SJiri Pirko 	devl_resource_occ_get_register(devlink,
98bf038f03SIdo Schimmel 				       MLXSW_SP_RESOURCE_SINGLE_RATE_POLICERS,
99bf038f03SIdo Schimmel 				       mlxsw_sp_policer_single_rate_occ_get,
100bf038f03SIdo Schimmel 				       family);
101bf038f03SIdo Schimmel 
1028d3fbae7SIdo Schimmel 	return 0;
1038d3fbae7SIdo Schimmel }
1048d3fbae7SIdo Schimmel 
1058d3fbae7SIdo Schimmel static void
mlxsw_sp_policer_single_rate_family_fini(struct mlxsw_sp_policer_family * family)1068d3fbae7SIdo Schimmel mlxsw_sp_policer_single_rate_family_fini(struct mlxsw_sp_policer_family *family)
1078d3fbae7SIdo Schimmel {
108bf038f03SIdo Schimmel 	struct devlink *devlink = priv_to_devlink(family->mlxsw_sp->core);
109bf038f03SIdo Schimmel 
110*72a4c8c9SJiri Pirko 	devl_resource_occ_get_unregister(devlink,
111bf038f03SIdo Schimmel 					 MLXSW_SP_RESOURCE_SINGLE_RATE_POLICERS);
112bf038f03SIdo Schimmel 	WARN_ON(atomic_read(&family->policers_count) != 0);
1138d3fbae7SIdo Schimmel }
1148d3fbae7SIdo Schimmel 
1158d3fbae7SIdo Schimmel static int
mlxsw_sp_policer_single_rate_index_alloc(struct mlxsw_sp_policer_family * family,struct mlxsw_sp_policer * policer)1168d3fbae7SIdo Schimmel mlxsw_sp_policer_single_rate_index_alloc(struct mlxsw_sp_policer_family *family,
1178d3fbae7SIdo Schimmel 					 struct mlxsw_sp_policer *policer)
1188d3fbae7SIdo Schimmel {
1198d3fbae7SIdo Schimmel 	int id;
1208d3fbae7SIdo Schimmel 
1218d3fbae7SIdo Schimmel 	mutex_lock(&family->lock);
1228d3fbae7SIdo Schimmel 	id = idr_alloc(&family->policer_idr, policer, family->start_index,
1238d3fbae7SIdo Schimmel 		       family->end_index, GFP_KERNEL);
1248d3fbae7SIdo Schimmel 	mutex_unlock(&family->lock);
1258d3fbae7SIdo Schimmel 
1268d3fbae7SIdo Schimmel 	if (id < 0)
1278d3fbae7SIdo Schimmel 		return id;
1288d3fbae7SIdo Schimmel 
129bf038f03SIdo Schimmel 	atomic_inc(&family->policers_count);
1308d3fbae7SIdo Schimmel 	policer->index = id;
1318d3fbae7SIdo Schimmel 
1328d3fbae7SIdo Schimmel 	return 0;
1338d3fbae7SIdo Schimmel }
1348d3fbae7SIdo Schimmel 
1358d3fbae7SIdo Schimmel static struct mlxsw_sp_policer *
mlxsw_sp_policer_single_rate_index_free(struct mlxsw_sp_policer_family * family,u16 policer_index)1368d3fbae7SIdo Schimmel mlxsw_sp_policer_single_rate_index_free(struct mlxsw_sp_policer_family *family,
1378d3fbae7SIdo Schimmel 					u16 policer_index)
1388d3fbae7SIdo Schimmel {
1398d3fbae7SIdo Schimmel 	struct mlxsw_sp_policer *policer;
1408d3fbae7SIdo Schimmel 
141bf038f03SIdo Schimmel 	atomic_dec(&family->policers_count);
142bf038f03SIdo Schimmel 
1438d3fbae7SIdo Schimmel 	mutex_lock(&family->lock);
1448d3fbae7SIdo Schimmel 	policer = idr_remove(&family->policer_idr, policer_index);
1458d3fbae7SIdo Schimmel 	mutex_unlock(&family->lock);
1468d3fbae7SIdo Schimmel 
1478d3fbae7SIdo Schimmel 	WARN_ON(!policer);
1488d3fbae7SIdo Schimmel 
1498d3fbae7SIdo Schimmel 	return policer;
1508d3fbae7SIdo Schimmel }
1518d3fbae7SIdo Schimmel 
1528d3fbae7SIdo Schimmel static int
mlxsw_sp_policer_single_rate_init(struct mlxsw_sp_policer_family * family,const struct mlxsw_sp_policer * policer)1538d3fbae7SIdo Schimmel mlxsw_sp_policer_single_rate_init(struct mlxsw_sp_policer_family *family,
1548d3fbae7SIdo Schimmel 				  const struct mlxsw_sp_policer *policer)
1558d3fbae7SIdo Schimmel {
1568d3fbae7SIdo Schimmel 	u64 rate_kbps = mlxsw_sp_policer_rate_bytes_ps_kbps(policer->params.rate);
1578d3fbae7SIdo Schimmel 	u8 bs = mlxsw_sp_policer_burst_bytes_hw_units(policer->params.burst);
1588d3fbae7SIdo Schimmel 	struct mlxsw_sp *mlxsw_sp = family->mlxsw_sp;
1598d3fbae7SIdo Schimmel 	char qpcr_pl[MLXSW_REG_QPCR_LEN];
1608d3fbae7SIdo Schimmel 
1618d3fbae7SIdo Schimmel 	mlxsw_reg_qpcr_pack(qpcr_pl, policer->index, MLXSW_REG_QPCR_IR_UNITS_K,
1628d3fbae7SIdo Schimmel 			    true, rate_kbps, bs);
1638d3fbae7SIdo Schimmel 	mlxsw_reg_qpcr_clear_counter_set(qpcr_pl, true);
1648d3fbae7SIdo Schimmel 
1658d3fbae7SIdo Schimmel 	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(qpcr), qpcr_pl);
1668d3fbae7SIdo Schimmel }
1678d3fbae7SIdo Schimmel 
1688d3fbae7SIdo Schimmel static int
mlxsw_sp_policer_single_rate_params_check(const struct mlxsw_sp_policer_family * family,const struct mlxsw_sp_policer_params * params,struct netlink_ext_ack * extack)1698d3fbae7SIdo Schimmel mlxsw_sp_policer_single_rate_params_check(const struct mlxsw_sp_policer_family *family,
1708d3fbae7SIdo Schimmel 					  const struct mlxsw_sp_policer_params *params,
1718d3fbae7SIdo Schimmel 					  struct netlink_ext_ack *extack)
1728d3fbae7SIdo Schimmel {
1738d3fbae7SIdo Schimmel 	struct mlxsw_sp_policer_core *policer_core = family->mlxsw_sp->policer_core;
1748d3fbae7SIdo Schimmel 	u64 rate_bps = params->rate * BITS_PER_BYTE;
1758d3fbae7SIdo Schimmel 	u8 bs;
1768d3fbae7SIdo Schimmel 
1778d3fbae7SIdo Schimmel 	if (!params->bytes) {
1788d3fbae7SIdo Schimmel 		NL_SET_ERR_MSG_MOD(extack, "Only bandwidth policing is currently supported by single rate policers");
1798d3fbae7SIdo Schimmel 		return -EINVAL;
1808d3fbae7SIdo Schimmel 	}
1818d3fbae7SIdo Schimmel 
1828d3fbae7SIdo Schimmel 	if (!is_power_of_2(params->burst)) {
1838d3fbae7SIdo Schimmel 		NL_SET_ERR_MSG_MOD(extack, "Policer burst size is not power of two");
1848d3fbae7SIdo Schimmel 		return -EINVAL;
1858d3fbae7SIdo Schimmel 	}
1868d3fbae7SIdo Schimmel 
1878d3fbae7SIdo Schimmel 	bs = mlxsw_sp_policer_burst_bytes_hw_units(params->burst);
1888d3fbae7SIdo Schimmel 
1898d3fbae7SIdo Schimmel 	if (bs < policer_core->lowest_bs_bits) {
1908d3fbae7SIdo Schimmel 		NL_SET_ERR_MSG_MOD(extack, "Policer burst size lower than limit");
1918d3fbae7SIdo Schimmel 		return -EINVAL;
1928d3fbae7SIdo Schimmel 	}
1938d3fbae7SIdo Schimmel 
1948d3fbae7SIdo Schimmel 	if (bs > policer_core->highest_bs_bits) {
1958d3fbae7SIdo Schimmel 		NL_SET_ERR_MSG_MOD(extack, "Policer burst size higher than limit");
1968d3fbae7SIdo Schimmel 		return -EINVAL;
1978d3fbae7SIdo Schimmel 	}
1988d3fbae7SIdo Schimmel 
1998d3fbae7SIdo Schimmel 	if (rate_bps < MLXSW_REG_QPCR_LOWEST_CIR_BITS) {
2008d3fbae7SIdo Schimmel 		NL_SET_ERR_MSG_MOD(extack, "Policer rate lower than limit");
2018d3fbae7SIdo Schimmel 		return -EINVAL;
2028d3fbae7SIdo Schimmel 	}
2038d3fbae7SIdo Schimmel 
2048d3fbae7SIdo Schimmel 	if (rate_bps > MLXSW_REG_QPCR_HIGHEST_CIR_BITS) {
2058d3fbae7SIdo Schimmel 		NL_SET_ERR_MSG_MOD(extack, "Policer rate higher than limit");
2068d3fbae7SIdo Schimmel 		return -EINVAL;
2078d3fbae7SIdo Schimmel 	}
2088d3fbae7SIdo Schimmel 
2098d3fbae7SIdo Schimmel 	return 0;
2108d3fbae7SIdo Schimmel }
2118d3fbae7SIdo Schimmel 
2128d3fbae7SIdo Schimmel static const struct mlxsw_sp_policer_family_ops mlxsw_sp_policer_single_rate_ops = {
2138d3fbae7SIdo Schimmel 	.init			= mlxsw_sp_policer_single_rate_family_init,
2148d3fbae7SIdo Schimmel 	.fini			= mlxsw_sp_policer_single_rate_family_fini,
2158d3fbae7SIdo Schimmel 	.policer_index_alloc	= mlxsw_sp_policer_single_rate_index_alloc,
2168d3fbae7SIdo Schimmel 	.policer_index_free	= mlxsw_sp_policer_single_rate_index_free,
2178d3fbae7SIdo Schimmel 	.policer_init		= mlxsw_sp_policer_single_rate_init,
2188d3fbae7SIdo Schimmel 	.policer_params_check	= mlxsw_sp_policer_single_rate_params_check,
2198d3fbae7SIdo Schimmel };
2208d3fbae7SIdo Schimmel 
2218d3fbae7SIdo Schimmel static const struct mlxsw_sp_policer_family mlxsw_sp_policer_single_rate_family = {
2228d3fbae7SIdo Schimmel 	.type		= MLXSW_SP_POLICER_TYPE_SINGLE_RATE,
2238d3fbae7SIdo Schimmel 	.qpcr_type	= MLXSW_REG_QPCR_G_GLOBAL,
2248d3fbae7SIdo Schimmel 	.ops		= &mlxsw_sp_policer_single_rate_ops,
2258d3fbae7SIdo Schimmel };
2268d3fbae7SIdo Schimmel 
2278d3fbae7SIdo Schimmel static const struct mlxsw_sp_policer_family *mlxsw_sp_policer_family_arr[] = {
2288d3fbae7SIdo Schimmel 	[MLXSW_SP_POLICER_TYPE_SINGLE_RATE]	= &mlxsw_sp_policer_single_rate_family,
2298d3fbae7SIdo Schimmel };
2308d3fbae7SIdo Schimmel 
mlxsw_sp_policer_add(struct mlxsw_sp * mlxsw_sp,enum mlxsw_sp_policer_type type,const struct mlxsw_sp_policer_params * params,struct netlink_ext_ack * extack,u16 * p_policer_index)2318d3fbae7SIdo Schimmel int mlxsw_sp_policer_add(struct mlxsw_sp *mlxsw_sp,
2328d3fbae7SIdo Schimmel 			 enum mlxsw_sp_policer_type type,
2338d3fbae7SIdo Schimmel 			 const struct mlxsw_sp_policer_params *params,
2348d3fbae7SIdo Schimmel 			 struct netlink_ext_ack *extack, u16 *p_policer_index)
2358d3fbae7SIdo Schimmel {
2368d3fbae7SIdo Schimmel 	struct mlxsw_sp_policer_family *family;
2378d3fbae7SIdo Schimmel 	struct mlxsw_sp_policer *policer;
2388d3fbae7SIdo Schimmel 	int err;
2398d3fbae7SIdo Schimmel 
2408d3fbae7SIdo Schimmel 	family = mlxsw_sp->policer_core->family_arr[type];
2418d3fbae7SIdo Schimmel 
2428d3fbae7SIdo Schimmel 	err = family->ops->policer_params_check(family, params, extack);
2438d3fbae7SIdo Schimmel 	if (err)
2448d3fbae7SIdo Schimmel 		return err;
2458d3fbae7SIdo Schimmel 
2468d3fbae7SIdo Schimmel 	policer = kmalloc(sizeof(*policer), GFP_KERNEL);
2478d3fbae7SIdo Schimmel 	if (!policer)
2488d3fbae7SIdo Schimmel 		return -ENOMEM;
2498d3fbae7SIdo Schimmel 	policer->params = *params;
2508d3fbae7SIdo Schimmel 
2518d3fbae7SIdo Schimmel 	err = family->ops->policer_index_alloc(family, policer);
2528d3fbae7SIdo Schimmel 	if (err) {
2538d3fbae7SIdo Schimmel 		NL_SET_ERR_MSG_MOD(extack, "Failed to allocate policer index");
2548d3fbae7SIdo Schimmel 		goto err_policer_index_alloc;
2558d3fbae7SIdo Schimmel 	}
2568d3fbae7SIdo Schimmel 
2578d3fbae7SIdo Schimmel 	err = family->ops->policer_init(family, policer);
2588d3fbae7SIdo Schimmel 	if (err) {
2598d3fbae7SIdo Schimmel 		NL_SET_ERR_MSG_MOD(extack, "Failed to initialize policer");
2608d3fbae7SIdo Schimmel 		goto err_policer_init;
2618d3fbae7SIdo Schimmel 	}
2628d3fbae7SIdo Schimmel 
2638d3fbae7SIdo Schimmel 	*p_policer_index = policer->index;
2648d3fbae7SIdo Schimmel 
2658d3fbae7SIdo Schimmel 	return 0;
2668d3fbae7SIdo Schimmel 
2678d3fbae7SIdo Schimmel err_policer_init:
2688d3fbae7SIdo Schimmel 	family->ops->policer_index_free(family, policer->index);
2698d3fbae7SIdo Schimmel err_policer_index_alloc:
2708d3fbae7SIdo Schimmel 	kfree(policer);
2718d3fbae7SIdo Schimmel 	return err;
2728d3fbae7SIdo Schimmel }
2738d3fbae7SIdo Schimmel 
mlxsw_sp_policer_del(struct mlxsw_sp * mlxsw_sp,enum mlxsw_sp_policer_type type,u16 policer_index)2748d3fbae7SIdo Schimmel void mlxsw_sp_policer_del(struct mlxsw_sp *mlxsw_sp,
2758d3fbae7SIdo Schimmel 			  enum mlxsw_sp_policer_type type, u16 policer_index)
2768d3fbae7SIdo Schimmel {
2778d3fbae7SIdo Schimmel 	struct mlxsw_sp_policer_family *family;
2788d3fbae7SIdo Schimmel 	struct mlxsw_sp_policer *policer;
2798d3fbae7SIdo Schimmel 
2808d3fbae7SIdo Schimmel 	family = mlxsw_sp->policer_core->family_arr[type];
2818d3fbae7SIdo Schimmel 	policer = family->ops->policer_index_free(family, policer_index);
2828d3fbae7SIdo Schimmel 	kfree(policer);
2838d3fbae7SIdo Schimmel }
2848d3fbae7SIdo Schimmel 
mlxsw_sp_policer_drops_counter_get(struct mlxsw_sp * mlxsw_sp,enum mlxsw_sp_policer_type type,u16 policer_index,u64 * p_drops)2858d3fbae7SIdo Schimmel int mlxsw_sp_policer_drops_counter_get(struct mlxsw_sp *mlxsw_sp,
2868d3fbae7SIdo Schimmel 				       enum mlxsw_sp_policer_type type,
2878d3fbae7SIdo Schimmel 				       u16 policer_index, u64 *p_drops)
2888d3fbae7SIdo Schimmel {
2898d3fbae7SIdo Schimmel 	struct mlxsw_sp_policer_family *family;
2908d3fbae7SIdo Schimmel 	char qpcr_pl[MLXSW_REG_QPCR_LEN];
2918d3fbae7SIdo Schimmel 	int err;
2928d3fbae7SIdo Schimmel 
2938d3fbae7SIdo Schimmel 	family = mlxsw_sp->policer_core->family_arr[type];
2948d3fbae7SIdo Schimmel 
2958d3fbae7SIdo Schimmel 	MLXSW_REG_ZERO(qpcr, qpcr_pl);
2968d3fbae7SIdo Schimmel 	mlxsw_reg_qpcr_pid_set(qpcr_pl, policer_index);
2978d3fbae7SIdo Schimmel 	mlxsw_reg_qpcr_g_set(qpcr_pl, family->qpcr_type);
2988d3fbae7SIdo Schimmel 	err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(qpcr), qpcr_pl);
2998d3fbae7SIdo Schimmel 	if (err)
3008d3fbae7SIdo Schimmel 		return err;
3018d3fbae7SIdo Schimmel 
3028d3fbae7SIdo Schimmel 	*p_drops = mlxsw_reg_qpcr_violate_count_get(qpcr_pl);
3038d3fbae7SIdo Schimmel 
3048d3fbae7SIdo Schimmel 	return 0;
3058d3fbae7SIdo Schimmel }
3068d3fbae7SIdo Schimmel 
3078d3fbae7SIdo Schimmel static int
mlxsw_sp_policer_family_register(struct mlxsw_sp * mlxsw_sp,const struct mlxsw_sp_policer_family * tmpl)3088d3fbae7SIdo Schimmel mlxsw_sp_policer_family_register(struct mlxsw_sp *mlxsw_sp,
3098d3fbae7SIdo Schimmel 				 const struct mlxsw_sp_policer_family *tmpl)
3108d3fbae7SIdo Schimmel {
3118d3fbae7SIdo Schimmel 	struct mlxsw_sp_policer_family *family;
3128d3fbae7SIdo Schimmel 	int err;
3138d3fbae7SIdo Schimmel 
3148d3fbae7SIdo Schimmel 	family = kmemdup(tmpl, sizeof(*family), GFP_KERNEL);
3158d3fbae7SIdo Schimmel 	if (!family)
3168d3fbae7SIdo Schimmel 		return -ENOMEM;
3178d3fbae7SIdo Schimmel 
3188d3fbae7SIdo Schimmel 	family->mlxsw_sp = mlxsw_sp;
3198d3fbae7SIdo Schimmel 	idr_init(&family->policer_idr);
3208d3fbae7SIdo Schimmel 	mutex_init(&family->lock);
3218d3fbae7SIdo Schimmel 
3228d3fbae7SIdo Schimmel 	err = family->ops->init(family);
3238d3fbae7SIdo Schimmel 	if (err)
3248d3fbae7SIdo Schimmel 		goto err_family_init;
3258d3fbae7SIdo Schimmel 
3268d3fbae7SIdo Schimmel 	if (WARN_ON(family->start_index >= family->end_index)) {
3278d3fbae7SIdo Schimmel 		err = -EINVAL;
3288d3fbae7SIdo Schimmel 		goto err_index_check;
3298d3fbae7SIdo Schimmel 	}
3308d3fbae7SIdo Schimmel 
3318d3fbae7SIdo Schimmel 	mlxsw_sp->policer_core->family_arr[tmpl->type] = family;
3328d3fbae7SIdo Schimmel 
3338d3fbae7SIdo Schimmel 	return 0;
3348d3fbae7SIdo Schimmel 
3358d3fbae7SIdo Schimmel err_index_check:
3368d3fbae7SIdo Schimmel 	family->ops->fini(family);
3378d3fbae7SIdo Schimmel err_family_init:
3388d3fbae7SIdo Schimmel 	mutex_destroy(&family->lock);
3398d3fbae7SIdo Schimmel 	idr_destroy(&family->policer_idr);
3408d3fbae7SIdo Schimmel 	kfree(family);
3418d3fbae7SIdo Schimmel 	return err;
3428d3fbae7SIdo Schimmel }
3438d3fbae7SIdo Schimmel 
3448d3fbae7SIdo Schimmel static void
mlxsw_sp_policer_family_unregister(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_policer_family * family)3458d3fbae7SIdo Schimmel mlxsw_sp_policer_family_unregister(struct mlxsw_sp *mlxsw_sp,
3468d3fbae7SIdo Schimmel 				   struct mlxsw_sp_policer_family *family)
3478d3fbae7SIdo Schimmel {
3488d3fbae7SIdo Schimmel 	family->ops->fini(family);
3498d3fbae7SIdo Schimmel 	mutex_destroy(&family->lock);
3508d3fbae7SIdo Schimmel 	WARN_ON(!idr_is_empty(&family->policer_idr));
3518d3fbae7SIdo Schimmel 	idr_destroy(&family->policer_idr);
3528d3fbae7SIdo Schimmel 	kfree(family);
3538d3fbae7SIdo Schimmel }
3548d3fbae7SIdo Schimmel 
mlxsw_sp_policers_init(struct mlxsw_sp * mlxsw_sp)3558d3fbae7SIdo Schimmel int mlxsw_sp_policers_init(struct mlxsw_sp *mlxsw_sp)
3568d3fbae7SIdo Schimmel {
3578d3fbae7SIdo Schimmel 	struct mlxsw_sp_policer_core *policer_core;
3588d3fbae7SIdo Schimmel 	int i, err;
3598d3fbae7SIdo Schimmel 
3608d3fbae7SIdo Schimmel 	policer_core = kzalloc(sizeof(*policer_core), GFP_KERNEL);
3618d3fbae7SIdo Schimmel 	if (!policer_core)
3628d3fbae7SIdo Schimmel 		return -ENOMEM;
3638d3fbae7SIdo Schimmel 	mlxsw_sp->policer_core = policer_core;
3648d3fbae7SIdo Schimmel 	policer_core->ops = mlxsw_sp->policer_core_ops;
3658d3fbae7SIdo Schimmel 
3668d3fbae7SIdo Schimmel 	err = policer_core->ops->init(policer_core);
3678d3fbae7SIdo Schimmel 	if (err)
3688d3fbae7SIdo Schimmel 		goto err_init;
3698d3fbae7SIdo Schimmel 
3708d3fbae7SIdo Schimmel 	for (i = 0; i < MLXSW_SP_POLICER_TYPE_MAX + 1; i++) {
3718d3fbae7SIdo Schimmel 		err = mlxsw_sp_policer_family_register(mlxsw_sp, mlxsw_sp_policer_family_arr[i]);
3728d3fbae7SIdo Schimmel 		if (err)
3738d3fbae7SIdo Schimmel 			goto err_family_register;
3748d3fbae7SIdo Schimmel 	}
3758d3fbae7SIdo Schimmel 
3768d3fbae7SIdo Schimmel 	return 0;
3778d3fbae7SIdo Schimmel 
3788d3fbae7SIdo Schimmel err_family_register:
3798d3fbae7SIdo Schimmel 	for (i--; i >= 0; i--) {
3808d3fbae7SIdo Schimmel 		struct mlxsw_sp_policer_family *family;
3818d3fbae7SIdo Schimmel 
3828d3fbae7SIdo Schimmel 		family = mlxsw_sp->policer_core->family_arr[i];
3838d3fbae7SIdo Schimmel 		mlxsw_sp_policer_family_unregister(mlxsw_sp, family);
3848d3fbae7SIdo Schimmel 	}
3858d3fbae7SIdo Schimmel err_init:
3868d3fbae7SIdo Schimmel 	kfree(mlxsw_sp->policer_core);
3878d3fbae7SIdo Schimmel 	return err;
3888d3fbae7SIdo Schimmel }
3898d3fbae7SIdo Schimmel 
mlxsw_sp_policers_fini(struct mlxsw_sp * mlxsw_sp)3908d3fbae7SIdo Schimmel void mlxsw_sp_policers_fini(struct mlxsw_sp *mlxsw_sp)
3918d3fbae7SIdo Schimmel {
3928d3fbae7SIdo Schimmel 	int i;
3938d3fbae7SIdo Schimmel 
3948d3fbae7SIdo Schimmel 	for (i = MLXSW_SP_POLICER_TYPE_MAX; i >= 0; i--) {
3958d3fbae7SIdo Schimmel 		struct mlxsw_sp_policer_family *family;
3968d3fbae7SIdo Schimmel 
3978d3fbae7SIdo Schimmel 		family = mlxsw_sp->policer_core->family_arr[i];
3988d3fbae7SIdo Schimmel 		mlxsw_sp_policer_family_unregister(mlxsw_sp, family);
3998d3fbae7SIdo Schimmel 	}
4008d3fbae7SIdo Schimmel 
4018d3fbae7SIdo Schimmel 	kfree(mlxsw_sp->policer_core);
4028d3fbae7SIdo Schimmel }
4038d3fbae7SIdo Schimmel 
mlxsw_sp_policer_resources_register(struct mlxsw_core * mlxsw_core)404bf038f03SIdo Schimmel int mlxsw_sp_policer_resources_register(struct mlxsw_core *mlxsw_core)
405bf038f03SIdo Schimmel {
406bf038f03SIdo Schimmel 	u64 global_policers, cpu_policers, single_rate_policers;
407bf038f03SIdo Schimmel 	struct devlink *devlink = priv_to_devlink(mlxsw_core);
408bf038f03SIdo Schimmel 	struct devlink_resource_size_params size_params;
409bf038f03SIdo Schimmel 	int err;
410bf038f03SIdo Schimmel 
411bf038f03SIdo Schimmel 	if (!MLXSW_CORE_RES_VALID(mlxsw_core, MAX_GLOBAL_POLICERS) ||
412bf038f03SIdo Schimmel 	    !MLXSW_CORE_RES_VALID(mlxsw_core, MAX_CPU_POLICERS))
413bf038f03SIdo Schimmel 		return -EIO;
414bf038f03SIdo Schimmel 
415bf038f03SIdo Schimmel 	global_policers = MLXSW_CORE_RES_GET(mlxsw_core, MAX_GLOBAL_POLICERS);
416bf038f03SIdo Schimmel 	cpu_policers = MLXSW_CORE_RES_GET(mlxsw_core, MAX_CPU_POLICERS);
417bf038f03SIdo Schimmel 	single_rate_policers = global_policers - cpu_policers;
418bf038f03SIdo Schimmel 
419bf038f03SIdo Schimmel 	devlink_resource_size_params_init(&size_params, global_policers,
420bf038f03SIdo Schimmel 					  global_policers, 1,
421bf038f03SIdo Schimmel 					  DEVLINK_RESOURCE_UNIT_ENTRY);
422*72a4c8c9SJiri Pirko 	err = devl_resource_register(devlink, "global_policers",
423bf038f03SIdo Schimmel 				     global_policers,
424bf038f03SIdo Schimmel 				     MLXSW_SP_RESOURCE_GLOBAL_POLICERS,
425bf038f03SIdo Schimmel 				     DEVLINK_RESOURCE_ID_PARENT_TOP,
426bf038f03SIdo Schimmel 				     &size_params);
427bf038f03SIdo Schimmel 	if (err)
428bf038f03SIdo Schimmel 		return err;
429bf038f03SIdo Schimmel 
430bf038f03SIdo Schimmel 	devlink_resource_size_params_init(&size_params, single_rate_policers,
431bf038f03SIdo Schimmel 					  single_rate_policers, 1,
432bf038f03SIdo Schimmel 					  DEVLINK_RESOURCE_UNIT_ENTRY);
433*72a4c8c9SJiri Pirko 	err = devl_resource_register(devlink, "single_rate_policers",
434bf038f03SIdo Schimmel 				     single_rate_policers,
435bf038f03SIdo Schimmel 				     MLXSW_SP_RESOURCE_SINGLE_RATE_POLICERS,
436bf038f03SIdo Schimmel 				     MLXSW_SP_RESOURCE_GLOBAL_POLICERS,
437bf038f03SIdo Schimmel 				     &size_params);
438bf038f03SIdo Schimmel 	if (err)
439bf038f03SIdo Schimmel 		return err;
440bf038f03SIdo Schimmel 
441bf038f03SIdo Schimmel 	return 0;
442bf038f03SIdo Schimmel }
443bf038f03SIdo Schimmel 
4448d3fbae7SIdo Schimmel static int
mlxsw_sp1_policer_core_init(struct mlxsw_sp_policer_core * policer_core)4458d3fbae7SIdo Schimmel mlxsw_sp1_policer_core_init(struct mlxsw_sp_policer_core *policer_core)
4468d3fbae7SIdo Schimmel {
4478d3fbae7SIdo Schimmel 	policer_core->lowest_bs_bits = MLXSW_REG_QPCR_LOWEST_CBS_BITS_SP1;
4488d3fbae7SIdo Schimmel 	policer_core->highest_bs_bits = MLXSW_REG_QPCR_HIGHEST_CBS_BITS_SP1;
4498d3fbae7SIdo Schimmel 
4508d3fbae7SIdo Schimmel 	return 0;
4518d3fbae7SIdo Schimmel }
4528d3fbae7SIdo Schimmel 
4538d3fbae7SIdo Schimmel const struct mlxsw_sp_policer_core_ops mlxsw_sp1_policer_core_ops = {
4548d3fbae7SIdo Schimmel 	.init = mlxsw_sp1_policer_core_init,
4558d3fbae7SIdo Schimmel };
4568d3fbae7SIdo Schimmel 
4578d3fbae7SIdo Schimmel static int
mlxsw_sp2_policer_core_init(struct mlxsw_sp_policer_core * policer_core)4588d3fbae7SIdo Schimmel mlxsw_sp2_policer_core_init(struct mlxsw_sp_policer_core *policer_core)
4598d3fbae7SIdo Schimmel {
4608d3fbae7SIdo Schimmel 	policer_core->lowest_bs_bits = MLXSW_REG_QPCR_LOWEST_CBS_BITS_SP2;
4618d3fbae7SIdo Schimmel 	policer_core->highest_bs_bits = MLXSW_REG_QPCR_HIGHEST_CBS_BITS_SP2;
4628d3fbae7SIdo Schimmel 
4638d3fbae7SIdo Schimmel 	return 0;
4648d3fbae7SIdo Schimmel }
4658d3fbae7SIdo Schimmel 
4668d3fbae7SIdo Schimmel const struct mlxsw_sp_policer_core_ops mlxsw_sp2_policer_core_ops = {
4678d3fbae7SIdo Schimmel 	.init = mlxsw_sp2_policer_core_init,
4688d3fbae7SIdo Schimmel };
469