FILE
table.c
File preview with syntax highlighting.
linux-6.19.14/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/table.c
1// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB 2/* Copyright (c) 2024 NVIDIA Corporation & Affiliates */ 3 4#include "internal.h" 5 6u32 mlx5hws_table_get_id(struct mlx5hws_table *tbl) 7{ 8 return tbl->ft_id; 9} 10 11static void hws_table_init_next_ft_attr(struct mlx5hws_table *tbl, 12 u16 uid, 13 struct mlx5hws_cmd_ft_create_attr *ft_attr) 14{ 15 ft_attr->type = tbl->fw_ft_type; 16 if (tbl->type == MLX5HWS_TABLE_TYPE_FDB) 17 ft_attr->level = tbl->ctx->caps->fdb_ft.max_level - 1; 18 else 19 ft_attr->level = tbl->ctx->caps->nic_ft.max_level - 1; 20 21 ft_attr->rtc_valid = true; 22 ft_attr->uid = uid; 23} 24 25static void hws_table_set_cap_attr(struct mlx5hws_table *tbl, 26 struct mlx5hws_cmd_ft_create_attr *ft_attr) 27{ 28 /* Enabling reformat_en or decap_en for the first flow table 29 * must be done when all VFs are down. 30 * However, HWS doesn't know when it is required to create the first FT. 31 * On the other hand, HWS doesn't use all these FT capabilities at all 32 * (the API doesn't even provide a way to specify these flags), so we'll 33 * just set these caps on all the flow tables. 34 * If HCA_CAP.fdb_dynamic_tunnel is set, this constraint is N/A. 35 */ 36 if (!MLX5_CAP_ESW_FLOWTABLE(tbl->ctx->mdev, fdb_dynamic_tunnel)) { 37 ft_attr->reformat_en = true; 38 ft_attr->decap_en = true; 39 } 40} 41 42static int hws_table_up_default_fdb_miss_tbl(struct mlx5hws_table *tbl) 43__must_hold(&tbl->ctx->ctrl_lock) 44{ 45 struct mlx5hws_cmd_ft_create_attr ft_attr = {0}; 46 struct mlx5hws_cmd_set_fte_attr fte_attr = {0}; 47 struct mlx5hws_cmd_forward_tbl *default_miss; 48 struct mlx5hws_cmd_set_fte_dest dest = {0}; 49 struct mlx5hws_context *ctx = tbl->ctx; 50 u8 tbl_type = tbl->type; 51 52 if (tbl->type != MLX5HWS_TABLE_TYPE_FDB) 53 return 0; 54 55 if (ctx->common_res.default_miss) { 56 ctx->common_res.default_miss->refcount++; 57 return 0; 58 } 59 60 ft_attr.type = tbl->fw_ft_type; 61 ft_attr.level = tbl->ctx->caps->fdb_ft.max_level; /* The last level */ 62 ft_attr.rtc_valid = false; 63 64 dest.destination_type = MLX5_FLOW_DESTINATION_TYPE_VPORT; 65 dest.destination_id = ctx->caps->eswitch_manager_vport_number; 66 67 fte_attr.action_flags = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST; 68 fte_attr.dests_num = 1; 69 fte_attr.dests = &dest; 70 71 default_miss = mlx5hws_cmd_forward_tbl_create(ctx->mdev, &ft_attr, &fte_attr); 72 if (!default_miss) { 73 mlx5hws_err(ctx, "Failed to default miss table type: 0x%x\n", tbl_type); 74 return -EINVAL; 75 } 76 77 ctx->common_res.default_miss = default_miss; 78 ctx->common_res.default_miss->refcount++; 79 80 return 0; 81} 82 83/* Called under ctx->ctrl_lock */ 84static void hws_table_down_default_fdb_miss_tbl(struct mlx5hws_table *tbl) 85__must_hold(&tbl->ctx->ctrl_lock) 86{ 87 struct mlx5hws_cmd_forward_tbl *default_miss; 88 struct mlx5hws_context *ctx = tbl->ctx; 89 90 if (tbl->type != MLX5HWS_TABLE_TYPE_FDB) 91 return; 92 93 default_miss = ctx->common_res.default_miss; 94 if (--default_miss->refcount) 95 return; 96 97 mlx5hws_cmd_forward_tbl_destroy(ctx->mdev, default_miss); 98 ctx->common_res.default_miss = NULL; 99} 100 101static int hws_table_connect_to_default_miss_tbl(struct mlx5hws_table *tbl, u32 ft_id) 102{ 103 struct mlx5hws_cmd_ft_modify_attr ft_attr = {0}; 104 int ret; 105 106 if (unlikely(tbl->type != MLX5HWS_TABLE_TYPE_FDB)) 107 pr_warn("HWS: invalid table type %d\n", tbl->type); 108 109 mlx5hws_cmd_set_attr_connect_miss_tbl(tbl->ctx, 110 tbl->fw_ft_type, 111 tbl->type, 112 &ft_attr); 113 114 ret = mlx5hws_cmd_flow_table_modify(tbl->ctx->mdev, &ft_attr, ft_id); 115 if (ret) { 116 mlx5hws_err(tbl->ctx, "Failed to connect FT to default FDB FT\n"); 117 return ret; 118 } 119 120 return 0; 121} 122 123int mlx5hws_table_create_default_ft(struct mlx5_core_dev *mdev, 124 struct mlx5hws_table *tbl, 125 u16 uid, u32 *ft_id) 126{ 127 struct mlx5hws_cmd_ft_create_attr ft_attr = {0}; 128 int ret; 129 130 hws_table_init_next_ft_attr(tbl, uid, &ft_attr); 131 hws_table_set_cap_attr(tbl, &ft_attr); 132 133 ret = mlx5hws_cmd_flow_table_create(mdev, &ft_attr, ft_id); 134 if (ret) { 135 mlx5hws_err(tbl->ctx, "Failed creating default ft\n"); 136 return ret; 137 } 138 139 if (tbl->type == MLX5HWS_TABLE_TYPE_FDB) { 140 /* Take/create ref over the default miss */ 141 ret = hws_table_up_default_fdb_miss_tbl(tbl); 142 if (ret) { 143 mlx5hws_err(tbl->ctx, "Failed to get default fdb miss\n"); 144 goto free_ft_obj; 145 } 146 ret = hws_table_connect_to_default_miss_tbl(tbl, *ft_id); 147 if (ret) { 148 mlx5hws_err(tbl->ctx, "Failed connecting to default miss tbl\n"); 149 goto down_miss_tbl; 150 } 151 } 152 153 return 0; 154 155down_miss_tbl: 156 hws_table_down_default_fdb_miss_tbl(tbl); 157free_ft_obj: 158 mlx5hws_cmd_flow_table_destroy(mdev, ft_attr.type, *ft_id); 159 return ret; 160} 161 162void mlx5hws_table_destroy_default_ft(struct mlx5hws_table *tbl, 163 u32 ft_id) 164{ 165 mlx5hws_cmd_flow_table_destroy(tbl->ctx->mdev, tbl->fw_ft_type, ft_id); 166 hws_table_down_default_fdb_miss_tbl(tbl); 167} 168 169static int hws_table_init_check_hws_support(struct mlx5hws_context *ctx, 170 struct mlx5hws_table *tbl) 171{ 172 if (!(ctx->flags & MLX5HWS_CONTEXT_FLAG_HWS_SUPPORT)) { 173 mlx5hws_err(ctx, "HWS not supported, cannot create mlx5hws_table\n"); 174 return -EOPNOTSUPP; 175 } 176 177 return 0; 178} 179 180static int hws_table_init(struct mlx5hws_table *tbl) 181{ 182 struct mlx5hws_context *ctx = tbl->ctx; 183 int ret; 184 185 ret = hws_table_init_check_hws_support(ctx, tbl); 186 if (ret) 187 return ret; 188 189 if (mlx5hws_table_get_fw_ft_type(tbl->type, (u8 *)&tbl->fw_ft_type)) { 190 pr_warn("HWS: invalid table type %d\n", tbl->type); 191 return -EOPNOTSUPP; 192 } 193 194 mutex_lock(&ctx->ctrl_lock); 195 ret = mlx5hws_table_create_default_ft(tbl->ctx->mdev, 196 tbl, 197 tbl->uid, 198 &tbl->ft_id); 199 if (ret) { 200 mlx5hws_err(tbl->ctx, "Failed to create flow table object\n"); 201 mutex_unlock(&ctx->ctrl_lock); 202 return ret; 203 } 204 205 ret = mlx5hws_action_get_default_stc(ctx, tbl->type); 206 if (ret) 207 goto tbl_destroy; 208 209 INIT_LIST_HEAD(&tbl->matchers_list); 210 INIT_LIST_HEAD(&tbl->default_miss.head); 211 212 mutex_unlock(&ctx->ctrl_lock); 213 214 return 0; 215 216tbl_destroy: 217 mlx5hws_table_destroy_default_ft(tbl, tbl->ft_id); 218 mutex_unlock(&ctx->ctrl_lock); 219 return ret; 220} 221 222static void hws_table_uninit(struct mlx5hws_table *tbl) 223{ 224 mutex_lock(&tbl->ctx->ctrl_lock); 225 mlx5hws_action_put_default_stc(tbl->ctx, tbl->type); 226 mlx5hws_table_destroy_default_ft(tbl, tbl->ft_id); 227 mutex_unlock(&tbl->ctx->ctrl_lock); 228} 229 230struct mlx5hws_table *mlx5hws_table_create(struct mlx5hws_context *ctx, 231 struct mlx5hws_table_attr *attr) 232{ 233 struct mlx5hws_table *tbl; 234 int ret; 235 236 if (attr->type > MLX5HWS_TABLE_TYPE_FDB) { 237 mlx5hws_err(ctx, "Invalid table type %d\n", attr->type); 238 return NULL; 239 } 240 241 tbl = kzalloc(sizeof(*tbl), GFP_KERNEL); 242 if (!tbl) 243 return NULL; 244 245 tbl->ctx = ctx; 246 tbl->type = attr->type; 247 tbl->level = attr->level; 248 tbl->uid = attr->uid; 249 250 ret = hws_table_init(tbl); 251 if (ret) { 252 mlx5hws_err(ctx, "Failed to initialise table\n"); 253 goto free_tbl; 254 } 255 256 mutex_lock(&ctx->ctrl_lock); 257 list_add(&tbl->tbl_list_node, &ctx->tbl_list); 258 mutex_unlock(&ctx->ctrl_lock); 259 260 return tbl; 261 262free_tbl: 263 kfree(tbl); 264 return NULL; 265} 266 267int mlx5hws_table_destroy(struct mlx5hws_table *tbl) 268{ 269 struct mlx5hws_context *ctx = tbl->ctx; 270 int ret; 271 272 mutex_lock(&ctx->ctrl_lock); 273 if (!list_empty(&tbl->matchers_list)) { 274 mlx5hws_err(tbl->ctx, "Cannot destroy table containing matchers\n"); 275 ret = -EBUSY; 276 goto unlock_err; 277 } 278 279 if (!list_empty(&tbl->default_miss.head)) { 280 mlx5hws_err(tbl->ctx, "Cannot destroy table pointed by default miss\n"); 281 ret = -EBUSY; 282 goto unlock_err; 283 } 284 285 list_del_init(&tbl->tbl_list_node); 286 mutex_unlock(&ctx->ctrl_lock); 287 288 hws_table_uninit(tbl); 289 kfree(tbl); 290 291 return 0; 292 293unlock_err: 294 mutex_unlock(&ctx->ctrl_lock); 295 return ret; 296} 297 298static u32 hws_table_get_last_ft(struct mlx5hws_table *tbl) 299{ 300 struct mlx5hws_matcher *matcher; 301 302 if (list_empty(&tbl->matchers_list)) 303 return tbl->ft_id; 304 305 matcher = list_last_entry(&tbl->matchers_list, struct mlx5hws_matcher, list_node); 306 return matcher->end_ft_id; 307} 308 309int mlx5hws_table_ft_set_default_next_ft(struct mlx5hws_table *tbl, u32 ft_id) 310{ 311 struct mlx5hws_cmd_ft_modify_attr ft_attr = {0}; 312 int ret; 313 314 /* Due to FW limitation, resetting the flow table to default action will 315 * disconnect RTC when ignore_flow_level_rtc_valid is not supported. 316 */ 317 if (!tbl->ctx->caps->nic_ft.ignore_flow_level_rtc_valid) 318 return 0; 319 320 if (tbl->type == MLX5HWS_TABLE_TYPE_FDB) 321 return hws_table_connect_to_default_miss_tbl(tbl, ft_id); 322 323 ft_attr.type = tbl->fw_ft_type; 324 ft_attr.modify_fs = MLX5_IFC_MODIFY_FLOW_TABLE_MISS_ACTION; 325 ft_attr.table_miss_action = MLX5_IFC_MODIFY_FLOW_TABLE_MISS_ACTION_DEFAULT; 326 327 ret = mlx5hws_cmd_flow_table_modify(tbl->ctx->mdev, &ft_attr, ft_id); 328 if (ret) { 329 mlx5hws_err(tbl->ctx, "Failed to set FT default miss action\n"); 330 return ret; 331 } 332 333 return 0; 334} 335 336int mlx5hws_table_ft_set_next_rtc(struct mlx5hws_context *ctx, 337 u32 ft_id, 338 u32 fw_ft_type, 339 u32 rtc_0_id, 340 u32 rtc_1_id) 341{ 342 struct mlx5hws_cmd_ft_modify_attr ft_attr = {0}; 343 344 ft_attr.modify_fs = MLX5_IFC_MODIFY_FLOW_TABLE_RTC_ID; 345 ft_attr.type = fw_ft_type; 346 ft_attr.rtc_id_0 = rtc_0_id; 347 ft_attr.rtc_id_1 = rtc_1_id; 348 349 return mlx5hws_cmd_flow_table_modify(ctx->mdev, &ft_attr, ft_id); 350} 351 352int mlx5hws_table_ft_set_next_ft(struct mlx5hws_context *ctx, 353 u32 ft_id, 354 u32 fw_ft_type, 355 u32 next_ft_id) 356{ 357 struct mlx5hws_cmd_ft_modify_attr ft_attr = {0}; 358 359 ft_attr.modify_fs = MLX5_IFC_MODIFY_FLOW_TABLE_MISS_ACTION; 360 ft_attr.table_miss_action = MLX5_IFC_MODIFY_FLOW_TABLE_MISS_ACTION_GOTO_TBL; 361 ft_attr.type = fw_ft_type; 362 ft_attr.table_miss_id = next_ft_id; 363 364 return mlx5hws_cmd_flow_table_modify(ctx->mdev, &ft_attr, ft_id); 365} 366 367int mlx5hws_table_update_connected_miss_tables(struct mlx5hws_table *dst_tbl) 368{ 369 struct mlx5hws_table *src_tbl; 370 int ret; 371 372 if (list_empty(&dst_tbl->default_miss.head)) 373 return 0; 374 375 list_for_each_entry(src_tbl, &dst_tbl->default_miss.head, default_miss.next) { 376 ret = mlx5hws_table_connect_to_miss_table(src_tbl, dst_tbl); 377 if (ret) { 378 mlx5hws_err(dst_tbl->ctx, 379 "Failed to update source miss table, unexpected behavior\n"); 380 return ret; 381 } 382 } 383 384 return 0; 385} 386 387int mlx5hws_table_connect_to_miss_table(struct mlx5hws_table *src_tbl, 388 struct mlx5hws_table *dst_tbl) 389{ 390 struct mlx5hws_matcher *matcher; 391 u32 last_ft_id; 392 int ret; 393 394 last_ft_id = hws_table_get_last_ft(src_tbl); 395 396 if (dst_tbl) { 397 if (list_empty(&dst_tbl->matchers_list)) { 398 /* Connect src_tbl last_ft to dst_tbl start anchor */ 399 ret = mlx5hws_table_ft_set_next_ft(src_tbl->ctx, 400 last_ft_id, 401 src_tbl->fw_ft_type, 402 dst_tbl->ft_id); 403 if (ret) 404 return ret; 405 406 /* Reset last_ft RTC to default RTC */ 407 ret = mlx5hws_table_ft_set_next_rtc(src_tbl->ctx, 408 last_ft_id, 409 src_tbl->fw_ft_type, 410 0, 0); 411 if (ret) 412 return ret; 413 } else { 414 /* Connect src_tbl last_ft to first matcher RTC */ 415 matcher = list_first_entry(&dst_tbl->matchers_list, 416 struct mlx5hws_matcher, 417 list_node); 418 ret = mlx5hws_table_ft_set_next_rtc(src_tbl->ctx, 419 last_ft_id, 420 src_tbl->fw_ft_type, 421 matcher->match_ste.rtc_0_id, 422 matcher->match_ste.rtc_1_id); 423 if (ret) 424 return ret; 425 426 /* Reset next miss FT to default */ 427 ret = mlx5hws_table_ft_set_default_next_ft(src_tbl, last_ft_id); 428 if (ret) 429 return ret; 430 } 431 } else { 432 /* Reset next miss FT to default */ 433 ret = mlx5hws_table_ft_set_default_next_ft(src_tbl, last_ft_id); 434 if (ret) 435 return ret; 436 437 /* Reset last_ft RTC to default RTC */ 438 ret = mlx5hws_table_ft_set_next_rtc(src_tbl->ctx, 439 last_ft_id, 440 src_tbl->fw_ft_type, 441 0, 0); 442 if (ret) 443 return ret; 444 } 445 446 src_tbl->default_miss.miss_tbl = dst_tbl; 447 448 return 0; 449} 450 451static int hws_table_set_default_miss_not_valid(struct mlx5hws_table *tbl, 452 struct mlx5hws_table *miss_tbl) 453{ 454 if (!tbl->ctx->caps->nic_ft.ignore_flow_level_rtc_valid) { 455 mlx5hws_err(tbl->ctx, "Default miss table is not supported\n"); 456 return -EOPNOTSUPP; 457 } 458 459 if ((miss_tbl && miss_tbl->type != tbl->type)) { 460 mlx5hws_err(tbl->ctx, "Invalid arguments\n"); 461 return -EINVAL; 462 } 463 464 return 0; 465} 466 467int mlx5hws_table_set_default_miss(struct mlx5hws_table *tbl, 468 struct mlx5hws_table *miss_tbl) 469{ 470 struct mlx5hws_context *ctx = tbl->ctx; 471 struct mlx5hws_table *old_miss_tbl; 472 int ret; 473 474 ret = hws_table_set_default_miss_not_valid(tbl, miss_tbl); 475 if (ret) 476 return ret; 477 478 mutex_lock(&ctx->ctrl_lock); 479 480 old_miss_tbl = tbl->default_miss.miss_tbl; 481 ret = mlx5hws_table_connect_to_miss_table(tbl, miss_tbl); 482 if (ret) 483 goto out; 484 485 if (old_miss_tbl) 486 list_del_init(&tbl->default_miss.next); 487 488 if (miss_tbl) 489 list_add(&tbl->default_miss.next, &miss_tbl->default_miss.head); 490 491out: 492 mutex_unlock(&ctx->ctrl_lock); 493 return ret; 494}
~/
Read-only file preview.