1 // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB 2 /* Copyright (c) 2019 Mellanox Technologies */ 3 4 #include <linux/mlx5/vport.h> 5 #include "mlx5_core.h" 6 #include "fs_core.h" 7 #include "fs_cmd.h" 8 #include "mlx5dr.h" 9 #include "fs_dr.h" 10 11 static bool mlx5_dr_is_fw_table(u32 flags) 12 { 13 if (flags & MLX5_FLOW_TABLE_TERMINATION) 14 return true; 15 16 return false; 17 } 18 19 static int mlx5_cmd_dr_update_root_ft(struct mlx5_flow_root_namespace *ns, 20 struct mlx5_flow_table *ft, 21 u32 underlay_qpn, 22 bool disconnect) 23 { 24 return mlx5_fs_cmd_get_fw_cmds()->update_root_ft(ns, ft, underlay_qpn, 25 disconnect); 26 } 27 28 static int set_miss_action(struct mlx5_flow_root_namespace *ns, 29 struct mlx5_flow_table *ft, 30 struct mlx5_flow_table *next_ft) 31 { 32 struct mlx5dr_action *old_miss_action; 33 struct mlx5dr_action *action = NULL; 34 struct mlx5dr_table *next_tbl; 35 int err; 36 37 next_tbl = next_ft ? next_ft->fs_dr_table.dr_table : NULL; 38 if (next_tbl) { 39 action = mlx5dr_action_create_dest_table(next_tbl); 40 if (!action) 41 return -EINVAL; 42 } 43 old_miss_action = ft->fs_dr_table.miss_action; 44 err = mlx5dr_table_set_miss_action(ft->fs_dr_table.dr_table, action); 45 if (err && action) { 46 err = mlx5dr_action_destroy(action); 47 if (err) { 48 action = NULL; 49 mlx5_core_err(ns->dev, "Failed to destroy action (%d)\n", 50 err); 51 } 52 } 53 ft->fs_dr_table.miss_action = action; 54 if (old_miss_action) { 55 err = mlx5dr_action_destroy(old_miss_action); 56 if (err) 57 mlx5_core_err(ns->dev, "Failed to destroy action (%d)\n", 58 err); 59 } 60 61 return err; 62 } 63 64 static int mlx5_cmd_dr_create_flow_table(struct mlx5_flow_root_namespace *ns, 65 struct mlx5_flow_table *ft, 66 unsigned int size, 67 struct mlx5_flow_table *next_ft) 68 { 69 struct mlx5dr_table *tbl; 70 u32 flags; 71 int err; 72 73 if (mlx5_dr_is_fw_table(ft->flags)) 74 return mlx5_fs_cmd_get_fw_cmds()->create_flow_table(ns, ft, 75 size, 76 next_ft); 77 flags = ft->flags; 78 /* turn off encap/decap if not supported for sw-str by fw */ 79 if (!MLX5_CAP_FLOWTABLE(ns->dev, sw_owner_reformat_supported)) 80 flags = ft->flags & ~(MLX5_FLOW_TABLE_TUNNEL_EN_REFORMAT | 81 MLX5_FLOW_TABLE_TUNNEL_EN_DECAP); 82 83 tbl = mlx5dr_table_create(ns->fs_dr_domain.dr_domain, ft->level, flags); 84 if (!tbl) { 85 mlx5_core_err(ns->dev, "Failed creating dr flow_table\n"); 86 return -EINVAL; 87 } 88 89 ft->fs_dr_table.dr_table = tbl; 90 ft->id = mlx5dr_table_get_id(tbl); 91 92 if (next_ft) { 93 err = set_miss_action(ns, ft, next_ft); 94 if (err) { 95 mlx5dr_table_destroy(tbl); 96 ft->fs_dr_table.dr_table = NULL; 97 return err; 98 } 99 } 100 101 ft->max_fte = INT_MAX; 102 103 return 0; 104 } 105 106 static int mlx5_cmd_dr_destroy_flow_table(struct mlx5_flow_root_namespace *ns, 107 struct mlx5_flow_table *ft) 108 { 109 struct mlx5dr_action *action = ft->fs_dr_table.miss_action; 110 int err; 111 112 if (mlx5_dr_is_fw_table(ft->flags)) 113 return mlx5_fs_cmd_get_fw_cmds()->destroy_flow_table(ns, ft); 114 115 err = mlx5dr_table_destroy(ft->fs_dr_table.dr_table); 116 if (err) { 117 mlx5_core_err(ns->dev, "Failed to destroy flow_table (%d)\n", 118 err); 119 return err; 120 } 121 if (action) { 122 err = mlx5dr_action_destroy(action); 123 if (err) { 124 mlx5_core_err(ns->dev, "Failed to destroy action(%d)\n", 125 err); 126 return err; 127 } 128 } 129 130 return err; 131 } 132 133 static int mlx5_cmd_dr_modify_flow_table(struct mlx5_flow_root_namespace *ns, 134 struct mlx5_flow_table *ft, 135 struct mlx5_flow_table *next_ft) 136 { 137 if (mlx5_dr_is_fw_table(ft->flags)) 138 return mlx5_fs_cmd_get_fw_cmds()->modify_flow_table(ns, ft, next_ft); 139 140 return set_miss_action(ns, ft, next_ft); 141 } 142 143 static int mlx5_cmd_dr_create_flow_group(struct mlx5_flow_root_namespace *ns, 144 struct mlx5_flow_table *ft, 145 u32 *in, 146 struct mlx5_flow_group *fg) 147 { 148 struct mlx5dr_matcher *matcher; 149 u32 priority = MLX5_GET(create_flow_group_in, in, 150 start_flow_index); 151 u8 match_criteria_enable = MLX5_GET(create_flow_group_in, 152 in, 153 match_criteria_enable); 154 struct mlx5dr_match_parameters mask; 155 156 if (mlx5_dr_is_fw_table(ft->flags)) 157 return mlx5_fs_cmd_get_fw_cmds()->create_flow_group(ns, ft, in, 158 fg); 159 160 mask.match_buf = MLX5_ADDR_OF(create_flow_group_in, 161 in, match_criteria); 162 mask.match_sz = sizeof(fg->mask.match_criteria); 163 164 matcher = mlx5dr_matcher_create(ft->fs_dr_table.dr_table, 165 priority, 166 match_criteria_enable, 167 &mask); 168 if (!matcher) { 169 mlx5_core_err(ns->dev, "Failed creating matcher\n"); 170 return -EINVAL; 171 } 172 173 fg->fs_dr_matcher.dr_matcher = matcher; 174 return 0; 175 } 176 177 static int mlx5_cmd_dr_destroy_flow_group(struct mlx5_flow_root_namespace *ns, 178 struct mlx5_flow_table *ft, 179 struct mlx5_flow_group *fg) 180 { 181 if (mlx5_dr_is_fw_table(ft->flags)) 182 return mlx5_fs_cmd_get_fw_cmds()->destroy_flow_group(ns, ft, fg); 183 184 return mlx5dr_matcher_destroy(fg->fs_dr_matcher.dr_matcher); 185 } 186 187 static struct mlx5dr_action *create_vport_action(struct mlx5dr_domain *domain, 188 struct mlx5_flow_rule *dst) 189 { 190 struct mlx5_flow_destination *dest_attr = &dst->dest_attr; 191 192 return mlx5dr_action_create_dest_vport(domain, dest_attr->vport.num, 193 dest_attr->vport.flags & 194 MLX5_FLOW_DEST_VPORT_VHCA_ID, 195 dest_attr->vport.vhca_id); 196 } 197 198 static struct mlx5dr_action *create_uplink_action(struct mlx5dr_domain *domain, 199 struct mlx5_flow_rule *dst) 200 { 201 struct mlx5_flow_destination *dest_attr = &dst->dest_attr; 202 203 return mlx5dr_action_create_dest_vport(domain, MLX5_VPORT_UPLINK, 1, 204 dest_attr->vport.vhca_id); 205 } 206 207 static struct mlx5dr_action *create_ft_action(struct mlx5dr_domain *domain, 208 struct mlx5_flow_rule *dst) 209 { 210 struct mlx5_flow_table *dest_ft = dst->dest_attr.ft; 211 212 if (mlx5_dr_is_fw_table(dest_ft->flags)) 213 return mlx5dr_action_create_dest_flow_fw_table(domain, dest_ft); 214 return mlx5dr_action_create_dest_table(dest_ft->fs_dr_table.dr_table); 215 } 216 217 static struct mlx5dr_action *create_action_push_vlan(struct mlx5dr_domain *domain, 218 struct mlx5_fs_vlan *vlan) 219 { 220 u16 n_ethtype = vlan->ethtype; 221 u8 prio = vlan->prio; 222 u16 vid = vlan->vid; 223 u32 vlan_hdr; 224 225 vlan_hdr = (u32)n_ethtype << 16 | (u32)(prio) << 12 | (u32)vid; 226 return mlx5dr_action_create_push_vlan(domain, htonl(vlan_hdr)); 227 } 228 229 static bool contain_vport_reformat_action(struct mlx5_flow_rule *dst) 230 { 231 return (dst->dest_attr.type == MLX5_FLOW_DESTINATION_TYPE_VPORT || 232 dst->dest_attr.type == MLX5_FLOW_DESTINATION_TYPE_UPLINK) && 233 dst->dest_attr.vport.flags & MLX5_FLOW_DEST_VPORT_REFORMAT_ID; 234 } 235 236 #define MLX5_FLOW_CONTEXT_ACTION_MAX 32 237 static int mlx5_cmd_dr_create_fte(struct mlx5_flow_root_namespace *ns, 238 struct mlx5_flow_table *ft, 239 struct mlx5_flow_group *group, 240 struct fs_fte *fte) 241 { 242 struct mlx5dr_domain *domain = ns->fs_dr_domain.dr_domain; 243 struct mlx5dr_action_dest *term_actions; 244 struct mlx5dr_match_parameters params; 245 struct mlx5_core_dev *dev = ns->dev; 246 struct mlx5dr_action **fs_dr_actions; 247 struct mlx5dr_action *tmp_action; 248 struct mlx5dr_action **actions; 249 bool delay_encap_set = false; 250 struct mlx5dr_rule *rule; 251 struct mlx5_flow_rule *dst; 252 int fs_dr_num_actions = 0; 253 int num_term_actions = 0; 254 int num_actions = 0; 255 size_t match_sz; 256 int err = 0; 257 int i; 258 259 if (mlx5_dr_is_fw_table(ft->flags)) 260 return mlx5_fs_cmd_get_fw_cmds()->create_fte(ns, ft, group, fte); 261 262 actions = kcalloc(MLX5_FLOW_CONTEXT_ACTION_MAX, sizeof(*actions), 263 GFP_KERNEL); 264 if (!actions) { 265 err = -ENOMEM; 266 goto out_err; 267 } 268 269 fs_dr_actions = kcalloc(MLX5_FLOW_CONTEXT_ACTION_MAX, 270 sizeof(*fs_dr_actions), GFP_KERNEL); 271 if (!fs_dr_actions) { 272 err = -ENOMEM; 273 goto free_actions_alloc; 274 } 275 276 term_actions = kcalloc(MLX5_FLOW_CONTEXT_ACTION_MAX, 277 sizeof(*term_actions), GFP_KERNEL); 278 if (!term_actions) { 279 err = -ENOMEM; 280 goto free_fs_dr_actions_alloc; 281 } 282 283 match_sz = sizeof(fte->val); 284 285 /* Drop reformat action bit if destination vport set with reformat */ 286 if (fte->action.action & MLX5_FLOW_CONTEXT_ACTION_FWD_DEST) { 287 list_for_each_entry(dst, &fte->node.children, node.list) { 288 if (!contain_vport_reformat_action(dst)) 289 continue; 290 291 fte->action.action &= ~MLX5_FLOW_CONTEXT_ACTION_PACKET_REFORMAT; 292 break; 293 } 294 } 295 296 /* The order of the actions are must to be keep, only the following 297 * order is supported by SW steering: 298 * TX: modify header -> push vlan -> encap 299 * RX: decap -> pop vlan -> modify header 300 */ 301 if (fte->action.action & MLX5_FLOW_CONTEXT_ACTION_DECAP) { 302 enum mlx5dr_action_reformat_type decap_type = 303 DR_ACTION_REFORMAT_TYP_TNL_L2_TO_L2; 304 305 tmp_action = mlx5dr_action_create_packet_reformat(domain, 306 decap_type, 307 0, 0, 0, 308 NULL); 309 if (!tmp_action) { 310 err = -ENOMEM; 311 goto free_actions; 312 } 313 fs_dr_actions[fs_dr_num_actions++] = tmp_action; 314 actions[num_actions++] = tmp_action; 315 } 316 317 if (fte->action.action & MLX5_FLOW_CONTEXT_ACTION_PACKET_REFORMAT) { 318 bool is_decap = fte->action.pkt_reformat->reformat_type == 319 MLX5_REFORMAT_TYPE_L3_TUNNEL_TO_L2; 320 321 if (is_decap) 322 actions[num_actions++] = 323 fte->action.pkt_reformat->action.dr_action; 324 else 325 delay_encap_set = true; 326 } 327 328 if (fte->action.action & MLX5_FLOW_CONTEXT_ACTION_VLAN_POP) { 329 tmp_action = 330 mlx5dr_action_create_pop_vlan(); 331 if (!tmp_action) { 332 err = -ENOMEM; 333 goto free_actions; 334 } 335 fs_dr_actions[fs_dr_num_actions++] = tmp_action; 336 actions[num_actions++] = tmp_action; 337 } 338 339 if (fte->action.action & MLX5_FLOW_CONTEXT_ACTION_VLAN_POP_2) { 340 tmp_action = 341 mlx5dr_action_create_pop_vlan(); 342 if (!tmp_action) { 343 err = -ENOMEM; 344 goto free_actions; 345 } 346 fs_dr_actions[fs_dr_num_actions++] = tmp_action; 347 actions[num_actions++] = tmp_action; 348 } 349 350 if (fte->action.action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR) 351 actions[num_actions++] = 352 fte->action.modify_hdr->action.dr_action; 353 354 if (fte->action.action & MLX5_FLOW_CONTEXT_ACTION_VLAN_PUSH) { 355 tmp_action = create_action_push_vlan(domain, &fte->action.vlan[0]); 356 if (!tmp_action) { 357 err = -ENOMEM; 358 goto free_actions; 359 } 360 fs_dr_actions[fs_dr_num_actions++] = tmp_action; 361 actions[num_actions++] = tmp_action; 362 } 363 364 if (fte->action.action & MLX5_FLOW_CONTEXT_ACTION_VLAN_PUSH_2) { 365 tmp_action = create_action_push_vlan(domain, &fte->action.vlan[1]); 366 if (!tmp_action) { 367 err = -ENOMEM; 368 goto free_actions; 369 } 370 fs_dr_actions[fs_dr_num_actions++] = tmp_action; 371 actions[num_actions++] = tmp_action; 372 } 373 374 if (delay_encap_set) 375 actions[num_actions++] = 376 fte->action.pkt_reformat->action.dr_action; 377 378 /* The order of the actions below is not important */ 379 380 if (fte->action.action & MLX5_FLOW_CONTEXT_ACTION_DROP) { 381 tmp_action = mlx5dr_action_create_drop(); 382 if (!tmp_action) { 383 err = -ENOMEM; 384 goto free_actions; 385 } 386 fs_dr_actions[fs_dr_num_actions++] = tmp_action; 387 term_actions[num_term_actions++].dest = tmp_action; 388 } 389 390 if (fte->flow_context.flow_tag) { 391 tmp_action = 392 mlx5dr_action_create_tag(fte->flow_context.flow_tag); 393 if (!tmp_action) { 394 err = -ENOMEM; 395 goto free_actions; 396 } 397 fs_dr_actions[fs_dr_num_actions++] = tmp_action; 398 actions[num_actions++] = tmp_action; 399 } 400 401 if (fte->action.action & MLX5_FLOW_CONTEXT_ACTION_FWD_DEST) { 402 list_for_each_entry(dst, &fte->node.children, node.list) { 403 enum mlx5_flow_destination_type type = dst->dest_attr.type; 404 u32 id; 405 406 if (num_actions == MLX5_FLOW_CONTEXT_ACTION_MAX || 407 num_term_actions >= MLX5_FLOW_CONTEXT_ACTION_MAX) { 408 err = -ENOSPC; 409 goto free_actions; 410 } 411 412 if (type == MLX5_FLOW_DESTINATION_TYPE_COUNTER) 413 continue; 414 415 switch (type) { 416 case MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE: 417 tmp_action = create_ft_action(domain, dst); 418 if (!tmp_action) { 419 err = -ENOMEM; 420 goto free_actions; 421 } 422 fs_dr_actions[fs_dr_num_actions++] = tmp_action; 423 term_actions[num_term_actions++].dest = tmp_action; 424 break; 425 case MLX5_FLOW_DESTINATION_TYPE_UPLINK: 426 case MLX5_FLOW_DESTINATION_TYPE_VPORT: 427 tmp_action = type == MLX5_FLOW_DESTINATION_TYPE_VPORT ? 428 create_vport_action(domain, dst) : 429 create_uplink_action(domain, dst); 430 if (!tmp_action) { 431 err = -ENOMEM; 432 goto free_actions; 433 } 434 fs_dr_actions[fs_dr_num_actions++] = tmp_action; 435 term_actions[num_term_actions].dest = tmp_action; 436 437 if (dst->dest_attr.vport.flags & 438 MLX5_FLOW_DEST_VPORT_REFORMAT_ID) 439 term_actions[num_term_actions].reformat = 440 dst->dest_attr.vport.pkt_reformat->action.dr_action; 441 442 num_term_actions++; 443 break; 444 case MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE_NUM: 445 id = dst->dest_attr.ft_num; 446 tmp_action = mlx5dr_action_create_dest_table_num(domain, 447 id); 448 if (!tmp_action) { 449 err = -ENOMEM; 450 goto free_actions; 451 } 452 fs_dr_actions[fs_dr_num_actions++] = tmp_action; 453 term_actions[num_term_actions++].dest = tmp_action; 454 break; 455 case MLX5_FLOW_DESTINATION_TYPE_FLOW_SAMPLER: 456 id = dst->dest_attr.sampler_id; 457 tmp_action = mlx5dr_action_create_flow_sampler(domain, 458 id); 459 if (!tmp_action) { 460 err = -ENOMEM; 461 goto free_actions; 462 } 463 fs_dr_actions[fs_dr_num_actions++] = tmp_action; 464 term_actions[num_term_actions++].dest = tmp_action; 465 break; 466 default: 467 err = -EOPNOTSUPP; 468 goto free_actions; 469 } 470 } 471 } 472 473 if (fte->action.action & MLX5_FLOW_CONTEXT_ACTION_COUNT) { 474 list_for_each_entry(dst, &fte->node.children, node.list) { 475 u32 id; 476 477 if (dst->dest_attr.type != 478 MLX5_FLOW_DESTINATION_TYPE_COUNTER) 479 continue; 480 481 if (num_actions == MLX5_FLOW_CONTEXT_ACTION_MAX) { 482 err = -ENOSPC; 483 goto free_actions; 484 } 485 486 id = dst->dest_attr.counter_id; 487 tmp_action = 488 mlx5dr_action_create_flow_counter(id); 489 if (!tmp_action) { 490 err = -ENOMEM; 491 goto free_actions; 492 } 493 494 fs_dr_actions[fs_dr_num_actions++] = tmp_action; 495 actions[num_actions++] = tmp_action; 496 } 497 } 498 499 params.match_sz = match_sz; 500 params.match_buf = (u64 *)fte->val; 501 if (num_term_actions == 1) { 502 if (term_actions->reformat) 503 actions[num_actions++] = term_actions->reformat; 504 505 actions[num_actions++] = term_actions->dest; 506 } else if (num_term_actions > 1) { 507 bool ignore_flow_level = 508 !!(fte->action.flags & FLOW_ACT_IGNORE_FLOW_LEVEL); 509 510 tmp_action = mlx5dr_action_create_mult_dest_tbl(domain, 511 term_actions, 512 num_term_actions, 513 ignore_flow_level); 514 if (!tmp_action) { 515 err = -EOPNOTSUPP; 516 goto free_actions; 517 } 518 fs_dr_actions[fs_dr_num_actions++] = tmp_action; 519 actions[num_actions++] = tmp_action; 520 } 521 522 rule = mlx5dr_rule_create(group->fs_dr_matcher.dr_matcher, 523 ¶ms, 524 num_actions, 525 actions, 526 fte->flow_context.flow_source); 527 if (!rule) { 528 err = -EINVAL; 529 goto free_actions; 530 } 531 532 kfree(term_actions); 533 kfree(actions); 534 535 fte->fs_dr_rule.dr_rule = rule; 536 fte->fs_dr_rule.num_actions = fs_dr_num_actions; 537 fte->fs_dr_rule.dr_actions = fs_dr_actions; 538 539 return 0; 540 541 free_actions: 542 /* Free in reverse order to handle action dependencies */ 543 for (i = fs_dr_num_actions - 1; i >= 0; i--) 544 if (!IS_ERR_OR_NULL(fs_dr_actions[i])) 545 mlx5dr_action_destroy(fs_dr_actions[i]); 546 547 kfree(term_actions); 548 free_fs_dr_actions_alloc: 549 kfree(fs_dr_actions); 550 free_actions_alloc: 551 kfree(actions); 552 out_err: 553 mlx5_core_err(dev, "Failed to create dr rule err(%d)\n", err); 554 return err; 555 } 556 557 static int mlx5_cmd_dr_packet_reformat_alloc(struct mlx5_flow_root_namespace *ns, 558 struct mlx5_pkt_reformat_params *params, 559 enum mlx5_flow_namespace_type namespace, 560 struct mlx5_pkt_reformat *pkt_reformat) 561 { 562 struct mlx5dr_domain *dr_domain = ns->fs_dr_domain.dr_domain; 563 struct mlx5dr_action *action; 564 int dr_reformat; 565 566 switch (params->type) { 567 case MLX5_REFORMAT_TYPE_L2_TO_VXLAN: 568 case MLX5_REFORMAT_TYPE_L2_TO_NVGRE: 569 case MLX5_REFORMAT_TYPE_L2_TO_L2_TUNNEL: 570 dr_reformat = DR_ACTION_REFORMAT_TYP_L2_TO_TNL_L2; 571 break; 572 case MLX5_REFORMAT_TYPE_L3_TUNNEL_TO_L2: 573 dr_reformat = DR_ACTION_REFORMAT_TYP_TNL_L3_TO_L2; 574 break; 575 case MLX5_REFORMAT_TYPE_L2_TO_L3_TUNNEL: 576 dr_reformat = DR_ACTION_REFORMAT_TYP_L2_TO_TNL_L3; 577 break; 578 case MLX5_REFORMAT_TYPE_INSERT_HDR: 579 dr_reformat = DR_ACTION_REFORMAT_TYP_INSERT_HDR; 580 break; 581 case MLX5_REFORMAT_TYPE_REMOVE_HDR: 582 dr_reformat = DR_ACTION_REFORMAT_TYP_REMOVE_HDR; 583 break; 584 default: 585 mlx5_core_err(ns->dev, "Packet-reformat not supported(%d)\n", 586 params->type); 587 return -EOPNOTSUPP; 588 } 589 590 action = mlx5dr_action_create_packet_reformat(dr_domain, 591 dr_reformat, 592 params->param_0, 593 params->param_1, 594 params->size, 595 params->data); 596 if (!action) { 597 mlx5_core_err(ns->dev, "Failed allocating packet-reformat action\n"); 598 return -EINVAL; 599 } 600 601 pkt_reformat->action.dr_action = action; 602 603 return 0; 604 } 605 606 static void mlx5_cmd_dr_packet_reformat_dealloc(struct mlx5_flow_root_namespace *ns, 607 struct mlx5_pkt_reformat *pkt_reformat) 608 { 609 mlx5dr_action_destroy(pkt_reformat->action.dr_action); 610 } 611 612 static int mlx5_cmd_dr_modify_header_alloc(struct mlx5_flow_root_namespace *ns, 613 u8 namespace, u8 num_actions, 614 void *modify_actions, 615 struct mlx5_modify_hdr *modify_hdr) 616 { 617 struct mlx5dr_domain *dr_domain = ns->fs_dr_domain.dr_domain; 618 struct mlx5dr_action *action; 619 size_t actions_sz; 620 621 actions_sz = MLX5_UN_SZ_BYTES(set_add_copy_action_in_auto) * 622 num_actions; 623 action = mlx5dr_action_create_modify_header(dr_domain, 0, 624 actions_sz, 625 modify_actions); 626 if (!action) { 627 mlx5_core_err(ns->dev, "Failed allocating modify-header action\n"); 628 return -EINVAL; 629 } 630 631 modify_hdr->action.dr_action = action; 632 633 return 0; 634 } 635 636 static void mlx5_cmd_dr_modify_header_dealloc(struct mlx5_flow_root_namespace *ns, 637 struct mlx5_modify_hdr *modify_hdr) 638 { 639 mlx5dr_action_destroy(modify_hdr->action.dr_action); 640 } 641 642 static int 643 mlx5_cmd_dr_destroy_match_definer(struct mlx5_flow_root_namespace *ns, 644 int definer_id) 645 { 646 return -EOPNOTSUPP; 647 } 648 649 static int mlx5_cmd_dr_create_match_definer(struct mlx5_flow_root_namespace *ns, 650 u16 format_id, u32 *match_mask) 651 { 652 return -EOPNOTSUPP; 653 } 654 655 static int mlx5_cmd_dr_delete_fte(struct mlx5_flow_root_namespace *ns, 656 struct mlx5_flow_table *ft, 657 struct fs_fte *fte) 658 { 659 struct mlx5_fs_dr_rule *rule = &fte->fs_dr_rule; 660 int err; 661 int i; 662 663 if (mlx5_dr_is_fw_table(ft->flags)) 664 return mlx5_fs_cmd_get_fw_cmds()->delete_fte(ns, ft, fte); 665 666 err = mlx5dr_rule_destroy(rule->dr_rule); 667 if (err) 668 return err; 669 670 /* Free in reverse order to handle action dependencies */ 671 for (i = rule->num_actions - 1; i >= 0; i--) 672 if (!IS_ERR_OR_NULL(rule->dr_actions[i])) 673 mlx5dr_action_destroy(rule->dr_actions[i]); 674 675 kfree(rule->dr_actions); 676 return 0; 677 } 678 679 static int mlx5_cmd_dr_update_fte(struct mlx5_flow_root_namespace *ns, 680 struct mlx5_flow_table *ft, 681 struct mlx5_flow_group *group, 682 int modify_mask, 683 struct fs_fte *fte) 684 { 685 struct fs_fte fte_tmp = {}; 686 int ret; 687 688 if (mlx5_dr_is_fw_table(ft->flags)) 689 return mlx5_fs_cmd_get_fw_cmds()->update_fte(ns, ft, group, modify_mask, fte); 690 691 /* Backup current dr rule details */ 692 fte_tmp.fs_dr_rule = fte->fs_dr_rule; 693 memset(&fte->fs_dr_rule, 0, sizeof(struct mlx5_fs_dr_rule)); 694 695 /* First add the new updated rule, then delete the old rule */ 696 ret = mlx5_cmd_dr_create_fte(ns, ft, group, fte); 697 if (ret) 698 goto restore_fte; 699 700 ret = mlx5_cmd_dr_delete_fte(ns, ft, &fte_tmp); 701 WARN_ONCE(ret, "dr update fte duplicate rule deletion failed\n"); 702 return ret; 703 704 restore_fte: 705 fte->fs_dr_rule = fte_tmp.fs_dr_rule; 706 return ret; 707 } 708 709 static int mlx5_cmd_dr_set_peer(struct mlx5_flow_root_namespace *ns, 710 struct mlx5_flow_root_namespace *peer_ns) 711 { 712 struct mlx5dr_domain *peer_domain = NULL; 713 714 if (peer_ns) 715 peer_domain = peer_ns->fs_dr_domain.dr_domain; 716 mlx5dr_domain_set_peer(ns->fs_dr_domain.dr_domain, 717 peer_domain); 718 return 0; 719 } 720 721 static int mlx5_cmd_dr_create_ns(struct mlx5_flow_root_namespace *ns) 722 { 723 ns->fs_dr_domain.dr_domain = 724 mlx5dr_domain_create(ns->dev, 725 MLX5DR_DOMAIN_TYPE_FDB); 726 if (!ns->fs_dr_domain.dr_domain) { 727 mlx5_core_err(ns->dev, "Failed to create dr flow namespace\n"); 728 return -EOPNOTSUPP; 729 } 730 return 0; 731 } 732 733 static int mlx5_cmd_dr_destroy_ns(struct mlx5_flow_root_namespace *ns) 734 { 735 return mlx5dr_domain_destroy(ns->fs_dr_domain.dr_domain); 736 } 737 738 bool mlx5_fs_dr_is_supported(struct mlx5_core_dev *dev) 739 { 740 return mlx5dr_is_supported(dev); 741 } 742 743 static const struct mlx5_flow_cmds mlx5_flow_cmds_dr = { 744 .create_flow_table = mlx5_cmd_dr_create_flow_table, 745 .destroy_flow_table = mlx5_cmd_dr_destroy_flow_table, 746 .modify_flow_table = mlx5_cmd_dr_modify_flow_table, 747 .create_flow_group = mlx5_cmd_dr_create_flow_group, 748 .destroy_flow_group = mlx5_cmd_dr_destroy_flow_group, 749 .create_fte = mlx5_cmd_dr_create_fte, 750 .update_fte = mlx5_cmd_dr_update_fte, 751 .delete_fte = mlx5_cmd_dr_delete_fte, 752 .update_root_ft = mlx5_cmd_dr_update_root_ft, 753 .packet_reformat_alloc = mlx5_cmd_dr_packet_reformat_alloc, 754 .packet_reformat_dealloc = mlx5_cmd_dr_packet_reformat_dealloc, 755 .modify_header_alloc = mlx5_cmd_dr_modify_header_alloc, 756 .modify_header_dealloc = mlx5_cmd_dr_modify_header_dealloc, 757 .create_match_definer = mlx5_cmd_dr_create_match_definer, 758 .destroy_match_definer = mlx5_cmd_dr_destroy_match_definer, 759 .set_peer = mlx5_cmd_dr_set_peer, 760 .create_ns = mlx5_cmd_dr_create_ns, 761 .destroy_ns = mlx5_cmd_dr_destroy_ns, 762 }; 763 764 const struct mlx5_flow_cmds *mlx5_fs_cmd_get_dr_cmds(void) 765 { 766 return &mlx5_flow_cmds_dr; 767 } 768