162306a36Sopenharmony_ci// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 262306a36Sopenharmony_ci/* Copyright (c) 2017-2018 Mellanox Technologies. All rights reserved */ 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include <linux/kernel.h> 562306a36Sopenharmony_ci#include <linux/bitops.h> 662306a36Sopenharmony_ci#include <linux/spinlock.h> 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include "spectrum_cnt.h" 962306a36Sopenharmony_ci 1062306a36Sopenharmony_cistruct mlxsw_sp_counter_sub_pool { 1162306a36Sopenharmony_ci u64 size; 1262306a36Sopenharmony_ci unsigned int base_index; 1362306a36Sopenharmony_ci enum mlxsw_res_id entry_size_res_id; 1462306a36Sopenharmony_ci const char *resource_name; /* devlink resource name */ 1562306a36Sopenharmony_ci u64 resource_id; /* devlink resource id */ 1662306a36Sopenharmony_ci unsigned int entry_size; 1762306a36Sopenharmony_ci unsigned int bank_count; 1862306a36Sopenharmony_ci atomic_t active_entries_count; 1962306a36Sopenharmony_ci}; 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistruct mlxsw_sp_counter_pool { 2262306a36Sopenharmony_ci u64 pool_size; 2362306a36Sopenharmony_ci unsigned long *usage; /* Usage bitmap */ 2462306a36Sopenharmony_ci spinlock_t counter_pool_lock; /* Protects counter pool allocations */ 2562306a36Sopenharmony_ci atomic_t active_entries_count; 2662306a36Sopenharmony_ci unsigned int sub_pools_count; 2762306a36Sopenharmony_ci struct mlxsw_sp_counter_sub_pool sub_pools[]; 2862306a36Sopenharmony_ci}; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistatic const struct mlxsw_sp_counter_sub_pool mlxsw_sp_counter_sub_pools[] = { 3162306a36Sopenharmony_ci [MLXSW_SP_COUNTER_SUB_POOL_FLOW] = { 3262306a36Sopenharmony_ci .entry_size_res_id = MLXSW_RES_ID_COUNTER_SIZE_PACKETS_BYTES, 3362306a36Sopenharmony_ci .resource_name = MLXSW_SP_RESOURCE_NAME_COUNTERS_FLOW, 3462306a36Sopenharmony_ci .resource_id = MLXSW_SP_RESOURCE_COUNTERS_FLOW, 3562306a36Sopenharmony_ci .bank_count = 6, 3662306a36Sopenharmony_ci }, 3762306a36Sopenharmony_ci [MLXSW_SP_COUNTER_SUB_POOL_RIF] = { 3862306a36Sopenharmony_ci .entry_size_res_id = MLXSW_RES_ID_COUNTER_SIZE_ROUTER_BASIC, 3962306a36Sopenharmony_ci .resource_name = MLXSW_SP_RESOURCE_NAME_COUNTERS_RIF, 4062306a36Sopenharmony_ci .resource_id = MLXSW_SP_RESOURCE_COUNTERS_RIF, 4162306a36Sopenharmony_ci .bank_count = 2, 4262306a36Sopenharmony_ci } 4362306a36Sopenharmony_ci}; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic u64 mlxsw_sp_counter_sub_pool_occ_get(void *priv) 4662306a36Sopenharmony_ci{ 4762306a36Sopenharmony_ci const struct mlxsw_sp_counter_sub_pool *sub_pool = priv; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci return atomic_read(&sub_pool->active_entries_count); 5062306a36Sopenharmony_ci} 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic int mlxsw_sp_counter_sub_pools_init(struct mlxsw_sp *mlxsw_sp) 5362306a36Sopenharmony_ci{ 5462306a36Sopenharmony_ci struct mlxsw_sp_counter_pool *pool = mlxsw_sp->counter_pool; 5562306a36Sopenharmony_ci struct devlink *devlink = priv_to_devlink(mlxsw_sp->core); 5662306a36Sopenharmony_ci struct mlxsw_sp_counter_sub_pool *sub_pool; 5762306a36Sopenharmony_ci unsigned int base_index = 0; 5862306a36Sopenharmony_ci enum mlxsw_res_id res_id; 5962306a36Sopenharmony_ci int err; 6062306a36Sopenharmony_ci int i; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci for (i = 0; i < pool->sub_pools_count; i++) { 6362306a36Sopenharmony_ci sub_pool = &pool->sub_pools[i]; 6462306a36Sopenharmony_ci res_id = sub_pool->entry_size_res_id; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci if (!mlxsw_core_res_valid(mlxsw_sp->core, res_id)) 6762306a36Sopenharmony_ci return -EIO; 6862306a36Sopenharmony_ci sub_pool->entry_size = mlxsw_core_res_get(mlxsw_sp->core, 6962306a36Sopenharmony_ci res_id); 7062306a36Sopenharmony_ci err = devl_resource_size_get(devlink, 7162306a36Sopenharmony_ci sub_pool->resource_id, 7262306a36Sopenharmony_ci &sub_pool->size); 7362306a36Sopenharmony_ci if (err) 7462306a36Sopenharmony_ci goto err_resource_size_get; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci devl_resource_occ_get_register(devlink, 7762306a36Sopenharmony_ci sub_pool->resource_id, 7862306a36Sopenharmony_ci mlxsw_sp_counter_sub_pool_occ_get, 7962306a36Sopenharmony_ci sub_pool); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci sub_pool->base_index = base_index; 8262306a36Sopenharmony_ci base_index += sub_pool->size; 8362306a36Sopenharmony_ci atomic_set(&sub_pool->active_entries_count, 0); 8462306a36Sopenharmony_ci } 8562306a36Sopenharmony_ci return 0; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cierr_resource_size_get: 8862306a36Sopenharmony_ci for (i--; i >= 0; i--) { 8962306a36Sopenharmony_ci sub_pool = &pool->sub_pools[i]; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci devl_resource_occ_get_unregister(devlink, 9262306a36Sopenharmony_ci sub_pool->resource_id); 9362306a36Sopenharmony_ci } 9462306a36Sopenharmony_ci return err; 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cistatic void mlxsw_sp_counter_sub_pools_fini(struct mlxsw_sp *mlxsw_sp) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci struct mlxsw_sp_counter_pool *pool = mlxsw_sp->counter_pool; 10062306a36Sopenharmony_ci struct devlink *devlink = priv_to_devlink(mlxsw_sp->core); 10162306a36Sopenharmony_ci struct mlxsw_sp_counter_sub_pool *sub_pool; 10262306a36Sopenharmony_ci int i; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci for (i = 0; i < pool->sub_pools_count; i++) { 10562306a36Sopenharmony_ci sub_pool = &pool->sub_pools[i]; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci WARN_ON(atomic_read(&sub_pool->active_entries_count)); 10862306a36Sopenharmony_ci devl_resource_occ_get_unregister(devlink, 10962306a36Sopenharmony_ci sub_pool->resource_id); 11062306a36Sopenharmony_ci } 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cistatic u64 mlxsw_sp_counter_pool_occ_get(void *priv) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci const struct mlxsw_sp_counter_pool *pool = priv; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci return atomic_read(&pool->active_entries_count); 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ciint mlxsw_sp_counter_pool_init(struct mlxsw_sp *mlxsw_sp) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci unsigned int sub_pools_count = ARRAY_SIZE(mlxsw_sp_counter_sub_pools); 12362306a36Sopenharmony_ci struct devlink *devlink = priv_to_devlink(mlxsw_sp->core); 12462306a36Sopenharmony_ci struct mlxsw_sp_counter_pool *pool; 12562306a36Sopenharmony_ci int err; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci pool = kzalloc(struct_size(pool, sub_pools, sub_pools_count), 12862306a36Sopenharmony_ci GFP_KERNEL); 12962306a36Sopenharmony_ci if (!pool) 13062306a36Sopenharmony_ci return -ENOMEM; 13162306a36Sopenharmony_ci mlxsw_sp->counter_pool = pool; 13262306a36Sopenharmony_ci pool->sub_pools_count = sub_pools_count; 13362306a36Sopenharmony_ci memcpy(pool->sub_pools, mlxsw_sp_counter_sub_pools, 13462306a36Sopenharmony_ci flex_array_size(pool, sub_pools, pool->sub_pools_count)); 13562306a36Sopenharmony_ci spin_lock_init(&pool->counter_pool_lock); 13662306a36Sopenharmony_ci atomic_set(&pool->active_entries_count, 0); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci err = devl_resource_size_get(devlink, MLXSW_SP_RESOURCE_COUNTERS, 13962306a36Sopenharmony_ci &pool->pool_size); 14062306a36Sopenharmony_ci if (err) 14162306a36Sopenharmony_ci goto err_pool_resource_size_get; 14262306a36Sopenharmony_ci devl_resource_occ_get_register(devlink, MLXSW_SP_RESOURCE_COUNTERS, 14362306a36Sopenharmony_ci mlxsw_sp_counter_pool_occ_get, pool); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci pool->usage = bitmap_zalloc(pool->pool_size, GFP_KERNEL); 14662306a36Sopenharmony_ci if (!pool->usage) { 14762306a36Sopenharmony_ci err = -ENOMEM; 14862306a36Sopenharmony_ci goto err_usage_alloc; 14962306a36Sopenharmony_ci } 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci err = mlxsw_sp_counter_sub_pools_init(mlxsw_sp); 15262306a36Sopenharmony_ci if (err) 15362306a36Sopenharmony_ci goto err_sub_pools_init; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci return 0; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cierr_sub_pools_init: 15862306a36Sopenharmony_ci bitmap_free(pool->usage); 15962306a36Sopenharmony_cierr_usage_alloc: 16062306a36Sopenharmony_ci devl_resource_occ_get_unregister(devlink, 16162306a36Sopenharmony_ci MLXSW_SP_RESOURCE_COUNTERS); 16262306a36Sopenharmony_cierr_pool_resource_size_get: 16362306a36Sopenharmony_ci kfree(pool); 16462306a36Sopenharmony_ci return err; 16562306a36Sopenharmony_ci} 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_civoid mlxsw_sp_counter_pool_fini(struct mlxsw_sp *mlxsw_sp) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci struct mlxsw_sp_counter_pool *pool = mlxsw_sp->counter_pool; 17062306a36Sopenharmony_ci struct devlink *devlink = priv_to_devlink(mlxsw_sp->core); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci mlxsw_sp_counter_sub_pools_fini(mlxsw_sp); 17362306a36Sopenharmony_ci WARN_ON(find_first_bit(pool->usage, pool->pool_size) != 17462306a36Sopenharmony_ci pool->pool_size); 17562306a36Sopenharmony_ci WARN_ON(atomic_read(&pool->active_entries_count)); 17662306a36Sopenharmony_ci bitmap_free(pool->usage); 17762306a36Sopenharmony_ci devl_resource_occ_get_unregister(devlink, 17862306a36Sopenharmony_ci MLXSW_SP_RESOURCE_COUNTERS); 17962306a36Sopenharmony_ci kfree(pool); 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ciint mlxsw_sp_counter_alloc(struct mlxsw_sp *mlxsw_sp, 18362306a36Sopenharmony_ci enum mlxsw_sp_counter_sub_pool_id sub_pool_id, 18462306a36Sopenharmony_ci unsigned int *p_counter_index) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci struct mlxsw_sp_counter_pool *pool = mlxsw_sp->counter_pool; 18762306a36Sopenharmony_ci struct mlxsw_sp_counter_sub_pool *sub_pool; 18862306a36Sopenharmony_ci unsigned int entry_index; 18962306a36Sopenharmony_ci unsigned int stop_index; 19062306a36Sopenharmony_ci int i, err; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci sub_pool = &pool->sub_pools[sub_pool_id]; 19362306a36Sopenharmony_ci stop_index = sub_pool->base_index + sub_pool->size; 19462306a36Sopenharmony_ci entry_index = sub_pool->base_index; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci spin_lock(&pool->counter_pool_lock); 19762306a36Sopenharmony_ci entry_index = find_next_zero_bit(pool->usage, stop_index, entry_index); 19862306a36Sopenharmony_ci if (entry_index == stop_index) { 19962306a36Sopenharmony_ci err = -ENOBUFS; 20062306a36Sopenharmony_ci goto err_alloc; 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci /* The sub-pools can contain non-integer number of entries 20362306a36Sopenharmony_ci * so we must check for overflow 20462306a36Sopenharmony_ci */ 20562306a36Sopenharmony_ci if (entry_index + sub_pool->entry_size > stop_index) { 20662306a36Sopenharmony_ci err = -ENOBUFS; 20762306a36Sopenharmony_ci goto err_alloc; 20862306a36Sopenharmony_ci } 20962306a36Sopenharmony_ci for (i = 0; i < sub_pool->entry_size; i++) 21062306a36Sopenharmony_ci __set_bit(entry_index + i, pool->usage); 21162306a36Sopenharmony_ci spin_unlock(&pool->counter_pool_lock); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci *p_counter_index = entry_index; 21462306a36Sopenharmony_ci atomic_add(sub_pool->entry_size, &sub_pool->active_entries_count); 21562306a36Sopenharmony_ci atomic_add(sub_pool->entry_size, &pool->active_entries_count); 21662306a36Sopenharmony_ci return 0; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_cierr_alloc: 21962306a36Sopenharmony_ci spin_unlock(&pool->counter_pool_lock); 22062306a36Sopenharmony_ci return err; 22162306a36Sopenharmony_ci} 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_civoid mlxsw_sp_counter_free(struct mlxsw_sp *mlxsw_sp, 22462306a36Sopenharmony_ci enum mlxsw_sp_counter_sub_pool_id sub_pool_id, 22562306a36Sopenharmony_ci unsigned int counter_index) 22662306a36Sopenharmony_ci{ 22762306a36Sopenharmony_ci struct mlxsw_sp_counter_pool *pool = mlxsw_sp->counter_pool; 22862306a36Sopenharmony_ci struct mlxsw_sp_counter_sub_pool *sub_pool; 22962306a36Sopenharmony_ci int i; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci if (WARN_ON(counter_index >= pool->pool_size)) 23262306a36Sopenharmony_ci return; 23362306a36Sopenharmony_ci sub_pool = &pool->sub_pools[sub_pool_id]; 23462306a36Sopenharmony_ci spin_lock(&pool->counter_pool_lock); 23562306a36Sopenharmony_ci for (i = 0; i < sub_pool->entry_size; i++) 23662306a36Sopenharmony_ci __clear_bit(counter_index + i, pool->usage); 23762306a36Sopenharmony_ci spin_unlock(&pool->counter_pool_lock); 23862306a36Sopenharmony_ci atomic_sub(sub_pool->entry_size, &sub_pool->active_entries_count); 23962306a36Sopenharmony_ci atomic_sub(sub_pool->entry_size, &pool->active_entries_count); 24062306a36Sopenharmony_ci} 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ciint mlxsw_sp_counter_resources_register(struct mlxsw_core *mlxsw_core) 24362306a36Sopenharmony_ci{ 24462306a36Sopenharmony_ci static struct devlink_resource_size_params size_params; 24562306a36Sopenharmony_ci struct devlink *devlink = priv_to_devlink(mlxsw_core); 24662306a36Sopenharmony_ci const struct mlxsw_sp_counter_sub_pool *sub_pool; 24762306a36Sopenharmony_ci unsigned int total_bank_config; 24862306a36Sopenharmony_ci u64 sub_pool_size; 24962306a36Sopenharmony_ci u64 base_index; 25062306a36Sopenharmony_ci u64 pool_size; 25162306a36Sopenharmony_ci u64 bank_size; 25262306a36Sopenharmony_ci int err; 25362306a36Sopenharmony_ci int i; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci if (!MLXSW_CORE_RES_VALID(mlxsw_core, COUNTER_POOL_SIZE) || 25662306a36Sopenharmony_ci !MLXSW_CORE_RES_VALID(mlxsw_core, COUNTER_BANK_SIZE)) 25762306a36Sopenharmony_ci return -EIO; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci pool_size = MLXSW_CORE_RES_GET(mlxsw_core, COUNTER_POOL_SIZE); 26062306a36Sopenharmony_ci bank_size = MLXSW_CORE_RES_GET(mlxsw_core, COUNTER_BANK_SIZE); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci devlink_resource_size_params_init(&size_params, pool_size, 26362306a36Sopenharmony_ci pool_size, bank_size, 26462306a36Sopenharmony_ci DEVLINK_RESOURCE_UNIT_ENTRY); 26562306a36Sopenharmony_ci err = devl_resource_register(devlink, 26662306a36Sopenharmony_ci MLXSW_SP_RESOURCE_NAME_COUNTERS, 26762306a36Sopenharmony_ci pool_size, 26862306a36Sopenharmony_ci MLXSW_SP_RESOURCE_COUNTERS, 26962306a36Sopenharmony_ci DEVLINK_RESOURCE_ID_PARENT_TOP, 27062306a36Sopenharmony_ci &size_params); 27162306a36Sopenharmony_ci if (err) 27262306a36Sopenharmony_ci return err; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci /* Allocation is based on bank count which should be 27562306a36Sopenharmony_ci * specified for each sub pool statically. 27662306a36Sopenharmony_ci */ 27762306a36Sopenharmony_ci total_bank_config = 0; 27862306a36Sopenharmony_ci base_index = 0; 27962306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(mlxsw_sp_counter_sub_pools); i++) { 28062306a36Sopenharmony_ci sub_pool = &mlxsw_sp_counter_sub_pools[i]; 28162306a36Sopenharmony_ci sub_pool_size = sub_pool->bank_count * bank_size; 28262306a36Sopenharmony_ci /* The last bank can't be fully used */ 28362306a36Sopenharmony_ci if (base_index + sub_pool_size > pool_size) 28462306a36Sopenharmony_ci sub_pool_size = pool_size - base_index; 28562306a36Sopenharmony_ci base_index += sub_pool_size; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci devlink_resource_size_params_init(&size_params, sub_pool_size, 28862306a36Sopenharmony_ci sub_pool_size, bank_size, 28962306a36Sopenharmony_ci DEVLINK_RESOURCE_UNIT_ENTRY); 29062306a36Sopenharmony_ci err = devl_resource_register(devlink, 29162306a36Sopenharmony_ci sub_pool->resource_name, 29262306a36Sopenharmony_ci sub_pool_size, 29362306a36Sopenharmony_ci sub_pool->resource_id, 29462306a36Sopenharmony_ci MLXSW_SP_RESOURCE_COUNTERS, 29562306a36Sopenharmony_ci &size_params); 29662306a36Sopenharmony_ci if (err) 29762306a36Sopenharmony_ci return err; 29862306a36Sopenharmony_ci total_bank_config += sub_pool->bank_count; 29962306a36Sopenharmony_ci } 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci /* Check config is valid, no bank over subscription */ 30262306a36Sopenharmony_ci if (WARN_ON(total_bank_config > div64_u64(pool_size, bank_size) + 1)) 30362306a36Sopenharmony_ci return -EINVAL; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci return 0; 30662306a36Sopenharmony_ci} 307