18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 28c2ecf20Sopenharmony_ci/* Copyright (c) 2017-2018 Mellanox Technologies. All rights reserved */ 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci#include <linux/kernel.h> 58c2ecf20Sopenharmony_ci#include <linux/bitops.h> 68c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include "spectrum_cnt.h" 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_cistruct mlxsw_sp_counter_sub_pool { 118c2ecf20Sopenharmony_ci u64 size; 128c2ecf20Sopenharmony_ci unsigned int base_index; 138c2ecf20Sopenharmony_ci enum mlxsw_res_id entry_size_res_id; 148c2ecf20Sopenharmony_ci const char *resource_name; /* devlink resource name */ 158c2ecf20Sopenharmony_ci u64 resource_id; /* devlink resource id */ 168c2ecf20Sopenharmony_ci unsigned int entry_size; 178c2ecf20Sopenharmony_ci unsigned int bank_count; 188c2ecf20Sopenharmony_ci atomic_t active_entries_count; 198c2ecf20Sopenharmony_ci}; 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistruct mlxsw_sp_counter_pool { 228c2ecf20Sopenharmony_ci u64 pool_size; 238c2ecf20Sopenharmony_ci unsigned long *usage; /* Usage bitmap */ 248c2ecf20Sopenharmony_ci spinlock_t counter_pool_lock; /* Protects counter pool allocations */ 258c2ecf20Sopenharmony_ci atomic_t active_entries_count; 268c2ecf20Sopenharmony_ci unsigned int sub_pools_count; 278c2ecf20Sopenharmony_ci struct mlxsw_sp_counter_sub_pool sub_pools[]; 288c2ecf20Sopenharmony_ci}; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistatic const struct mlxsw_sp_counter_sub_pool mlxsw_sp_counter_sub_pools[] = { 318c2ecf20Sopenharmony_ci [MLXSW_SP_COUNTER_SUB_POOL_FLOW] = { 328c2ecf20Sopenharmony_ci .entry_size_res_id = MLXSW_RES_ID_COUNTER_SIZE_PACKETS_BYTES, 338c2ecf20Sopenharmony_ci .resource_name = MLXSW_SP_RESOURCE_NAME_COUNTERS_FLOW, 348c2ecf20Sopenharmony_ci .resource_id = MLXSW_SP_RESOURCE_COUNTERS_FLOW, 358c2ecf20Sopenharmony_ci .bank_count = 6, 368c2ecf20Sopenharmony_ci }, 378c2ecf20Sopenharmony_ci [MLXSW_SP_COUNTER_SUB_POOL_RIF] = { 388c2ecf20Sopenharmony_ci .entry_size_res_id = MLXSW_RES_ID_COUNTER_SIZE_ROUTER_BASIC, 398c2ecf20Sopenharmony_ci .resource_name = MLXSW_SP_RESOURCE_NAME_COUNTERS_RIF, 408c2ecf20Sopenharmony_ci .resource_id = MLXSW_SP_RESOURCE_COUNTERS_RIF, 418c2ecf20Sopenharmony_ci .bank_count = 2, 428c2ecf20Sopenharmony_ci } 438c2ecf20Sopenharmony_ci}; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistatic u64 mlxsw_sp_counter_sub_pool_occ_get(void *priv) 468c2ecf20Sopenharmony_ci{ 478c2ecf20Sopenharmony_ci const struct mlxsw_sp_counter_sub_pool *sub_pool = priv; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci return atomic_read(&sub_pool->active_entries_count); 508c2ecf20Sopenharmony_ci} 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic int mlxsw_sp_counter_sub_pools_init(struct mlxsw_sp *mlxsw_sp) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci struct mlxsw_sp_counter_pool *pool = mlxsw_sp->counter_pool; 558c2ecf20Sopenharmony_ci struct devlink *devlink = priv_to_devlink(mlxsw_sp->core); 568c2ecf20Sopenharmony_ci struct mlxsw_sp_counter_sub_pool *sub_pool; 578c2ecf20Sopenharmony_ci unsigned int base_index = 0; 588c2ecf20Sopenharmony_ci enum mlxsw_res_id res_id; 598c2ecf20Sopenharmony_ci int err; 608c2ecf20Sopenharmony_ci int i; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci for (i = 0; i < pool->sub_pools_count; i++) { 638c2ecf20Sopenharmony_ci sub_pool = &pool->sub_pools[i]; 648c2ecf20Sopenharmony_ci res_id = sub_pool->entry_size_res_id; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci if (!mlxsw_core_res_valid(mlxsw_sp->core, res_id)) 678c2ecf20Sopenharmony_ci return -EIO; 688c2ecf20Sopenharmony_ci sub_pool->entry_size = mlxsw_core_res_get(mlxsw_sp->core, 698c2ecf20Sopenharmony_ci res_id); 708c2ecf20Sopenharmony_ci err = devlink_resource_size_get(devlink, 718c2ecf20Sopenharmony_ci sub_pool->resource_id, 728c2ecf20Sopenharmony_ci &sub_pool->size); 738c2ecf20Sopenharmony_ci if (err) 748c2ecf20Sopenharmony_ci goto err_resource_size_get; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci devlink_resource_occ_get_register(devlink, 778c2ecf20Sopenharmony_ci sub_pool->resource_id, 788c2ecf20Sopenharmony_ci mlxsw_sp_counter_sub_pool_occ_get, 798c2ecf20Sopenharmony_ci sub_pool); 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci sub_pool->base_index = base_index; 828c2ecf20Sopenharmony_ci base_index += sub_pool->size; 838c2ecf20Sopenharmony_ci atomic_set(&sub_pool->active_entries_count, 0); 848c2ecf20Sopenharmony_ci } 858c2ecf20Sopenharmony_ci return 0; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cierr_resource_size_get: 888c2ecf20Sopenharmony_ci for (i--; i >= 0; i--) { 898c2ecf20Sopenharmony_ci sub_pool = &pool->sub_pools[i]; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci devlink_resource_occ_get_unregister(devlink, 928c2ecf20Sopenharmony_ci sub_pool->resource_id); 938c2ecf20Sopenharmony_ci } 948c2ecf20Sopenharmony_ci return err; 958c2ecf20Sopenharmony_ci} 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cistatic void mlxsw_sp_counter_sub_pools_fini(struct mlxsw_sp *mlxsw_sp) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci struct mlxsw_sp_counter_pool *pool = mlxsw_sp->counter_pool; 1008c2ecf20Sopenharmony_ci struct devlink *devlink = priv_to_devlink(mlxsw_sp->core); 1018c2ecf20Sopenharmony_ci struct mlxsw_sp_counter_sub_pool *sub_pool; 1028c2ecf20Sopenharmony_ci int i; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci for (i = 0; i < pool->sub_pools_count; i++) { 1058c2ecf20Sopenharmony_ci sub_pool = &pool->sub_pools[i]; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci WARN_ON(atomic_read(&sub_pool->active_entries_count)); 1088c2ecf20Sopenharmony_ci devlink_resource_occ_get_unregister(devlink, 1098c2ecf20Sopenharmony_ci sub_pool->resource_id); 1108c2ecf20Sopenharmony_ci } 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cistatic u64 mlxsw_sp_counter_pool_occ_get(void *priv) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci const struct mlxsw_sp_counter_pool *pool = priv; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci return atomic_read(&pool->active_entries_count); 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ciint mlxsw_sp_counter_pool_init(struct mlxsw_sp *mlxsw_sp) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci unsigned int sub_pools_count = ARRAY_SIZE(mlxsw_sp_counter_sub_pools); 1238c2ecf20Sopenharmony_ci struct devlink *devlink = priv_to_devlink(mlxsw_sp->core); 1248c2ecf20Sopenharmony_ci struct mlxsw_sp_counter_pool *pool; 1258c2ecf20Sopenharmony_ci int err; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci pool = kzalloc(struct_size(pool, sub_pools, sub_pools_count), 1288c2ecf20Sopenharmony_ci GFP_KERNEL); 1298c2ecf20Sopenharmony_ci if (!pool) 1308c2ecf20Sopenharmony_ci return -ENOMEM; 1318c2ecf20Sopenharmony_ci mlxsw_sp->counter_pool = pool; 1328c2ecf20Sopenharmony_ci pool->sub_pools_count = sub_pools_count; 1338c2ecf20Sopenharmony_ci memcpy(pool->sub_pools, mlxsw_sp_counter_sub_pools, 1348c2ecf20Sopenharmony_ci flex_array_size(pool, sub_pools, pool->sub_pools_count)); 1358c2ecf20Sopenharmony_ci spin_lock_init(&pool->counter_pool_lock); 1368c2ecf20Sopenharmony_ci atomic_set(&pool->active_entries_count, 0); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci err = devlink_resource_size_get(devlink, MLXSW_SP_RESOURCE_COUNTERS, 1398c2ecf20Sopenharmony_ci &pool->pool_size); 1408c2ecf20Sopenharmony_ci if (err) 1418c2ecf20Sopenharmony_ci goto err_pool_resource_size_get; 1428c2ecf20Sopenharmony_ci devlink_resource_occ_get_register(devlink, MLXSW_SP_RESOURCE_COUNTERS, 1438c2ecf20Sopenharmony_ci mlxsw_sp_counter_pool_occ_get, pool); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci pool->usage = bitmap_zalloc(pool->pool_size, GFP_KERNEL); 1468c2ecf20Sopenharmony_ci if (!pool->usage) { 1478c2ecf20Sopenharmony_ci err = -ENOMEM; 1488c2ecf20Sopenharmony_ci goto err_usage_alloc; 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci err = mlxsw_sp_counter_sub_pools_init(mlxsw_sp); 1528c2ecf20Sopenharmony_ci if (err) 1538c2ecf20Sopenharmony_ci goto err_sub_pools_init; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci return 0; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_cierr_sub_pools_init: 1588c2ecf20Sopenharmony_ci bitmap_free(pool->usage); 1598c2ecf20Sopenharmony_cierr_usage_alloc: 1608c2ecf20Sopenharmony_ci devlink_resource_occ_get_unregister(devlink, 1618c2ecf20Sopenharmony_ci MLXSW_SP_RESOURCE_COUNTERS); 1628c2ecf20Sopenharmony_cierr_pool_resource_size_get: 1638c2ecf20Sopenharmony_ci kfree(pool); 1648c2ecf20Sopenharmony_ci return err; 1658c2ecf20Sopenharmony_ci} 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_civoid mlxsw_sp_counter_pool_fini(struct mlxsw_sp *mlxsw_sp) 1688c2ecf20Sopenharmony_ci{ 1698c2ecf20Sopenharmony_ci struct mlxsw_sp_counter_pool *pool = mlxsw_sp->counter_pool; 1708c2ecf20Sopenharmony_ci struct devlink *devlink = priv_to_devlink(mlxsw_sp->core); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci mlxsw_sp_counter_sub_pools_fini(mlxsw_sp); 1738c2ecf20Sopenharmony_ci WARN_ON(find_first_bit(pool->usage, pool->pool_size) != 1748c2ecf20Sopenharmony_ci pool->pool_size); 1758c2ecf20Sopenharmony_ci WARN_ON(atomic_read(&pool->active_entries_count)); 1768c2ecf20Sopenharmony_ci bitmap_free(pool->usage); 1778c2ecf20Sopenharmony_ci devlink_resource_occ_get_unregister(devlink, 1788c2ecf20Sopenharmony_ci MLXSW_SP_RESOURCE_COUNTERS); 1798c2ecf20Sopenharmony_ci kfree(pool); 1808c2ecf20Sopenharmony_ci} 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ciint mlxsw_sp_counter_alloc(struct mlxsw_sp *mlxsw_sp, 1838c2ecf20Sopenharmony_ci enum mlxsw_sp_counter_sub_pool_id sub_pool_id, 1848c2ecf20Sopenharmony_ci unsigned int *p_counter_index) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci struct mlxsw_sp_counter_pool *pool = mlxsw_sp->counter_pool; 1878c2ecf20Sopenharmony_ci struct mlxsw_sp_counter_sub_pool *sub_pool; 1888c2ecf20Sopenharmony_ci unsigned int entry_index; 1898c2ecf20Sopenharmony_ci unsigned int stop_index; 1908c2ecf20Sopenharmony_ci int i, err; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci sub_pool = &pool->sub_pools[sub_pool_id]; 1938c2ecf20Sopenharmony_ci stop_index = sub_pool->base_index + sub_pool->size; 1948c2ecf20Sopenharmony_ci entry_index = sub_pool->base_index; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci spin_lock(&pool->counter_pool_lock); 1978c2ecf20Sopenharmony_ci entry_index = find_next_zero_bit(pool->usage, stop_index, entry_index); 1988c2ecf20Sopenharmony_ci if (entry_index == stop_index) { 1998c2ecf20Sopenharmony_ci err = -ENOBUFS; 2008c2ecf20Sopenharmony_ci goto err_alloc; 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci /* The sub-pools can contain non-integer number of entries 2038c2ecf20Sopenharmony_ci * so we must check for overflow 2048c2ecf20Sopenharmony_ci */ 2058c2ecf20Sopenharmony_ci if (entry_index + sub_pool->entry_size > stop_index) { 2068c2ecf20Sopenharmony_ci err = -ENOBUFS; 2078c2ecf20Sopenharmony_ci goto err_alloc; 2088c2ecf20Sopenharmony_ci } 2098c2ecf20Sopenharmony_ci for (i = 0; i < sub_pool->entry_size; i++) 2108c2ecf20Sopenharmony_ci __set_bit(entry_index + i, pool->usage); 2118c2ecf20Sopenharmony_ci spin_unlock(&pool->counter_pool_lock); 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci *p_counter_index = entry_index; 2148c2ecf20Sopenharmony_ci atomic_add(sub_pool->entry_size, &sub_pool->active_entries_count); 2158c2ecf20Sopenharmony_ci atomic_add(sub_pool->entry_size, &pool->active_entries_count); 2168c2ecf20Sopenharmony_ci return 0; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_cierr_alloc: 2198c2ecf20Sopenharmony_ci spin_unlock(&pool->counter_pool_lock); 2208c2ecf20Sopenharmony_ci return err; 2218c2ecf20Sopenharmony_ci} 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_civoid mlxsw_sp_counter_free(struct mlxsw_sp *mlxsw_sp, 2248c2ecf20Sopenharmony_ci enum mlxsw_sp_counter_sub_pool_id sub_pool_id, 2258c2ecf20Sopenharmony_ci unsigned int counter_index) 2268c2ecf20Sopenharmony_ci{ 2278c2ecf20Sopenharmony_ci struct mlxsw_sp_counter_pool *pool = mlxsw_sp->counter_pool; 2288c2ecf20Sopenharmony_ci struct mlxsw_sp_counter_sub_pool *sub_pool; 2298c2ecf20Sopenharmony_ci int i; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci if (WARN_ON(counter_index >= pool->pool_size)) 2328c2ecf20Sopenharmony_ci return; 2338c2ecf20Sopenharmony_ci sub_pool = &pool->sub_pools[sub_pool_id]; 2348c2ecf20Sopenharmony_ci spin_lock(&pool->counter_pool_lock); 2358c2ecf20Sopenharmony_ci for (i = 0; i < sub_pool->entry_size; i++) 2368c2ecf20Sopenharmony_ci __clear_bit(counter_index + i, pool->usage); 2378c2ecf20Sopenharmony_ci spin_unlock(&pool->counter_pool_lock); 2388c2ecf20Sopenharmony_ci atomic_sub(sub_pool->entry_size, &sub_pool->active_entries_count); 2398c2ecf20Sopenharmony_ci atomic_sub(sub_pool->entry_size, &pool->active_entries_count); 2408c2ecf20Sopenharmony_ci} 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ciint mlxsw_sp_counter_resources_register(struct mlxsw_core *mlxsw_core) 2438c2ecf20Sopenharmony_ci{ 2448c2ecf20Sopenharmony_ci static struct devlink_resource_size_params size_params; 2458c2ecf20Sopenharmony_ci struct devlink *devlink = priv_to_devlink(mlxsw_core); 2468c2ecf20Sopenharmony_ci const struct mlxsw_sp_counter_sub_pool *sub_pool; 2478c2ecf20Sopenharmony_ci unsigned int total_bank_config; 2488c2ecf20Sopenharmony_ci u64 sub_pool_size; 2498c2ecf20Sopenharmony_ci u64 base_index; 2508c2ecf20Sopenharmony_ci u64 pool_size; 2518c2ecf20Sopenharmony_ci u64 bank_size; 2528c2ecf20Sopenharmony_ci int err; 2538c2ecf20Sopenharmony_ci int i; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci if (!MLXSW_CORE_RES_VALID(mlxsw_core, COUNTER_POOL_SIZE) || 2568c2ecf20Sopenharmony_ci !MLXSW_CORE_RES_VALID(mlxsw_core, COUNTER_BANK_SIZE)) 2578c2ecf20Sopenharmony_ci return -EIO; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci pool_size = MLXSW_CORE_RES_GET(mlxsw_core, COUNTER_POOL_SIZE); 2608c2ecf20Sopenharmony_ci bank_size = MLXSW_CORE_RES_GET(mlxsw_core, COUNTER_BANK_SIZE); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci devlink_resource_size_params_init(&size_params, pool_size, 2638c2ecf20Sopenharmony_ci pool_size, bank_size, 2648c2ecf20Sopenharmony_ci DEVLINK_RESOURCE_UNIT_ENTRY); 2658c2ecf20Sopenharmony_ci err = devlink_resource_register(devlink, 2668c2ecf20Sopenharmony_ci MLXSW_SP_RESOURCE_NAME_COUNTERS, 2678c2ecf20Sopenharmony_ci pool_size, 2688c2ecf20Sopenharmony_ci MLXSW_SP_RESOURCE_COUNTERS, 2698c2ecf20Sopenharmony_ci DEVLINK_RESOURCE_ID_PARENT_TOP, 2708c2ecf20Sopenharmony_ci &size_params); 2718c2ecf20Sopenharmony_ci if (err) 2728c2ecf20Sopenharmony_ci return err; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci /* Allocation is based on bank count which should be 2758c2ecf20Sopenharmony_ci * specified for each sub pool statically. 2768c2ecf20Sopenharmony_ci */ 2778c2ecf20Sopenharmony_ci total_bank_config = 0; 2788c2ecf20Sopenharmony_ci base_index = 0; 2798c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(mlxsw_sp_counter_sub_pools); i++) { 2808c2ecf20Sopenharmony_ci sub_pool = &mlxsw_sp_counter_sub_pools[i]; 2818c2ecf20Sopenharmony_ci sub_pool_size = sub_pool->bank_count * bank_size; 2828c2ecf20Sopenharmony_ci /* The last bank can't be fully used */ 2838c2ecf20Sopenharmony_ci if (base_index + sub_pool_size > pool_size) 2848c2ecf20Sopenharmony_ci sub_pool_size = pool_size - base_index; 2858c2ecf20Sopenharmony_ci base_index += sub_pool_size; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci devlink_resource_size_params_init(&size_params, sub_pool_size, 2888c2ecf20Sopenharmony_ci sub_pool_size, bank_size, 2898c2ecf20Sopenharmony_ci DEVLINK_RESOURCE_UNIT_ENTRY); 2908c2ecf20Sopenharmony_ci err = devlink_resource_register(devlink, 2918c2ecf20Sopenharmony_ci sub_pool->resource_name, 2928c2ecf20Sopenharmony_ci sub_pool_size, 2938c2ecf20Sopenharmony_ci sub_pool->resource_id, 2948c2ecf20Sopenharmony_ci MLXSW_SP_RESOURCE_COUNTERS, 2958c2ecf20Sopenharmony_ci &size_params); 2968c2ecf20Sopenharmony_ci if (err) 2978c2ecf20Sopenharmony_ci return err; 2988c2ecf20Sopenharmony_ci total_bank_config += sub_pool->bank_count; 2998c2ecf20Sopenharmony_ci } 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci /* Check config is valid, no bank over subscription */ 3028c2ecf20Sopenharmony_ci if (WARN_ON(total_bank_config > div64_u64(pool_size, bank_size) + 1)) 3038c2ecf20Sopenharmony_ci return -EINVAL; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci return 0; 3068c2ecf20Sopenharmony_ci} 307