469 lines · c
1// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.02/* Copyright (c) 2020 Mellanox Technologies. All rights reserved */3 4#include <linux/idr.h>5#include <linux/log2.h>6#include <linux/mutex.h>7#include <linux/netlink.h>8#include <net/devlink.h>9 10#include "spectrum.h"11 12struct mlxsw_sp_policer_family {13 enum mlxsw_sp_policer_type type;14 enum mlxsw_reg_qpcr_g qpcr_type;15 struct mlxsw_sp *mlxsw_sp;16 u16 start_index; /* Inclusive */17 u16 end_index; /* Exclusive */18 struct idr policer_idr;19 struct mutex lock; /* Protects policer_idr */20 atomic_t policers_count;21 const struct mlxsw_sp_policer_family_ops *ops;22};23 24struct mlxsw_sp_policer {25 struct mlxsw_sp_policer_params params;26 u16 index;27};28 29struct mlxsw_sp_policer_family_ops {30 int (*init)(struct mlxsw_sp_policer_family *family);31 void (*fini)(struct mlxsw_sp_policer_family *family);32 int (*policer_index_alloc)(struct mlxsw_sp_policer_family *family,33 struct mlxsw_sp_policer *policer);34 struct mlxsw_sp_policer * (*policer_index_free)(struct mlxsw_sp_policer_family *family,35 u16 policer_index);36 int (*policer_init)(struct mlxsw_sp_policer_family *family,37 const struct mlxsw_sp_policer *policer);38 int (*policer_params_check)(const struct mlxsw_sp_policer_family *family,39 const struct mlxsw_sp_policer_params *params,40 struct netlink_ext_ack *extack);41};42 43struct mlxsw_sp_policer_core {44 struct mlxsw_sp_policer_family *family_arr[MLXSW_SP_POLICER_TYPE_MAX + 1];45 const struct mlxsw_sp_policer_core_ops *ops;46 u8 lowest_bs_bits;47 u8 highest_bs_bits;48};49 50struct mlxsw_sp_policer_core_ops {51 int (*init)(struct mlxsw_sp_policer_core *policer_core);52};53 54static u64 mlxsw_sp_policer_rate_bytes_ps_kbps(u64 rate_bytes_ps)55{56 return div_u64(rate_bytes_ps, 1000) * BITS_PER_BYTE;57}58 59static u8 mlxsw_sp_policer_burst_bytes_hw_units(u64 burst_bytes)60{61 /* Provided burst size is in bytes. The ASIC burst size value is62 * (2 ^ bs) * 512 bits. Convert the provided size to 512-bit units.63 */64 u64 bs512 = div_u64(burst_bytes, 64);65 66 if (!bs512)67 return 0;68 69 return fls64(bs512) - 1;70}71 72static u64 mlxsw_sp_policer_single_rate_occ_get(void *priv)73{74 struct mlxsw_sp_policer_family *family = priv;75 76 return atomic_read(&family->policers_count);77}78 79static int80mlxsw_sp_policer_single_rate_family_init(struct mlxsw_sp_policer_family *family)81{82 struct mlxsw_core *core = family->mlxsw_sp->core;83 struct devlink *devlink;84 85 /* CPU policers are allocated from the first N policers in the global86 * range, so skip them.87 */88 if (!MLXSW_CORE_RES_VALID(core, MAX_GLOBAL_POLICERS) ||89 !MLXSW_CORE_RES_VALID(core, MAX_CPU_POLICERS))90 return -EIO;91 92 family->start_index = MLXSW_CORE_RES_GET(core, MAX_CPU_POLICERS);93 family->end_index = MLXSW_CORE_RES_GET(core, MAX_GLOBAL_POLICERS);94 95 atomic_set(&family->policers_count, 0);96 devlink = priv_to_devlink(core);97 devl_resource_occ_get_register(devlink,98 MLXSW_SP_RESOURCE_SINGLE_RATE_POLICERS,99 mlxsw_sp_policer_single_rate_occ_get,100 family);101 102 return 0;103}104 105static void106mlxsw_sp_policer_single_rate_family_fini(struct mlxsw_sp_policer_family *family)107{108 struct devlink *devlink = priv_to_devlink(family->mlxsw_sp->core);109 110 devl_resource_occ_get_unregister(devlink,111 MLXSW_SP_RESOURCE_SINGLE_RATE_POLICERS);112 WARN_ON(atomic_read(&family->policers_count) != 0);113}114 115static int116mlxsw_sp_policer_single_rate_index_alloc(struct mlxsw_sp_policer_family *family,117 struct mlxsw_sp_policer *policer)118{119 int id;120 121 mutex_lock(&family->lock);122 id = idr_alloc(&family->policer_idr, policer, family->start_index,123 family->end_index, GFP_KERNEL);124 mutex_unlock(&family->lock);125 126 if (id < 0)127 return id;128 129 atomic_inc(&family->policers_count);130 policer->index = id;131 132 return 0;133}134 135static struct mlxsw_sp_policer *136mlxsw_sp_policer_single_rate_index_free(struct mlxsw_sp_policer_family *family,137 u16 policer_index)138{139 struct mlxsw_sp_policer *policer;140 141 atomic_dec(&family->policers_count);142 143 mutex_lock(&family->lock);144 policer = idr_remove(&family->policer_idr, policer_index);145 mutex_unlock(&family->lock);146 147 WARN_ON(!policer);148 149 return policer;150}151 152static int153mlxsw_sp_policer_single_rate_init(struct mlxsw_sp_policer_family *family,154 const struct mlxsw_sp_policer *policer)155{156 u64 rate_kbps = mlxsw_sp_policer_rate_bytes_ps_kbps(policer->params.rate);157 u8 bs = mlxsw_sp_policer_burst_bytes_hw_units(policer->params.burst);158 struct mlxsw_sp *mlxsw_sp = family->mlxsw_sp;159 char qpcr_pl[MLXSW_REG_QPCR_LEN];160 161 mlxsw_reg_qpcr_pack(qpcr_pl, policer->index, MLXSW_REG_QPCR_IR_UNITS_K,162 true, rate_kbps, bs);163 mlxsw_reg_qpcr_clear_counter_set(qpcr_pl, true);164 165 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(qpcr), qpcr_pl);166}167 168static int169mlxsw_sp_policer_single_rate_params_check(const struct mlxsw_sp_policer_family *family,170 const struct mlxsw_sp_policer_params *params,171 struct netlink_ext_ack *extack)172{173 struct mlxsw_sp_policer_core *policer_core = family->mlxsw_sp->policer_core;174 u64 rate_bps = params->rate * BITS_PER_BYTE;175 u8 bs;176 177 if (!params->bytes) {178 NL_SET_ERR_MSG_MOD(extack, "Only bandwidth policing is currently supported by single rate policers");179 return -EINVAL;180 }181 182 if (!is_power_of_2(params->burst)) {183 NL_SET_ERR_MSG_MOD(extack, "Policer burst size is not power of two");184 return -EINVAL;185 }186 187 bs = mlxsw_sp_policer_burst_bytes_hw_units(params->burst);188 189 if (bs < policer_core->lowest_bs_bits) {190 NL_SET_ERR_MSG_MOD(extack, "Policer burst size lower than limit");191 return -EINVAL;192 }193 194 if (bs > policer_core->highest_bs_bits) {195 NL_SET_ERR_MSG_MOD(extack, "Policer burst size higher than limit");196 return -EINVAL;197 }198 199 if (rate_bps < MLXSW_REG_QPCR_LOWEST_CIR_BITS) {200 NL_SET_ERR_MSG_MOD(extack, "Policer rate lower than limit");201 return -EINVAL;202 }203 204 if (rate_bps > MLXSW_REG_QPCR_HIGHEST_CIR_BITS) {205 NL_SET_ERR_MSG_MOD(extack, "Policer rate higher than limit");206 return -EINVAL;207 }208 209 return 0;210}211 212static const struct mlxsw_sp_policer_family_ops mlxsw_sp_policer_single_rate_ops = {213 .init = mlxsw_sp_policer_single_rate_family_init,214 .fini = mlxsw_sp_policer_single_rate_family_fini,215 .policer_index_alloc = mlxsw_sp_policer_single_rate_index_alloc,216 .policer_index_free = mlxsw_sp_policer_single_rate_index_free,217 .policer_init = mlxsw_sp_policer_single_rate_init,218 .policer_params_check = mlxsw_sp_policer_single_rate_params_check,219};220 221static const struct mlxsw_sp_policer_family mlxsw_sp_policer_single_rate_family = {222 .type = MLXSW_SP_POLICER_TYPE_SINGLE_RATE,223 .qpcr_type = MLXSW_REG_QPCR_G_GLOBAL,224 .ops = &mlxsw_sp_policer_single_rate_ops,225};226 227static const struct mlxsw_sp_policer_family *mlxsw_sp_policer_family_arr[] = {228 [MLXSW_SP_POLICER_TYPE_SINGLE_RATE] = &mlxsw_sp_policer_single_rate_family,229};230 231int mlxsw_sp_policer_add(struct mlxsw_sp *mlxsw_sp,232 enum mlxsw_sp_policer_type type,233 const struct mlxsw_sp_policer_params *params,234 struct netlink_ext_ack *extack, u16 *p_policer_index)235{236 struct mlxsw_sp_policer_family *family;237 struct mlxsw_sp_policer *policer;238 int err;239 240 family = mlxsw_sp->policer_core->family_arr[type];241 242 err = family->ops->policer_params_check(family, params, extack);243 if (err)244 return err;245 246 policer = kmalloc(sizeof(*policer), GFP_KERNEL);247 if (!policer)248 return -ENOMEM;249 policer->params = *params;250 251 err = family->ops->policer_index_alloc(family, policer);252 if (err) {253 NL_SET_ERR_MSG_MOD(extack, "Failed to allocate policer index");254 goto err_policer_index_alloc;255 }256 257 err = family->ops->policer_init(family, policer);258 if (err) {259 NL_SET_ERR_MSG_MOD(extack, "Failed to initialize policer");260 goto err_policer_init;261 }262 263 *p_policer_index = policer->index;264 265 return 0;266 267err_policer_init:268 family->ops->policer_index_free(family, policer->index);269err_policer_index_alloc:270 kfree(policer);271 return err;272}273 274void mlxsw_sp_policer_del(struct mlxsw_sp *mlxsw_sp,275 enum mlxsw_sp_policer_type type, u16 policer_index)276{277 struct mlxsw_sp_policer_family *family;278 struct mlxsw_sp_policer *policer;279 280 family = mlxsw_sp->policer_core->family_arr[type];281 policer = family->ops->policer_index_free(family, policer_index);282 kfree(policer);283}284 285int mlxsw_sp_policer_drops_counter_get(struct mlxsw_sp *mlxsw_sp,286 enum mlxsw_sp_policer_type type,287 u16 policer_index, u64 *p_drops)288{289 struct mlxsw_sp_policer_family *family;290 char qpcr_pl[MLXSW_REG_QPCR_LEN];291 int err;292 293 family = mlxsw_sp->policer_core->family_arr[type];294 295 MLXSW_REG_ZERO(qpcr, qpcr_pl);296 mlxsw_reg_qpcr_pid_set(qpcr_pl, policer_index);297 mlxsw_reg_qpcr_g_set(qpcr_pl, family->qpcr_type);298 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(qpcr), qpcr_pl);299 if (err)300 return err;301 302 *p_drops = mlxsw_reg_qpcr_violate_count_get(qpcr_pl);303 304 return 0;305}306 307static int308mlxsw_sp_policer_family_register(struct mlxsw_sp *mlxsw_sp,309 const struct mlxsw_sp_policer_family *tmpl)310{311 struct mlxsw_sp_policer_family *family;312 int err;313 314 family = kmemdup(tmpl, sizeof(*family), GFP_KERNEL);315 if (!family)316 return -ENOMEM;317 318 family->mlxsw_sp = mlxsw_sp;319 idr_init(&family->policer_idr);320 mutex_init(&family->lock);321 322 err = family->ops->init(family);323 if (err)324 goto err_family_init;325 326 if (WARN_ON(family->start_index >= family->end_index)) {327 err = -EINVAL;328 goto err_index_check;329 }330 331 mlxsw_sp->policer_core->family_arr[tmpl->type] = family;332 333 return 0;334 335err_index_check:336 family->ops->fini(family);337err_family_init:338 mutex_destroy(&family->lock);339 idr_destroy(&family->policer_idr);340 kfree(family);341 return err;342}343 344static void345mlxsw_sp_policer_family_unregister(struct mlxsw_sp *mlxsw_sp,346 struct mlxsw_sp_policer_family *family)347{348 family->ops->fini(family);349 mutex_destroy(&family->lock);350 WARN_ON(!idr_is_empty(&family->policer_idr));351 idr_destroy(&family->policer_idr);352 kfree(family);353}354 355int mlxsw_sp_policers_init(struct mlxsw_sp *mlxsw_sp)356{357 struct mlxsw_sp_policer_core *policer_core;358 int i, err;359 360 policer_core = kzalloc(sizeof(*policer_core), GFP_KERNEL);361 if (!policer_core)362 return -ENOMEM;363 mlxsw_sp->policer_core = policer_core;364 policer_core->ops = mlxsw_sp->policer_core_ops;365 366 err = policer_core->ops->init(policer_core);367 if (err)368 goto err_init;369 370 for (i = 0; i < MLXSW_SP_POLICER_TYPE_MAX + 1; i++) {371 err = mlxsw_sp_policer_family_register(mlxsw_sp, mlxsw_sp_policer_family_arr[i]);372 if (err)373 goto err_family_register;374 }375 376 return 0;377 378err_family_register:379 for (i--; i >= 0; i--) {380 struct mlxsw_sp_policer_family *family;381 382 family = mlxsw_sp->policer_core->family_arr[i];383 mlxsw_sp_policer_family_unregister(mlxsw_sp, family);384 }385err_init:386 kfree(mlxsw_sp->policer_core);387 return err;388}389 390void mlxsw_sp_policers_fini(struct mlxsw_sp *mlxsw_sp)391{392 int i;393 394 for (i = MLXSW_SP_POLICER_TYPE_MAX; i >= 0; i--) {395 struct mlxsw_sp_policer_family *family;396 397 family = mlxsw_sp->policer_core->family_arr[i];398 mlxsw_sp_policer_family_unregister(mlxsw_sp, family);399 }400 401 kfree(mlxsw_sp->policer_core);402}403 404int mlxsw_sp_policer_resources_register(struct mlxsw_core *mlxsw_core)405{406 u64 global_policers, cpu_policers, single_rate_policers;407 struct devlink *devlink = priv_to_devlink(mlxsw_core);408 struct devlink_resource_size_params size_params;409 int err;410 411 if (!MLXSW_CORE_RES_VALID(mlxsw_core, MAX_GLOBAL_POLICERS) ||412 !MLXSW_CORE_RES_VALID(mlxsw_core, MAX_CPU_POLICERS))413 return -EIO;414 415 global_policers = MLXSW_CORE_RES_GET(mlxsw_core, MAX_GLOBAL_POLICERS);416 cpu_policers = MLXSW_CORE_RES_GET(mlxsw_core, MAX_CPU_POLICERS);417 single_rate_policers = global_policers - cpu_policers;418 419 devlink_resource_size_params_init(&size_params, global_policers,420 global_policers, 1,421 DEVLINK_RESOURCE_UNIT_ENTRY);422 err = devl_resource_register(devlink, "global_policers",423 global_policers,424 MLXSW_SP_RESOURCE_GLOBAL_POLICERS,425 DEVLINK_RESOURCE_ID_PARENT_TOP,426 &size_params);427 if (err)428 return err;429 430 devlink_resource_size_params_init(&size_params, single_rate_policers,431 single_rate_policers, 1,432 DEVLINK_RESOURCE_UNIT_ENTRY);433 err = devl_resource_register(devlink, "single_rate_policers",434 single_rate_policers,435 MLXSW_SP_RESOURCE_SINGLE_RATE_POLICERS,436 MLXSW_SP_RESOURCE_GLOBAL_POLICERS,437 &size_params);438 if (err)439 return err;440 441 return 0;442}443 444static int445mlxsw_sp1_policer_core_init(struct mlxsw_sp_policer_core *policer_core)446{447 policer_core->lowest_bs_bits = MLXSW_REG_QPCR_LOWEST_CBS_BITS_SP1;448 policer_core->highest_bs_bits = MLXSW_REG_QPCR_HIGHEST_CBS_BITS_SP1;449 450 return 0;451}452 453const struct mlxsw_sp_policer_core_ops mlxsw_sp1_policer_core_ops = {454 .init = mlxsw_sp1_policer_core_init,455};456 457static int458mlxsw_sp2_policer_core_init(struct mlxsw_sp_policer_core *policer_core)459{460 policer_core->lowest_bs_bits = MLXSW_REG_QPCR_LOWEST_CBS_BITS_SP2;461 policer_core->highest_bs_bits = MLXSW_REG_QPCR_HIGHEST_CBS_BITS_SP2;462 463 return 0;464}465 466const struct mlxsw_sp_policer_core_ops mlxsw_sp2_policer_core_ops = {467 .init = mlxsw_sp2_policer_core_init,468};469