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 NULL;
  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, u32 log_alignment, u16 uid,
  94                         phys_addr_t *addr, u32 *obj_id)
  95{
  96        u32 num_blocks = DIV_ROUND_UP_ULL(length, MLX5_SW_ICM_BLOCK_SIZE(dev));
  97        u32 out[MLX5_ST_SZ_DW(general_obj_out_cmd_hdr)] = {};
  98        u32 in[MLX5_ST_SZ_DW(create_sw_icm_in)] = {};
  99        struct mlx5_dm *dm = dev->dm;
 100        unsigned long *block_map;
 101        u64 icm_start_addr;
 102        u32 log_icm_size;
 103        u64 align_mask;
 104        u32 max_blocks;
 105        u64 block_idx;
 106        void *sw_icm;
 107        int ret;
 108
 109        if (!dev->dm)
 110                return -EOPNOTSUPP;
 111
 112        if (!length || (length & (length - 1)) ||
 113            length & (MLX5_SW_ICM_BLOCK_SIZE(dev) - 1))
 114                return -EINVAL;
 115
 116        MLX5_SET(general_obj_in_cmd_hdr, in, opcode,
 117                 MLX5_CMD_OP_CREATE_GENERAL_OBJECT);
 118        MLX5_SET(general_obj_in_cmd_hdr, in, obj_type, MLX5_OBJ_TYPE_SW_ICM);
 119        MLX5_SET(general_obj_in_cmd_hdr, in, uid, uid);
 120
 121        switch (type) {
 122        case MLX5_SW_ICM_TYPE_STEERING:
 123                icm_start_addr = MLX5_CAP64_DEV_MEM(dev, steering_sw_icm_start_address);
 124                log_icm_size = MLX5_CAP_DEV_MEM(dev, log_steering_sw_icm_size);
 125                block_map = dm->steering_sw_icm_alloc_blocks;
 126                break;
 127        case MLX5_SW_ICM_TYPE_HEADER_MODIFY:
 128                icm_start_addr = MLX5_CAP64_DEV_MEM(dev, header_modify_sw_icm_start_address);
 129                log_icm_size = MLX5_CAP_DEV_MEM(dev,
 130                                                log_header_modify_sw_icm_size);
 131                block_map = dm->header_modify_sw_icm_alloc_blocks;
 132                break;
 133        default:
 134                return -EINVAL;
 135        }
 136
 137        if (!block_map)
 138                return -EOPNOTSUPP;
 139
 140        max_blocks = BIT(log_icm_size - MLX5_LOG_SW_ICM_BLOCK_SIZE(dev));
 141
 142        if (log_alignment < MLX5_LOG_SW_ICM_BLOCK_SIZE(dev))
 143                log_alignment = MLX5_LOG_SW_ICM_BLOCK_SIZE(dev);
 144        align_mask = BIT(log_alignment - MLX5_LOG_SW_ICM_BLOCK_SIZE(dev)) - 1;
 145
 146        spin_lock(&dm->lock);
 147        block_idx = bitmap_find_next_zero_area(block_map, max_blocks, 0,
 148                                               num_blocks, align_mask);
 149
 150        if (block_idx < max_blocks)
 151                bitmap_set(block_map,
 152                           block_idx, num_blocks);
 153
 154        spin_unlock(&dm->lock);
 155
 156        if (block_idx >= max_blocks)
 157                return -ENOMEM;
 158
 159        sw_icm = MLX5_ADDR_OF(create_sw_icm_in, in, sw_icm);
 160        icm_start_addr += block_idx << MLX5_LOG_SW_ICM_BLOCK_SIZE(dev);
 161        MLX5_SET64(sw_icm, sw_icm, sw_icm_start_addr,
 162                   icm_start_addr);
 163        MLX5_SET(sw_icm, sw_icm, log_sw_icm_size, ilog2(length));
 164
 165        ret = mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
 166        if (ret) {
 167                spin_lock(&dm->lock);
 168                bitmap_clear(block_map,
 169                             block_idx, num_blocks);
 170                spin_unlock(&dm->lock);
 171
 172                return ret;
 173        }
 174
 175        *addr = icm_start_addr;
 176        *obj_id = MLX5_GET(general_obj_out_cmd_hdr, out, obj_id);
 177
 178        return 0;
 179}
 180EXPORT_SYMBOL_GPL(mlx5_dm_sw_icm_alloc);
 181
 182int mlx5_dm_sw_icm_dealloc(struct mlx5_core_dev *dev, enum mlx5_sw_icm_type type,
 183                           u64 length, u16 uid, phys_addr_t addr, u32 obj_id)
 184{
 185        u32 num_blocks = DIV_ROUND_UP_ULL(length, MLX5_SW_ICM_BLOCK_SIZE(dev));
 186        u32 out[MLX5_ST_SZ_DW(general_obj_out_cmd_hdr)] = {};
 187        u32 in[MLX5_ST_SZ_DW(general_obj_in_cmd_hdr)] = {};
 188        struct mlx5_dm *dm = dev->dm;
 189        unsigned long *block_map;
 190        u64 icm_start_addr;
 191        u64 start_idx;
 192        int err;
 193
 194        if (!dev->dm)
 195                return -EOPNOTSUPP;
 196
 197        switch (type) {
 198        case MLX5_SW_ICM_TYPE_STEERING:
 199                icm_start_addr = MLX5_CAP64_DEV_MEM(dev, steering_sw_icm_start_address);
 200                block_map = dm->steering_sw_icm_alloc_blocks;
 201                break;
 202        case MLX5_SW_ICM_TYPE_HEADER_MODIFY:
 203                icm_start_addr = MLX5_CAP64_DEV_MEM(dev, header_modify_sw_icm_start_address);
 204                block_map = dm->header_modify_sw_icm_alloc_blocks;
 205                break;
 206        default:
 207                return -EINVAL;
 208        }
 209
 210        MLX5_SET(general_obj_in_cmd_hdr, in, opcode,
 211                 MLX5_CMD_OP_DESTROY_GENERAL_OBJECT);
 212        MLX5_SET(general_obj_in_cmd_hdr, in, obj_type, MLX5_OBJ_TYPE_SW_ICM);
 213        MLX5_SET(general_obj_in_cmd_hdr, in, obj_id, obj_id);
 214        MLX5_SET(general_obj_in_cmd_hdr, in, uid, uid);
 215
 216        err =  mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
 217        if (err)
 218                return err;
 219
 220        start_idx = (addr - icm_start_addr) >> MLX5_LOG_SW_ICM_BLOCK_SIZE(dev);
 221        spin_lock(&dm->lock);
 222        bitmap_clear(block_map,
 223                     start_idx, num_blocks);
 224        spin_unlock(&dm->lock);
 225
 226        return 0;
 227}
 228EXPORT_SYMBOL_GPL(mlx5_dm_sw_icm_dealloc);
 229