linux/drivers/net/ethernet/mellanox/mlx5/core/lib/dm.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
   2// Copyright (c) 2019 Mellanox Technologies
   3
   4#include <linux/mlx5/driver.h>
   5#include <linux/mlx5/device.h>
   6
   7#include "mlx5_core.h"
   8#include "lib/mlx5.h"
   9
  10struct mlx5_dm {
  11        /* protect access to icm bitmask */
  12        spinlock_t lock;
  13        unsigned long *steering_sw_icm_alloc_blocks;
  14        unsigned long *header_modify_sw_icm_alloc_blocks;
  15};
  16
  17struct mlx5_dm *mlx5_dm_create(struct mlx5_core_dev *dev)
  18{
  19        u64 header_modify_icm_blocks = 0;
  20        u64 steering_icm_blocks = 0;
  21        struct mlx5_dm *dm;
  22
  23        if (!(MLX5_CAP_GEN_64(dev, general_obj_types) & MLX5_GENERAL_OBJ_TYPES_CAP_SW_ICM))
  24                return 0;
  25
  26        dm = kzalloc(sizeof(*dm), GFP_KERNEL);
  27        if (!dm)
  28                return ERR_PTR(-ENOMEM);
  29
  30        spin_lock_init(&dm->lock);
  31
  32        if (MLX5_CAP64_DEV_MEM(dev, steering_sw_icm_start_address)) {
  33                steering_icm_blocks =
  34                        BIT(MLX5_CAP_DEV_MEM(dev, log_steering_sw_icm_size) -
  35                            MLX5_LOG_SW_ICM_BLOCK_SIZE(dev));
  36
  37                dm->steering_sw_icm_alloc_blocks =
  38                        kcalloc(BITS_TO_LONGS(steering_icm_blocks),
  39                                sizeof(unsigned long), GFP_KERNEL);
  40                if (!dm->steering_sw_icm_alloc_blocks)
  41                        goto err_steering;
  42        }
  43
  44        if (MLX5_CAP64_DEV_MEM(dev, header_modify_sw_icm_start_address)) {
  45                header_modify_icm_blocks =
  46                        BIT(MLX5_CAP_DEV_MEM(dev, log_header_modify_sw_icm_size) -
  47                            MLX5_LOG_SW_ICM_BLOCK_SIZE(dev));
  48
  49                dm->header_modify_sw_icm_alloc_blocks =
  50                        kcalloc(BITS_TO_LONGS(header_modify_icm_blocks),
  51                                sizeof(unsigned long), GFP_KERNEL);
  52                if (!dm->header_modify_sw_icm_alloc_blocks)
  53                        goto err_modify_hdr;
  54        }
  55
  56        return dm;
  57
  58err_modify_hdr:
  59        kfree(dm->steering_sw_icm_alloc_blocks);
  60
  61err_steering:
  62        kfree(dm);
  63
  64        return ERR_PTR(-ENOMEM);
  65}
  66
  67void mlx5_dm_cleanup(struct mlx5_core_dev *dev)
  68{
  69        struct mlx5_dm *dm = dev->dm;
  70
  71        if (!dev->dm)
  72                return;
  73
  74        if (dm->steering_sw_icm_alloc_blocks) {
  75                WARN_ON(!bitmap_empty(dm->steering_sw_icm_alloc_blocks,
  76                                      BIT(MLX5_CAP_DEV_MEM(dev, log_steering_sw_icm_size) -
  77                                          MLX5_LOG_SW_ICM_BLOCK_SIZE(dev))));
  78                kfree(dm->steering_sw_icm_alloc_blocks);
  79        }
  80
  81        if (dm->header_modify_sw_icm_alloc_blocks) {
  82                WARN_ON(!bitmap_empty(dm->header_modify_sw_icm_alloc_blocks,
  83                                      BIT(MLX5_CAP_DEV_MEM(dev,
  84                                                           log_header_modify_sw_icm_size) -
  85                                      MLX5_LOG_SW_ICM_BLOCK_SIZE(dev))));
  86                kfree(dm->header_modify_sw_icm_alloc_blocks);
  87        }
  88
  89        kfree(dm);
  90}
  91
  92int mlx5_dm_sw_icm_alloc(struct mlx5_core_dev *dev, enum mlx5_sw_icm_type type,
  93                         u64 length, u16 uid, phys_addr_t *addr, u32 *obj_id)
  94{
  95        u32 num_blocks = DIV_ROUND_UP_ULL(length, MLX5_SW_ICM_BLOCK_SIZE(dev));
  96        u32 out[MLX5_ST_SZ_DW(general_obj_out_cmd_hdr)] = {};
  97        u32 in[MLX5_ST_SZ_DW(create_sw_icm_in)] = {};
  98        struct mlx5_dm *dm = dev->dm;
  99        unsigned long *block_map;
 100        u64 icm_start_addr;
 101        u32 log_icm_size;
 102        u32 max_blocks;
 103        u64 block_idx;
 104        void *sw_icm;
 105        int ret;
 106
 107        if (!dev->dm)
 108                return -EOPNOTSUPP;
 109
 110        if (!length || (length & (length - 1)) ||
 111            length & (MLX5_SW_ICM_BLOCK_SIZE(dev) - 1))
 112                return -EINVAL;
 113
 114        MLX5_SET(general_obj_in_cmd_hdr, in, opcode,
 115                 MLX5_CMD_OP_CREATE_GENERAL_OBJECT);
 116        MLX5_SET(general_obj_in_cmd_hdr, in, obj_type, MLX5_OBJ_TYPE_SW_ICM);
 117        MLX5_SET(general_obj_in_cmd_hdr, in, uid, uid);
 118
 119        switch (type) {
 120        case MLX5_SW_ICM_TYPE_STEERING:
 121                icm_start_addr = MLX5_CAP64_DEV_MEM(dev, steering_sw_icm_start_address);
 122                log_icm_size = MLX5_CAP_DEV_MEM(dev, log_steering_sw_icm_size);
 123                block_map = dm->steering_sw_icm_alloc_blocks;
 124                break;
 125        case MLX5_SW_ICM_TYPE_HEADER_MODIFY:
 126                icm_start_addr = MLX5_CAP64_DEV_MEM(dev, header_modify_sw_icm_start_address);
 127                log_icm_size = MLX5_CAP_DEV_MEM(dev,
 128                                                log_header_modify_sw_icm_size);
 129                block_map = dm->header_modify_sw_icm_alloc_blocks;
 130                break;
 131        default:
 132                return -EINVAL;
 133        }
 134
 135        if (!block_map)
 136                return -EOPNOTSUPP;
 137
 138        max_blocks = BIT(log_icm_size - MLX5_LOG_SW_ICM_BLOCK_SIZE(dev));
 139        spin_lock(&dm->lock);
 140        block_idx = bitmap_find_next_zero_area(block_map,
 141                                               max_blocks,
 142                                               0,
 143                                               num_blocks, 0);
 144
 145        if (block_idx < max_blocks)
 146                bitmap_set(block_map,
 147                           block_idx, num_blocks);
 148
 149        spin_unlock(&dm->lock);
 150
 151        if (block_idx >= max_blocks)
 152                return -ENOMEM;
 153
 154        sw_icm = MLX5_ADDR_OF(create_sw_icm_in, in, sw_icm);
 155        icm_start_addr += block_idx << MLX5_LOG_SW_ICM_BLOCK_SIZE(dev);
 156        MLX5_SET64(sw_icm, sw_icm, sw_icm_start_addr,
 157                   icm_start_addr);
 158        MLX5_SET(sw_icm, sw_icm, log_sw_icm_size, ilog2(length));
 159
 160        ret = mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
 161        if (ret) {
 162                spin_lock(&dm->lock);
 163                bitmap_clear(block_map,
 164                             block_idx, num_blocks);
 165                spin_unlock(&dm->lock);
 166
 167                return ret;
 168        }
 169
 170        *addr = icm_start_addr;
 171        *obj_id = MLX5_GET(general_obj_out_cmd_hdr, out, obj_id);
 172
 173        return 0;
 174}
 175EXPORT_SYMBOL_GPL(mlx5_dm_sw_icm_alloc);
 176
 177int mlx5_dm_sw_icm_dealloc(struct mlx5_core_dev *dev, enum mlx5_sw_icm_type type,
 178                           u64 length, u16 uid, phys_addr_t addr, u32 obj_id)
 179{
 180        u32 num_blocks = DIV_ROUND_UP_ULL(length, MLX5_SW_ICM_BLOCK_SIZE(dev));
 181        u32 out[MLX5_ST_SZ_DW(general_obj_out_cmd_hdr)] = {};
 182        u32 in[MLX5_ST_SZ_DW(general_obj_in_cmd_hdr)] = {};
 183        struct mlx5_dm *dm = dev->dm;
 184        unsigned long *block_map;
 185        u64 icm_start_addr;
 186        u64 start_idx;
 187        int err;
 188
 189        if (!dev->dm)
 190                return -EOPNOTSUPP;
 191
 192        switch (type) {
 193        case MLX5_SW_ICM_TYPE_STEERING:
 194                icm_start_addr = MLX5_CAP64_DEV_MEM(dev, steering_sw_icm_start_address);
 195                block_map = dm->steering_sw_icm_alloc_blocks;
 196                break;
 197        case MLX5_SW_ICM_TYPE_HEADER_MODIFY:
 198                icm_start_addr = MLX5_CAP64_DEV_MEM(dev, header_modify_sw_icm_start_address);
 199                block_map = dm->header_modify_sw_icm_alloc_blocks;
 200                break;
 201        default:
 202                return -EINVAL;
 203        }
 204
 205        MLX5_SET(general_obj_in_cmd_hdr, in, opcode,
 206                 MLX5_CMD_OP_DESTROY_GENERAL_OBJECT);
 207        MLX5_SET(general_obj_in_cmd_hdr, in, obj_type, MLX5_OBJ_TYPE_SW_ICM);
 208        MLX5_SET(general_obj_in_cmd_hdr, in, obj_id, obj_id);
 209        MLX5_SET(general_obj_in_cmd_hdr, in, uid, uid);
 210
 211        err =  mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
 212        if (err)
 213                return err;
 214
 215        start_idx = (addr - icm_start_addr) >> MLX5_LOG_SW_ICM_BLOCK_SIZE(dev);
 216        spin_lock(&dm->lock);
 217        bitmap_clear(block_map,
 218                     start_idx, num_blocks);
 219        spin_unlock(&dm->lock);
 220
 221        return 0;
 222}
 223EXPORT_SYMBOL_GPL(mlx5_dm_sw_icm_dealloc);
 224