linux/drivers/net/ethernet/mellanox/mlxsw/spectrum_kvdl.c
<<
>>
Prefs
   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
  62struct 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
  70enum 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
  84static 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
  92struct mlxsw_sp_kvdl_part {
  93        struct mlxsw_sp_kvdl_part_info info;
  94        unsigned long usage[0]; /* Entries */
  95};
  96
  97struct mlxsw_sp_kvdl {
  98        struct mlxsw_sp_kvdl_part *parts[MLXSW_SP_KVDL_PARTS_INFO_LEN];
  99};
 100
 101static struct mlxsw_sp_kvdl_part *
 102mlxsw_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
 119static struct mlxsw_sp_kvdl_part *
 120mlxsw_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
 135static u32
 136mlxsw_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
 142static unsigned int
 143mlxsw_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
 149static 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
 167static 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
 177int 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
 192void 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
 202int 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
 217static 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
 230static struct mlxsw_sp_kvdl_part *
 231mlxsw_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
 263static void mlxsw_sp_kvdl_part_fini(struct mlxsw_sp_kvdl_part *part)
 264{
 265        kfree(part);
 266}
 267
 268static 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
 287err_kvdl_part_init:
 288        for (i--; i >= 0; i--)
 289                mlxsw_sp_kvdl_part_fini(kvdl->parts[i]);
 290        return err;
 291}
 292
 293static 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
 302static 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
 318static 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
 330static 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
 339static 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
 348static 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
 357int 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
 401int 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
 435err_kvdl_parts_init:
 436        kfree(mlxsw_sp->kvdl);
 437        return err;
 438}
 439
 440void 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