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 u64 mlxsw_sp_kvdl_occ_get(const struct mlxsw_sp *mlxsw_sp) 319 { 320 u64 occ = 0; 321 int i; 322 323 for (i = 0; i < MLXSW_SP_KVDL_PARTS_INFO_LEN; i++) 324 occ += mlxsw_sp_kvdl_part_occ(mlxsw_sp->kvdl->parts[i]); 325 326 return occ; 327 } 328 329 static u64 mlxsw_sp_kvdl_single_occ_get(struct devlink *devlink) 330 { 331 struct mlxsw_core *mlxsw_core = devlink_priv(devlink); 332 struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core); 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(struct devlink *devlink) 340 { 341 struct mlxsw_core *mlxsw_core = devlink_priv(devlink); 342 struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core); 343 struct mlxsw_sp_kvdl_part *part; 344 345 part = mlxsw_sp->kvdl->parts[MLXSW_SP_KVDL_PART_ID_CHUNKS]; 346 return mlxsw_sp_kvdl_part_occ(part); 347 } 348 349 static u64 mlxsw_sp_kvdl_large_chunks_occ_get(struct devlink *devlink) 350 { 351 struct mlxsw_core *mlxsw_core = devlink_priv(devlink); 352 struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core); 353 struct mlxsw_sp_kvdl_part *part; 354 355 part = mlxsw_sp->kvdl->parts[MLXSW_SP_KVDL_PART_ID_LARGE_CHUNKS]; 356 return mlxsw_sp_kvdl_part_occ(part); 357 } 358 359 static const struct devlink_resource_ops mlxsw_sp_kvdl_single_ops = { 360 .occ_get = mlxsw_sp_kvdl_single_occ_get, 361 }; 362 363 static const struct devlink_resource_ops mlxsw_sp_kvdl_chunks_ops = { 364 .occ_get = mlxsw_sp_kvdl_chunks_occ_get, 365 }; 366 367 static const struct devlink_resource_ops mlxsw_sp_kvdl_chunks_large_ops = { 368 .occ_get = mlxsw_sp_kvdl_large_chunks_occ_get, 369 }; 370 371 int mlxsw_sp_kvdl_resources_register(struct mlxsw_core *mlxsw_core) 372 { 373 struct devlink *devlink = priv_to_devlink(mlxsw_core); 374 static struct devlink_resource_size_params size_params; 375 u32 kvdl_max_size; 376 int err; 377 378 kvdl_max_size = MLXSW_CORE_RES_GET(mlxsw_core, KVD_SIZE) - 379 MLXSW_CORE_RES_GET(mlxsw_core, KVD_SINGLE_MIN_SIZE) - 380 MLXSW_CORE_RES_GET(mlxsw_core, KVD_DOUBLE_MIN_SIZE); 381 382 devlink_resource_size_params_init(&size_params, 0, kvdl_max_size, 383 MLXSW_SP_KVDL_SINGLE_ALLOC_SIZE, 384 DEVLINK_RESOURCE_UNIT_ENTRY); 385 err = devlink_resource_register(devlink, MLXSW_SP_RESOURCE_NAME_KVD_LINEAR_SINGLES, 386 MLXSW_SP_KVDL_SINGLE_SIZE, 387 MLXSW_SP_RESOURCE_KVD_LINEAR_SINGLE, 388 MLXSW_SP_RESOURCE_KVD_LINEAR, 389 &size_params, 390 &mlxsw_sp_kvdl_single_ops); 391 if (err) 392 return err; 393 394 devlink_resource_size_params_init(&size_params, 0, kvdl_max_size, 395 MLXSW_SP_KVDL_CHUNKS_ALLOC_SIZE, 396 DEVLINK_RESOURCE_UNIT_ENTRY); 397 err = devlink_resource_register(devlink, MLXSW_SP_RESOURCE_NAME_KVD_LINEAR_CHUNKS, 398 MLXSW_SP_KVDL_CHUNKS_SIZE, 399 MLXSW_SP_RESOURCE_KVD_LINEAR_CHUNKS, 400 MLXSW_SP_RESOURCE_KVD_LINEAR, 401 &size_params, 402 &mlxsw_sp_kvdl_chunks_ops); 403 if (err) 404 return err; 405 406 devlink_resource_size_params_init(&size_params, 0, kvdl_max_size, 407 MLXSW_SP_KVDL_LARGE_CHUNKS_ALLOC_SIZE, 408 DEVLINK_RESOURCE_UNIT_ENTRY); 409 err = devlink_resource_register(devlink, MLXSW_SP_RESOURCE_NAME_KVD_LINEAR_LARGE_CHUNKS, 410 MLXSW_SP_KVDL_LARGE_CHUNKS_SIZE, 411 MLXSW_SP_RESOURCE_KVD_LINEAR_LARGE_CHUNKS, 412 MLXSW_SP_RESOURCE_KVD_LINEAR, 413 &size_params, 414 &mlxsw_sp_kvdl_chunks_large_ops); 415 return err; 416 } 417 418 int mlxsw_sp_kvdl_init(struct mlxsw_sp *mlxsw_sp) 419 { 420 struct mlxsw_sp_kvdl *kvdl; 421 int err; 422 423 kvdl = kzalloc(sizeof(*mlxsw_sp->kvdl), GFP_KERNEL); 424 if (!kvdl) 425 return -ENOMEM; 426 mlxsw_sp->kvdl = kvdl; 427 428 err = mlxsw_sp_kvdl_parts_init(mlxsw_sp); 429 if (err) 430 goto err_kvdl_parts_init; 431 432 return 0; 433 434 err_kvdl_parts_init: 435 kfree(mlxsw_sp->kvdl); 436 return err; 437 } 438 439 void mlxsw_sp_kvdl_fini(struct mlxsw_sp *mlxsw_sp) 440 { 441 mlxsw_sp_kvdl_parts_fini(mlxsw_sp); 442 kfree(mlxsw_sp->kvdl); 443 } 444