1 /* 2 * drivers/net/ethernet/mellanox/mlxsw/spectrum_kvdl.c 3 * Copyright (c) 2016 Mellanox Technologies. All rights reserved. 4 * Copyright (c) 2016 Jiri Pirko <jiri@mellanox.com> 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. Neither the names of the copyright holders nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * Alternatively, this software may be distributed under the terms of the 19 * GNU General Public License ("GPL") version 2 as published by the Free 20 * Software Foundation. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 23 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 26 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 32 * POSSIBILITY OF SUCH DAMAGE. 33 */ 34 35 #include <linux/kernel.h> 36 #include <linux/bitops.h> 37 38 #include "spectrum.h" 39 40 #define MLXSW_SP_KVDL_SINGLE_BASE 0 41 #define MLXSW_SP_KVDL_SINGLE_SIZE 16384 42 #define MLXSW_SP_KVDL_SINGLE_END \ 43 (MLXSW_SP_KVDL_SINGLE_SIZE + MLXSW_SP_KVDL_SINGLE_BASE - 1) 44 45 #define MLXSW_SP_KVDL_CHUNKS_BASE \ 46 (MLXSW_SP_KVDL_SINGLE_BASE + MLXSW_SP_KVDL_SINGLE_SIZE) 47 #define MLXSW_SP_KVDL_CHUNKS_SIZE 49152 48 #define MLXSW_SP_KVDL_CHUNKS_END \ 49 (MLXSW_SP_KVDL_CHUNKS_SIZE + MLXSW_SP_KVDL_CHUNKS_BASE - 1) 50 51 #define MLXSW_SP_KVDL_LARGE_CHUNKS_BASE \ 52 (MLXSW_SP_KVDL_CHUNKS_BASE + MLXSW_SP_KVDL_CHUNKS_SIZE) 53 #define MLXSW_SP_KVDL_LARGE_CHUNKS_SIZE \ 54 (MLXSW_SP_KVD_LINEAR_SIZE - MLXSW_SP_KVDL_LARGE_CHUNKS_BASE) 55 #define MLXSW_SP_KVDL_LARGE_CHUNKS_END \ 56 (MLXSW_SP_KVDL_LARGE_CHUNKS_SIZE + MLXSW_SP_KVDL_LARGE_CHUNKS_BASE - 1) 57 58 #define MLXSW_SP_KVDL_SINGLE_ALLOC_SIZE 1 59 #define MLXSW_SP_KVDL_CHUNKS_ALLOC_SIZE 32 60 #define MLXSW_SP_KVDL_LARGE_CHUNKS_ALLOC_SIZE 512 61 62 struct mlxsw_sp_kvdl_part_info { 63 unsigned int part_index; 64 unsigned int start_index; 65 unsigned int end_index; 66 unsigned int alloc_size; 67 enum mlxsw_sp_resource_id resource_id; 68 }; 69 70 enum mlxsw_sp_kvdl_part_id { 71 MLXSW_SP_KVDL_PART_ID_SINGLE, 72 MLXSW_SP_KVDL_PART_ID_CHUNKS, 73 MLXSW_SP_KVDL_PART_ID_LARGE_CHUNKS, 74 }; 75 76 #define MLXSW_SP_KVDL_PART_INFO(id) \ 77 [MLXSW_SP_KVDL_PART_ID_##id] = { \ 78 .start_index = MLXSW_SP_KVDL_##id##_BASE, \ 79 .end_index = MLXSW_SP_KVDL_##id##_END, \ 80 .alloc_size = MLXSW_SP_KVDL_##id##_ALLOC_SIZE, \ 81 .resource_id = MLXSW_SP_RESOURCE_KVD_LINEAR_##id, \ 82 } 83 84 static const struct mlxsw_sp_kvdl_part_info mlxsw_sp_kvdl_parts_info[] = { 85 MLXSW_SP_KVDL_PART_INFO(SINGLE), 86 MLXSW_SP_KVDL_PART_INFO(CHUNKS), 87 MLXSW_SP_KVDL_PART_INFO(LARGE_CHUNKS), 88 }; 89 90 #define MLXSW_SP_KVDL_PARTS_INFO_LEN ARRAY_SIZE(mlxsw_sp_kvdl_parts_info) 91 92 struct mlxsw_sp_kvdl_part { 93 struct mlxsw_sp_kvdl_part_info info; 94 unsigned long usage[0]; /* Entries */ 95 }; 96 97 struct mlxsw_sp_kvdl { 98 struct mlxsw_sp_kvdl_part *parts[MLXSW_SP_KVDL_PARTS_INFO_LEN]; 99 }; 100 101 static struct mlxsw_sp_kvdl_part * 102 mlxsw_sp_kvdl_alloc_size_part(struct mlxsw_sp_kvdl *kvdl, 103 unsigned int alloc_size) 104 { 105 struct mlxsw_sp_kvdl_part *part, *min_part = NULL; 106 int i; 107 108 for (i = 0; i < MLXSW_SP_KVDL_PARTS_INFO_LEN; i++) { 109 part = kvdl->parts[i]; 110 if (alloc_size <= part->info.alloc_size && 111 (!min_part || 112 part->info.alloc_size <= min_part->info.alloc_size)) 113 min_part = part; 114 } 115 116 return min_part ?: ERR_PTR(-ENOBUFS); 117 } 118 119 static struct mlxsw_sp_kvdl_part * 120 mlxsw_sp_kvdl_index_part(struct mlxsw_sp_kvdl *kvdl, u32 kvdl_index) 121 { 122 struct mlxsw_sp_kvdl_part *part; 123 int i; 124 125 for (i = 0; i < MLXSW_SP_KVDL_PARTS_INFO_LEN; i++) { 126 part = kvdl->parts[i]; 127 if (kvdl_index >= part->info.start_index && 128 kvdl_index <= part->info.end_index) 129 return part; 130 } 131 132 return ERR_PTR(-EINVAL); 133 } 134 135 static u32 136 mlxsw_sp_entry_index_kvdl_index(const struct mlxsw_sp_kvdl_part_info *info, 137 unsigned int entry_index) 138 { 139 return info->start_index + entry_index * info->alloc_size; 140 } 141 142 static unsigned int 143 mlxsw_sp_kvdl_index_entry_index(const struct mlxsw_sp_kvdl_part_info *info, 144 u32 kvdl_index) 145 { 146 return (kvdl_index - info->start_index) / info->alloc_size; 147 } 148 149 static int mlxsw_sp_kvdl_part_alloc(struct mlxsw_sp_kvdl_part *part, 150 u32 *p_kvdl_index) 151 { 152 const struct mlxsw_sp_kvdl_part_info *info = &part->info; 153 unsigned int entry_index, nr_entries; 154 155 nr_entries = (info->end_index - info->start_index + 1) / 156 info->alloc_size; 157 entry_index = find_first_zero_bit(part->usage, nr_entries); 158 if (entry_index == nr_entries) 159 return -ENOBUFS; 160 __set_bit(entry_index, part->usage); 161 162 *p_kvdl_index = mlxsw_sp_entry_index_kvdl_index(info, entry_index); 163 164 return 0; 165 } 166 167 static void mlxsw_sp_kvdl_part_free(struct mlxsw_sp_kvdl_part *part, 168 u32 kvdl_index) 169 { 170 const struct mlxsw_sp_kvdl_part_info *info = &part->info; 171 unsigned int entry_index; 172 173 entry_index = mlxsw_sp_kvdl_index_entry_index(info, kvdl_index); 174 __clear_bit(entry_index, part->usage); 175 } 176 177 int mlxsw_sp_kvdl_alloc(struct mlxsw_sp *mlxsw_sp, unsigned int entry_count, 178 u32 *p_entry_index) 179 { 180 struct mlxsw_sp_kvdl_part *part; 181 182 /* Find partition with smallest allocation size satisfying the 183 * requested size. 184 */ 185 part = mlxsw_sp_kvdl_alloc_size_part(mlxsw_sp->kvdl, entry_count); 186 if (IS_ERR(part)) 187 return PTR_ERR(part); 188 189 return mlxsw_sp_kvdl_part_alloc(part, p_entry_index); 190 } 191 192 void mlxsw_sp_kvdl_free(struct mlxsw_sp *mlxsw_sp, int entry_index) 193 { 194 struct mlxsw_sp_kvdl_part *part; 195 196 part = mlxsw_sp_kvdl_index_part(mlxsw_sp->kvdl, entry_index); 197 if (IS_ERR(part)) 198 return; 199 mlxsw_sp_kvdl_part_free(part, entry_index); 200 } 201 202 int mlxsw_sp_kvdl_alloc_size_query(struct mlxsw_sp *mlxsw_sp, 203 unsigned int entry_count, 204 unsigned int *p_alloc_size) 205 { 206 struct mlxsw_sp_kvdl_part *part; 207 208 part = mlxsw_sp_kvdl_alloc_size_part(mlxsw_sp->kvdl, entry_count); 209 if (IS_ERR(part)) 210 return PTR_ERR(part); 211 212 *p_alloc_size = part->info.alloc_size; 213 214 return 0; 215 } 216 217 static void mlxsw_sp_kvdl_part_update(struct mlxsw_sp_kvdl_part *part, 218 struct mlxsw_sp_kvdl_part *part_prev, 219 unsigned int size) 220 { 221 222 if (!part_prev) { 223 part->info.end_index = size - 1; 224 } else { 225 part->info.start_index = part_prev->info.end_index + 1; 226 part->info.end_index = part->info.start_index + size - 1; 227 } 228 } 229 230 static struct mlxsw_sp_kvdl_part * 231 mlxsw_sp_kvdl_part_init(struct mlxsw_sp *mlxsw_sp, 232 const struct mlxsw_sp_kvdl_part_info *info, 233 struct mlxsw_sp_kvdl_part *part_prev) 234 { 235 struct devlink *devlink = priv_to_devlink(mlxsw_sp->core); 236 struct mlxsw_sp_kvdl_part *part; 237 bool need_update = true; 238 unsigned int nr_entries; 239 size_t usage_size; 240 u64 resource_size; 241 int err; 242 243 err = devlink_resource_size_get(devlink, info->resource_id, 244 &resource_size); 245 if (err) { 246 need_update = false; 247 resource_size = info->end_index - info->start_index + 1; 248 } 249 250 nr_entries = div_u64(resource_size, info->alloc_size); 251 usage_size = BITS_TO_LONGS(nr_entries) * sizeof(unsigned long); 252 part = kzalloc(sizeof(*part) + usage_size, GFP_KERNEL); 253 if (!part) 254 return ERR_PTR(-ENOMEM); 255 256 memcpy(&part->info, info, sizeof(part->info)); 257 258 if (need_update) 259 mlxsw_sp_kvdl_part_update(part, part_prev, resource_size); 260 return part; 261 } 262 263 static void mlxsw_sp_kvdl_part_fini(struct mlxsw_sp_kvdl_part *part) 264 { 265 kfree(part); 266 } 267 268 static int mlxsw_sp_kvdl_parts_init(struct mlxsw_sp *mlxsw_sp) 269 { 270 struct mlxsw_sp_kvdl *kvdl = mlxsw_sp->kvdl; 271 const struct mlxsw_sp_kvdl_part_info *info; 272 struct mlxsw_sp_kvdl_part *part_prev = NULL; 273 int err, i; 274 275 for (i = 0; i < MLXSW_SP_KVDL_PARTS_INFO_LEN; i++) { 276 info = &mlxsw_sp_kvdl_parts_info[i]; 277 kvdl->parts[i] = mlxsw_sp_kvdl_part_init(mlxsw_sp, info, 278 part_prev); 279 if (IS_ERR(kvdl->parts[i])) { 280 err = PTR_ERR(kvdl->parts[i]); 281 goto err_kvdl_part_init; 282 } 283 part_prev = kvdl->parts[i]; 284 } 285 return 0; 286 287 err_kvdl_part_init: 288 for (i--; i >= 0; i--) 289 mlxsw_sp_kvdl_part_fini(kvdl->parts[i]); 290 return err; 291 } 292 293 static void mlxsw_sp_kvdl_parts_fini(struct mlxsw_sp *mlxsw_sp) 294 { 295 struct mlxsw_sp_kvdl *kvdl = mlxsw_sp->kvdl; 296 int i; 297 298 for (i = 0; i < MLXSW_SP_KVDL_PARTS_INFO_LEN; i++) 299 mlxsw_sp_kvdl_part_fini(kvdl->parts[i]); 300 } 301 302 static u64 mlxsw_sp_kvdl_part_occ(struct mlxsw_sp_kvdl_part *part) 303 { 304 const struct mlxsw_sp_kvdl_part_info *info = &part->info; 305 unsigned int nr_entries; 306 int bit = -1; 307 u64 occ = 0; 308 309 nr_entries = (info->end_index - 310 info->start_index + 1) / 311 info->alloc_size; 312 while ((bit = find_next_bit(part->usage, nr_entries, bit + 1)) 313 < nr_entries) 314 occ += info->alloc_size; 315 return occ; 316 } 317 318 static u64 mlxsw_sp_kvdl_occ_get(void *priv) 319 { 320 const struct mlxsw_sp *mlxsw_sp = priv; 321 u64 occ = 0; 322 int i; 323 324 for (i = 0; i < MLXSW_SP_KVDL_PARTS_INFO_LEN; i++) 325 occ += mlxsw_sp_kvdl_part_occ(mlxsw_sp->kvdl->parts[i]); 326 327 return occ; 328 } 329 330 static u64 mlxsw_sp_kvdl_single_occ_get(void *priv) 331 { 332 const struct mlxsw_sp *mlxsw_sp = priv; 333 struct mlxsw_sp_kvdl_part *part; 334 335 part = mlxsw_sp->kvdl->parts[MLXSW_SP_KVDL_PART_ID_SINGLE]; 336 return mlxsw_sp_kvdl_part_occ(part); 337 } 338 339 static u64 mlxsw_sp_kvdl_chunks_occ_get(void *priv) 340 { 341 const struct mlxsw_sp *mlxsw_sp = priv; 342 struct mlxsw_sp_kvdl_part *part; 343 344 part = mlxsw_sp->kvdl->parts[MLXSW_SP_KVDL_PART_ID_CHUNKS]; 345 return mlxsw_sp_kvdl_part_occ(part); 346 } 347 348 static u64 mlxsw_sp_kvdl_large_chunks_occ_get(void *priv) 349 { 350 const struct mlxsw_sp *mlxsw_sp = priv; 351 struct mlxsw_sp_kvdl_part *part; 352 353 part = mlxsw_sp->kvdl->parts[MLXSW_SP_KVDL_PART_ID_LARGE_CHUNKS]; 354 return mlxsw_sp_kvdl_part_occ(part); 355 } 356 357 int mlxsw_sp_kvdl_resources_register(struct mlxsw_core *mlxsw_core) 358 { 359 struct devlink *devlink = priv_to_devlink(mlxsw_core); 360 static struct devlink_resource_size_params size_params; 361 u32 kvdl_max_size; 362 int err; 363 364 kvdl_max_size = MLXSW_CORE_RES_GET(mlxsw_core, KVD_SIZE) - 365 MLXSW_CORE_RES_GET(mlxsw_core, KVD_SINGLE_MIN_SIZE) - 366 MLXSW_CORE_RES_GET(mlxsw_core, KVD_DOUBLE_MIN_SIZE); 367 368 devlink_resource_size_params_init(&size_params, 0, kvdl_max_size, 369 MLXSW_SP_KVDL_SINGLE_ALLOC_SIZE, 370 DEVLINK_RESOURCE_UNIT_ENTRY); 371 err = devlink_resource_register(devlink, MLXSW_SP_RESOURCE_NAME_KVD_LINEAR_SINGLES, 372 MLXSW_SP_KVDL_SINGLE_SIZE, 373 MLXSW_SP_RESOURCE_KVD_LINEAR_SINGLE, 374 MLXSW_SP_RESOURCE_KVD_LINEAR, 375 &size_params); 376 if (err) 377 return err; 378 379 devlink_resource_size_params_init(&size_params, 0, kvdl_max_size, 380 MLXSW_SP_KVDL_CHUNKS_ALLOC_SIZE, 381 DEVLINK_RESOURCE_UNIT_ENTRY); 382 err = devlink_resource_register(devlink, MLXSW_SP_RESOURCE_NAME_KVD_LINEAR_CHUNKS, 383 MLXSW_SP_KVDL_CHUNKS_SIZE, 384 MLXSW_SP_RESOURCE_KVD_LINEAR_CHUNKS, 385 MLXSW_SP_RESOURCE_KVD_LINEAR, 386 &size_params); 387 if (err) 388 return err; 389 390 devlink_resource_size_params_init(&size_params, 0, kvdl_max_size, 391 MLXSW_SP_KVDL_LARGE_CHUNKS_ALLOC_SIZE, 392 DEVLINK_RESOURCE_UNIT_ENTRY); 393 err = devlink_resource_register(devlink, MLXSW_SP_RESOURCE_NAME_KVD_LINEAR_LARGE_CHUNKS, 394 MLXSW_SP_KVDL_LARGE_CHUNKS_SIZE, 395 MLXSW_SP_RESOURCE_KVD_LINEAR_LARGE_CHUNKS, 396 MLXSW_SP_RESOURCE_KVD_LINEAR, 397 &size_params); 398 return err; 399 } 400 401 int mlxsw_sp_kvdl_init(struct mlxsw_sp *mlxsw_sp) 402 { 403 struct devlink *devlink = priv_to_devlink(mlxsw_sp->core); 404 struct mlxsw_sp_kvdl *kvdl; 405 int err; 406 407 kvdl = kzalloc(sizeof(*mlxsw_sp->kvdl), GFP_KERNEL); 408 if (!kvdl) 409 return -ENOMEM; 410 mlxsw_sp->kvdl = kvdl; 411 412 err = mlxsw_sp_kvdl_parts_init(mlxsw_sp); 413 if (err) 414 goto err_kvdl_parts_init; 415 416 devlink_resource_occ_get_register(devlink, 417 MLXSW_SP_RESOURCE_KVD_LINEAR, 418 mlxsw_sp_kvdl_occ_get, 419 mlxsw_sp); 420 devlink_resource_occ_get_register(devlink, 421 MLXSW_SP_RESOURCE_KVD_LINEAR_SINGLE, 422 mlxsw_sp_kvdl_single_occ_get, 423 mlxsw_sp); 424 devlink_resource_occ_get_register(devlink, 425 MLXSW_SP_RESOURCE_KVD_LINEAR_CHUNKS, 426 mlxsw_sp_kvdl_chunks_occ_get, 427 mlxsw_sp); 428 devlink_resource_occ_get_register(devlink, 429 MLXSW_SP_RESOURCE_KVD_LINEAR_LARGE_CHUNKS, 430 mlxsw_sp_kvdl_large_chunks_occ_get, 431 mlxsw_sp); 432 433 return 0; 434 435 err_kvdl_parts_init: 436 kfree(mlxsw_sp->kvdl); 437 return err; 438 } 439 440 void mlxsw_sp_kvdl_fini(struct mlxsw_sp *mlxsw_sp) 441 { 442 struct devlink *devlink = priv_to_devlink(mlxsw_sp->core); 443 444 devlink_resource_occ_get_unregister(devlink, 445 MLXSW_SP_RESOURCE_KVD_LINEAR_LARGE_CHUNKS); 446 devlink_resource_occ_get_unregister(devlink, 447 MLXSW_SP_RESOURCE_KVD_LINEAR_CHUNKS); 448 devlink_resource_occ_get_unregister(devlink, 449 MLXSW_SP_RESOURCE_KVD_LINEAR_SINGLE); 450 devlink_resource_occ_get_unregister(devlink, 451 MLXSW_SP_RESOURCE_KVD_LINEAR); 452 mlxsw_sp_kvdl_parts_fini(mlxsw_sp); 453 kfree(mlxsw_sp->kvdl); 454 } 455