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