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 10 struct 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 17 struct 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 58 err_modify_hdr: 59 kfree(dm->steering_sw_icm_alloc_blocks); 60 61 err_steering: 62 kfree(dm); 63 64 return ERR_PTR(-ENOMEM); 65 } 66 67 void 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 92 int 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 } 175 EXPORT_SYMBOL_GPL(mlx5_dm_sw_icm_alloc); 176 177 int 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 } 223 EXPORT_SYMBOL_GPL(mlx5_dm_sw_icm_dealloc); 224