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; 335 336 if (fte->action.pkt_reformat->owner == MLX5_FLOW_RESOURCE_OWNER_FW) { 337 err = -EINVAL; 338 mlx5dr_err(domain, "FW-owned reformat can't be used in SW rule\n"); 339 goto free_actions; 340 } 341 342 is_decap = fte->action.pkt_reformat->reformat_type == 343 MLX5_REFORMAT_TYPE_L3_TUNNEL_TO_L2; 344 345 if (is_decap) 346 actions[num_actions++] = 347 fte->action.pkt_reformat->action.dr_action; 348 else 349 delay_encap_set = true; 350 } 351 352 if (fte->action.action & MLX5_FLOW_CONTEXT_ACTION_VLAN_POP) { 353 tmp_action = 354 mlx5dr_action_create_pop_vlan(); 355 if (!tmp_action) { 356 err = -ENOMEM; 357 goto free_actions; 358 } 359 fs_dr_actions[fs_dr_num_actions++] = tmp_action; 360 actions[num_actions++] = tmp_action; 361 } 362 363 if (fte->action.action & MLX5_FLOW_CONTEXT_ACTION_VLAN_POP_2) { 364 tmp_action = 365 mlx5dr_action_create_pop_vlan(); 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 (fte->action.action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR) 375 actions[num_actions++] = 376 fte->action.modify_hdr->action.dr_action; 377 378 if (fte->action.action & MLX5_FLOW_CONTEXT_ACTION_VLAN_PUSH) { 379 tmp_action = create_action_push_vlan(domain, &fte->action.vlan[0]); 380 if (!tmp_action) { 381 err = -ENOMEM; 382 goto free_actions; 383 } 384 fs_dr_actions[fs_dr_num_actions++] = tmp_action; 385 actions[num_actions++] = tmp_action; 386 } 387 388 if (fte->action.action & MLX5_FLOW_CONTEXT_ACTION_VLAN_PUSH_2) { 389 tmp_action = create_action_push_vlan(domain, &fte->action.vlan[1]); 390 if (!tmp_action) { 391 err = -ENOMEM; 392 goto free_actions; 393 } 394 fs_dr_actions[fs_dr_num_actions++] = tmp_action; 395 actions[num_actions++] = tmp_action; 396 } 397 398 if (delay_encap_set) 399 actions[num_actions++] = 400 fte->action.pkt_reformat->action.dr_action; 401 402 /* The order of the actions below is not important */ 403 404 if (fte->action.action & MLX5_FLOW_CONTEXT_ACTION_DROP) { 405 tmp_action = mlx5dr_action_create_drop(); 406 if (!tmp_action) { 407 err = -ENOMEM; 408 goto free_actions; 409 } 410 fs_dr_actions[fs_dr_num_actions++] = tmp_action; 411 term_actions[num_term_actions++].dest = tmp_action; 412 } 413 414 if (fte->flow_context.flow_tag) { 415 tmp_action = 416 mlx5dr_action_create_tag(fte->flow_context.flow_tag); 417 if (!tmp_action) { 418 err = -ENOMEM; 419 goto free_actions; 420 } 421 fs_dr_actions[fs_dr_num_actions++] = tmp_action; 422 actions[num_actions++] = tmp_action; 423 } 424 425 if (fte->action.action & MLX5_FLOW_CONTEXT_ACTION_FWD_DEST) { 426 list_for_each_entry(dst, &fte->node.children, node.list) { 427 enum mlx5_flow_destination_type type = dst->dest_attr.type; 428 u32 id; 429 430 if (fs_dr_num_actions == MLX5_FLOW_CONTEXT_ACTION_MAX || 431 num_term_actions == MLX5_FLOW_CONTEXT_ACTION_MAX) { 432 err = -EOPNOTSUPP; 433 goto free_actions; 434 } 435 436 if (type == MLX5_FLOW_DESTINATION_TYPE_COUNTER) 437 continue; 438 439 switch (type) { 440 case MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE: 441 tmp_action = create_ft_action(domain, dst); 442 if (!tmp_action) { 443 err = -ENOMEM; 444 goto free_actions; 445 } 446 fs_dr_actions[fs_dr_num_actions++] = tmp_action; 447 term_actions[num_term_actions++].dest = tmp_action; 448 break; 449 case MLX5_FLOW_DESTINATION_TYPE_UPLINK: 450 case MLX5_FLOW_DESTINATION_TYPE_VPORT: 451 tmp_action = type == MLX5_FLOW_DESTINATION_TYPE_VPORT ? 452 create_vport_action(domain, dst) : 453 create_uplink_action(domain, dst); 454 if (!tmp_action) { 455 err = -ENOMEM; 456 goto free_actions; 457 } 458 fs_dr_actions[fs_dr_num_actions++] = tmp_action; 459 term_actions[num_term_actions].dest = tmp_action; 460 461 if (dst->dest_attr.vport.flags & 462 MLX5_FLOW_DEST_VPORT_REFORMAT_ID) 463 term_actions[num_term_actions].reformat = 464 dst->dest_attr.vport.pkt_reformat->action.dr_action; 465 466 num_term_actions++; 467 break; 468 case MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE_NUM: 469 id = dst->dest_attr.ft_num; 470 tmp_action = mlx5dr_action_create_dest_table_num(domain, 471 id); 472 if (!tmp_action) { 473 err = -ENOMEM; 474 goto free_actions; 475 } 476 fs_dr_actions[fs_dr_num_actions++] = tmp_action; 477 term_actions[num_term_actions++].dest = tmp_action; 478 break; 479 case MLX5_FLOW_DESTINATION_TYPE_FLOW_SAMPLER: 480 id = dst->dest_attr.sampler_id; 481 tmp_action = mlx5dr_action_create_flow_sampler(domain, 482 id); 483 if (!tmp_action) { 484 err = -ENOMEM; 485 goto free_actions; 486 } 487 fs_dr_actions[fs_dr_num_actions++] = tmp_action; 488 term_actions[num_term_actions++].dest = tmp_action; 489 break; 490 case MLX5_FLOW_DESTINATION_TYPE_RANGE: 491 tmp_action = create_range_action(domain, dst); 492 if (!tmp_action) { 493 err = -ENOMEM; 494 goto free_actions; 495 } 496 fs_dr_actions[fs_dr_num_actions++] = tmp_action; 497 term_actions[num_term_actions++].dest = tmp_action; 498 break; 499 default: 500 err = -EOPNOTSUPP; 501 goto free_actions; 502 } 503 } 504 } 505 506 if (fte->action.action & MLX5_FLOW_CONTEXT_ACTION_COUNT) { 507 list_for_each_entry(dst, &fte->node.children, node.list) { 508 u32 id; 509 510 if (dst->dest_attr.type != 511 MLX5_FLOW_DESTINATION_TYPE_COUNTER) 512 continue; 513 514 if (num_actions == MLX5_FLOW_CONTEXT_ACTION_MAX || 515 fs_dr_num_actions == MLX5_FLOW_CONTEXT_ACTION_MAX) { 516 err = -EOPNOTSUPP; 517 goto free_actions; 518 } 519 520 id = dst->dest_attr.counter_id; 521 tmp_action = 522 mlx5dr_action_create_flow_counter(id); 523 if (!tmp_action) { 524 err = -ENOMEM; 525 goto free_actions; 526 } 527 528 fs_dr_actions[fs_dr_num_actions++] = tmp_action; 529 actions[num_actions++] = tmp_action; 530 } 531 } 532 533 if (fte->action.action & MLX5_FLOW_CONTEXT_ACTION_EXECUTE_ASO) { 534 if (fte->action.exe_aso.type != MLX5_EXE_ASO_FLOW_METER) { 535 err = -EOPNOTSUPP; 536 goto free_actions; 537 } 538 539 tmp_action = 540 mlx5dr_action_create_aso(domain, 541 fte->action.exe_aso.object_id, 542 fte->action.exe_aso.return_reg_id, 543 fte->action.exe_aso.type, 544 fte->action.exe_aso.flow_meter.init_color, 545 fte->action.exe_aso.flow_meter.meter_idx); 546 if (!tmp_action) { 547 err = -ENOMEM; 548 goto free_actions; 549 } 550 fs_dr_actions[fs_dr_num_actions++] = tmp_action; 551 actions[num_actions++] = tmp_action; 552 } 553 554 params.match_sz = match_sz; 555 params.match_buf = (u64 *)fte->val; 556 if (num_term_actions == 1) { 557 if (term_actions->reformat) { 558 if (num_actions == MLX5_FLOW_CONTEXT_ACTION_MAX) { 559 err = -EOPNOTSUPP; 560 goto free_actions; 561 } 562 actions[num_actions++] = term_actions->reformat; 563 } 564 565 if (num_actions == MLX5_FLOW_CONTEXT_ACTION_MAX) { 566 err = -EOPNOTSUPP; 567 goto free_actions; 568 } 569 actions[num_actions++] = term_actions->dest; 570 } else if (num_term_actions > 1) { 571 bool ignore_flow_level = 572 !!(fte->action.flags & FLOW_ACT_IGNORE_FLOW_LEVEL); 573 u32 flow_source = fte->flow_context.flow_source; 574 575 if (num_actions == MLX5_FLOW_CONTEXT_ACTION_MAX || 576 fs_dr_num_actions == MLX5_FLOW_CONTEXT_ACTION_MAX) { 577 err = -EOPNOTSUPP; 578 goto free_actions; 579 } 580 tmp_action = mlx5dr_action_create_mult_dest_tbl(domain, 581 term_actions, 582 num_term_actions, 583 ignore_flow_level, 584 flow_source); 585 if (!tmp_action) { 586 err = -EOPNOTSUPP; 587 goto free_actions; 588 } 589 fs_dr_actions[fs_dr_num_actions++] = tmp_action; 590 actions[num_actions++] = tmp_action; 591 } 592 593 rule = mlx5dr_rule_create(group->fs_dr_matcher.dr_matcher, 594 ¶ms, 595 num_actions, 596 actions, 597 fte->flow_context.flow_source); 598 if (!rule) { 599 err = -EINVAL; 600 goto free_actions; 601 } 602 603 kfree(term_actions); 604 kfree(actions); 605 606 fte->fs_dr_rule.dr_rule = rule; 607 fte->fs_dr_rule.num_actions = fs_dr_num_actions; 608 fte->fs_dr_rule.dr_actions = fs_dr_actions; 609 610 return 0; 611 612 free_actions: 613 /* Free in reverse order to handle action dependencies */ 614 for (i = fs_dr_num_actions - 1; i >= 0; i--) 615 if (!IS_ERR_OR_NULL(fs_dr_actions[i])) 616 mlx5dr_action_destroy(fs_dr_actions[i]); 617 618 kfree(term_actions); 619 free_fs_dr_actions_alloc: 620 kfree(fs_dr_actions); 621 free_actions_alloc: 622 kfree(actions); 623 out_err: 624 mlx5_core_err(dev, "Failed to create dr rule err(%d)\n", err); 625 return err; 626 } 627 628 static int mlx5_cmd_dr_packet_reformat_alloc(struct mlx5_flow_root_namespace *ns, 629 struct mlx5_pkt_reformat_params *params, 630 enum mlx5_flow_namespace_type namespace, 631 struct mlx5_pkt_reformat *pkt_reformat) 632 { 633 struct mlx5dr_domain *dr_domain = ns->fs_dr_domain.dr_domain; 634 struct mlx5dr_action *action; 635 int dr_reformat; 636 637 switch (params->type) { 638 case MLX5_REFORMAT_TYPE_L2_TO_VXLAN: 639 case MLX5_REFORMAT_TYPE_L2_TO_NVGRE: 640 case MLX5_REFORMAT_TYPE_L2_TO_L2_TUNNEL: 641 dr_reformat = DR_ACTION_REFORMAT_TYP_L2_TO_TNL_L2; 642 break; 643 case MLX5_REFORMAT_TYPE_L3_TUNNEL_TO_L2: 644 dr_reformat = DR_ACTION_REFORMAT_TYP_TNL_L3_TO_L2; 645 break; 646 case MLX5_REFORMAT_TYPE_L2_TO_L3_TUNNEL: 647 dr_reformat = DR_ACTION_REFORMAT_TYP_L2_TO_TNL_L3; 648 break; 649 case MLX5_REFORMAT_TYPE_INSERT_HDR: 650 dr_reformat = DR_ACTION_REFORMAT_TYP_INSERT_HDR; 651 break; 652 case MLX5_REFORMAT_TYPE_REMOVE_HDR: 653 dr_reformat = DR_ACTION_REFORMAT_TYP_REMOVE_HDR; 654 break; 655 default: 656 mlx5_core_err(ns->dev, "Packet-reformat not supported(%d)\n", 657 params->type); 658 return -EOPNOTSUPP; 659 } 660 661 action = mlx5dr_action_create_packet_reformat(dr_domain, 662 dr_reformat, 663 params->param_0, 664 params->param_1, 665 params->size, 666 params->data); 667 if (!action) { 668 mlx5_core_err(ns->dev, "Failed allocating packet-reformat action\n"); 669 return -EINVAL; 670 } 671 672 pkt_reformat->owner = MLX5_FLOW_RESOURCE_OWNER_SW; 673 pkt_reformat->action.dr_action = action; 674 675 return 0; 676 } 677 678 static void mlx5_cmd_dr_packet_reformat_dealloc(struct mlx5_flow_root_namespace *ns, 679 struct mlx5_pkt_reformat *pkt_reformat) 680 { 681 mlx5dr_action_destroy(pkt_reformat->action.dr_action); 682 } 683 684 static int mlx5_cmd_dr_modify_header_alloc(struct mlx5_flow_root_namespace *ns, 685 u8 namespace, u8 num_actions, 686 void *modify_actions, 687 struct mlx5_modify_hdr *modify_hdr) 688 { 689 struct mlx5dr_domain *dr_domain = ns->fs_dr_domain.dr_domain; 690 struct mlx5dr_action *action; 691 size_t actions_sz; 692 693 actions_sz = MLX5_UN_SZ_BYTES(set_add_copy_action_in_auto) * 694 num_actions; 695 action = mlx5dr_action_create_modify_header(dr_domain, 0, 696 actions_sz, 697 modify_actions); 698 if (!action) { 699 mlx5_core_err(ns->dev, "Failed allocating modify-header action\n"); 700 return -EINVAL; 701 } 702 703 modify_hdr->owner = MLX5_FLOW_RESOURCE_OWNER_SW; 704 modify_hdr->action.dr_action = action; 705 706 return 0; 707 } 708 709 static void mlx5_cmd_dr_modify_header_dealloc(struct mlx5_flow_root_namespace *ns, 710 struct mlx5_modify_hdr *modify_hdr) 711 { 712 mlx5dr_action_destroy(modify_hdr->action.dr_action); 713 } 714 715 static int 716 mlx5_cmd_dr_destroy_match_definer(struct mlx5_flow_root_namespace *ns, 717 int definer_id) 718 { 719 return -EOPNOTSUPP; 720 } 721 722 static int mlx5_cmd_dr_create_match_definer(struct mlx5_flow_root_namespace *ns, 723 u16 format_id, u32 *match_mask) 724 { 725 return -EOPNOTSUPP; 726 } 727 728 static int mlx5_cmd_dr_delete_fte(struct mlx5_flow_root_namespace *ns, 729 struct mlx5_flow_table *ft, 730 struct fs_fte *fte) 731 { 732 struct mlx5_fs_dr_rule *rule = &fte->fs_dr_rule; 733 int err; 734 int i; 735 736 if (dr_is_fw_term_table(ft)) 737 return mlx5_fs_cmd_get_fw_cmds()->delete_fte(ns, ft, fte); 738 739 err = mlx5dr_rule_destroy(rule->dr_rule); 740 if (err) 741 return err; 742 743 /* Free in reverse order to handle action dependencies */ 744 for (i = rule->num_actions - 1; i >= 0; i--) 745 if (!IS_ERR_OR_NULL(rule->dr_actions[i])) 746 mlx5dr_action_destroy(rule->dr_actions[i]); 747 748 kfree(rule->dr_actions); 749 return 0; 750 } 751 752 static int mlx5_cmd_dr_update_fte(struct mlx5_flow_root_namespace *ns, 753 struct mlx5_flow_table *ft, 754 struct mlx5_flow_group *group, 755 int modify_mask, 756 struct fs_fte *fte) 757 { 758 struct fs_fte fte_tmp = {}; 759 int ret; 760 761 if (dr_is_fw_term_table(ft)) 762 return mlx5_fs_cmd_get_fw_cmds()->update_fte(ns, ft, group, modify_mask, fte); 763 764 /* Backup current dr rule details */ 765 fte_tmp.fs_dr_rule = fte->fs_dr_rule; 766 memset(&fte->fs_dr_rule, 0, sizeof(struct mlx5_fs_dr_rule)); 767 768 /* First add the new updated rule, then delete the old rule */ 769 ret = mlx5_cmd_dr_create_fte(ns, ft, group, fte); 770 if (ret) 771 goto restore_fte; 772 773 ret = mlx5_cmd_dr_delete_fte(ns, ft, &fte_tmp); 774 WARN_ONCE(ret, "dr update fte duplicate rule deletion failed\n"); 775 return ret; 776 777 restore_fte: 778 fte->fs_dr_rule = fte_tmp.fs_dr_rule; 779 return ret; 780 } 781 782 static int mlx5_cmd_dr_set_peer(struct mlx5_flow_root_namespace *ns, 783 struct mlx5_flow_root_namespace *peer_ns, 784 u16 peer_vhca_id) 785 { 786 struct mlx5dr_domain *peer_domain = NULL; 787 788 if (peer_ns) 789 peer_domain = peer_ns->fs_dr_domain.dr_domain; 790 mlx5dr_domain_set_peer(ns->fs_dr_domain.dr_domain, 791 peer_domain, peer_vhca_id); 792 return 0; 793 } 794 795 static int mlx5_cmd_dr_create_ns(struct mlx5_flow_root_namespace *ns) 796 { 797 ns->fs_dr_domain.dr_domain = 798 mlx5dr_domain_create(ns->dev, 799 MLX5DR_DOMAIN_TYPE_FDB); 800 if (!ns->fs_dr_domain.dr_domain) { 801 mlx5_core_err(ns->dev, "Failed to create dr flow namespace\n"); 802 return -EOPNOTSUPP; 803 } 804 return 0; 805 } 806 807 static int mlx5_cmd_dr_destroy_ns(struct mlx5_flow_root_namespace *ns) 808 { 809 return mlx5dr_domain_destroy(ns->fs_dr_domain.dr_domain); 810 } 811 812 static u32 mlx5_cmd_dr_get_capabilities(struct mlx5_flow_root_namespace *ns, 813 enum fs_flow_table_type ft_type) 814 { 815 u32 steering_caps = 0; 816 817 if (ft_type != FS_FT_FDB || 818 MLX5_CAP_GEN(ns->dev, steering_format_version) == MLX5_STEERING_FORMAT_CONNECTX_5) 819 return 0; 820 821 steering_caps |= MLX5_FLOW_STEERING_CAP_VLAN_PUSH_ON_RX; 822 steering_caps |= MLX5_FLOW_STEERING_CAP_VLAN_POP_ON_TX; 823 824 if (mlx5dr_supp_match_ranges(ns->dev)) 825 steering_caps |= MLX5_FLOW_STEERING_CAP_MATCH_RANGES; 826 827 return steering_caps; 828 } 829 830 int mlx5_fs_dr_action_get_pkt_reformat_id(struct mlx5_pkt_reformat *pkt_reformat) 831 { 832 switch (pkt_reformat->reformat_type) { 833 case MLX5_REFORMAT_TYPE_L2_TO_VXLAN: 834 case MLX5_REFORMAT_TYPE_L2_TO_NVGRE: 835 case MLX5_REFORMAT_TYPE_L2_TO_L2_TUNNEL: 836 case MLX5_REFORMAT_TYPE_L2_TO_L3_TUNNEL: 837 case MLX5_REFORMAT_TYPE_INSERT_HDR: 838 return mlx5dr_action_get_pkt_reformat_id(pkt_reformat->action.dr_action); 839 } 840 return -EOPNOTSUPP; 841 } 842 843 bool mlx5_fs_dr_is_supported(struct mlx5_core_dev *dev) 844 { 845 return mlx5dr_is_supported(dev); 846 } 847 848 static const struct mlx5_flow_cmds mlx5_flow_cmds_dr = { 849 .create_flow_table = mlx5_cmd_dr_create_flow_table, 850 .destroy_flow_table = mlx5_cmd_dr_destroy_flow_table, 851 .modify_flow_table = mlx5_cmd_dr_modify_flow_table, 852 .create_flow_group = mlx5_cmd_dr_create_flow_group, 853 .destroy_flow_group = mlx5_cmd_dr_destroy_flow_group, 854 .create_fte = mlx5_cmd_dr_create_fte, 855 .update_fte = mlx5_cmd_dr_update_fte, 856 .delete_fte = mlx5_cmd_dr_delete_fte, 857 .update_root_ft = mlx5_cmd_dr_update_root_ft, 858 .packet_reformat_alloc = mlx5_cmd_dr_packet_reformat_alloc, 859 .packet_reformat_dealloc = mlx5_cmd_dr_packet_reformat_dealloc, 860 .modify_header_alloc = mlx5_cmd_dr_modify_header_alloc, 861 .modify_header_dealloc = mlx5_cmd_dr_modify_header_dealloc, 862 .create_match_definer = mlx5_cmd_dr_create_match_definer, 863 .destroy_match_definer = mlx5_cmd_dr_destroy_match_definer, 864 .set_peer = mlx5_cmd_dr_set_peer, 865 .create_ns = mlx5_cmd_dr_create_ns, 866 .destroy_ns = mlx5_cmd_dr_destroy_ns, 867 .get_capabilities = mlx5_cmd_dr_get_capabilities, 868 }; 869 870 const struct mlx5_flow_cmds *mlx5_fs_cmd_get_dr_cmds(void) 871 { 872 return &mlx5_flow_cmds_dr; 873 } 874