1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright (c) 2022 NVIDIA Corporation & Affiliates 3 */ 4 5 #include "mlx5dr_internal.h" 6 7 static void mlx5dr_table_init_next_ft_attr(struct mlx5dr_table *tbl, 8 struct mlx5dr_cmd_ft_create_attr *ft_attr) 9 { 10 ft_attr->type = tbl->fw_ft_type; 11 if (tbl->type == MLX5DR_TABLE_TYPE_FDB) 12 ft_attr->level = tbl->ctx->caps->fdb_ft.max_level - 1; 13 else 14 ft_attr->level = tbl->ctx->caps->nic_ft.max_level - 1; 15 ft_attr->rtc_valid = true; 16 } 17 18 /* Call this under ctx->ctrl_lock */ 19 static int 20 mlx5dr_table_up_default_fdb_miss_tbl(struct mlx5dr_table *tbl) 21 { 22 struct mlx5dr_cmd_ft_create_attr ft_attr = {0}; 23 struct mlx5dr_cmd_forward_tbl *default_miss; 24 struct mlx5dr_context *ctx = tbl->ctx; 25 uint8_t tbl_type = tbl->type; 26 uint32_t vport; 27 28 if (tbl->type != MLX5DR_TABLE_TYPE_FDB) 29 return 0; 30 31 if (ctx->common_res[tbl_type].default_miss) { 32 ctx->common_res[tbl_type].default_miss->refcount++; 33 return 0; 34 } 35 36 ft_attr.type = tbl->fw_ft_type; 37 ft_attr.level = tbl->ctx->caps->fdb_ft.max_level; /* The last level */ 38 ft_attr.rtc_valid = false; 39 40 assert(ctx->caps->eswitch_manager); 41 vport = ctx->caps->eswitch_manager_vport_number; 42 43 default_miss = mlx5dr_cmd_miss_ft_create(mlx5dr_context_get_local_ibv(ctx), 44 &ft_attr, vport); 45 if (!default_miss) { 46 DR_LOG(ERR, "Failed to default miss table type: 0x%x", tbl_type); 47 return rte_errno; 48 } 49 50 ctx->common_res[tbl_type].default_miss = default_miss; 51 ctx->common_res[tbl_type].default_miss->refcount++; 52 return 0; 53 } 54 55 /* Called under pthread_spin_lock(&ctx->ctrl_lock) */ 56 static void mlx5dr_table_down_default_fdb_miss_tbl(struct mlx5dr_table *tbl) 57 { 58 struct mlx5dr_cmd_forward_tbl *default_miss; 59 struct mlx5dr_context *ctx = tbl->ctx; 60 uint8_t tbl_type = tbl->type; 61 62 if (tbl->type != MLX5DR_TABLE_TYPE_FDB) 63 return; 64 65 default_miss = ctx->common_res[tbl_type].default_miss; 66 if (--default_miss->refcount) 67 return; 68 69 mlx5dr_cmd_miss_ft_destroy(default_miss); 70 71 simple_free(default_miss); 72 ctx->common_res[tbl_type].default_miss = NULL; 73 } 74 75 static int 76 mlx5dr_table_connect_to_default_miss_tbl(struct mlx5dr_table *tbl, 77 struct mlx5dr_devx_obj *ft) 78 { 79 struct mlx5dr_cmd_ft_modify_attr ft_attr = {0}; 80 int ret; 81 82 assert(tbl->type == MLX5DR_TABLE_TYPE_FDB); 83 84 mlx5dr_cmd_set_attr_connect_miss_tbl(tbl->ctx, 85 tbl->fw_ft_type, 86 tbl->type, 87 &ft_attr); 88 89 /* Connect to next */ 90 ret = mlx5dr_cmd_flow_table_modify(ft, &ft_attr); 91 if (ret) { 92 DR_LOG(ERR, "Failed to connect FT to default FDB FT"); 93 return errno; 94 } 95 96 return 0; 97 } 98 99 struct mlx5dr_devx_obj * 100 mlx5dr_table_create_default_ft(struct ibv_context *ibv, 101 struct mlx5dr_table *tbl) 102 { 103 struct mlx5dr_cmd_ft_create_attr ft_attr = {0}; 104 struct mlx5dr_devx_obj *ft_obj; 105 int ret; 106 107 mlx5dr_table_init_next_ft_attr(tbl, &ft_attr); 108 109 ft_obj = mlx5dr_cmd_flow_table_create(ibv, &ft_attr); 110 if (ft_obj && tbl->type == MLX5DR_TABLE_TYPE_FDB) { 111 /* Take/create ref over the default miss */ 112 ret = mlx5dr_table_up_default_fdb_miss_tbl(tbl); 113 if (ret) { 114 DR_LOG(ERR, "Failed to get default fdb miss"); 115 goto free_ft_obj; 116 } 117 ret = mlx5dr_table_connect_to_default_miss_tbl(tbl, ft_obj); 118 if (ret) { 119 DR_LOG(ERR, "Failed connecting to default miss tbl"); 120 goto down_miss_tbl; 121 } 122 } 123 124 return ft_obj; 125 126 down_miss_tbl: 127 mlx5dr_table_down_default_fdb_miss_tbl(tbl); 128 free_ft_obj: 129 mlx5dr_cmd_destroy_obj(ft_obj); 130 return NULL; 131 } 132 133 static int 134 mlx5dr_table_init_check_hws_support(struct mlx5dr_context *ctx, 135 struct mlx5dr_table *tbl) 136 { 137 if (!(ctx->flags & MLX5DR_CONTEXT_FLAG_HWS_SUPPORT)) { 138 DR_LOG(ERR, "HWS not supported, cannot create mlx5dr_table"); 139 rte_errno = EOPNOTSUPP; 140 return rte_errno; 141 } 142 143 if (mlx5dr_context_shared_gvmi_used(ctx) && tbl->type == MLX5DR_TABLE_TYPE_FDB) { 144 DR_LOG(ERR, "FDB with shared port resources is not supported"); 145 rte_errno = EOPNOTSUPP; 146 return rte_errno; 147 } 148 149 return 0; 150 } 151 152 static int 153 mlx5dr_table_shared_gvmi_resource_create(struct mlx5dr_context *ctx, 154 enum mlx5dr_table_type type, 155 struct mlx5dr_context_shared_gvmi_res *gvmi_res) 156 { 157 struct mlx5dr_cmd_ft_create_attr ft_attr = {0}; 158 uint32_t calculated_ft_id; 159 int ret; 160 161 if (!mlx5dr_context_shared_gvmi_used(ctx)) 162 return 0; 163 164 ft_attr.type = mlx5dr_table_get_res_fw_ft_type(type, false); 165 ft_attr.level = ctx->caps->nic_ft.max_level - 1; 166 ft_attr.rtc_valid = true; 167 168 gvmi_res->end_ft = 169 mlx5dr_cmd_flow_table_create(mlx5dr_context_get_local_ibv(ctx), 170 &ft_attr); 171 if (!gvmi_res->end_ft) { 172 DR_LOG(ERR, "Failed to create end-ft"); 173 return rte_errno; 174 } 175 176 calculated_ft_id = 177 mlx5dr_table_get_res_fw_ft_type(type, false) << FT_ID_FT_TYPE_OFFSET; 178 calculated_ft_id |= gvmi_res->end_ft->id; 179 180 /* create alias to that FT */ 181 ret = mlx5dr_matcher_create_aliased_obj(ctx, 182 ctx->local_ibv_ctx, 183 ctx->ibv_ctx, 184 ctx->caps->vhca_id, 185 calculated_ft_id, 186 MLX5_GENERAL_OBJ_TYPE_FT_ALIAS, 187 &gvmi_res->aliased_end_ft); 188 if (ret) { 189 DR_LOG(ERR, "Failed to create alias end-ft"); 190 goto free_end_ft; 191 } 192 193 return 0; 194 195 free_end_ft: 196 mlx5dr_cmd_destroy_obj(gvmi_res->end_ft); 197 198 return rte_errno; 199 } 200 201 static void 202 mlx5dr_table_shared_gvmi_resourse_destroy(struct mlx5dr_context *ctx, 203 struct mlx5dr_context_shared_gvmi_res *gvmi_res) 204 { 205 if (!mlx5dr_context_shared_gvmi_used(ctx)) 206 return; 207 208 if (gvmi_res->aliased_end_ft) { 209 mlx5dr_cmd_destroy_obj(gvmi_res->aliased_end_ft); 210 gvmi_res->aliased_end_ft = NULL; 211 } 212 if (gvmi_res->end_ft) { 213 mlx5dr_cmd_destroy_obj(gvmi_res->end_ft); 214 gvmi_res->end_ft = NULL; 215 } 216 } 217 218 /* called under spinlock ctx->ctrl_lock */ 219 static struct mlx5dr_context_shared_gvmi_res * 220 mlx5dr_table_get_shared_gvmi_res(struct mlx5dr_context *ctx, enum mlx5dr_table_type type) 221 { 222 int ret; 223 224 if (!mlx5dr_context_shared_gvmi_used(ctx)) 225 return NULL; 226 227 if (ctx->gvmi_res[type].aliased_end_ft) { 228 ctx->gvmi_res[type].refcount++; 229 return &ctx->gvmi_res[type]; 230 } 231 232 ret = mlx5dr_table_shared_gvmi_resource_create(ctx, type, &ctx->gvmi_res[type]); 233 if (ret) { 234 DR_LOG(ERR, "Failed to create shared gvmi res for type: %d", type); 235 goto out; 236 } 237 238 ctx->gvmi_res[type].refcount = 1; 239 240 return &ctx->gvmi_res[type]; 241 242 out: 243 return NULL; 244 } 245 246 /* called under spinlock ctx->ctrl_lock */ 247 static void mlx5dr_table_put_shared_gvmi_res(struct mlx5dr_table *tbl) 248 { 249 struct mlx5dr_context *ctx = tbl->ctx; 250 251 if (!mlx5dr_context_shared_gvmi_used(ctx)) 252 return; 253 254 if (--ctx->gvmi_res[tbl->type].refcount) 255 return; 256 257 mlx5dr_table_shared_gvmi_resourse_destroy(ctx, &ctx->gvmi_res[tbl->type]); 258 } 259 260 static void mlx5dr_table_uninit_shared_ctx_res(struct mlx5dr_table *tbl) 261 { 262 struct mlx5dr_context *ctx = tbl->ctx; 263 264 if (!mlx5dr_context_shared_gvmi_used(ctx)) 265 return; 266 267 mlx5dr_cmd_destroy_obj(tbl->local_ft); 268 269 mlx5dr_table_put_shared_gvmi_res(tbl); 270 } 271 272 /* called under spin_lock ctx->ctrl_lock */ 273 static int mlx5dr_table_init_shared_ctx_res(struct mlx5dr_context *ctx, struct mlx5dr_table *tbl) 274 { 275 if (!mlx5dr_context_shared_gvmi_used(ctx)) 276 return 0; 277 278 /* create local-ft for root access */ 279 tbl->local_ft = 280 mlx5dr_table_create_default_ft(mlx5dr_context_get_local_ibv(ctx), tbl); 281 if (!tbl->local_ft) { 282 DR_LOG(ERR, "Failed to create local-ft"); 283 return rte_errno; 284 } 285 286 if (!mlx5dr_table_get_shared_gvmi_res(tbl->ctx, tbl->type)) { 287 DR_LOG(ERR, "Failed to shared gvmi resources"); 288 goto clean_local_ft; 289 } 290 291 return 0; 292 293 clean_local_ft: 294 mlx5dr_table_destroy_default_ft(tbl, tbl->local_ft); 295 return rte_errno; 296 } 297 298 void mlx5dr_table_destroy_default_ft(struct mlx5dr_table *tbl, 299 struct mlx5dr_devx_obj *ft_obj) 300 { 301 mlx5dr_cmd_destroy_obj(ft_obj); 302 mlx5dr_table_down_default_fdb_miss_tbl(tbl); 303 } 304 305 static int mlx5dr_table_init(struct mlx5dr_table *tbl) 306 { 307 struct mlx5dr_context *ctx = tbl->ctx; 308 int ret; 309 310 if (mlx5dr_table_is_root(tbl)) 311 return 0; 312 313 ret = mlx5dr_table_init_check_hws_support(ctx, tbl); 314 if (ret) 315 return ret; 316 317 switch (tbl->type) { 318 case MLX5DR_TABLE_TYPE_NIC_RX: 319 tbl->fw_ft_type = FS_FT_NIC_RX; 320 break; 321 case MLX5DR_TABLE_TYPE_NIC_TX: 322 tbl->fw_ft_type = FS_FT_NIC_TX; 323 break; 324 case MLX5DR_TABLE_TYPE_FDB: 325 tbl->fw_ft_type = FS_FT_FDB; 326 break; 327 default: 328 assert(0); 329 break; 330 } 331 332 pthread_spin_lock(&ctx->ctrl_lock); 333 tbl->ft = mlx5dr_table_create_default_ft(tbl->ctx->ibv_ctx, tbl); 334 if (!tbl->ft) { 335 DR_LOG(ERR, "Failed to create flow table devx object"); 336 pthread_spin_unlock(&ctx->ctrl_lock); 337 return rte_errno; 338 } 339 340 ret = mlx5dr_action_get_default_stc(ctx, tbl->type); 341 if (ret) 342 goto tbl_destroy; 343 344 ret = mlx5dr_table_init_shared_ctx_res(ctx, tbl); 345 if (ret) 346 goto put_stc; 347 348 pthread_spin_unlock(&ctx->ctrl_lock); 349 350 return 0; 351 352 put_stc: 353 mlx5dr_action_put_default_stc(ctx, tbl->type); 354 tbl_destroy: 355 mlx5dr_table_destroy_default_ft(tbl, tbl->ft); 356 pthread_spin_unlock(&ctx->ctrl_lock); 357 return rte_errno; 358 } 359 360 static void mlx5dr_table_uninit(struct mlx5dr_table *tbl) 361 { 362 if (mlx5dr_table_is_root(tbl)) 363 return; 364 pthread_spin_lock(&tbl->ctx->ctrl_lock); 365 mlx5dr_action_put_default_stc(tbl->ctx, tbl->type); 366 mlx5dr_table_destroy_default_ft(tbl, tbl->ft); 367 mlx5dr_table_uninit_shared_ctx_res(tbl); 368 pthread_spin_unlock(&tbl->ctx->ctrl_lock); 369 } 370 371 struct mlx5dr_table *mlx5dr_table_create(struct mlx5dr_context *ctx, 372 struct mlx5dr_table_attr *attr) 373 { 374 struct mlx5dr_table *tbl; 375 int ret; 376 377 if (attr->type > MLX5DR_TABLE_TYPE_FDB) { 378 DR_LOG(ERR, "Invalid table type %d", attr->type); 379 return NULL; 380 } 381 382 tbl = simple_malloc(sizeof(*tbl)); 383 if (!tbl) { 384 rte_errno = ENOMEM; 385 return NULL; 386 } 387 388 tbl->ctx = ctx; 389 tbl->type = attr->type; 390 tbl->level = attr->level; 391 LIST_INIT(&tbl->head); 392 393 ret = mlx5dr_table_init(tbl); 394 if (ret) { 395 DR_LOG(ERR, "Failed to initialise table"); 396 goto free_tbl; 397 } 398 399 pthread_spin_lock(&ctx->ctrl_lock); 400 LIST_INSERT_HEAD(&ctx->head, tbl, next); 401 pthread_spin_unlock(&ctx->ctrl_lock); 402 403 return tbl; 404 405 free_tbl: 406 simple_free(tbl); 407 return NULL; 408 } 409 410 int mlx5dr_table_destroy(struct mlx5dr_table *tbl) 411 { 412 struct mlx5dr_context *ctx = tbl->ctx; 413 414 pthread_spin_lock(&ctx->ctrl_lock); 415 LIST_REMOVE(tbl, next); 416 pthread_spin_unlock(&ctx->ctrl_lock); 417 mlx5dr_table_uninit(tbl); 418 simple_free(tbl); 419 420 return 0; 421 } 422