FILE
bwc_complex.c
File preview with syntax highlighting.
linux-6.19.14/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/bwc_complex.c
1// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB 2/* Copyright (c) 2024 NVIDIA Corporation & Affiliates */ 3 4#include "internal.h" 5 6/* We chain submatchers by applying three rules on a subrule: modify header (to 7 * set register C6), jump to table (to the next submatcher) and the mandatory 8 * last rule. 9 */ 10#define HWS_NUM_CHAIN_ACTIONS 3 11 12static const struct rhashtable_params hws_rules_hash_params = { 13 .key_len = sizeof_field(struct mlx5hws_bwc_complex_subrule_data, 14 match_tag), 15 .key_offset = 16 offsetof(struct mlx5hws_bwc_complex_subrule_data, match_tag), 17 .head_offset = 18 offsetof(struct mlx5hws_bwc_complex_subrule_data, hash_node), 19 .automatic_shrinking = true, .min_size = 1, 20}; 21 22static bool 23hws_match_params_exceeds_definer(struct mlx5hws_context *ctx, 24 u8 match_criteria_enable, 25 struct mlx5hws_match_parameters *mask, 26 bool allow_jumbo) 27{ 28 struct mlx5hws_definer match_layout = {0}; 29 struct mlx5hws_match_template *mt; 30 bool is_complex = false; 31 int ret; 32 33 if (!match_criteria_enable) 34 return false; /* empty matcher */ 35 36 mt = mlx5hws_match_template_create(ctx, 37 mask->match_buf, 38 mask->match_sz, 39 match_criteria_enable); 40 if (!mt) { 41 mlx5hws_err(ctx, "Complex matcher: failed creating match template\n"); 42 return false; 43 } 44 45 ret = mlx5hws_definer_calc_layout(ctx, mt, &match_layout, allow_jumbo); 46 if (ret) { 47 /* The only case that we're interested in is E2BIG, 48 * which means that the match parameters need to be 49 * split into complex martcher. 50 * For all other cases (good or bad) - just return true 51 * and let the usual match creation path handle it, 52 * both for good and bad flows. 53 */ 54 if (ret == -E2BIG) { 55 is_complex = true; 56 mlx5hws_dbg(ctx, "Matcher definer layout: need complex matcher\n"); 57 } else { 58 mlx5hws_err(ctx, "Failed to calculate matcher definer layout\n"); 59 } 60 } else { 61 kfree(mt->fc); 62 } 63 64 mlx5hws_match_template_destroy(mt); 65 66 return is_complex; 67} 68 69bool mlx5hws_bwc_match_params_is_complex(struct mlx5hws_context *ctx, 70 u8 match_criteria_enable, 71 struct mlx5hws_match_parameters *mask) 72{ 73 return hws_match_params_exceeds_definer(ctx, match_criteria_enable, 74 mask, true); 75} 76 77static int 78hws_get_last_set_dword_idx(const struct mlx5hws_match_parameters *mask) 79{ 80 int i; 81 82 for (i = mask->match_sz / 4 - 1; i >= 0; i--) 83 if (mask->match_buf[i]) 84 return i; 85 86 return -1; 87} 88 89static bool hws_match_mask_is_empty(const struct mlx5hws_match_parameters *mask) 90{ 91 return hws_get_last_set_dword_idx(mask) == -1; 92} 93 94static bool hws_dword_is_inner_ipaddr_off(int dword_off) 95{ 96 /* IPv4 and IPv6 addresses share the same entry via a union, and the 97 * source and dest addresses are contiguous in the fte_match_param. So 98 * we need to check 8 words. 99 */ 100 static const int inner_ip_dword_off = 101 __mlx5_dw_off(fte_match_param, inner_headers.src_ipv4_src_ipv6); 102 103 return dword_off >= inner_ip_dword_off && 104 dword_off < inner_ip_dword_off + 8; 105} 106 107static bool hws_dword_is_outer_ipaddr_off(int dword_off) 108{ 109 static const int outer_ip_dword_off = 110 __mlx5_dw_off(fte_match_param, outer_headers.src_ipv4_src_ipv6); 111 112 return dword_off >= outer_ip_dword_off && 113 dword_off < outer_ip_dword_off + 8; 114} 115 116static void hws_add_dword_to_mask(struct mlx5hws_match_parameters *mask, 117 const struct mlx5hws_match_parameters *orig, 118 int dword_idx, bool *added_inner_ipv, 119 bool *added_outer_ipv) 120{ 121 mask->match_buf[dword_idx] |= orig->match_buf[dword_idx]; 122 123 *added_inner_ipv = false; 124 *added_outer_ipv = false; 125 126 /* Any IP address fragment must be accompanied by a match on IP version. 127 * Use the `added_ipv` variables to keep track if we added IP versions 128 * specifically for this dword, so that we can roll them back if the 129 * match params become too large to fit into a definer. 130 */ 131 if (hws_dword_is_inner_ipaddr_off(dword_idx) && 132 !MLX5_GET(fte_match_param, mask->match_buf, 133 inner_headers.ip_version)) { 134 MLX5_SET(fte_match_param, mask->match_buf, 135 inner_headers.ip_version, 0xf); 136 *added_inner_ipv = true; 137 } 138 if (hws_dword_is_outer_ipaddr_off(dword_idx) && 139 !MLX5_GET(fte_match_param, mask->match_buf, 140 outer_headers.ip_version)) { 141 MLX5_SET(fte_match_param, mask->match_buf, 142 outer_headers.ip_version, 0xf); 143 *added_outer_ipv = true; 144 } 145} 146 147static void hws_remove_dword_from_mask(struct mlx5hws_match_parameters *mask, 148 int dword_idx, bool added_inner_ipv, 149 bool added_outer_ipv) 150{ 151 mask->match_buf[dword_idx] = 0; 152 if (added_inner_ipv) 153 MLX5_SET(fte_match_param, mask->match_buf, 154 inner_headers.ip_version, 0); 155 if (added_outer_ipv) 156 MLX5_SET(fte_match_param, mask->match_buf, 157 outer_headers.ip_version, 0); 158} 159 160/* Avoid leaving a single lower dword in `mask` if there are others present in 161 * `orig`. Splitting IPv6 addresses like this causes them to be interpreted as 162 * IPv4. 163 */ 164static void hws_avoid_ipv6_split_of(struct mlx5hws_match_parameters *orig, 165 struct mlx5hws_match_parameters *mask, 166 int off) 167{ 168 /* Masks are allocated to a full fte_match_param, but it can't hurt to 169 * double check. 170 */ 171 if (orig->match_sz <= off + 3 || mask->match_sz <= off + 3) 172 return; 173 174 /* Lower dword is not set, nothing to do. */ 175 if (!mask->match_buf[off + 3]) 176 return; 177 178 /* Higher dwords also present in `mask`, no ambiguity. */ 179 if (mask->match_buf[off] || mask->match_buf[off + 1] || 180 mask->match_buf[off + 2]) 181 return; 182 183 /* There are no higher dwords in `orig`, i.e. we match on IPv4. */ 184 if (!orig->match_buf[off] && !orig->match_buf[off + 1] && 185 !orig->match_buf[off + 2]) 186 return; 187 188 /* Put the lower dword back in `orig`. It is always safe to do this, the 189 * dword will just be picked up in the next submask. 190 */ 191 orig->match_buf[off + 3] = mask->match_buf[off + 3]; 192 mask->match_buf[off + 3] = 0; 193} 194 195static void hws_avoid_ipv6_split(struct mlx5hws_match_parameters *orig, 196 struct mlx5hws_match_parameters *mask) 197{ 198 hws_avoid_ipv6_split_of(orig, mask, 199 __mlx5_dw_off(fte_match_param, 200 outer_headers.src_ipv4_src_ipv6)); 201 hws_avoid_ipv6_split_of(orig, mask, 202 __mlx5_dw_off(fte_match_param, 203 outer_headers.dst_ipv4_dst_ipv6)); 204 hws_avoid_ipv6_split_of(orig, mask, 205 __mlx5_dw_off(fte_match_param, 206 inner_headers.src_ipv4_src_ipv6)); 207 hws_avoid_ipv6_split_of(orig, mask, 208 __mlx5_dw_off(fte_match_param, 209 inner_headers.dst_ipv4_dst_ipv6)); 210} 211 212/* Build a subset of the `orig` match parameters into `mask`. This subset is 213 * guaranteed to fit in a single definer an as such is a candidate for being a 214 * part of a complex matcher. Upon successful execution, the match params that 215 * go into `mask` are cleared from `orig`. 216 */ 217static int hws_get_simple_params(struct mlx5hws_context *ctx, u8 match_criteria, 218 struct mlx5hws_match_parameters *orig, 219 struct mlx5hws_match_parameters *mask) 220{ 221 bool added_inner_ipv, added_outer_ipv; 222 int dword_idx; 223 u32 *backup; 224 int ret; 225 226 dword_idx = hws_get_last_set_dword_idx(orig); 227 /* Nothing to do, we consumed all of the match params before. */ 228 if (dword_idx == -1) 229 return 0; 230 231 backup = kzalloc(MLX5_ST_SZ_BYTES(fte_match_param), GFP_KERNEL); 232 if (!backup) 233 return -ENOMEM; 234 235 while (1) { 236 dword_idx = hws_get_last_set_dword_idx(orig); 237 /* Nothing to do, we consumed all of the original match params 238 * into this subset, which still fits into a single matcher. 239 */ 240 if (dword_idx == -1) { 241 ret = 0; 242 goto free_backup; 243 } 244 245 memcpy(backup, mask->match_buf, mask->match_sz); 246 247 /* Try to add this dword to the current subset. */ 248 hws_add_dword_to_mask(mask, orig, dword_idx, &added_inner_ipv, 249 &added_outer_ipv); 250 251 if (hws_match_params_exceeds_definer(ctx, match_criteria, mask, 252 false)) { 253 /* We just added a match param that makes the definer 254 * too large. Revert and return what we had before. 255 * Note that we can't just zero out the affected fields, 256 * because it's possible that the dword we're looking at 257 * wasn't zero before (e.g. it included auto-added 258 * matches in IP version. This is why we employ the 259 * rather cumbersome memcpy for backing up. 260 */ 261 memcpy(mask->match_buf, backup, mask->match_sz); 262 /* Possible future improvement: We can't add any more 263 * dwords, but it may be possible to squeeze in 264 * individual bytes, as definers have special slots for 265 * those. 266 * 267 * For now, keep the code simple. This results in an 268 * extra submatcher in some cases, but it's good enough. 269 */ 270 ret = 0; 271 break; 272 } 273 274 /* The current subset of match params still fits in a single 275 * definer. Remove the dword from the original mask. 276 * 277 * Also remove any explicit match on IP version if we just 278 * included one here. We will still automatically add it to 279 * accompany any IP address fragment, but do not need to 280 * consider it by itself. 281 */ 282 hws_remove_dword_from_mask(orig, dword_idx, added_inner_ipv, 283 added_outer_ipv); 284 } 285 286 /* Make sure we have not picked up a single lower dword of an IPv6 287 * address, as the firmware will erroneously treat it as an IPv4 288 * address. 289 */ 290 hws_avoid_ipv6_split(orig, mask); 291 292free_backup: 293 kfree(backup); 294 295 return ret; 296} 297 298static int 299hws_bwc_matcher_split_mask(struct mlx5hws_context *ctx, u8 match_criteria, 300 const struct mlx5hws_match_parameters *mask, 301 struct mlx5hws_match_parameters *submasks, 302 int *num_submasks) 303{ 304 struct mlx5hws_match_parameters mask_copy; 305 int ret, i = 0; 306 307 mask_copy.match_sz = MLX5_ST_SZ_BYTES(fte_match_param); 308 mask_copy.match_buf = kzalloc(mask_copy.match_sz, GFP_KERNEL); 309 if (!mask_copy.match_buf) 310 return -ENOMEM; 311 312 memcpy(mask_copy.match_buf, mask->match_buf, mask->match_sz); 313 314 while (!hws_match_mask_is_empty(&mask_copy)) { 315 if (i >= MLX5HWS_BWC_COMPLEX_MAX_SUBMATCHERS) { 316 mlx5hws_err(ctx, 317 "Complex matcher: mask too large for %d matchers\n", 318 MLX5HWS_BWC_COMPLEX_MAX_SUBMATCHERS); 319 ret = -E2BIG; 320 goto free_copy; 321 } 322 /* All but the first matcher need to match on register C6 to 323 * connect pieces of the complex rule together. 324 */ 325 if (i > 0) { 326 MLX5_SET(fte_match_param, submasks[i].match_buf, 327 misc_parameters_2.metadata_reg_c_6, -1); 328 match_criteria |= MLX5HWS_DEFINER_MATCH_CRITERIA_MISC2; 329 } 330 ret = hws_get_simple_params(ctx, match_criteria, &mask_copy, 331 &submasks[i]); 332 if (ret < 0) 333 goto free_copy; 334 i++; 335 } 336 337 *num_submasks = i; 338 ret = 0; 339 340free_copy: 341 kfree(mask_copy.match_buf); 342 343 return ret; 344} 345 346static struct mlx5hws_table * 347hws_isolated_table_create(const struct mlx5hws_bwc_matcher *cmatcher) 348{ 349 struct mlx5hws_bwc_complex_submatcher *first_subm; 350 struct mlx5hws_cmd_ft_modify_attr ft_attr = {0}; 351 struct mlx5hws_table_attr tbl_attr = {0}; 352 struct mlx5hws_table *orig_tbl; 353 struct mlx5hws_context *ctx; 354 struct mlx5hws_table *tbl; 355 int ret; 356 357 first_subm = &cmatcher->complex->submatchers[0]; 358 orig_tbl = first_subm->tbl; 359 ctx = orig_tbl->ctx; 360 361 tbl_attr.type = orig_tbl->type; 362 tbl_attr.level = orig_tbl->level; 363 tbl = mlx5hws_table_create(ctx, &tbl_attr); 364 if (!tbl) 365 return ERR_PTR(-EINVAL); 366 367 /* Set the default miss of the isolated table to point 368 * to the end anchor of the original matcher. 369 */ 370 mlx5hws_cmd_set_attr_connect_miss_tbl(ctx, tbl->fw_ft_type, 371 tbl->type, &ft_attr); 372 ft_attr.table_miss_id = first_subm->bwc_matcher->matcher->end_ft_id; 373 374 ret = mlx5hws_cmd_flow_table_modify(ctx->mdev, &ft_attr, tbl->ft_id); 375 if (ret) { 376 mlx5hws_err(ctx, "Complex matcher: failed to set isolated tbl default miss\n"); 377 goto destroy_tbl; 378 } 379 380 return tbl; 381 382destroy_tbl: 383 mlx5hws_table_destroy(tbl); 384 385 return ERR_PTR(ret); 386} 387 388static int hws_submatcher_init_first(struct mlx5hws_bwc_matcher *cmatcher, 389 struct mlx5hws_table *table, u32 priority, 390 u8 match_criteria, 391 struct mlx5hws_match_parameters *mask) 392{ 393 enum mlx5hws_action_type action_types[HWS_NUM_CHAIN_ACTIONS]; 394 struct mlx5hws_bwc_complex_submatcher *subm; 395 int ret; 396 397 subm = &cmatcher->complex->submatchers[0]; 398 399 /* The first submatcher lives in the original table and does not have an 400 * associated jump to table action. It also points to the outer complex 401 * matcher. 402 */ 403 subm->tbl = table; 404 subm->action_tbl = NULL; 405 subm->bwc_matcher = cmatcher; 406 407 action_types[0] = MLX5HWS_ACTION_TYP_MODIFY_HDR; 408 action_types[1] = MLX5HWS_ACTION_TYP_TBL; 409 action_types[2] = MLX5HWS_ACTION_TYP_LAST; 410 411 ret = mlx5hws_bwc_matcher_create_simple(subm->bwc_matcher, subm->tbl, 412 priority, match_criteria, mask, 413 action_types); 414 if (ret) 415 return ret; 416 417 subm->bwc_matcher->matcher_type = MLX5HWS_BWC_MATCHER_COMPLEX_FIRST; 418 419 ret = rhashtable_init(&subm->rules_hash, &hws_rules_hash_params); 420 if (ret) 421 goto destroy_matcher; 422 mutex_init(&subm->hash_lock); 423 ida_init(&subm->chain_ida); 424 425 return 0; 426 427destroy_matcher: 428 mlx5hws_bwc_matcher_destroy_simple(subm->bwc_matcher); 429 430 return ret; 431} 432 433static int hws_submatcher_init(struct mlx5hws_bwc_matcher *cmatcher, int idx, 434 struct mlx5hws_table *table, u32 priority, 435 u8 match_criteria, 436 struct mlx5hws_match_parameters *mask) 437{ 438 enum mlx5hws_action_type action_types[HWS_NUM_CHAIN_ACTIONS]; 439 struct mlx5hws_bwc_complex_submatcher *subm; 440 bool is_last; 441 int ret; 442 443 if (!idx) 444 return hws_submatcher_init_first(cmatcher, table, priority, 445 match_criteria, mask); 446 447 subm = &cmatcher->complex->submatchers[idx]; 448 is_last = idx == cmatcher->complex->num_submatchers - 1; 449 450 subm->tbl = hws_isolated_table_create(cmatcher); 451 if (IS_ERR(subm->tbl)) 452 return PTR_ERR(subm->tbl); 453 454 subm->action_tbl = 455 mlx5hws_action_create_dest_table(subm->tbl->ctx, subm->tbl, 456 MLX5HWS_ACTION_FLAG_HWS_FDB); 457 if (!subm->action_tbl) { 458 ret = -EINVAL; 459 goto destroy_tbl; 460 } 461 462 subm->bwc_matcher = kzalloc(sizeof(*subm->bwc_matcher), GFP_KERNEL); 463 if (!subm->bwc_matcher) { 464 ret = -ENOMEM; 465 goto destroy_action; 466 } 467 468 /* Every matcher other than the first also matches of register C6 to 469 * bind subrules together in the complex rule using the chain ids. 470 */ 471 match_criteria |= MLX5HWS_DEFINER_MATCH_CRITERIA_MISC2; 472 473 action_types[0] = MLX5HWS_ACTION_TYP_MODIFY_HDR; 474 action_types[1] = MLX5HWS_ACTION_TYP_TBL; 475 action_types[2] = MLX5HWS_ACTION_TYP_LAST; 476 477 /* Every matcher other than the last sets register C6 and jumps to the 478 * next submatcher's table. The final submatcher will use the 479 * user-supplied actions and will attach an action template at rule 480 * insertion time. 481 */ 482 ret = mlx5hws_bwc_matcher_create_simple(subm->bwc_matcher, subm->tbl, 483 priority, match_criteria, mask, 484 is_last ? NULL : action_types); 485 if (ret) 486 goto free_matcher; 487 488 subm->bwc_matcher->matcher_type = 489 MLX5HWS_BWC_MATCHER_COMPLEX_SUBMATCHER; 490 491 ret = rhashtable_init(&subm->rules_hash, &hws_rules_hash_params); 492 if (ret) 493 goto destroy_matcher; 494 mutex_init(&subm->hash_lock); 495 ida_init(&subm->chain_ida); 496 497 return 0; 498 499destroy_matcher: 500 mlx5hws_bwc_matcher_destroy_simple(subm->bwc_matcher); 501free_matcher: 502 kfree(subm->bwc_matcher); 503destroy_action: 504 mlx5hws_action_destroy(subm->action_tbl); 505destroy_tbl: 506 mlx5hws_table_destroy(subm->tbl); 507 508 return ret; 509} 510 511static void hws_submatcher_destroy(struct mlx5hws_bwc_matcher *cmatcher, 512 int idx) 513{ 514 struct mlx5hws_bwc_complex_submatcher *subm; 515 516 subm = &cmatcher->complex->submatchers[idx]; 517 518 ida_destroy(&subm->chain_ida); 519 mutex_destroy(&subm->hash_lock); 520 rhashtable_destroy(&subm->rules_hash); 521 522 if (subm->bwc_matcher) { 523 mlx5hws_bwc_matcher_destroy_simple(subm->bwc_matcher); 524 if (idx) 525 kfree(subm->bwc_matcher); 526 } 527 528 /* We own all of the isolated tables, but not the original one. */ 529 if (idx) { 530 mlx5hws_action_destroy(subm->action_tbl); 531 mlx5hws_table_destroy(subm->tbl); 532 } 533} 534 535static int 536hws_complex_data_actions_init(struct mlx5hws_bwc_matcher_complex_data *cdata) 537{ 538 struct mlx5hws_context *ctx = cdata->submatchers[0].tbl->ctx; 539 u8 modify_hdr_action[MLX5_ST_SZ_BYTES(set_action_in)] = {0}; 540 struct mlx5hws_action_mh_pattern ptrn; 541 int ret = 0; 542 543 /* Create modify header action to set REG_C_6 */ 544 MLX5_SET(set_action_in, modify_hdr_action, 545 action_type, MLX5_MODIFICATION_TYPE_SET); 546 MLX5_SET(set_action_in, modify_hdr_action, 547 field, MLX5_MODI_META_REG_C_6); 548 MLX5_SET(set_action_in, modify_hdr_action, 549 length, 0); /* zero means length of 32 */ 550 MLX5_SET(set_action_in, modify_hdr_action, offset, 0); 551 MLX5_SET(set_action_in, modify_hdr_action, data, 0); 552 553 ptrn.data = (void *)modify_hdr_action; 554 ptrn.sz = MLX5HWS_ACTION_DOUBLE_SIZE; 555 556 cdata->action_metadata = 557 mlx5hws_action_create_modify_header(ctx, 1, &ptrn, 0, 558 MLX5HWS_ACTION_FLAG_HWS_FDB); 559 if (!cdata->action_metadata) { 560 mlx5hws_err(ctx, "Complex matcher: failed to create set reg C6 action\n"); 561 return -EINVAL; 562 } 563 564 /* Create last action */ 565 cdata->action_last = 566 mlx5hws_action_create_last(ctx, MLX5HWS_ACTION_FLAG_HWS_FDB); 567 if (!cdata->action_last) { 568 mlx5hws_err(ctx, "Complex matcher: failed to create last action\n"); 569 ret = -EINVAL; 570 goto destroy_action_metadata; 571 } 572 573 return 0; 574 575destroy_action_metadata: 576 mlx5hws_action_destroy(cdata->action_metadata); 577 578 return ret; 579} 580 581static void 582hws_complex_data_actions_destroy(struct mlx5hws_bwc_matcher_complex_data *cdata) 583{ 584 mlx5hws_action_destroy(cdata->action_last); 585 mlx5hws_action_destroy(cdata->action_metadata); 586} 587 588int mlx5hws_bwc_matcher_create_complex(struct mlx5hws_bwc_matcher *bwc_matcher, 589 struct mlx5hws_table *table, 590 u32 priority, u8 match_criteria_enable, 591 struct mlx5hws_match_parameters *mask) 592{ 593 struct mlx5hws_match_parameters 594 submasks[MLX5HWS_BWC_COMPLEX_MAX_SUBMATCHERS] = {0}; 595 struct mlx5hws_bwc_matcher_complex_data *cdata; 596 struct mlx5hws_context *ctx = table->ctx; 597 int num_submatchers; 598 int i, ret; 599 600 for (i = 0; i < ARRAY_SIZE(submasks); i++) { 601 submasks[i].match_sz = MLX5_ST_SZ_BYTES(fte_match_param); 602 submasks[i].match_buf = kzalloc(submasks[i].match_sz, 603 GFP_KERNEL); 604 if (!submasks[i].match_buf) { 605 ret = -ENOMEM; 606 goto free_submasks; 607 } 608 } 609 610 ret = hws_bwc_matcher_split_mask(ctx, match_criteria_enable, mask, 611 submasks, &num_submatchers); 612 if (ret) 613 goto free_submasks; 614 615 cdata = kzalloc(sizeof(*cdata), GFP_KERNEL); 616 if (!cdata) { 617 ret = -ENOMEM; 618 goto free_submasks; 619 } 620 621 bwc_matcher->complex = cdata; 622 cdata->num_submatchers = num_submatchers; 623 624 for (i = 0; i < num_submatchers; i++) { 625 ret = hws_submatcher_init(bwc_matcher, i, table, priority, 626 match_criteria_enable, &submasks[i]); 627 if (ret) 628 goto destroy_submatchers; 629 } 630 631 ret = hws_complex_data_actions_init(cdata); 632 if (ret) 633 goto destroy_submatchers; 634 635 ret = 0; 636 goto free_submasks; 637 638destroy_submatchers: 639 while (i--) 640 hws_submatcher_destroy(bwc_matcher, i); 641 kfree(cdata); 642 bwc_matcher->complex = NULL; 643 644free_submasks: 645 for (i = 0; i < ARRAY_SIZE(submasks); i++) 646 kfree(submasks[i].match_buf); 647 648 return ret; 649} 650 651void 652mlx5hws_bwc_matcher_destroy_complex(struct mlx5hws_bwc_matcher *bwc_matcher) 653{ 654 int i; 655 656 hws_complex_data_actions_destroy(bwc_matcher->complex); 657 for (i = 0; i < bwc_matcher->complex->num_submatchers; i++) 658 hws_submatcher_destroy(bwc_matcher, i); 659 kfree(bwc_matcher->complex); 660 bwc_matcher->complex = NULL; 661} 662 663static int 664hws_complex_get_subrule_data(struct mlx5hws_bwc_rule *bwc_rule, 665 struct mlx5hws_bwc_complex_submatcher *subm, 666 u32 *match_params) 667__must_hold(&subm->hash_lock) 668{ 669 struct mlx5hws_bwc_matcher *bwc_matcher = subm->bwc_matcher; 670 struct mlx5hws_bwc_complex_subrule_data *sr_data, *old_data; 671 struct mlx5hws_match_template *mt; 672 int ret; 673 674 sr_data = kzalloc(sizeof(*sr_data), GFP_KERNEL); 675 if (!sr_data) 676 return -ENOMEM; 677 678 ret = ida_alloc(&subm->chain_ida, GFP_KERNEL); 679 if (ret < 0) 680 goto free_sr_data; 681 sr_data->chain_id = ret; 682 683 refcount_set(&sr_data->refcount, 1); 684 685 mt = bwc_matcher->matcher->mt; 686 mlx5hws_definer_create_tag(match_params, mt->fc, mt->fc_sz, 687 (u8 *)&sr_data->match_tag); 688 689 old_data = rhashtable_lookup_get_insert_fast(&subm->rules_hash, 690 &sr_data->hash_node, 691 hws_rules_hash_params); 692 if (IS_ERR(old_data)) { 693 ret = PTR_ERR(old_data); 694 goto free_ida; 695 } 696 697 if (old_data) { 698 /* Rule with the same tag already exists - update refcount */ 699 refcount_inc(&old_data->refcount); 700 /* Let the new rule use the same tag as the existing rule. 701 * Note that we don't have any indication for the rule creation 702 * process that a rule with similar matching params already 703 * exists - no harm done when this rule is be overwritten by 704 * the same STE. 705 * There's some performance advantage in skipping such cases, 706 * so this is left for future optimizations. 707 */ 708 bwc_rule->subrule_data = old_data; 709 ret = 0; 710 goto free_ida; 711 } 712 713 bwc_rule->subrule_data = sr_data; 714 return 0; 715 716free_ida: 717 ida_free(&subm->chain_ida, sr_data->chain_id); 718free_sr_data: 719 kfree(sr_data); 720 721 return ret; 722} 723 724static void 725hws_complex_put_subrule_data(struct mlx5hws_bwc_rule *bwc_rule, 726 struct mlx5hws_bwc_complex_submatcher *subm, 727 bool *is_last_rule) 728__must_hold(&subm->hash_lock) 729{ 730 struct mlx5hws_bwc_complex_subrule_data *sr_data; 731 732 if (is_last_rule) 733 *is_last_rule = false; 734 735 sr_data = bwc_rule->subrule_data; 736 if (refcount_dec_and_test(&sr_data->refcount)) { 737 rhashtable_remove_fast(&subm->rules_hash, 738 &sr_data->hash_node, 739 hws_rules_hash_params); 740 ida_free(&subm->chain_ida, sr_data->chain_id); 741 kfree(sr_data); 742 if (is_last_rule) 743 *is_last_rule = true; 744 } 745 746 bwc_rule->subrule_data = NULL; 747} 748 749static int hws_complex_subrule_create(struct mlx5hws_bwc_matcher *cmatcher, 750 struct mlx5hws_bwc_rule *subrule, 751 u32 *match_params, u32 flow_source, 752 int bwc_queue_idx, int subm_idx, 753 struct mlx5hws_rule_action *actions, 754 u32 *chain_id) 755{ 756 struct mlx5hws_rule_action chain_actions[HWS_NUM_CHAIN_ACTIONS] = {0}; 757 u8 modify_hdr_action[MLX5_ST_SZ_BYTES(set_action_in)] = {0}; 758 struct mlx5hws_bwc_matcher_complex_data *cdata; 759 struct mlx5hws_bwc_complex_submatcher *subm; 760 int ret; 761 762 cdata = cmatcher->complex; 763 subm = &cdata->submatchers[subm_idx]; 764 765 mutex_lock(&subm->hash_lock); 766 767 ret = hws_complex_get_subrule_data(subrule, subm, match_params); 768 if (ret) 769 goto unlock; 770 771 *chain_id = subrule->subrule_data->chain_id; 772 773 if (!actions) { 774 MLX5_SET(set_action_in, modify_hdr_action, data, *chain_id); 775 chain_actions[0].action = cdata->action_metadata; 776 chain_actions[0].modify_header.data = modify_hdr_action; 777 chain_actions[1].action = 778 cdata->submatchers[subm_idx + 1].action_tbl; 779 chain_actions[2].action = cdata->action_last; 780 actions = chain_actions; 781 } 782 783 ret = mlx5hws_bwc_rule_create_simple(subrule, match_params, actions, 784 flow_source, bwc_queue_idx); 785 if (ret) 786 goto put_subrule_data; 787 788 ret = 0; 789 goto unlock; 790 791put_subrule_data: 792 hws_complex_put_subrule_data(subrule, subm, NULL); 793unlock: 794 mutex_unlock(&subm->hash_lock); 795 796 return ret; 797} 798 799static int hws_complex_subrule_destroy(struct mlx5hws_bwc_rule *bwc_rule, 800 struct mlx5hws_bwc_matcher *cmatcher, 801 int subm_idx) 802{ 803 struct mlx5hws_bwc_matcher_complex_data *cdata; 804 struct mlx5hws_bwc_complex_submatcher *subm; 805 struct mlx5hws_context *ctx; 806 bool is_last_rule; 807 int ret = 0; 808 809 cdata = cmatcher->complex; 810 subm = &cdata->submatchers[subm_idx]; 811 ctx = subm->tbl->ctx; 812 813 mutex_lock(&subm->hash_lock); 814 815 hws_complex_put_subrule_data(bwc_rule, subm, &is_last_rule); 816 bwc_rule->rule->skip_delete = !is_last_rule; 817 ret = mlx5hws_bwc_rule_destroy_simple(bwc_rule); 818 if (unlikely(ret)) 819 mlx5hws_err(ctx, 820 "Complex rule: failed to delete subrule %d (%d)\n", 821 subm_idx, ret); 822 823 if (subm_idx) 824 mlx5hws_bwc_rule_free(bwc_rule); 825 826 mutex_unlock(&subm->hash_lock); 827 828 return ret; 829} 830 831int mlx5hws_bwc_rule_create_complex(struct mlx5hws_bwc_rule *bwc_rule, 832 struct mlx5hws_match_parameters *params, 833 u32 flow_source, 834 struct mlx5hws_rule_action rule_actions[], 835 u16 bwc_queue_idx) 836{ 837 struct mlx5hws_bwc_rule 838 *subrules[MLX5HWS_BWC_COMPLEX_MAX_SUBMATCHERS] = {0}; 839 struct mlx5hws_bwc_matcher *cmatcher = bwc_rule->bwc_matcher; 840 struct mlx5hws_bwc_matcher_complex_data *cdata; 841 struct mlx5hws_rule_action *subrule_actions; 842 struct mlx5hws_bwc_complex_submatcher *subm; 843 struct mlx5hws_bwc_rule *subrule; 844 u32 *match_params; 845 u32 chain_id; 846 int i, ret; 847 848 cdata = cmatcher->complex; 849 if (!cdata) 850 return -EINVAL; 851 852 /* Duplicate user data because we will modify it to set register C6 853 * values. For the same reason, make sure that we allocate a full 854 * match_param even if the user gave us fewer bytes. We need to ensure 855 * there is space for the match on C6. 856 */ 857 match_params = kzalloc(MLX5_ST_SZ_BYTES(fte_match_param), GFP_KERNEL); 858 if (!match_params) 859 return -ENOMEM; 860 861 memcpy(match_params, params->match_buf, params->match_sz); 862 863 ret = hws_complex_subrule_create(cmatcher, bwc_rule, match_params, 864 flow_source, bwc_queue_idx, 0, 865 NULL, &chain_id); 866 if (ret) 867 goto free_match_params; 868 subrules[0] = bwc_rule; 869 870 for (i = 1; i < cdata->num_submatchers; i++) { 871 subm = &cdata->submatchers[i]; 872 subrule = mlx5hws_bwc_rule_alloc(subm->bwc_matcher); 873 if (!subrule) { 874 ret = -ENOMEM; 875 goto destroy_subrules; 876 } 877 878 /* Match on the previous subrule's chain_id. This is how 879 * subrules are connected in steering. 880 */ 881 MLX5_SET(fte_match_param, match_params, 882 misc_parameters_2.metadata_reg_c_6, chain_id); 883 884 /* The last subrule uses the complex rule's user-specified 885 * actions. Everything else uses the chaining rules based on the 886 * next table and chain_id. 887 */ 888 subrule_actions = 889 i == cdata->num_submatchers - 1 ? rule_actions : NULL; 890 891 ret = hws_complex_subrule_create(cmatcher, subrule, 892 match_params, flow_source, 893 bwc_queue_idx, i, 894 subrule_actions, &chain_id); 895 if (ret) { 896 mlx5hws_bwc_rule_free(subrule); 897 goto destroy_subrules; 898 } 899 900 subrules[i] = subrule; 901 } 902 903 for (i = 0; i < cdata->num_submatchers - 1; i++) 904 subrules[i]->next_subrule = subrules[i + 1]; 905 906 kfree(match_params); 907 908 return 0; 909 910destroy_subrules: 911 while (i--) 912 hws_complex_subrule_destroy(subrules[i], cmatcher, i); 913free_match_params: 914 kfree(match_params); 915 916 return ret; 917} 918 919int mlx5hws_bwc_rule_destroy_complex(struct mlx5hws_bwc_rule *bwc_rule) 920{ 921 struct mlx5hws_bwc_matcher *bwc_matcher = bwc_rule->bwc_matcher; 922 struct mlx5hws_bwc_rule 923 *subrules[MLX5HWS_BWC_COMPLEX_MAX_SUBMATCHERS] = {0}; 924 struct mlx5hws_bwc_matcher_complex_data *cdata; 925 int i, err, ret_val; 926 927 cdata = bwc_matcher->complex; 928 929 /* Construct a list of all the subrules we need to destroy. */ 930 subrules[0] = bwc_rule; 931 for (i = 1; i < cdata->num_submatchers; i++) 932 subrules[i] = subrules[i - 1]->next_subrule; 933 934 ret_val = 0; 935 for (i = 0; i < cdata->num_submatchers; i++) { 936 err = hws_complex_subrule_destroy(subrules[i], bwc_matcher, i); 937 /* If something goes wrong, plow along to destroy all of the 938 * subrules but return an error upstack. 939 */ 940 if (unlikely(err)) 941 ret_val = err; 942 } 943 944 return ret_val; 945} 946 947static void 948hws_bwc_matcher_init_move(struct mlx5hws_bwc_matcher *bwc_matcher) 949{ 950 struct mlx5hws_context *ctx = bwc_matcher->matcher->tbl->ctx; 951 u16 bwc_queues = mlx5hws_bwc_queues(ctx); 952 struct mlx5hws_bwc_rule *bwc_rule; 953 struct list_head *rules_list; 954 int i; 955 956 for (i = 0; i < bwc_queues; i++) { 957 rules_list = &bwc_matcher->rules[i]; 958 if (list_empty(rules_list)) 959 continue; 960 961 list_for_each_entry(bwc_rule, rules_list, list_node) { 962 if (!bwc_rule->subrule_data) 963 continue; 964 bwc_rule->subrule_data->was_moved = false; 965 } 966 } 967} 968 969int mlx5hws_bwc_matcher_complex_move(struct mlx5hws_bwc_matcher *bwc_matcher) 970{ 971 struct mlx5hws_context *ctx = bwc_matcher->matcher->tbl->ctx; 972 struct mlx5hws_matcher *matcher = bwc_matcher->matcher; 973 u16 bwc_queues = mlx5hws_bwc_queues(ctx); 974 struct mlx5hws_bwc_rule *tmp_bwc_rule; 975 struct mlx5hws_rule_attr rule_attr; 976 int move_error = 0, poll_error = 0; 977 struct mlx5hws_rule *tmp_rule; 978 struct list_head *rules_list; 979 u32 expected_completions = 1; 980 int i, ret = 0; 981 982 hws_bwc_matcher_init_move(bwc_matcher); 983 984 mlx5hws_bwc_rule_fill_attr(bwc_matcher, 0, 0, &rule_attr); 985 986 for (i = 0; i < bwc_queues; i++) { 987 rules_list = &bwc_matcher->rules[i]; 988 if (list_empty(rules_list)) 989 continue; 990 991 rule_attr.queue_id = mlx5hws_bwc_get_queue_id(ctx, i); 992 993 list_for_each_entry(tmp_bwc_rule, rules_list, list_node) { 994 /* Check if a rule with similar tag has already 995 * been moved. 996 */ 997 if (tmp_bwc_rule->subrule_data->was_moved) { 998 /* This rule is a duplicate of rule with 999 * identical tag that has already been moved 1000 * earlier. Just update this rule's RTCs. 1001 */ 1002 tmp_bwc_rule->rule->rtc_0 = 1003 tmp_bwc_rule->subrule_data->rtc_0; 1004 tmp_bwc_rule->rule->rtc_1 = 1005 tmp_bwc_rule->subrule_data->rtc_1; 1006 tmp_bwc_rule->rule->matcher = 1007 tmp_bwc_rule->rule->matcher->resize_dst; 1008 continue; 1009 } 1010 1011 /* First time we're moving rule with this tag. 1012 * Move it for real. 1013 */ 1014 tmp_rule = tmp_bwc_rule->rule; 1015 tmp_rule->skip_delete = false; 1016 ret = mlx5hws_matcher_resize_rule_move(matcher, 1017 tmp_rule, 1018 &rule_attr); 1019 if (unlikely(ret)) { 1020 if (!move_error) { 1021 mlx5hws_err(ctx, 1022 "Moving complex BWC rule: move failed (%d), attempting to move rest of the rules\n", 1023 ret); 1024 move_error = ret; 1025 } 1026 /* Rule wasn't queued, no need to poll */ 1027 continue; 1028 } 1029 1030 expected_completions = 1; 1031 ret = mlx5hws_bwc_queue_poll(ctx, 1032 rule_attr.queue_id, 1033 &expected_completions, 1034 true); 1035 if (unlikely(ret)) { 1036 if (ret == -ETIMEDOUT) { 1037 mlx5hws_err(ctx, 1038 "Moving complex BWC rule: timeout polling for completions (%d), aborting rehash\n", 1039 ret); 1040 return ret; 1041 } 1042 if (!poll_error) { 1043 mlx5hws_err(ctx, 1044 "Moving complex BWC rule: polling for completions failed (%d), attempting to move rest of the rules\n", 1045 ret); 1046 poll_error = ret; 1047 } 1048 } 1049 1050 /* Done moving the rule to the new matcher, 1051 * now update RTCs for all the duplicated rules. 1052 */ 1053 tmp_bwc_rule->subrule_data->rtc_0 = 1054 tmp_bwc_rule->rule->rtc_0; 1055 tmp_bwc_rule->subrule_data->rtc_1 = 1056 tmp_bwc_rule->rule->rtc_1; 1057 1058 tmp_bwc_rule->subrule_data->was_moved = true; 1059 } 1060 } 1061 1062 /* Return the first error that happened */ 1063 if (unlikely(move_error)) 1064 return move_error; 1065 if (unlikely(poll_error)) 1066 return poll_error; 1067 1068 return ret; 1069} 1070 1071int 1072mlx5hws_bwc_matcher_complex_move_first(struct mlx5hws_bwc_matcher *bwc_matcher) 1073{ 1074 struct mlx5hws_context *ctx = bwc_matcher->matcher->tbl->ctx; 1075 struct mlx5hws_bwc_matcher_complex_data *cdata; 1076 struct mlx5hws_table *isolated_tbl; 1077 u32 end_ft_id; 1078 int i, ret; 1079 1080 cdata = bwc_matcher->complex; 1081 1082 /* We are rehashing the first submatcher. We need to update the 1083 * subsequent submatchers to point to the end_ft of this new matcher. 1084 * This needs to be done before moving any rules to prevent possible 1085 * steering loops. 1086 */ 1087 end_ft_id = bwc_matcher->matcher->resize_dst->end_ft_id; 1088 for (i = 1; i < cdata->num_submatchers; i++) { 1089 isolated_tbl = cdata->submatchers[i].tbl; 1090 ret = mlx5hws_matcher_update_end_ft_isolated(isolated_tbl, 1091 end_ft_id); 1092 if (ret) { 1093 mlx5hws_err(ctx, 1094 "Complex matcher: failed updating end_ft of isolated matcher (%d)\n", 1095 ret); 1096 return ret; 1097 } 1098 } 1099 1100 return mlx5hws_bwc_matcher_complex_move(bwc_matcher); 1101}
~/
Read-only file preview.