0001
0002
0003
0004 #include <linux/kernel.h>
0005 #include <linux/bitops.h>
0006 #include <linux/spinlock.h>
0007
0008 #include "spectrum_cnt.h"
0009
0010 struct mlxsw_sp_counter_sub_pool {
0011 u64 size;
0012 unsigned int base_index;
0013 enum mlxsw_res_id entry_size_res_id;
0014 const char *resource_name;
0015 u64 resource_id;
0016 unsigned int entry_size;
0017 unsigned int bank_count;
0018 atomic_t active_entries_count;
0019 };
0020
0021 struct mlxsw_sp_counter_pool {
0022 u64 pool_size;
0023 unsigned long *usage;
0024 spinlock_t counter_pool_lock;
0025 atomic_t active_entries_count;
0026 unsigned int sub_pools_count;
0027 struct mlxsw_sp_counter_sub_pool sub_pools[];
0028 };
0029
0030 static const struct mlxsw_sp_counter_sub_pool mlxsw_sp_counter_sub_pools[] = {
0031 [MLXSW_SP_COUNTER_SUB_POOL_FLOW] = {
0032 .entry_size_res_id = MLXSW_RES_ID_COUNTER_SIZE_PACKETS_BYTES,
0033 .resource_name = MLXSW_SP_RESOURCE_NAME_COUNTERS_FLOW,
0034 .resource_id = MLXSW_SP_RESOURCE_COUNTERS_FLOW,
0035 .bank_count = 6,
0036 },
0037 [MLXSW_SP_COUNTER_SUB_POOL_RIF] = {
0038 .entry_size_res_id = MLXSW_RES_ID_COUNTER_SIZE_ROUTER_BASIC,
0039 .resource_name = MLXSW_SP_RESOURCE_NAME_COUNTERS_RIF,
0040 .resource_id = MLXSW_SP_RESOURCE_COUNTERS_RIF,
0041 .bank_count = 2,
0042 }
0043 };
0044
0045 static u64 mlxsw_sp_counter_sub_pool_occ_get(void *priv)
0046 {
0047 const struct mlxsw_sp_counter_sub_pool *sub_pool = priv;
0048
0049 return atomic_read(&sub_pool->active_entries_count);
0050 }
0051
0052 static int mlxsw_sp_counter_sub_pools_init(struct mlxsw_sp *mlxsw_sp)
0053 {
0054 struct mlxsw_sp_counter_pool *pool = mlxsw_sp->counter_pool;
0055 struct devlink *devlink = priv_to_devlink(mlxsw_sp->core);
0056 struct mlxsw_sp_counter_sub_pool *sub_pool;
0057 unsigned int base_index = 0;
0058 enum mlxsw_res_id res_id;
0059 int err;
0060 int i;
0061
0062 for (i = 0; i < pool->sub_pools_count; i++) {
0063 sub_pool = &pool->sub_pools[i];
0064 res_id = sub_pool->entry_size_res_id;
0065
0066 if (!mlxsw_core_res_valid(mlxsw_sp->core, res_id))
0067 return -EIO;
0068 sub_pool->entry_size = mlxsw_core_res_get(mlxsw_sp->core,
0069 res_id);
0070 err = devl_resource_size_get(devlink,
0071 sub_pool->resource_id,
0072 &sub_pool->size);
0073 if (err)
0074 goto err_resource_size_get;
0075
0076 devl_resource_occ_get_register(devlink,
0077 sub_pool->resource_id,
0078 mlxsw_sp_counter_sub_pool_occ_get,
0079 sub_pool);
0080
0081 sub_pool->base_index = base_index;
0082 base_index += sub_pool->size;
0083 atomic_set(&sub_pool->active_entries_count, 0);
0084 }
0085 return 0;
0086
0087 err_resource_size_get:
0088 for (i--; i >= 0; i--) {
0089 sub_pool = &pool->sub_pools[i];
0090
0091 devl_resource_occ_get_unregister(devlink,
0092 sub_pool->resource_id);
0093 }
0094 return err;
0095 }
0096
0097 static void mlxsw_sp_counter_sub_pools_fini(struct mlxsw_sp *mlxsw_sp)
0098 {
0099 struct mlxsw_sp_counter_pool *pool = mlxsw_sp->counter_pool;
0100 struct devlink *devlink = priv_to_devlink(mlxsw_sp->core);
0101 struct mlxsw_sp_counter_sub_pool *sub_pool;
0102 int i;
0103
0104 for (i = 0; i < pool->sub_pools_count; i++) {
0105 sub_pool = &pool->sub_pools[i];
0106
0107 WARN_ON(atomic_read(&sub_pool->active_entries_count));
0108 devl_resource_occ_get_unregister(devlink,
0109 sub_pool->resource_id);
0110 }
0111 }
0112
0113 static u64 mlxsw_sp_counter_pool_occ_get(void *priv)
0114 {
0115 const struct mlxsw_sp_counter_pool *pool = priv;
0116
0117 return atomic_read(&pool->active_entries_count);
0118 }
0119
0120 int mlxsw_sp_counter_pool_init(struct mlxsw_sp *mlxsw_sp)
0121 {
0122 unsigned int sub_pools_count = ARRAY_SIZE(mlxsw_sp_counter_sub_pools);
0123 struct devlink *devlink = priv_to_devlink(mlxsw_sp->core);
0124 struct mlxsw_sp_counter_pool *pool;
0125 int err;
0126
0127 pool = kzalloc(struct_size(pool, sub_pools, sub_pools_count),
0128 GFP_KERNEL);
0129 if (!pool)
0130 return -ENOMEM;
0131 mlxsw_sp->counter_pool = pool;
0132 pool->sub_pools_count = sub_pools_count;
0133 memcpy(pool->sub_pools, mlxsw_sp_counter_sub_pools,
0134 flex_array_size(pool, sub_pools, pool->sub_pools_count));
0135 spin_lock_init(&pool->counter_pool_lock);
0136 atomic_set(&pool->active_entries_count, 0);
0137
0138 err = devl_resource_size_get(devlink, MLXSW_SP_RESOURCE_COUNTERS,
0139 &pool->pool_size);
0140 if (err)
0141 goto err_pool_resource_size_get;
0142 devl_resource_occ_get_register(devlink, MLXSW_SP_RESOURCE_COUNTERS,
0143 mlxsw_sp_counter_pool_occ_get, pool);
0144
0145 pool->usage = bitmap_zalloc(pool->pool_size, GFP_KERNEL);
0146 if (!pool->usage) {
0147 err = -ENOMEM;
0148 goto err_usage_alloc;
0149 }
0150
0151 err = mlxsw_sp_counter_sub_pools_init(mlxsw_sp);
0152 if (err)
0153 goto err_sub_pools_init;
0154
0155 return 0;
0156
0157 err_sub_pools_init:
0158 bitmap_free(pool->usage);
0159 err_usage_alloc:
0160 devl_resource_occ_get_unregister(devlink,
0161 MLXSW_SP_RESOURCE_COUNTERS);
0162 err_pool_resource_size_get:
0163 kfree(pool);
0164 return err;
0165 }
0166
0167 void mlxsw_sp_counter_pool_fini(struct mlxsw_sp *mlxsw_sp)
0168 {
0169 struct mlxsw_sp_counter_pool *pool = mlxsw_sp->counter_pool;
0170 struct devlink *devlink = priv_to_devlink(mlxsw_sp->core);
0171
0172 mlxsw_sp_counter_sub_pools_fini(mlxsw_sp);
0173 WARN_ON(find_first_bit(pool->usage, pool->pool_size) !=
0174 pool->pool_size);
0175 WARN_ON(atomic_read(&pool->active_entries_count));
0176 bitmap_free(pool->usage);
0177 devl_resource_occ_get_unregister(devlink,
0178 MLXSW_SP_RESOURCE_COUNTERS);
0179 kfree(pool);
0180 }
0181
0182 int mlxsw_sp_counter_alloc(struct mlxsw_sp *mlxsw_sp,
0183 enum mlxsw_sp_counter_sub_pool_id sub_pool_id,
0184 unsigned int *p_counter_index)
0185 {
0186 struct mlxsw_sp_counter_pool *pool = mlxsw_sp->counter_pool;
0187 struct mlxsw_sp_counter_sub_pool *sub_pool;
0188 unsigned int entry_index;
0189 unsigned int stop_index;
0190 int i, err;
0191
0192 sub_pool = &pool->sub_pools[sub_pool_id];
0193 stop_index = sub_pool->base_index + sub_pool->size;
0194 entry_index = sub_pool->base_index;
0195
0196 spin_lock(&pool->counter_pool_lock);
0197 entry_index = find_next_zero_bit(pool->usage, stop_index, entry_index);
0198 if (entry_index == stop_index) {
0199 err = -ENOBUFS;
0200 goto err_alloc;
0201 }
0202
0203
0204
0205 if (entry_index + sub_pool->entry_size > stop_index) {
0206 err = -ENOBUFS;
0207 goto err_alloc;
0208 }
0209 for (i = 0; i < sub_pool->entry_size; i++)
0210 __set_bit(entry_index + i, pool->usage);
0211 spin_unlock(&pool->counter_pool_lock);
0212
0213 *p_counter_index = entry_index;
0214 atomic_add(sub_pool->entry_size, &sub_pool->active_entries_count);
0215 atomic_add(sub_pool->entry_size, &pool->active_entries_count);
0216 return 0;
0217
0218 err_alloc:
0219 spin_unlock(&pool->counter_pool_lock);
0220 return err;
0221 }
0222
0223 void mlxsw_sp_counter_free(struct mlxsw_sp *mlxsw_sp,
0224 enum mlxsw_sp_counter_sub_pool_id sub_pool_id,
0225 unsigned int counter_index)
0226 {
0227 struct mlxsw_sp_counter_pool *pool = mlxsw_sp->counter_pool;
0228 struct mlxsw_sp_counter_sub_pool *sub_pool;
0229 int i;
0230
0231 if (WARN_ON(counter_index >= pool->pool_size))
0232 return;
0233 sub_pool = &pool->sub_pools[sub_pool_id];
0234 spin_lock(&pool->counter_pool_lock);
0235 for (i = 0; i < sub_pool->entry_size; i++)
0236 __clear_bit(counter_index + i, pool->usage);
0237 spin_unlock(&pool->counter_pool_lock);
0238 atomic_sub(sub_pool->entry_size, &sub_pool->active_entries_count);
0239 atomic_sub(sub_pool->entry_size, &pool->active_entries_count);
0240 }
0241
0242 int mlxsw_sp_counter_resources_register(struct mlxsw_core *mlxsw_core)
0243 {
0244 static struct devlink_resource_size_params size_params;
0245 struct devlink *devlink = priv_to_devlink(mlxsw_core);
0246 const struct mlxsw_sp_counter_sub_pool *sub_pool;
0247 unsigned int total_bank_config;
0248 u64 sub_pool_size;
0249 u64 base_index;
0250 u64 pool_size;
0251 u64 bank_size;
0252 int err;
0253 int i;
0254
0255 if (!MLXSW_CORE_RES_VALID(mlxsw_core, COUNTER_POOL_SIZE) ||
0256 !MLXSW_CORE_RES_VALID(mlxsw_core, COUNTER_BANK_SIZE))
0257 return -EIO;
0258
0259 pool_size = MLXSW_CORE_RES_GET(mlxsw_core, COUNTER_POOL_SIZE);
0260 bank_size = MLXSW_CORE_RES_GET(mlxsw_core, COUNTER_BANK_SIZE);
0261
0262 devlink_resource_size_params_init(&size_params, pool_size,
0263 pool_size, bank_size,
0264 DEVLINK_RESOURCE_UNIT_ENTRY);
0265 err = devl_resource_register(devlink,
0266 MLXSW_SP_RESOURCE_NAME_COUNTERS,
0267 pool_size,
0268 MLXSW_SP_RESOURCE_COUNTERS,
0269 DEVLINK_RESOURCE_ID_PARENT_TOP,
0270 &size_params);
0271 if (err)
0272 return err;
0273
0274
0275
0276
0277 total_bank_config = 0;
0278 base_index = 0;
0279 for (i = 0; i < ARRAY_SIZE(mlxsw_sp_counter_sub_pools); i++) {
0280 sub_pool = &mlxsw_sp_counter_sub_pools[i];
0281 sub_pool_size = sub_pool->bank_count * bank_size;
0282
0283 if (base_index + sub_pool_size > pool_size)
0284 sub_pool_size = pool_size - base_index;
0285 base_index += sub_pool_size;
0286
0287 devlink_resource_size_params_init(&size_params, sub_pool_size,
0288 sub_pool_size, bank_size,
0289 DEVLINK_RESOURCE_UNIT_ENTRY);
0290 err = devl_resource_register(devlink,
0291 sub_pool->resource_name,
0292 sub_pool_size,
0293 sub_pool->resource_id,
0294 MLXSW_SP_RESOURCE_COUNTERS,
0295 &size_params);
0296 if (err)
0297 return err;
0298 total_bank_config += sub_pool->bank_count;
0299 }
0300
0301
0302 if (WARN_ON(total_bank_config > div64_u64(pool_size, bank_size) + 1))
0303 return -EINVAL;
0304
0305 return 0;
0306 }