428 lines · c
1// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.02/* Copyright (c) 2018 Mellanox Technologies. All rights reserved */3 4#include <linux/kernel.h>5#include <linux/bitops.h>6 7#include "spectrum.h"8 9#define MLXSW_SP1_KVDL_SINGLE_BASE 010#define MLXSW_SP1_KVDL_SINGLE_SIZE 1638411#define MLXSW_SP1_KVDL_SINGLE_END \12 (MLXSW_SP1_KVDL_SINGLE_SIZE + MLXSW_SP1_KVDL_SINGLE_BASE - 1)13 14#define MLXSW_SP1_KVDL_CHUNKS_BASE \15 (MLXSW_SP1_KVDL_SINGLE_BASE + MLXSW_SP1_KVDL_SINGLE_SIZE)16#define MLXSW_SP1_KVDL_CHUNKS_SIZE 4915217#define MLXSW_SP1_KVDL_CHUNKS_END \18 (MLXSW_SP1_KVDL_CHUNKS_SIZE + MLXSW_SP1_KVDL_CHUNKS_BASE - 1)19 20#define MLXSW_SP1_KVDL_LARGE_CHUNKS_BASE \21 (MLXSW_SP1_KVDL_CHUNKS_BASE + MLXSW_SP1_KVDL_CHUNKS_SIZE)22#define MLXSW_SP1_KVDL_LARGE_CHUNKS_SIZE \23 (MLXSW_SP_KVD_LINEAR_SIZE - MLXSW_SP1_KVDL_LARGE_CHUNKS_BASE)24#define MLXSW_SP1_KVDL_LARGE_CHUNKS_END \25 (MLXSW_SP1_KVDL_LARGE_CHUNKS_SIZE + MLXSW_SP1_KVDL_LARGE_CHUNKS_BASE - 1)26 27#define MLXSW_SP1_KVDL_SINGLE_ALLOC_SIZE 128#define MLXSW_SP1_KVDL_CHUNKS_ALLOC_SIZE 3229#define MLXSW_SP1_KVDL_LARGE_CHUNKS_ALLOC_SIZE 51230 31struct mlxsw_sp1_kvdl_part_info {32 unsigned int part_index;33 unsigned int start_index;34 unsigned int end_index;35 unsigned int alloc_size;36 enum mlxsw_sp_resource_id resource_id;37};38 39enum mlxsw_sp1_kvdl_part_id {40 MLXSW_SP1_KVDL_PART_ID_SINGLE,41 MLXSW_SP1_KVDL_PART_ID_CHUNKS,42 MLXSW_SP1_KVDL_PART_ID_LARGE_CHUNKS,43};44 45#define MLXSW_SP1_KVDL_PART_INFO(id) \46[MLXSW_SP1_KVDL_PART_ID_##id] = { \47 .start_index = MLXSW_SP1_KVDL_##id##_BASE, \48 .end_index = MLXSW_SP1_KVDL_##id##_END, \49 .alloc_size = MLXSW_SP1_KVDL_##id##_ALLOC_SIZE, \50 .resource_id = MLXSW_SP_RESOURCE_KVD_LINEAR_##id, \51}52 53static const struct mlxsw_sp1_kvdl_part_info mlxsw_sp1_kvdl_parts_info[] = {54 MLXSW_SP1_KVDL_PART_INFO(SINGLE),55 MLXSW_SP1_KVDL_PART_INFO(CHUNKS),56 MLXSW_SP1_KVDL_PART_INFO(LARGE_CHUNKS),57};58 59#define MLXSW_SP1_KVDL_PARTS_INFO_LEN ARRAY_SIZE(mlxsw_sp1_kvdl_parts_info)60 61struct mlxsw_sp1_kvdl_part {62 struct mlxsw_sp1_kvdl_part_info info;63 unsigned long usage[]; /* Entries */64};65 66struct mlxsw_sp1_kvdl {67 struct mlxsw_sp1_kvdl_part *parts[MLXSW_SP1_KVDL_PARTS_INFO_LEN];68};69 70static struct mlxsw_sp1_kvdl_part *71mlxsw_sp1_kvdl_alloc_size_part(struct mlxsw_sp1_kvdl *kvdl,72 unsigned int alloc_size)73{74 struct mlxsw_sp1_kvdl_part *part, *min_part = NULL;75 int i;76 77 for (i = 0; i < MLXSW_SP1_KVDL_PARTS_INFO_LEN; i++) {78 part = kvdl->parts[i];79 if (alloc_size <= part->info.alloc_size &&80 (!min_part ||81 part->info.alloc_size <= min_part->info.alloc_size))82 min_part = part;83 }84 85 return min_part ?: ERR_PTR(-ENOBUFS);86}87 88static struct mlxsw_sp1_kvdl_part *89mlxsw_sp1_kvdl_index_part(struct mlxsw_sp1_kvdl *kvdl, u32 kvdl_index)90{91 struct mlxsw_sp1_kvdl_part *part;92 int i;93 94 for (i = 0; i < MLXSW_SP1_KVDL_PARTS_INFO_LEN; i++) {95 part = kvdl->parts[i];96 if (kvdl_index >= part->info.start_index &&97 kvdl_index <= part->info.end_index)98 return part;99 }100 101 return ERR_PTR(-EINVAL);102}103 104static u32105mlxsw_sp1_kvdl_to_kvdl_index(const struct mlxsw_sp1_kvdl_part_info *info,106 unsigned int entry_index)107{108 return info->start_index + entry_index * info->alloc_size;109}110 111static unsigned int112mlxsw_sp1_kvdl_to_entry_index(const struct mlxsw_sp1_kvdl_part_info *info,113 u32 kvdl_index)114{115 return (kvdl_index - info->start_index) / info->alloc_size;116}117 118static int mlxsw_sp1_kvdl_part_alloc(struct mlxsw_sp1_kvdl_part *part,119 u32 *p_kvdl_index)120{121 const struct mlxsw_sp1_kvdl_part_info *info = &part->info;122 unsigned int entry_index, nr_entries;123 124 nr_entries = (info->end_index - info->start_index + 1) /125 info->alloc_size;126 entry_index = find_first_zero_bit(part->usage, nr_entries);127 if (entry_index == nr_entries)128 return -ENOBUFS;129 __set_bit(entry_index, part->usage);130 131 *p_kvdl_index = mlxsw_sp1_kvdl_to_kvdl_index(info, entry_index);132 133 return 0;134}135 136static void mlxsw_sp1_kvdl_part_free(struct mlxsw_sp1_kvdl_part *part,137 u32 kvdl_index)138{139 const struct mlxsw_sp1_kvdl_part_info *info = &part->info;140 unsigned int entry_index;141 142 entry_index = mlxsw_sp1_kvdl_to_entry_index(info, kvdl_index);143 __clear_bit(entry_index, part->usage);144}145 146static int mlxsw_sp1_kvdl_alloc(struct mlxsw_sp *mlxsw_sp, void *priv,147 enum mlxsw_sp_kvdl_entry_type type,148 unsigned int entry_count,149 u32 *p_entry_index)150{151 struct mlxsw_sp1_kvdl *kvdl = priv;152 struct mlxsw_sp1_kvdl_part *part;153 154 /* Find partition with smallest allocation size satisfying the155 * requested size.156 */157 part = mlxsw_sp1_kvdl_alloc_size_part(kvdl, entry_count);158 if (IS_ERR(part))159 return PTR_ERR(part);160 161 return mlxsw_sp1_kvdl_part_alloc(part, p_entry_index);162}163 164static void mlxsw_sp1_kvdl_free(struct mlxsw_sp *mlxsw_sp, void *priv,165 enum mlxsw_sp_kvdl_entry_type type,166 unsigned int entry_count, int entry_index)167{168 struct mlxsw_sp1_kvdl *kvdl = priv;169 struct mlxsw_sp1_kvdl_part *part;170 171 part = mlxsw_sp1_kvdl_index_part(kvdl, entry_index);172 if (IS_ERR(part))173 return;174 mlxsw_sp1_kvdl_part_free(part, entry_index);175}176 177static int mlxsw_sp1_kvdl_alloc_size_query(struct mlxsw_sp *mlxsw_sp,178 void *priv,179 enum mlxsw_sp_kvdl_entry_type type,180 unsigned int entry_count,181 unsigned int *p_alloc_size)182{183 struct mlxsw_sp1_kvdl *kvdl = priv;184 struct mlxsw_sp1_kvdl_part *part;185 186 part = mlxsw_sp1_kvdl_alloc_size_part(kvdl, entry_count);187 if (IS_ERR(part))188 return PTR_ERR(part);189 190 *p_alloc_size = part->info.alloc_size;191 192 return 0;193}194 195static void mlxsw_sp1_kvdl_part_update(struct mlxsw_sp1_kvdl_part *part,196 struct mlxsw_sp1_kvdl_part *part_prev,197 unsigned int size)198{199 if (!part_prev) {200 part->info.end_index = size - 1;201 } else {202 part->info.start_index = part_prev->info.end_index + 1;203 part->info.end_index = part->info.start_index + size - 1;204 }205}206 207static struct mlxsw_sp1_kvdl_part *208mlxsw_sp1_kvdl_part_init(struct mlxsw_sp *mlxsw_sp,209 const struct mlxsw_sp1_kvdl_part_info *info,210 struct mlxsw_sp1_kvdl_part *part_prev)211{212 struct devlink *devlink = priv_to_devlink(mlxsw_sp->core);213 struct mlxsw_sp1_kvdl_part *part;214 bool need_update = true;215 unsigned int nr_entries;216 u64 resource_size;217 int err;218 219 err = devl_resource_size_get(devlink, info->resource_id,220 &resource_size);221 if (err) {222 need_update = false;223 resource_size = info->end_index - info->start_index + 1;224 }225 226 nr_entries = div_u64(resource_size, info->alloc_size);227 part = kzalloc(struct_size(part, usage, BITS_TO_LONGS(nr_entries)),228 GFP_KERNEL);229 if (!part)230 return ERR_PTR(-ENOMEM);231 232 memcpy(&part->info, info, sizeof(part->info));233 234 if (need_update)235 mlxsw_sp1_kvdl_part_update(part, part_prev, resource_size);236 return part;237}238 239static void mlxsw_sp1_kvdl_part_fini(struct mlxsw_sp1_kvdl_part *part)240{241 kfree(part);242}243 244static int mlxsw_sp1_kvdl_parts_init(struct mlxsw_sp *mlxsw_sp,245 struct mlxsw_sp1_kvdl *kvdl)246{247 const struct mlxsw_sp1_kvdl_part_info *info;248 struct mlxsw_sp1_kvdl_part *part_prev = NULL;249 int err, i;250 251 for (i = 0; i < MLXSW_SP1_KVDL_PARTS_INFO_LEN; i++) {252 info = &mlxsw_sp1_kvdl_parts_info[i];253 kvdl->parts[i] = mlxsw_sp1_kvdl_part_init(mlxsw_sp, info,254 part_prev);255 if (IS_ERR(kvdl->parts[i])) {256 err = PTR_ERR(kvdl->parts[i]);257 goto err_kvdl_part_init;258 }259 part_prev = kvdl->parts[i];260 }261 return 0;262 263err_kvdl_part_init:264 for (i--; i >= 0; i--)265 mlxsw_sp1_kvdl_part_fini(kvdl->parts[i]);266 return err;267}268 269static void mlxsw_sp1_kvdl_parts_fini(struct mlxsw_sp1_kvdl *kvdl)270{271 int i;272 273 for (i = 0; i < MLXSW_SP1_KVDL_PARTS_INFO_LEN; i++)274 mlxsw_sp1_kvdl_part_fini(kvdl->parts[i]);275}276 277static u64 mlxsw_sp1_kvdl_part_occ(struct mlxsw_sp1_kvdl_part *part)278{279 const struct mlxsw_sp1_kvdl_part_info *info = &part->info;280 unsigned int nr_entries;281 int bit = -1;282 u64 occ = 0;283 284 nr_entries = (info->end_index -285 info->start_index + 1) /286 info->alloc_size;287 while ((bit = find_next_bit(part->usage, nr_entries, bit + 1))288 < nr_entries)289 occ += info->alloc_size;290 return occ;291}292 293static u64 mlxsw_sp1_kvdl_occ_get(void *priv)294{295 const struct mlxsw_sp1_kvdl *kvdl = priv;296 u64 occ = 0;297 int i;298 299 for (i = 0; i < MLXSW_SP1_KVDL_PARTS_INFO_LEN; i++)300 occ += mlxsw_sp1_kvdl_part_occ(kvdl->parts[i]);301 302 return occ;303}304 305static u64 mlxsw_sp1_kvdl_single_occ_get(void *priv)306{307 const struct mlxsw_sp1_kvdl *kvdl = priv;308 struct mlxsw_sp1_kvdl_part *part;309 310 part = kvdl->parts[MLXSW_SP1_KVDL_PART_ID_SINGLE];311 return mlxsw_sp1_kvdl_part_occ(part);312}313 314static u64 mlxsw_sp1_kvdl_chunks_occ_get(void *priv)315{316 const struct mlxsw_sp1_kvdl *kvdl = priv;317 struct mlxsw_sp1_kvdl_part *part;318 319 part = kvdl->parts[MLXSW_SP1_KVDL_PART_ID_CHUNKS];320 return mlxsw_sp1_kvdl_part_occ(part);321}322 323static u64 mlxsw_sp1_kvdl_large_chunks_occ_get(void *priv)324{325 const struct mlxsw_sp1_kvdl *kvdl = priv;326 struct mlxsw_sp1_kvdl_part *part;327 328 part = kvdl->parts[MLXSW_SP1_KVDL_PART_ID_LARGE_CHUNKS];329 return mlxsw_sp1_kvdl_part_occ(part);330}331 332static int mlxsw_sp1_kvdl_init(struct mlxsw_sp *mlxsw_sp, void *priv)333{334 struct devlink *devlink = priv_to_devlink(mlxsw_sp->core);335 struct mlxsw_sp1_kvdl *kvdl = priv;336 int err;337 338 err = mlxsw_sp1_kvdl_parts_init(mlxsw_sp, kvdl);339 if (err)340 return err;341 devl_resource_occ_get_register(devlink,342 MLXSW_SP_RESOURCE_KVD_LINEAR,343 mlxsw_sp1_kvdl_occ_get,344 kvdl);345 devl_resource_occ_get_register(devlink,346 MLXSW_SP_RESOURCE_KVD_LINEAR_SINGLE,347 mlxsw_sp1_kvdl_single_occ_get,348 kvdl);349 devl_resource_occ_get_register(devlink,350 MLXSW_SP_RESOURCE_KVD_LINEAR_CHUNKS,351 mlxsw_sp1_kvdl_chunks_occ_get,352 kvdl);353 devl_resource_occ_get_register(devlink,354 MLXSW_SP_RESOURCE_KVD_LINEAR_LARGE_CHUNKS,355 mlxsw_sp1_kvdl_large_chunks_occ_get,356 kvdl);357 return 0;358}359 360static void mlxsw_sp1_kvdl_fini(struct mlxsw_sp *mlxsw_sp, void *priv)361{362 struct devlink *devlink = priv_to_devlink(mlxsw_sp->core);363 struct mlxsw_sp1_kvdl *kvdl = priv;364 365 devl_resource_occ_get_unregister(devlink,366 MLXSW_SP_RESOURCE_KVD_LINEAR_LARGE_CHUNKS);367 devl_resource_occ_get_unregister(devlink,368 MLXSW_SP_RESOURCE_KVD_LINEAR_CHUNKS);369 devl_resource_occ_get_unregister(devlink,370 MLXSW_SP_RESOURCE_KVD_LINEAR_SINGLE);371 devl_resource_occ_get_unregister(devlink,372 MLXSW_SP_RESOURCE_KVD_LINEAR);373 mlxsw_sp1_kvdl_parts_fini(kvdl);374}375 376const struct mlxsw_sp_kvdl_ops mlxsw_sp1_kvdl_ops = {377 .priv_size = sizeof(struct mlxsw_sp1_kvdl),378 .init = mlxsw_sp1_kvdl_init,379 .fini = mlxsw_sp1_kvdl_fini,380 .alloc = mlxsw_sp1_kvdl_alloc,381 .free = mlxsw_sp1_kvdl_free,382 .alloc_size_query = mlxsw_sp1_kvdl_alloc_size_query,383};384 385int mlxsw_sp1_kvdl_resources_register(struct mlxsw_core *mlxsw_core)386{387 struct devlink *devlink = priv_to_devlink(mlxsw_core);388 static struct devlink_resource_size_params size_params;389 u32 kvdl_max_size;390 int err;391 392 kvdl_max_size = MLXSW_CORE_RES_GET(mlxsw_core, KVD_SIZE) -393 MLXSW_CORE_RES_GET(mlxsw_core, KVD_SINGLE_MIN_SIZE) -394 MLXSW_CORE_RES_GET(mlxsw_core, KVD_DOUBLE_MIN_SIZE);395 396 devlink_resource_size_params_init(&size_params, 0, kvdl_max_size,397 MLXSW_SP1_KVDL_SINGLE_ALLOC_SIZE,398 DEVLINK_RESOURCE_UNIT_ENTRY);399 err = devl_resource_register(devlink, MLXSW_SP_RESOURCE_NAME_KVD_LINEAR_SINGLES,400 MLXSW_SP1_KVDL_SINGLE_SIZE,401 MLXSW_SP_RESOURCE_KVD_LINEAR_SINGLE,402 MLXSW_SP_RESOURCE_KVD_LINEAR,403 &size_params);404 if (err)405 return err;406 407 devlink_resource_size_params_init(&size_params, 0, kvdl_max_size,408 MLXSW_SP1_KVDL_CHUNKS_ALLOC_SIZE,409 DEVLINK_RESOURCE_UNIT_ENTRY);410 err = devl_resource_register(devlink, MLXSW_SP_RESOURCE_NAME_KVD_LINEAR_CHUNKS,411 MLXSW_SP1_KVDL_CHUNKS_SIZE,412 MLXSW_SP_RESOURCE_KVD_LINEAR_CHUNKS,413 MLXSW_SP_RESOURCE_KVD_LINEAR,414 &size_params);415 if (err)416 return err;417 418 devlink_resource_size_params_init(&size_params, 0, kvdl_max_size,419 MLXSW_SP1_KVDL_LARGE_CHUNKS_ALLOC_SIZE,420 DEVLINK_RESOURCE_UNIT_ENTRY);421 err = devl_resource_register(devlink, MLXSW_SP_RESOURCE_NAME_KVD_LINEAR_LARGE_CHUNKS,422 MLXSW_SP1_KVDL_LARGE_CHUNKS_SIZE,423 MLXSW_SP_RESOURCE_KVD_LINEAR_LARGE_CHUNKS,424 MLXSW_SP_RESOURCE_KVD_LINEAR,425 &size_params);426 return err;427}428