linux/drivers/net/ethernet/mellanox/mlxsw/spectrum1_kvdl.c
<<
>>
Prefs
   1// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
   2/* 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 0
  10#define MLXSW_SP1_KVDL_SINGLE_SIZE 16384
  11#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 49152
  17#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 1
  28#define MLXSW_SP1_KVDL_CHUNKS_ALLOC_SIZE 32
  29#define MLXSW_SP1_KVDL_LARGE_CHUNKS_ALLOC_SIZE 512
  30
  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 u32
 105mlxsw_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 int
 112mlxsw_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 the
 155         * 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        size_t usage_size;
 217        u64 resource_size;
 218        int err;
 219
 220        err = devlink_resource_size_get(devlink, info->resource_id,
 221                                        &resource_size);
 222        if (err) {
 223                need_update = false;
 224                resource_size = info->end_index - info->start_index + 1;
 225        }
 226
 227        nr_entries = div_u64(resource_size, info->alloc_size);
 228        usage_size = BITS_TO_LONGS(nr_entries) * sizeof(unsigned long);
 229        part = kzalloc(sizeof(*part) + usage_size, GFP_KERNEL);
 230        if (!part)
 231                return ERR_PTR(-ENOMEM);
 232
 233        memcpy(&part->info, info, sizeof(part->info));
 234
 235        if (need_update)
 236                mlxsw_sp1_kvdl_part_update(part, part_prev, resource_size);
 237        return part;
 238}
 239
 240static void mlxsw_sp1_kvdl_part_fini(struct mlxsw_sp1_kvdl_part *part)
 241{
 242        kfree(part);
 243}
 244
 245static int mlxsw_sp1_kvdl_parts_init(struct mlxsw_sp *mlxsw_sp,
 246                                     struct mlxsw_sp1_kvdl *kvdl)
 247{
 248        const struct mlxsw_sp1_kvdl_part_info *info;
 249        struct mlxsw_sp1_kvdl_part *part_prev = NULL;
 250        int err, i;
 251
 252        for (i = 0; i < MLXSW_SP1_KVDL_PARTS_INFO_LEN; i++) {
 253                info = &mlxsw_sp1_kvdl_parts_info[i];
 254                kvdl->parts[i] = mlxsw_sp1_kvdl_part_init(mlxsw_sp, info,
 255                                                          part_prev);
 256                if (IS_ERR(kvdl->parts[i])) {
 257                        err = PTR_ERR(kvdl->parts[i]);
 258                        goto err_kvdl_part_init;
 259                }
 260                part_prev = kvdl->parts[i];
 261        }
 262        return 0;
 263
 264err_kvdl_part_init:
 265        for (i--; i >= 0; i--)
 266                mlxsw_sp1_kvdl_part_fini(kvdl->parts[i]);
 267        return err;
 268}
 269
 270static void mlxsw_sp1_kvdl_parts_fini(struct mlxsw_sp1_kvdl *kvdl)
 271{
 272        int i;
 273
 274        for (i = 0; i < MLXSW_SP1_KVDL_PARTS_INFO_LEN; i++)
 275                mlxsw_sp1_kvdl_part_fini(kvdl->parts[i]);
 276}
 277
 278static u64 mlxsw_sp1_kvdl_part_occ(struct mlxsw_sp1_kvdl_part *part)
 279{
 280        const struct mlxsw_sp1_kvdl_part_info *info = &part->info;
 281        unsigned int nr_entries;
 282        int bit = -1;
 283        u64 occ = 0;
 284
 285        nr_entries = (info->end_index -
 286                      info->start_index + 1) /
 287                      info->alloc_size;
 288        while ((bit = find_next_bit(part->usage, nr_entries, bit + 1))
 289                < nr_entries)
 290                occ += info->alloc_size;
 291        return occ;
 292}
 293
 294static u64 mlxsw_sp1_kvdl_occ_get(void *priv)
 295{
 296        const struct mlxsw_sp1_kvdl *kvdl = priv;
 297        u64 occ = 0;
 298        int i;
 299
 300        for (i = 0; i < MLXSW_SP1_KVDL_PARTS_INFO_LEN; i++)
 301                occ += mlxsw_sp1_kvdl_part_occ(kvdl->parts[i]);
 302
 303        return occ;
 304}
 305
 306static u64 mlxsw_sp1_kvdl_single_occ_get(void *priv)
 307{
 308        const struct mlxsw_sp1_kvdl *kvdl = priv;
 309        struct mlxsw_sp1_kvdl_part *part;
 310
 311        part = kvdl->parts[MLXSW_SP1_KVDL_PART_ID_SINGLE];
 312        return mlxsw_sp1_kvdl_part_occ(part);
 313}
 314
 315static u64 mlxsw_sp1_kvdl_chunks_occ_get(void *priv)
 316{
 317        const struct mlxsw_sp1_kvdl *kvdl = priv;
 318        struct mlxsw_sp1_kvdl_part *part;
 319
 320        part = kvdl->parts[MLXSW_SP1_KVDL_PART_ID_CHUNKS];
 321        return mlxsw_sp1_kvdl_part_occ(part);
 322}
 323
 324static u64 mlxsw_sp1_kvdl_large_chunks_occ_get(void *priv)
 325{
 326        const struct mlxsw_sp1_kvdl *kvdl = priv;
 327        struct mlxsw_sp1_kvdl_part *part;
 328
 329        part = kvdl->parts[MLXSW_SP1_KVDL_PART_ID_LARGE_CHUNKS];
 330        return mlxsw_sp1_kvdl_part_occ(part);
 331}
 332
 333static int mlxsw_sp1_kvdl_init(struct mlxsw_sp *mlxsw_sp, void *priv)
 334{
 335        struct devlink *devlink = priv_to_devlink(mlxsw_sp->core);
 336        struct mlxsw_sp1_kvdl *kvdl = priv;
 337        int err;
 338
 339        err = mlxsw_sp1_kvdl_parts_init(mlxsw_sp, kvdl);
 340        if (err)
 341                return err;
 342        devlink_resource_occ_get_register(devlink,
 343                                          MLXSW_SP_RESOURCE_KVD_LINEAR,
 344                                          mlxsw_sp1_kvdl_occ_get,
 345                                          kvdl);
 346        devlink_resource_occ_get_register(devlink,
 347                                          MLXSW_SP_RESOURCE_KVD_LINEAR_SINGLE,
 348                                          mlxsw_sp1_kvdl_single_occ_get,
 349                                          kvdl);
 350        devlink_resource_occ_get_register(devlink,
 351                                          MLXSW_SP_RESOURCE_KVD_LINEAR_CHUNKS,
 352                                          mlxsw_sp1_kvdl_chunks_occ_get,
 353                                          kvdl);
 354        devlink_resource_occ_get_register(devlink,
 355                                          MLXSW_SP_RESOURCE_KVD_LINEAR_LARGE_CHUNKS,
 356                                          mlxsw_sp1_kvdl_large_chunks_occ_get,
 357                                          kvdl);
 358        return 0;
 359}
 360
 361static void mlxsw_sp1_kvdl_fini(struct mlxsw_sp *mlxsw_sp, void *priv)
 362{
 363        struct devlink *devlink = priv_to_devlink(mlxsw_sp->core);
 364        struct mlxsw_sp1_kvdl *kvdl = priv;
 365
 366        devlink_resource_occ_get_unregister(devlink,
 367                                            MLXSW_SP_RESOURCE_KVD_LINEAR_LARGE_CHUNKS);
 368        devlink_resource_occ_get_unregister(devlink,
 369                                            MLXSW_SP_RESOURCE_KVD_LINEAR_CHUNKS);
 370        devlink_resource_occ_get_unregister(devlink,
 371                                            MLXSW_SP_RESOURCE_KVD_LINEAR_SINGLE);
 372        devlink_resource_occ_get_unregister(devlink,
 373                                            MLXSW_SP_RESOURCE_KVD_LINEAR);
 374        mlxsw_sp1_kvdl_parts_fini(kvdl);
 375}
 376
 377const struct mlxsw_sp_kvdl_ops mlxsw_sp1_kvdl_ops = {
 378        .priv_size = sizeof(struct mlxsw_sp1_kvdl),
 379        .init = mlxsw_sp1_kvdl_init,
 380        .fini = mlxsw_sp1_kvdl_fini,
 381        .alloc = mlxsw_sp1_kvdl_alloc,
 382        .free = mlxsw_sp1_kvdl_free,
 383        .alloc_size_query = mlxsw_sp1_kvdl_alloc_size_query,
 384};
 385
 386int mlxsw_sp1_kvdl_resources_register(struct mlxsw_core *mlxsw_core)
 387{
 388        struct devlink *devlink = priv_to_devlink(mlxsw_core);
 389        static struct devlink_resource_size_params size_params;
 390        u32 kvdl_max_size;
 391        int err;
 392
 393        kvdl_max_size = MLXSW_CORE_RES_GET(mlxsw_core, KVD_SIZE) -
 394                        MLXSW_CORE_RES_GET(mlxsw_core, KVD_SINGLE_MIN_SIZE) -
 395                        MLXSW_CORE_RES_GET(mlxsw_core, KVD_DOUBLE_MIN_SIZE);
 396
 397        devlink_resource_size_params_init(&size_params, 0, kvdl_max_size,
 398                                          MLXSW_SP1_KVDL_SINGLE_ALLOC_SIZE,
 399                                          DEVLINK_RESOURCE_UNIT_ENTRY);
 400        err = devlink_resource_register(devlink, MLXSW_SP_RESOURCE_NAME_KVD_LINEAR_SINGLES,
 401                                        MLXSW_SP1_KVDL_SINGLE_SIZE,
 402                                        MLXSW_SP_RESOURCE_KVD_LINEAR_SINGLE,
 403                                        MLXSW_SP_RESOURCE_KVD_LINEAR,
 404                                        &size_params);
 405        if (err)
 406                return err;
 407
 408        devlink_resource_size_params_init(&size_params, 0, kvdl_max_size,
 409                                          MLXSW_SP1_KVDL_CHUNKS_ALLOC_SIZE,
 410                                          DEVLINK_RESOURCE_UNIT_ENTRY);
 411        err = devlink_resource_register(devlink, MLXSW_SP_RESOURCE_NAME_KVD_LINEAR_CHUNKS,
 412                                        MLXSW_SP1_KVDL_CHUNKS_SIZE,
 413                                        MLXSW_SP_RESOURCE_KVD_LINEAR_CHUNKS,
 414                                        MLXSW_SP_RESOURCE_KVD_LINEAR,
 415                                        &size_params);
 416        if (err)
 417                return err;
 418
 419        devlink_resource_size_params_init(&size_params, 0, kvdl_max_size,
 420                                          MLXSW_SP1_KVDL_LARGE_CHUNKS_ALLOC_SIZE,
 421                                          DEVLINK_RESOURCE_UNIT_ENTRY);
 422        err = devlink_resource_register(devlink, MLXSW_SP_RESOURCE_NAME_KVD_LINEAR_LARGE_CHUNKS,
 423                                        MLXSW_SP1_KVDL_LARGE_CHUNKS_SIZE,
 424                                        MLXSW_SP_RESOURCE_KVD_LINEAR_LARGE_CHUNKS,
 425                                        MLXSW_SP_RESOURCE_KVD_LINEAR,
 426                                        &size_params);
 427        return err;
 428}
 429