linux/drivers/net/ethernet/mellanox/mlxsw/spectrum_cnt.c
<<
>>
Prefs
   1// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
   2/* Copyright (c) 2017-2018 Mellanox Technologies. All rights reserved */
   3
   4#include <linux/kernel.h>
   5#include <linux/bitops.h>
   6
   7#include "spectrum_cnt.h"
   8
   9#define MLXSW_SP_COUNTER_POOL_BANK_SIZE 4096
  10
  11struct mlxsw_sp_counter_sub_pool {
  12        unsigned int base_index;
  13        unsigned int size;
  14        unsigned int entry_size;
  15        unsigned int bank_count;
  16};
  17
  18struct mlxsw_sp_counter_pool {
  19        unsigned int pool_size;
  20        unsigned long *usage; /* Usage bitmap */
  21        struct mlxsw_sp_counter_sub_pool *sub_pools;
  22};
  23
  24static struct mlxsw_sp_counter_sub_pool mlxsw_sp_counter_sub_pools[] = {
  25        [MLXSW_SP_COUNTER_SUB_POOL_FLOW] = {
  26                .bank_count = 6,
  27        },
  28        [MLXSW_SP_COUNTER_SUB_POOL_RIF] = {
  29                .bank_count = 2,
  30        }
  31};
  32
  33static int mlxsw_sp_counter_pool_validate(struct mlxsw_sp *mlxsw_sp)
  34{
  35        unsigned int total_bank_config = 0;
  36        unsigned int pool_size;
  37        int i;
  38
  39        pool_size = MLXSW_CORE_RES_GET(mlxsw_sp->core, COUNTER_POOL_SIZE);
  40        /* Check config is valid, no bank over subscription */
  41        for (i = 0; i < ARRAY_SIZE(mlxsw_sp_counter_sub_pools); i++)
  42                total_bank_config += mlxsw_sp_counter_sub_pools[i].bank_count;
  43        if (total_bank_config > pool_size / MLXSW_SP_COUNTER_POOL_BANK_SIZE + 1)
  44                return -EINVAL;
  45        return 0;
  46}
  47
  48static int mlxsw_sp_counter_sub_pools_prepare(struct mlxsw_sp *mlxsw_sp)
  49{
  50        struct mlxsw_sp_counter_sub_pool *sub_pool;
  51
  52        /* Prepare generic flow pool*/
  53        sub_pool = &mlxsw_sp_counter_sub_pools[MLXSW_SP_COUNTER_SUB_POOL_FLOW];
  54        if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, COUNTER_SIZE_PACKETS_BYTES))
  55                return -EIO;
  56        sub_pool->entry_size = MLXSW_CORE_RES_GET(mlxsw_sp->core,
  57                                                  COUNTER_SIZE_PACKETS_BYTES);
  58        /* Prepare erif pool*/
  59        sub_pool = &mlxsw_sp_counter_sub_pools[MLXSW_SP_COUNTER_SUB_POOL_RIF];
  60        if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, COUNTER_SIZE_ROUTER_BASIC))
  61                return -EIO;
  62        sub_pool->entry_size = MLXSW_CORE_RES_GET(mlxsw_sp->core,
  63                                                  COUNTER_SIZE_ROUTER_BASIC);
  64        return 0;
  65}
  66
  67int mlxsw_sp_counter_pool_init(struct mlxsw_sp *mlxsw_sp)
  68{
  69        struct mlxsw_sp_counter_sub_pool *sub_pool;
  70        struct mlxsw_sp_counter_pool *pool;
  71        unsigned int base_index;
  72        unsigned int map_size;
  73        int i;
  74        int err;
  75
  76        if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, COUNTER_POOL_SIZE))
  77                return -EIO;
  78
  79        err = mlxsw_sp_counter_pool_validate(mlxsw_sp);
  80        if (err)
  81                return err;
  82
  83        err = mlxsw_sp_counter_sub_pools_prepare(mlxsw_sp);
  84        if (err)
  85                return err;
  86
  87        pool = kzalloc(sizeof(*pool), GFP_KERNEL);
  88        if (!pool)
  89                return -ENOMEM;
  90
  91        pool->pool_size = MLXSW_CORE_RES_GET(mlxsw_sp->core, COUNTER_POOL_SIZE);
  92        map_size = BITS_TO_LONGS(pool->pool_size) * sizeof(unsigned long);
  93
  94        pool->usage = kzalloc(map_size, GFP_KERNEL);
  95        if (!pool->usage) {
  96                err = -ENOMEM;
  97                goto err_usage_alloc;
  98        }
  99
 100        pool->sub_pools = mlxsw_sp_counter_sub_pools;
 101        /* Allocation is based on bank count which should be
 102         * specified for each sub pool statically.
 103         */
 104        base_index = 0;
 105        for (i = 0; i < ARRAY_SIZE(mlxsw_sp_counter_sub_pools); i++) {
 106                sub_pool = &pool->sub_pools[i];
 107                sub_pool->size = sub_pool->bank_count *
 108                                 MLXSW_SP_COUNTER_POOL_BANK_SIZE;
 109                sub_pool->base_index = base_index;
 110                base_index += sub_pool->size;
 111                /* The last bank can't be fully used */
 112                if (sub_pool->base_index + sub_pool->size > pool->pool_size)
 113                        sub_pool->size = pool->pool_size - sub_pool->base_index;
 114        }
 115
 116        mlxsw_sp->counter_pool = pool;
 117        return 0;
 118
 119err_usage_alloc:
 120        kfree(pool);
 121        return err;
 122}
 123
 124void mlxsw_sp_counter_pool_fini(struct mlxsw_sp *mlxsw_sp)
 125{
 126        struct mlxsw_sp_counter_pool *pool = mlxsw_sp->counter_pool;
 127
 128        WARN_ON(find_first_bit(pool->usage, pool->pool_size) !=
 129                               pool->pool_size);
 130        kfree(pool->usage);
 131        kfree(pool);
 132}
 133
 134int mlxsw_sp_counter_alloc(struct mlxsw_sp *mlxsw_sp,
 135                           enum mlxsw_sp_counter_sub_pool_id sub_pool_id,
 136                           unsigned int *p_counter_index)
 137{
 138        struct mlxsw_sp_counter_pool *pool = mlxsw_sp->counter_pool;
 139        struct mlxsw_sp_counter_sub_pool *sub_pool;
 140        unsigned int entry_index;
 141        unsigned int stop_index;
 142        int i;
 143
 144        sub_pool = &mlxsw_sp_counter_sub_pools[sub_pool_id];
 145        stop_index = sub_pool->base_index + sub_pool->size;
 146        entry_index = sub_pool->base_index;
 147
 148        entry_index = find_next_zero_bit(pool->usage, stop_index, entry_index);
 149        if (entry_index == stop_index)
 150                return -ENOBUFS;
 151        /* The sub-pools can contain non-integer number of entries
 152         * so we must check for overflow
 153         */
 154        if (entry_index + sub_pool->entry_size > stop_index)
 155                return -ENOBUFS;
 156        for (i = 0; i < sub_pool->entry_size; i++)
 157                __set_bit(entry_index + i, pool->usage);
 158
 159        *p_counter_index = entry_index;
 160        return 0;
 161}
 162
 163void mlxsw_sp_counter_free(struct mlxsw_sp *mlxsw_sp,
 164                           enum mlxsw_sp_counter_sub_pool_id sub_pool_id,
 165                           unsigned int counter_index)
 166{
 167        struct mlxsw_sp_counter_pool *pool = mlxsw_sp->counter_pool;
 168        struct mlxsw_sp_counter_sub_pool *sub_pool;
 169        int i;
 170
 171        if (WARN_ON(counter_index >= pool->pool_size))
 172                return;
 173        sub_pool = &mlxsw_sp_counter_sub_pools[sub_pool_id];
 174        for (i = 0; i < sub_pool->entry_size; i++)
 175                __clear_bit(counter_index + i, pool->usage);
 176}
 177