1 /* 2 * drivers/net/ethernet/mellanox/mlxsw/spectrum1_kvdl.c 3 * Copyright (c) 2018 Mellanox Technologies. All rights reserved. 4 * Copyright (c) 2018 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_SP1_KVDL_SINGLE_BASE 0 41 #define MLXSW_SP1_KVDL_SINGLE_SIZE 16384 42 #define MLXSW_SP1_KVDL_SINGLE_END \ 43 (MLXSW_SP1_KVDL_SINGLE_SIZE + MLXSW_SP1_KVDL_SINGLE_BASE - 1) 44 45 #define MLXSW_SP1_KVDL_CHUNKS_BASE \ 46 (MLXSW_SP1_KVDL_SINGLE_BASE + MLXSW_SP1_KVDL_SINGLE_SIZE) 47 #define MLXSW_SP1_KVDL_CHUNKS_SIZE 49152 48 #define MLXSW_SP1_KVDL_CHUNKS_END \ 49 (MLXSW_SP1_KVDL_CHUNKS_SIZE + MLXSW_SP1_KVDL_CHUNKS_BASE - 1) 50 51 #define MLXSW_SP1_KVDL_LARGE_CHUNKS_BASE \ 52 (MLXSW_SP1_KVDL_CHUNKS_BASE + MLXSW_SP1_KVDL_CHUNKS_SIZE) 53 #define MLXSW_SP1_KVDL_LARGE_CHUNKS_SIZE \ 54 (MLXSW_SP_KVD_LINEAR_SIZE - MLXSW_SP1_KVDL_LARGE_CHUNKS_BASE) 55 #define MLXSW_SP1_KVDL_LARGE_CHUNKS_END \ 56 (MLXSW_SP1_KVDL_LARGE_CHUNKS_SIZE + MLXSW_SP1_KVDL_LARGE_CHUNKS_BASE - 1) 57 58 #define MLXSW_SP1_KVDL_SINGLE_ALLOC_SIZE 1 59 #define MLXSW_SP1_KVDL_CHUNKS_ALLOC_SIZE 32 60 #define MLXSW_SP1_KVDL_LARGE_CHUNKS_ALLOC_SIZE 512 61 62 struct mlxsw_sp1_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_sp1_kvdl_part_id { 71 MLXSW_SP1_KVDL_PART_ID_SINGLE, 72 MLXSW_SP1_KVDL_PART_ID_CHUNKS, 73 MLXSW_SP1_KVDL_PART_ID_LARGE_CHUNKS, 74 }; 75 76 #define MLXSW_SP1_KVDL_PART_INFO(id) \ 77 [MLXSW_SP1_KVDL_PART_ID_##id] = { \ 78 .start_index = MLXSW_SP1_KVDL_##id##_BASE, \ 79 .end_index = MLXSW_SP1_KVDL_##id##_END, \ 80 .alloc_size = MLXSW_SP1_KVDL_##id##_ALLOC_SIZE, \ 81 .resource_id = MLXSW_SP_RESOURCE_KVD_LINEAR_##id, \ 82 } 83 84 static const struct mlxsw_sp1_kvdl_part_info mlxsw_sp1_kvdl_parts_info[] = { 85 MLXSW_SP1_KVDL_PART_INFO(SINGLE), 86 MLXSW_SP1_KVDL_PART_INFO(CHUNKS), 87 MLXSW_SP1_KVDL_PART_INFO(LARGE_CHUNKS), 88 }; 89 90 #define MLXSW_SP1_KVDL_PARTS_INFO_LEN ARRAY_SIZE(mlxsw_sp1_kvdl_parts_info) 91 92 struct mlxsw_sp1_kvdl_part { 93 struct mlxsw_sp1_kvdl_part_info info; 94 unsigned long usage[0]; /* Entries */ 95 }; 96 97 struct mlxsw_sp1_kvdl { 98 struct mlxsw_sp1_kvdl_part *parts[MLXSW_SP1_KVDL_PARTS_INFO_LEN]; 99 }; 100 101 static struct mlxsw_sp1_kvdl_part * 102 mlxsw_sp1_kvdl_alloc_size_part(struct mlxsw_sp1_kvdl *kvdl, 103 unsigned int alloc_size) 104 { 105 struct mlxsw_sp1_kvdl_part *part, *min_part = NULL; 106 int i; 107 108 for (i = 0; i < MLXSW_SP1_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_sp1_kvdl_part * 120 mlxsw_sp1_kvdl_index_part(struct mlxsw_sp1_kvdl *kvdl, u32 kvdl_index) 121 { 122 struct mlxsw_sp1_kvdl_part *part; 123 int i; 124 125 for (i = 0; i < MLXSW_SP1_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_sp1_kvdl_to_kvdl_index(const struct mlxsw_sp1_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_sp1_kvdl_to_entry_index(const struct mlxsw_sp1_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_sp1_kvdl_part_alloc(struct mlxsw_sp1_kvdl_part *part, 150 u32 *p_kvdl_index) 151 { 152 const struct mlxsw_sp1_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_sp1_kvdl_to_kvdl_index(info, entry_index); 163 164 return 0; 165 } 166 167 static void mlxsw_sp1_kvdl_part_free(struct mlxsw_sp1_kvdl_part *part, 168 u32 kvdl_index) 169 { 170 const struct mlxsw_sp1_kvdl_part_info *info = &part->info; 171 unsigned int entry_index; 172 173 entry_index = mlxsw_sp1_kvdl_to_entry_index(info, kvdl_index); 174 __clear_bit(entry_index, part->usage); 175 } 176 177 static int mlxsw_sp1_kvdl_alloc(struct mlxsw_sp *mlxsw_sp, void *priv, 178 enum mlxsw_sp_kvdl_entry_type type, 179 unsigned int entry_count, 180 u32 *p_entry_index) 181 { 182 struct mlxsw_sp1_kvdl *kvdl = priv; 183 struct mlxsw_sp1_kvdl_part *part; 184 185 /* Find partition with smallest allocation size satisfying the 186 * requested size. 187 */ 188 part = mlxsw_sp1_kvdl_alloc_size_part(kvdl, entry_count); 189 if (IS_ERR(part)) 190 return PTR_ERR(part); 191 192 return mlxsw_sp1_kvdl_part_alloc(part, p_entry_index); 193 } 194 195 static void mlxsw_sp1_kvdl_free(struct mlxsw_sp *mlxsw_sp, void *priv, 196 enum mlxsw_sp_kvdl_entry_type type, 197 unsigned int entry_count, int entry_index) 198 { 199 struct mlxsw_sp1_kvdl *kvdl = priv; 200 struct mlxsw_sp1_kvdl_part *part; 201 202 part = mlxsw_sp1_kvdl_index_part(kvdl, entry_index); 203 if (IS_ERR(part)) 204 return; 205 mlxsw_sp1_kvdl_part_free(part, entry_index); 206 } 207 208 static int mlxsw_sp1_kvdl_alloc_size_query(struct mlxsw_sp *mlxsw_sp, 209 void *priv, 210 enum mlxsw_sp_kvdl_entry_type type, 211 unsigned int entry_count, 212 unsigned int *p_alloc_size) 213 { 214 struct mlxsw_sp1_kvdl *kvdl = priv; 215 struct mlxsw_sp1_kvdl_part *part; 216 217 part = mlxsw_sp1_kvdl_alloc_size_part(kvdl, entry_count); 218 if (IS_ERR(part)) 219 return PTR_ERR(part); 220 221 *p_alloc_size = part->info.alloc_size; 222 223 return 0; 224 } 225 226 static void mlxsw_sp1_kvdl_part_update(struct mlxsw_sp1_kvdl_part *part, 227 struct mlxsw_sp1_kvdl_part *part_prev, 228 unsigned int size) 229 { 230 if (!part_prev) { 231 part->info.end_index = size - 1; 232 } else { 233 part->info.start_index = part_prev->info.end_index + 1; 234 part->info.end_index = part->info.start_index + size - 1; 235 } 236 } 237 238 static struct mlxsw_sp1_kvdl_part * 239 mlxsw_sp1_kvdl_part_init(struct mlxsw_sp *mlxsw_sp, 240 const struct mlxsw_sp1_kvdl_part_info *info, 241 struct mlxsw_sp1_kvdl_part *part_prev) 242 { 243 struct devlink *devlink = priv_to_devlink(mlxsw_sp->core); 244 struct mlxsw_sp1_kvdl_part *part; 245 bool need_update = true; 246 unsigned int nr_entries; 247 size_t usage_size; 248 u64 resource_size; 249 int err; 250 251 err = devlink_resource_size_get(devlink, info->resource_id, 252 &resource_size); 253 if (err) { 254 need_update = false; 255 resource_size = info->end_index - info->start_index + 1; 256 } 257 258 nr_entries = div_u64(resource_size, info->alloc_size); 259 usage_size = BITS_TO_LONGS(nr_entries) * sizeof(unsigned long); 260 part = kzalloc(sizeof(*part) + usage_size, GFP_KERNEL); 261 if (!part) 262 return ERR_PTR(-ENOMEM); 263 264 memcpy(&part->info, info, sizeof(part->info)); 265 266 if (need_update) 267 mlxsw_sp1_kvdl_part_update(part, part_prev, resource_size); 268 return part; 269 } 270 271 static void mlxsw_sp1_kvdl_part_fini(struct mlxsw_sp1_kvdl_part *part) 272 { 273 kfree(part); 274 } 275 276 static int mlxsw_sp1_kvdl_parts_init(struct mlxsw_sp *mlxsw_sp, 277 struct mlxsw_sp1_kvdl *kvdl) 278 { 279 const struct mlxsw_sp1_kvdl_part_info *info; 280 struct mlxsw_sp1_kvdl_part *part_prev = NULL; 281 int err, i; 282 283 for (i = 0; i < MLXSW_SP1_KVDL_PARTS_INFO_LEN; i++) { 284 info = &mlxsw_sp1_kvdl_parts_info[i]; 285 kvdl->parts[i] = mlxsw_sp1_kvdl_part_init(mlxsw_sp, info, 286 part_prev); 287 if (IS_ERR(kvdl->parts[i])) { 288 err = PTR_ERR(kvdl->parts[i]); 289 goto err_kvdl_part_init; 290 } 291 part_prev = kvdl->parts[i]; 292 } 293 return 0; 294 295 err_kvdl_part_init: 296 for (i--; i >= 0; i--) 297 mlxsw_sp1_kvdl_part_fini(kvdl->parts[i]); 298 return err; 299 } 300 301 static void mlxsw_sp1_kvdl_parts_fini(struct mlxsw_sp1_kvdl *kvdl) 302 { 303 int i; 304 305 for (i = 0; i < MLXSW_SP1_KVDL_PARTS_INFO_LEN; i++) 306 mlxsw_sp1_kvdl_part_fini(kvdl->parts[i]); 307 } 308 309 static u64 mlxsw_sp1_kvdl_part_occ(struct mlxsw_sp1_kvdl_part *part) 310 { 311 const struct mlxsw_sp1_kvdl_part_info *info = &part->info; 312 unsigned int nr_entries; 313 int bit = -1; 314 u64 occ = 0; 315 316 nr_entries = (info->end_index - 317 info->start_index + 1) / 318 info->alloc_size; 319 while ((bit = find_next_bit(part->usage, nr_entries, bit + 1)) 320 < nr_entries) 321 occ += info->alloc_size; 322 return occ; 323 } 324 325 static u64 mlxsw_sp1_kvdl_occ_get(void *priv) 326 { 327 const struct mlxsw_sp1_kvdl *kvdl = priv; 328 u64 occ = 0; 329 int i; 330 331 for (i = 0; i < MLXSW_SP1_KVDL_PARTS_INFO_LEN; i++) 332 occ += mlxsw_sp1_kvdl_part_occ(kvdl->parts[i]); 333 334 return occ; 335 } 336 337 static u64 mlxsw_sp1_kvdl_single_occ_get(void *priv) 338 { 339 const struct mlxsw_sp1_kvdl *kvdl = priv; 340 struct mlxsw_sp1_kvdl_part *part; 341 342 part = kvdl->parts[MLXSW_SP1_KVDL_PART_ID_SINGLE]; 343 return mlxsw_sp1_kvdl_part_occ(part); 344 } 345 346 static u64 mlxsw_sp1_kvdl_chunks_occ_get(void *priv) 347 { 348 const struct mlxsw_sp1_kvdl *kvdl = priv; 349 struct mlxsw_sp1_kvdl_part *part; 350 351 part = kvdl->parts[MLXSW_SP1_KVDL_PART_ID_CHUNKS]; 352 return mlxsw_sp1_kvdl_part_occ(part); 353 } 354 355 static u64 mlxsw_sp1_kvdl_large_chunks_occ_get(void *priv) 356 { 357 const struct mlxsw_sp1_kvdl *kvdl = priv; 358 struct mlxsw_sp1_kvdl_part *part; 359 360 part = kvdl->parts[MLXSW_SP1_KVDL_PART_ID_LARGE_CHUNKS]; 361 return mlxsw_sp1_kvdl_part_occ(part); 362 } 363 364 static int mlxsw_sp1_kvdl_init(struct mlxsw_sp *mlxsw_sp, void *priv) 365 { 366 struct devlink *devlink = priv_to_devlink(mlxsw_sp->core); 367 struct mlxsw_sp1_kvdl *kvdl = priv; 368 int err; 369 370 err = mlxsw_sp1_kvdl_parts_init(mlxsw_sp, kvdl); 371 if (err) 372 return err; 373 devlink_resource_occ_get_register(devlink, 374 MLXSW_SP_RESOURCE_KVD_LINEAR, 375 mlxsw_sp1_kvdl_occ_get, 376 kvdl); 377 devlink_resource_occ_get_register(devlink, 378 MLXSW_SP_RESOURCE_KVD_LINEAR_SINGLE, 379 mlxsw_sp1_kvdl_single_occ_get, 380 kvdl); 381 devlink_resource_occ_get_register(devlink, 382 MLXSW_SP_RESOURCE_KVD_LINEAR_CHUNKS, 383 mlxsw_sp1_kvdl_chunks_occ_get, 384 kvdl); 385 devlink_resource_occ_get_register(devlink, 386 MLXSW_SP_RESOURCE_KVD_LINEAR_LARGE_CHUNKS, 387 mlxsw_sp1_kvdl_large_chunks_occ_get, 388 kvdl); 389 return 0; 390 } 391 392 static void mlxsw_sp1_kvdl_fini(struct mlxsw_sp *mlxsw_sp, void *priv) 393 { 394 struct devlink *devlink = priv_to_devlink(mlxsw_sp->core); 395 struct mlxsw_sp1_kvdl *kvdl = priv; 396 397 devlink_resource_occ_get_unregister(devlink, 398 MLXSW_SP_RESOURCE_KVD_LINEAR_LARGE_CHUNKS); 399 devlink_resource_occ_get_unregister(devlink, 400 MLXSW_SP_RESOURCE_KVD_LINEAR_CHUNKS); 401 devlink_resource_occ_get_unregister(devlink, 402 MLXSW_SP_RESOURCE_KVD_LINEAR_SINGLE); 403 devlink_resource_occ_get_unregister(devlink, 404 MLXSW_SP_RESOURCE_KVD_LINEAR); 405 mlxsw_sp1_kvdl_parts_fini(kvdl); 406 } 407 408 const struct mlxsw_sp_kvdl_ops mlxsw_sp1_kvdl_ops = { 409 .priv_size = sizeof(struct mlxsw_sp1_kvdl), 410 .init = mlxsw_sp1_kvdl_init, 411 .fini = mlxsw_sp1_kvdl_fini, 412 .alloc = mlxsw_sp1_kvdl_alloc, 413 .free = mlxsw_sp1_kvdl_free, 414 .alloc_size_query = mlxsw_sp1_kvdl_alloc_size_query, 415 }; 416 417 int mlxsw_sp1_kvdl_resources_register(struct mlxsw_core *mlxsw_core) 418 { 419 struct devlink *devlink = priv_to_devlink(mlxsw_core); 420 static struct devlink_resource_size_params size_params; 421 u32 kvdl_max_size; 422 int err; 423 424 kvdl_max_size = MLXSW_CORE_RES_GET(mlxsw_core, KVD_SIZE) - 425 MLXSW_CORE_RES_GET(mlxsw_core, KVD_SINGLE_MIN_SIZE) - 426 MLXSW_CORE_RES_GET(mlxsw_core, KVD_DOUBLE_MIN_SIZE); 427 428 devlink_resource_size_params_init(&size_params, 0, kvdl_max_size, 429 MLXSW_SP1_KVDL_SINGLE_ALLOC_SIZE, 430 DEVLINK_RESOURCE_UNIT_ENTRY); 431 err = devlink_resource_register(devlink, MLXSW_SP_RESOURCE_NAME_KVD_LINEAR_SINGLES, 432 MLXSW_SP1_KVDL_SINGLE_SIZE, 433 MLXSW_SP_RESOURCE_KVD_LINEAR_SINGLE, 434 MLXSW_SP_RESOURCE_KVD_LINEAR, 435 &size_params); 436 if (err) 437 return err; 438 439 devlink_resource_size_params_init(&size_params, 0, kvdl_max_size, 440 MLXSW_SP1_KVDL_CHUNKS_ALLOC_SIZE, 441 DEVLINK_RESOURCE_UNIT_ENTRY); 442 err = devlink_resource_register(devlink, MLXSW_SP_RESOURCE_NAME_KVD_LINEAR_CHUNKS, 443 MLXSW_SP1_KVDL_CHUNKS_SIZE, 444 MLXSW_SP_RESOURCE_KVD_LINEAR_CHUNKS, 445 MLXSW_SP_RESOURCE_KVD_LINEAR, 446 &size_params); 447 if (err) 448 return err; 449 450 devlink_resource_size_params_init(&size_params, 0, kvdl_max_size, 451 MLXSW_SP1_KVDL_LARGE_CHUNKS_ALLOC_SIZE, 452 DEVLINK_RESOURCE_UNIT_ENTRY); 453 err = devlink_resource_register(devlink, MLXSW_SP_RESOURCE_NAME_KVD_LINEAR_LARGE_CHUNKS, 454 MLXSW_SP1_KVDL_LARGE_CHUNKS_SIZE, 455 MLXSW_SP_RESOURCE_KVD_LINEAR_LARGE_CHUNKS, 456 MLXSW_SP_RESOURCE_KVD_LINEAR, 457 &size_params); 458 return err; 459 } 460