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