linux/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_rule.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
   2/* Copyright (c) 2019 Mellanox Technologies. */
   3
   4#include "dr_types.h"
   5
   6#define DR_RULE_MAX_STE_CHAIN (DR_RULE_MAX_STES + DR_ACTION_MAX_STES)
   7
   8struct mlx5dr_rule_action_member {
   9        struct mlx5dr_action *action;
  10        struct list_head list;
  11};
  12
  13static int dr_rule_append_to_miss_list(struct mlx5dr_ste_ctx *ste_ctx,
  14                                       struct mlx5dr_ste *new_last_ste,
  15                                       struct list_head *miss_list,
  16                                       struct list_head *send_list)
  17{
  18        struct mlx5dr_ste_send_info *ste_info_last;
  19        struct mlx5dr_ste *last_ste;
  20
  21        /* The new entry will be inserted after the last */
  22        last_ste = list_last_entry(miss_list, struct mlx5dr_ste, miss_list_node);
  23        WARN_ON(!last_ste);
  24
  25        ste_info_last = kzalloc(sizeof(*ste_info_last), GFP_KERNEL);
  26        if (!ste_info_last)
  27                return -ENOMEM;
  28
  29        mlx5dr_ste_set_miss_addr(ste_ctx, last_ste->hw_ste,
  30                                 mlx5dr_ste_get_icm_addr(new_last_ste));
  31        list_add_tail(&new_last_ste->miss_list_node, miss_list);
  32
  33        mlx5dr_send_fill_and_append_ste_send_info(last_ste, DR_STE_SIZE_CTRL,
  34                                                  0, last_ste->hw_ste,
  35                                                  ste_info_last, send_list, true);
  36
  37        return 0;
  38}
  39
  40static struct mlx5dr_ste *
  41dr_rule_create_collision_htbl(struct mlx5dr_matcher *matcher,
  42                              struct mlx5dr_matcher_rx_tx *nic_matcher,
  43                              u8 *hw_ste)
  44{
  45        struct mlx5dr_domain *dmn = matcher->tbl->dmn;
  46        struct mlx5dr_ste_ctx *ste_ctx = dmn->ste_ctx;
  47        struct mlx5dr_ste_htbl *new_htbl;
  48        struct mlx5dr_ste *ste;
  49
  50        /* Create new table for miss entry */
  51        new_htbl = mlx5dr_ste_htbl_alloc(dmn->ste_icm_pool,
  52                                         DR_CHUNK_SIZE_1,
  53                                         MLX5DR_STE_LU_TYPE_DONT_CARE,
  54                                         0);
  55        if (!new_htbl) {
  56                mlx5dr_dbg(dmn, "Failed allocating collision table\n");
  57                return NULL;
  58        }
  59
  60        /* One and only entry, never grows */
  61        ste = new_htbl->ste_arr;
  62        mlx5dr_ste_set_miss_addr(ste_ctx, hw_ste,
  63                                 nic_matcher->e_anchor->chunk->icm_addr);
  64        mlx5dr_htbl_get(new_htbl);
  65
  66        return ste;
  67}
  68
  69static struct mlx5dr_ste *
  70dr_rule_create_collision_entry(struct mlx5dr_matcher *matcher,
  71                               struct mlx5dr_matcher_rx_tx *nic_matcher,
  72                               u8 *hw_ste,
  73                               struct mlx5dr_ste *orig_ste)
  74{
  75        struct mlx5dr_ste *ste;
  76
  77        ste = dr_rule_create_collision_htbl(matcher, nic_matcher, hw_ste);
  78        if (!ste) {
  79                mlx5dr_dbg(matcher->tbl->dmn, "Failed creating collision entry\n");
  80                return NULL;
  81        }
  82
  83        ste->ste_chain_location = orig_ste->ste_chain_location;
  84        ste->htbl->pointing_ste = orig_ste->htbl->pointing_ste;
  85
  86        /* In collision entry, all members share the same miss_list_head */
  87        ste->htbl->miss_list = mlx5dr_ste_get_miss_list(orig_ste);
  88
  89        /* Next table */
  90        if (mlx5dr_ste_create_next_htbl(matcher, nic_matcher, ste, hw_ste,
  91                                        DR_CHUNK_SIZE_1)) {
  92                mlx5dr_dbg(matcher->tbl->dmn, "Failed allocating table\n");
  93                goto free_tbl;
  94        }
  95
  96        return ste;
  97
  98free_tbl:
  99        mlx5dr_ste_free(ste, matcher, nic_matcher);
 100        return NULL;
 101}
 102
 103static int
 104dr_rule_handle_one_ste_in_update_list(struct mlx5dr_ste_send_info *ste_info,
 105                                      struct mlx5dr_domain *dmn)
 106{
 107        int ret;
 108
 109        list_del(&ste_info->send_list);
 110
 111        /* Copy data to ste, only reduced size or control, the last 16B (mask)
 112         * is already written to the hw.
 113         */
 114        if (ste_info->size == DR_STE_SIZE_CTRL)
 115                memcpy(ste_info->ste->hw_ste, ste_info->data, DR_STE_SIZE_CTRL);
 116        else
 117                memcpy(ste_info->ste->hw_ste, ste_info->data, DR_STE_SIZE_REDUCED);
 118
 119        ret = mlx5dr_send_postsend_ste(dmn, ste_info->ste, ste_info->data,
 120                                       ste_info->size, ste_info->offset);
 121        if (ret)
 122                goto out;
 123
 124out:
 125        kfree(ste_info);
 126        return ret;
 127}
 128
 129static int dr_rule_send_update_list(struct list_head *send_ste_list,
 130                                    struct mlx5dr_domain *dmn,
 131                                    bool is_reverse)
 132{
 133        struct mlx5dr_ste_send_info *ste_info, *tmp_ste_info;
 134        int ret;
 135
 136        if (is_reverse) {
 137                list_for_each_entry_safe_reverse(ste_info, tmp_ste_info,
 138                                                 send_ste_list, send_list) {
 139                        ret = dr_rule_handle_one_ste_in_update_list(ste_info,
 140                                                                    dmn);
 141                        if (ret)
 142                                return ret;
 143                }
 144        } else {
 145                list_for_each_entry_safe(ste_info, tmp_ste_info,
 146                                         send_ste_list, send_list) {
 147                        ret = dr_rule_handle_one_ste_in_update_list(ste_info,
 148                                                                    dmn);
 149                        if (ret)
 150                                return ret;
 151                }
 152        }
 153
 154        return 0;
 155}
 156
 157static struct mlx5dr_ste *
 158dr_rule_find_ste_in_miss_list(struct list_head *miss_list, u8 *hw_ste)
 159{
 160        struct mlx5dr_ste *ste;
 161
 162        if (list_empty(miss_list))
 163                return NULL;
 164
 165        /* Check if hw_ste is present in the list */
 166        list_for_each_entry(ste, miss_list, miss_list_node) {
 167                if (mlx5dr_ste_equal_tag(ste->hw_ste, hw_ste))
 168                        return ste;
 169        }
 170
 171        return NULL;
 172}
 173
 174static struct mlx5dr_ste *
 175dr_rule_rehash_handle_collision(struct mlx5dr_matcher *matcher,
 176                                struct mlx5dr_matcher_rx_tx *nic_matcher,
 177                                struct list_head *update_list,
 178                                struct mlx5dr_ste *col_ste,
 179                                u8 *hw_ste)
 180{
 181        struct mlx5dr_domain *dmn = matcher->tbl->dmn;
 182        struct mlx5dr_ste *new_ste;
 183        int ret;
 184
 185        new_ste = dr_rule_create_collision_htbl(matcher, nic_matcher, hw_ste);
 186        if (!new_ste)
 187                return NULL;
 188
 189        /* Update collision pointing STE */
 190        new_ste->htbl->pointing_ste = col_ste->htbl->pointing_ste;
 191
 192        /* In collision entry, all members share the same miss_list_head */
 193        new_ste->htbl->miss_list = mlx5dr_ste_get_miss_list(col_ste);
 194
 195        /* Update the previous from the list */
 196        ret = dr_rule_append_to_miss_list(dmn->ste_ctx, new_ste,
 197                                          mlx5dr_ste_get_miss_list(col_ste),
 198                                          update_list);
 199        if (ret) {
 200                mlx5dr_dbg(dmn, "Failed update dup entry\n");
 201                goto err_exit;
 202        }
 203
 204        return new_ste;
 205
 206err_exit:
 207        mlx5dr_ste_free(new_ste, matcher, nic_matcher);
 208        return NULL;
 209}
 210
 211static void dr_rule_rehash_copy_ste_ctrl(struct mlx5dr_matcher *matcher,
 212                                         struct mlx5dr_matcher_rx_tx *nic_matcher,
 213                                         struct mlx5dr_ste *cur_ste,
 214                                         struct mlx5dr_ste *new_ste)
 215{
 216        new_ste->next_htbl = cur_ste->next_htbl;
 217        new_ste->ste_chain_location = cur_ste->ste_chain_location;
 218
 219        if (new_ste->next_htbl)
 220                new_ste->next_htbl->pointing_ste = new_ste;
 221
 222        /* We need to copy the refcount since this ste
 223         * may have been traversed several times
 224         */
 225        new_ste->refcount = cur_ste->refcount;
 226
 227        /* Link old STEs rule to the new ste */
 228        mlx5dr_rule_set_last_member(cur_ste->rule_rx_tx, new_ste, false);
 229}
 230
 231static struct mlx5dr_ste *
 232dr_rule_rehash_copy_ste(struct mlx5dr_matcher *matcher,
 233                        struct mlx5dr_matcher_rx_tx *nic_matcher,
 234                        struct mlx5dr_ste *cur_ste,
 235                        struct mlx5dr_ste_htbl *new_htbl,
 236                        struct list_head *update_list)
 237{
 238        struct mlx5dr_domain *dmn = matcher->tbl->dmn;
 239        struct mlx5dr_ste_send_info *ste_info;
 240        bool use_update_list = false;
 241        u8 hw_ste[DR_STE_SIZE] = {};
 242        struct mlx5dr_ste *new_ste;
 243        int new_idx;
 244        u8 sb_idx;
 245
 246        /* Copy STE mask from the matcher */
 247        sb_idx = cur_ste->ste_chain_location - 1;
 248        mlx5dr_ste_set_bit_mask(hw_ste, nic_matcher->ste_builder[sb_idx].bit_mask);
 249
 250        /* Copy STE control and tag */
 251        memcpy(hw_ste, cur_ste->hw_ste, DR_STE_SIZE_REDUCED);
 252        mlx5dr_ste_set_miss_addr(dmn->ste_ctx, hw_ste,
 253                                 nic_matcher->e_anchor->chunk->icm_addr);
 254
 255        new_idx = mlx5dr_ste_calc_hash_index(hw_ste, new_htbl);
 256        new_ste = &new_htbl->ste_arr[new_idx];
 257
 258        if (mlx5dr_ste_is_not_used(new_ste)) {
 259                mlx5dr_htbl_get(new_htbl);
 260                list_add_tail(&new_ste->miss_list_node,
 261                              mlx5dr_ste_get_miss_list(new_ste));
 262        } else {
 263                new_ste = dr_rule_rehash_handle_collision(matcher,
 264                                                          nic_matcher,
 265                                                          update_list,
 266                                                          new_ste,
 267                                                          hw_ste);
 268                if (!new_ste) {
 269                        mlx5dr_dbg(dmn, "Failed adding collision entry, index: %d\n",
 270                                   new_idx);
 271                        return NULL;
 272                }
 273                new_htbl->ctrl.num_of_collisions++;
 274                use_update_list = true;
 275        }
 276
 277        memcpy(new_ste->hw_ste, hw_ste, DR_STE_SIZE_REDUCED);
 278
 279        new_htbl->ctrl.num_of_valid_entries++;
 280
 281        if (use_update_list) {
 282                ste_info = kzalloc(sizeof(*ste_info), GFP_KERNEL);
 283                if (!ste_info)
 284                        goto err_exit;
 285
 286                mlx5dr_send_fill_and_append_ste_send_info(new_ste, DR_STE_SIZE, 0,
 287                                                          hw_ste, ste_info,
 288                                                          update_list, true);
 289        }
 290
 291        dr_rule_rehash_copy_ste_ctrl(matcher, nic_matcher, cur_ste, new_ste);
 292
 293        return new_ste;
 294
 295err_exit:
 296        mlx5dr_ste_free(new_ste, matcher, nic_matcher);
 297        return NULL;
 298}
 299
 300static int dr_rule_rehash_copy_miss_list(struct mlx5dr_matcher *matcher,
 301                                         struct mlx5dr_matcher_rx_tx *nic_matcher,
 302                                         struct list_head *cur_miss_list,
 303                                         struct mlx5dr_ste_htbl *new_htbl,
 304                                         struct list_head *update_list)
 305{
 306        struct mlx5dr_ste *tmp_ste, *cur_ste, *new_ste;
 307
 308        if (list_empty(cur_miss_list))
 309                return 0;
 310
 311        list_for_each_entry_safe(cur_ste, tmp_ste, cur_miss_list, miss_list_node) {
 312                new_ste = dr_rule_rehash_copy_ste(matcher,
 313                                                  nic_matcher,
 314                                                  cur_ste,
 315                                                  new_htbl,
 316                                                  update_list);
 317                if (!new_ste)
 318                        goto err_insert;
 319
 320                list_del(&cur_ste->miss_list_node);
 321                mlx5dr_htbl_put(cur_ste->htbl);
 322        }
 323        return 0;
 324
 325err_insert:
 326        mlx5dr_err(matcher->tbl->dmn, "Fatal error during resize\n");
 327        WARN_ON(true);
 328        return -EINVAL;
 329}
 330
 331static int dr_rule_rehash_copy_htbl(struct mlx5dr_matcher *matcher,
 332                                    struct mlx5dr_matcher_rx_tx *nic_matcher,
 333                                    struct mlx5dr_ste_htbl *cur_htbl,
 334                                    struct mlx5dr_ste_htbl *new_htbl,
 335                                    struct list_head *update_list)
 336{
 337        struct mlx5dr_ste *cur_ste;
 338        int cur_entries;
 339        int err = 0;
 340        int i;
 341
 342        cur_entries = mlx5dr_icm_pool_chunk_size_to_entries(cur_htbl->chunk_size);
 343
 344        if (cur_entries < 1) {
 345                mlx5dr_dbg(matcher->tbl->dmn, "Invalid number of entries\n");
 346                return -EINVAL;
 347        }
 348
 349        for (i = 0; i < cur_entries; i++) {
 350                cur_ste = &cur_htbl->ste_arr[i];
 351                if (mlx5dr_ste_is_not_used(cur_ste)) /* Empty, nothing to copy */
 352                        continue;
 353
 354                err = dr_rule_rehash_copy_miss_list(matcher,
 355                                                    nic_matcher,
 356                                                    mlx5dr_ste_get_miss_list(cur_ste),
 357                                                    new_htbl,
 358                                                    update_list);
 359                if (err)
 360                        goto clean_copy;
 361        }
 362
 363clean_copy:
 364        return err;
 365}
 366
 367static struct mlx5dr_ste_htbl *
 368dr_rule_rehash_htbl(struct mlx5dr_rule *rule,
 369                    struct mlx5dr_rule_rx_tx *nic_rule,
 370                    struct mlx5dr_ste_htbl *cur_htbl,
 371                    u8 ste_location,
 372                    struct list_head *update_list,
 373                    enum mlx5dr_icm_chunk_size new_size)
 374{
 375        struct mlx5dr_ste_send_info *del_ste_info, *tmp_ste_info;
 376        struct mlx5dr_matcher *matcher = rule->matcher;
 377        struct mlx5dr_domain *dmn = matcher->tbl->dmn;
 378        struct mlx5dr_matcher_rx_tx *nic_matcher;
 379        struct mlx5dr_ste_send_info *ste_info;
 380        struct mlx5dr_htbl_connect_info info;
 381        struct mlx5dr_domain_rx_tx *nic_dmn;
 382        u8 formatted_ste[DR_STE_SIZE] = {};
 383        LIST_HEAD(rehash_table_send_list);
 384        struct mlx5dr_ste *ste_to_update;
 385        struct mlx5dr_ste_htbl *new_htbl;
 386        int err;
 387
 388        nic_matcher = nic_rule->nic_matcher;
 389        nic_dmn = nic_matcher->nic_tbl->nic_dmn;
 390
 391        ste_info = kzalloc(sizeof(*ste_info), GFP_KERNEL);
 392        if (!ste_info)
 393                return NULL;
 394
 395        new_htbl = mlx5dr_ste_htbl_alloc(dmn->ste_icm_pool,
 396                                         new_size,
 397                                         cur_htbl->lu_type,
 398                                         cur_htbl->byte_mask);
 399        if (!new_htbl) {
 400                mlx5dr_err(dmn, "Failed to allocate new hash table\n");
 401                goto free_ste_info;
 402        }
 403
 404        /* Write new table to HW */
 405        info.type = CONNECT_MISS;
 406        info.miss_icm_addr = nic_matcher->e_anchor->chunk->icm_addr;
 407        mlx5dr_ste_set_formatted_ste(dmn->ste_ctx,
 408                                     dmn->info.caps.gvmi,
 409                                     nic_dmn->type,
 410                                     new_htbl,
 411                                     formatted_ste,
 412                                     &info);
 413
 414        new_htbl->pointing_ste = cur_htbl->pointing_ste;
 415        new_htbl->pointing_ste->next_htbl = new_htbl;
 416        err = dr_rule_rehash_copy_htbl(matcher,
 417                                       nic_matcher,
 418                                       cur_htbl,
 419                                       new_htbl,
 420                                       &rehash_table_send_list);
 421        if (err)
 422                goto free_new_htbl;
 423
 424        if (mlx5dr_send_postsend_htbl(dmn, new_htbl, formatted_ste,
 425                                      nic_matcher->ste_builder[ste_location - 1].bit_mask)) {
 426                mlx5dr_err(dmn, "Failed writing table to HW\n");
 427                goto free_new_htbl;
 428        }
 429
 430        /* Writing to the hw is done in regular order of rehash_table_send_list,
 431         * in order to have the origin data written before the miss address of
 432         * collision entries, if exists.
 433         */
 434        if (dr_rule_send_update_list(&rehash_table_send_list, dmn, false)) {
 435                mlx5dr_err(dmn, "Failed updating table to HW\n");
 436                goto free_ste_list;
 437        }
 438
 439        /* Connect previous hash table to current */
 440        if (ste_location == 1) {
 441                /* The previous table is an anchor, anchors size is always one STE */
 442                struct mlx5dr_ste_htbl *prev_htbl = cur_htbl->pointing_ste->htbl;
 443
 444                /* On matcher s_anchor we keep an extra refcount */
 445                mlx5dr_htbl_get(new_htbl);
 446                mlx5dr_htbl_put(cur_htbl);
 447
 448                nic_matcher->s_htbl = new_htbl;
 449
 450                /* It is safe to operate dr_ste_set_hit_addr on the hw_ste here
 451                 * (48B len) which works only on first 32B
 452                 */
 453                mlx5dr_ste_set_hit_addr(dmn->ste_ctx,
 454                                        prev_htbl->ste_arr[0].hw_ste,
 455                                        new_htbl->chunk->icm_addr,
 456                                        new_htbl->chunk->num_of_entries);
 457
 458                ste_to_update = &prev_htbl->ste_arr[0];
 459        } else {
 460                mlx5dr_ste_set_hit_addr_by_next_htbl(dmn->ste_ctx,
 461                                                     cur_htbl->pointing_ste->hw_ste,
 462                                                     new_htbl);
 463                ste_to_update = cur_htbl->pointing_ste;
 464        }
 465
 466        mlx5dr_send_fill_and_append_ste_send_info(ste_to_update, DR_STE_SIZE_CTRL,
 467                                                  0, ste_to_update->hw_ste, ste_info,
 468                                                  update_list, false);
 469
 470        return new_htbl;
 471
 472free_ste_list:
 473        /* Clean all ste_info's from the new table */
 474        list_for_each_entry_safe(del_ste_info, tmp_ste_info,
 475                                 &rehash_table_send_list, send_list) {
 476                list_del(&del_ste_info->send_list);
 477                kfree(del_ste_info);
 478        }
 479
 480free_new_htbl:
 481        mlx5dr_ste_htbl_free(new_htbl);
 482free_ste_info:
 483        kfree(ste_info);
 484        mlx5dr_info(dmn, "Failed creating rehash table\n");
 485        return NULL;
 486}
 487
 488static struct mlx5dr_ste_htbl *dr_rule_rehash(struct mlx5dr_rule *rule,
 489                                              struct mlx5dr_rule_rx_tx *nic_rule,
 490                                              struct mlx5dr_ste_htbl *cur_htbl,
 491                                              u8 ste_location,
 492                                              struct list_head *update_list)
 493{
 494        struct mlx5dr_domain *dmn = rule->matcher->tbl->dmn;
 495        enum mlx5dr_icm_chunk_size new_size;
 496
 497        new_size = mlx5dr_icm_next_higher_chunk(cur_htbl->chunk_size);
 498        new_size = min_t(u32, new_size, dmn->info.max_log_sw_icm_sz);
 499
 500        if (new_size == cur_htbl->chunk_size)
 501                return NULL; /* Skip rehash, we already at the max size */
 502
 503        return dr_rule_rehash_htbl(rule, nic_rule, cur_htbl, ste_location,
 504                                   update_list, new_size);
 505}
 506
 507static struct mlx5dr_ste *
 508dr_rule_handle_collision(struct mlx5dr_matcher *matcher,
 509                         struct mlx5dr_matcher_rx_tx *nic_matcher,
 510                         struct mlx5dr_ste *ste,
 511                         u8 *hw_ste,
 512                         struct list_head *miss_list,
 513                         struct list_head *send_list)
 514{
 515        struct mlx5dr_domain *dmn = matcher->tbl->dmn;
 516        struct mlx5dr_ste_ctx *ste_ctx = dmn->ste_ctx;
 517        struct mlx5dr_ste_send_info *ste_info;
 518        struct mlx5dr_ste *new_ste;
 519
 520        ste_info = kzalloc(sizeof(*ste_info), GFP_KERNEL);
 521        if (!ste_info)
 522                return NULL;
 523
 524        new_ste = dr_rule_create_collision_entry(matcher, nic_matcher, hw_ste, ste);
 525        if (!new_ste)
 526                goto free_send_info;
 527
 528        if (dr_rule_append_to_miss_list(ste_ctx, new_ste,
 529                                        miss_list, send_list)) {
 530                mlx5dr_dbg(dmn, "Failed to update prev miss_list\n");
 531                goto err_exit;
 532        }
 533
 534        mlx5dr_send_fill_and_append_ste_send_info(new_ste, DR_STE_SIZE, 0, hw_ste,
 535                                                  ste_info, send_list, false);
 536
 537        ste->htbl->ctrl.num_of_collisions++;
 538        ste->htbl->ctrl.num_of_valid_entries++;
 539
 540        return new_ste;
 541
 542err_exit:
 543        mlx5dr_ste_free(new_ste, matcher, nic_matcher);
 544free_send_info:
 545        kfree(ste_info);
 546        return NULL;
 547}
 548
 549static void dr_rule_remove_action_members(struct mlx5dr_rule *rule)
 550{
 551        struct mlx5dr_rule_action_member *action_mem;
 552        struct mlx5dr_rule_action_member *tmp;
 553
 554        list_for_each_entry_safe(action_mem, tmp, &rule->rule_actions_list, list) {
 555                list_del(&action_mem->list);
 556                refcount_dec(&action_mem->action->refcount);
 557                kvfree(action_mem);
 558        }
 559}
 560
 561static int dr_rule_add_action_members(struct mlx5dr_rule *rule,
 562                                      size_t num_actions,
 563                                      struct mlx5dr_action *actions[])
 564{
 565        struct mlx5dr_rule_action_member *action_mem;
 566        int i;
 567
 568        for (i = 0; i < num_actions; i++) {
 569                action_mem = kvzalloc(sizeof(*action_mem), GFP_KERNEL);
 570                if (!action_mem)
 571                        goto free_action_members;
 572
 573                action_mem->action = actions[i];
 574                INIT_LIST_HEAD(&action_mem->list);
 575                list_add_tail(&action_mem->list, &rule->rule_actions_list);
 576                refcount_inc(&action_mem->action->refcount);
 577        }
 578
 579        return 0;
 580
 581free_action_members:
 582        dr_rule_remove_action_members(rule);
 583        return -ENOMEM;
 584}
 585
 586void mlx5dr_rule_set_last_member(struct mlx5dr_rule_rx_tx *nic_rule,
 587                                 struct mlx5dr_ste *ste,
 588                                 bool force)
 589{
 590        /* Update rule member is usually done for the last STE or during rule
 591         * creation to recover from mid-creation failure (for this peruse the
 592         * force flag is used)
 593         */
 594        if (ste->next_htbl && !force)
 595                return;
 596
 597        /* Update is required since each rule keeps track of its last STE */
 598        ste->rule_rx_tx = nic_rule;
 599        nic_rule->last_rule_ste = ste;
 600}
 601
 602static struct mlx5dr_ste *dr_rule_get_pointed_ste(struct mlx5dr_ste *curr_ste)
 603{
 604        struct mlx5dr_ste *first_ste;
 605
 606        first_ste = list_first_entry(mlx5dr_ste_get_miss_list(curr_ste),
 607                                     struct mlx5dr_ste, miss_list_node);
 608
 609        return first_ste->htbl->pointing_ste;
 610}
 611
 612int mlx5dr_rule_get_reverse_rule_members(struct mlx5dr_ste **ste_arr,
 613                                         struct mlx5dr_ste *curr_ste,
 614                                         int *num_of_stes)
 615{
 616        bool first = false;
 617
 618        *num_of_stes = 0;
 619
 620        if (!curr_ste)
 621                return -ENOENT;
 622
 623        /* Iterate from last to first */
 624        while (!first) {
 625                first = curr_ste->ste_chain_location == 1;
 626                ste_arr[*num_of_stes] = curr_ste;
 627                *num_of_stes += 1;
 628                curr_ste = dr_rule_get_pointed_ste(curr_ste);
 629        }
 630
 631        return 0;
 632}
 633
 634static void dr_rule_clean_rule_members(struct mlx5dr_rule *rule,
 635                                       struct mlx5dr_rule_rx_tx *nic_rule)
 636{
 637        struct mlx5dr_ste *ste_arr[DR_RULE_MAX_STES + DR_ACTION_MAX_STES];
 638        struct mlx5dr_ste *curr_ste = nic_rule->last_rule_ste;
 639        int i;
 640
 641        if (mlx5dr_rule_get_reverse_rule_members(ste_arr, curr_ste, &i))
 642                return;
 643
 644        while (i--)
 645                mlx5dr_ste_put(ste_arr[i], rule->matcher, nic_rule->nic_matcher);
 646}
 647
 648static u16 dr_get_bits_per_mask(u16 byte_mask)
 649{
 650        u16 bits = 0;
 651
 652        while (byte_mask) {
 653                byte_mask = byte_mask & (byte_mask - 1);
 654                bits++;
 655        }
 656
 657        return bits;
 658}
 659
 660static bool dr_rule_need_enlarge_hash(struct mlx5dr_ste_htbl *htbl,
 661                                      struct mlx5dr_domain *dmn,
 662                                      struct mlx5dr_domain_rx_tx *nic_dmn)
 663{
 664        struct mlx5dr_ste_htbl_ctrl *ctrl = &htbl->ctrl;
 665        int threshold;
 666
 667        if (dmn->info.max_log_sw_icm_sz <= htbl->chunk_size)
 668                return false;
 669
 670        if (!mlx5dr_ste_htbl_may_grow(htbl))
 671                return false;
 672
 673        if (dr_get_bits_per_mask(htbl->byte_mask) * BITS_PER_BYTE <= htbl->chunk_size)
 674                return false;
 675
 676        threshold = mlx5dr_ste_htbl_increase_threshold(htbl);
 677        if (ctrl->num_of_collisions >= threshold &&
 678            (ctrl->num_of_valid_entries - ctrl->num_of_collisions) >= threshold)
 679                return true;
 680
 681        return false;
 682}
 683
 684static int dr_rule_handle_action_stes(struct mlx5dr_rule *rule,
 685                                      struct mlx5dr_rule_rx_tx *nic_rule,
 686                                      struct list_head *send_ste_list,
 687                                      struct mlx5dr_ste *last_ste,
 688                                      u8 *hw_ste_arr,
 689                                      u32 new_hw_ste_arr_sz)
 690{
 691        struct mlx5dr_matcher_rx_tx *nic_matcher = nic_rule->nic_matcher;
 692        struct mlx5dr_ste_send_info *ste_info_arr[DR_ACTION_MAX_STES];
 693        u8 num_of_builders = nic_matcher->num_of_builders;
 694        struct mlx5dr_matcher *matcher = rule->matcher;
 695        struct mlx5dr_domain *dmn = matcher->tbl->dmn;
 696        u8 *curr_hw_ste, *prev_hw_ste;
 697        struct mlx5dr_ste *action_ste;
 698        int i, k;
 699
 700        /* Two cases:
 701         * 1. num_of_builders is equal to new_hw_ste_arr_sz, the action in the ste
 702         * 2. num_of_builders is less then new_hw_ste_arr_sz, new ste was added
 703         *    to support the action.
 704         */
 705
 706        for (i = num_of_builders, k = 0; i < new_hw_ste_arr_sz; i++, k++) {
 707                curr_hw_ste = hw_ste_arr + i * DR_STE_SIZE;
 708                prev_hw_ste = (i == 0) ? curr_hw_ste : hw_ste_arr + ((i - 1) * DR_STE_SIZE);
 709                action_ste = dr_rule_create_collision_htbl(matcher,
 710                                                           nic_matcher,
 711                                                           curr_hw_ste);
 712                if (!action_ste)
 713                        return -ENOMEM;
 714
 715                mlx5dr_ste_get(action_ste);
 716
 717                action_ste->htbl->pointing_ste = last_ste;
 718                last_ste->next_htbl = action_ste->htbl;
 719                last_ste = action_ste;
 720
 721                /* While free ste we go over the miss list, so add this ste to the list */
 722                list_add_tail(&action_ste->miss_list_node,
 723                              mlx5dr_ste_get_miss_list(action_ste));
 724
 725                ste_info_arr[k] = kzalloc(sizeof(*ste_info_arr[k]),
 726                                          GFP_KERNEL);
 727                if (!ste_info_arr[k])
 728                        goto err_exit;
 729
 730                /* Point current ste to the new action */
 731                mlx5dr_ste_set_hit_addr_by_next_htbl(dmn->ste_ctx,
 732                                                     prev_hw_ste,
 733                                                     action_ste->htbl);
 734
 735                mlx5dr_rule_set_last_member(nic_rule, action_ste, true);
 736
 737                mlx5dr_send_fill_and_append_ste_send_info(action_ste, DR_STE_SIZE, 0,
 738                                                          curr_hw_ste,
 739                                                          ste_info_arr[k],
 740                                                          send_ste_list, false);
 741        }
 742
 743        last_ste->next_htbl = NULL;
 744
 745        return 0;
 746
 747err_exit:
 748        mlx5dr_ste_put(action_ste, matcher, nic_matcher);
 749        return -ENOMEM;
 750}
 751
 752static int dr_rule_handle_empty_entry(struct mlx5dr_matcher *matcher,
 753                                      struct mlx5dr_matcher_rx_tx *nic_matcher,
 754                                      struct mlx5dr_ste_htbl *cur_htbl,
 755                                      struct mlx5dr_ste *ste,
 756                                      u8 ste_location,
 757                                      u8 *hw_ste,
 758                                      struct list_head *miss_list,
 759                                      struct list_head *send_list)
 760{
 761        struct mlx5dr_domain *dmn = matcher->tbl->dmn;
 762        struct mlx5dr_ste_send_info *ste_info;
 763
 764        /* Take ref on table, only on first time this ste is used */
 765        mlx5dr_htbl_get(cur_htbl);
 766
 767        /* new entry -> new branch */
 768        list_add_tail(&ste->miss_list_node, miss_list);
 769
 770        mlx5dr_ste_set_miss_addr(dmn->ste_ctx, hw_ste,
 771                                 nic_matcher->e_anchor->chunk->icm_addr);
 772
 773        ste->ste_chain_location = ste_location;
 774
 775        ste_info = kzalloc(sizeof(*ste_info), GFP_KERNEL);
 776        if (!ste_info)
 777                goto clean_ste_setting;
 778
 779        if (mlx5dr_ste_create_next_htbl(matcher,
 780                                        nic_matcher,
 781                                        ste,
 782                                        hw_ste,
 783                                        DR_CHUNK_SIZE_1)) {
 784                mlx5dr_dbg(dmn, "Failed allocating table\n");
 785                goto clean_ste_info;
 786        }
 787
 788        cur_htbl->ctrl.num_of_valid_entries++;
 789
 790        mlx5dr_send_fill_and_append_ste_send_info(ste, DR_STE_SIZE, 0, hw_ste,
 791                                                  ste_info, send_list, false);
 792
 793        return 0;
 794
 795clean_ste_info:
 796        kfree(ste_info);
 797clean_ste_setting:
 798        list_del_init(&ste->miss_list_node);
 799        mlx5dr_htbl_put(cur_htbl);
 800
 801        return -ENOMEM;
 802}
 803
 804static struct mlx5dr_ste *
 805dr_rule_handle_ste_branch(struct mlx5dr_rule *rule,
 806                          struct mlx5dr_rule_rx_tx *nic_rule,
 807                          struct list_head *send_ste_list,
 808                          struct mlx5dr_ste_htbl *cur_htbl,
 809                          u8 *hw_ste,
 810                          u8 ste_location,
 811                          struct mlx5dr_ste_htbl **put_htbl)
 812{
 813        struct mlx5dr_matcher *matcher = rule->matcher;
 814        struct mlx5dr_domain *dmn = matcher->tbl->dmn;
 815        struct mlx5dr_matcher_rx_tx *nic_matcher;
 816        struct mlx5dr_domain_rx_tx *nic_dmn;
 817        struct mlx5dr_ste_htbl *new_htbl;
 818        struct mlx5dr_ste *matched_ste;
 819        struct list_head *miss_list;
 820        bool skip_rehash = false;
 821        struct mlx5dr_ste *ste;
 822        int index;
 823
 824        nic_matcher = nic_rule->nic_matcher;
 825        nic_dmn = nic_matcher->nic_tbl->nic_dmn;
 826
 827again:
 828        index = mlx5dr_ste_calc_hash_index(hw_ste, cur_htbl);
 829        miss_list = &cur_htbl->chunk->miss_list[index];
 830        ste = &cur_htbl->ste_arr[index];
 831
 832        if (mlx5dr_ste_is_not_used(ste)) {
 833                if (dr_rule_handle_empty_entry(matcher, nic_matcher, cur_htbl,
 834                                               ste, ste_location,
 835                                               hw_ste, miss_list,
 836                                               send_ste_list))
 837                        return NULL;
 838        } else {
 839                /* Hash table index in use, check if this ste is in the miss list */
 840                matched_ste = dr_rule_find_ste_in_miss_list(miss_list, hw_ste);
 841                if (matched_ste) {
 842                        /* If it is last STE in the chain, and has the same tag
 843                         * it means that all the previous stes are the same,
 844                         * if so, this rule is duplicated.
 845                         */
 846                        if (!mlx5dr_ste_is_last_in_rule(nic_matcher, ste_location))
 847                                return matched_ste;
 848
 849                        mlx5dr_dbg(dmn, "Duplicate rule inserted\n");
 850                }
 851
 852                if (!skip_rehash && dr_rule_need_enlarge_hash(cur_htbl, dmn, nic_dmn)) {
 853                        /* Hash table index in use, try to resize of the hash */
 854                        skip_rehash = true;
 855
 856                        /* Hold the table till we update.
 857                         * Release in dr_rule_create_rule()
 858                         */
 859                        *put_htbl = cur_htbl;
 860                        mlx5dr_htbl_get(cur_htbl);
 861
 862                        new_htbl = dr_rule_rehash(rule, nic_rule, cur_htbl,
 863                                                  ste_location, send_ste_list);
 864                        if (!new_htbl) {
 865                                mlx5dr_err(dmn, "Failed creating rehash table, htbl-log_size: %d\n",
 866                                           cur_htbl->chunk_size);
 867                                mlx5dr_htbl_put(cur_htbl);
 868                        } else {
 869                                cur_htbl = new_htbl;
 870                        }
 871                        goto again;
 872                } else {
 873                        /* Hash table index in use, add another collision (miss) */
 874                        ste = dr_rule_handle_collision(matcher,
 875                                                       nic_matcher,
 876                                                       ste,
 877                                                       hw_ste,
 878                                                       miss_list,
 879                                                       send_ste_list);
 880                        if (!ste) {
 881                                mlx5dr_dbg(dmn, "failed adding collision entry, index: %d\n",
 882                                           index);
 883                                return NULL;
 884                        }
 885                }
 886        }
 887        return ste;
 888}
 889
 890static bool dr_rule_cmp_value_to_mask(u8 *mask, u8 *value,
 891                                      u32 s_idx, u32 e_idx)
 892{
 893        u32 i;
 894
 895        for (i = s_idx; i < e_idx; i++) {
 896                if (value[i] & ~mask[i]) {
 897                        pr_info("Rule parameters contains a value not specified by mask\n");
 898                        return false;
 899                }
 900        }
 901        return true;
 902}
 903
 904static bool dr_rule_verify(struct mlx5dr_matcher *matcher,
 905                           struct mlx5dr_match_parameters *value,
 906                           struct mlx5dr_match_param *param)
 907{
 908        u8 match_criteria = matcher->match_criteria;
 909        size_t value_size = value->match_sz;
 910        u8 *mask_p = (u8 *)&matcher->mask;
 911        u8 *param_p = (u8 *)param;
 912        u32 s_idx, e_idx;
 913
 914        if (!value_size ||
 915            (value_size > DR_SZ_MATCH_PARAM || (value_size % sizeof(u32)))) {
 916                mlx5dr_err(matcher->tbl->dmn, "Rule parameters length is incorrect\n");
 917                return false;
 918        }
 919
 920        mlx5dr_ste_copy_param(matcher->match_criteria, param, value);
 921
 922        if (match_criteria & DR_MATCHER_CRITERIA_OUTER) {
 923                s_idx = offsetof(struct mlx5dr_match_param, outer);
 924                e_idx = min(s_idx + sizeof(param->outer), value_size);
 925
 926                if (!dr_rule_cmp_value_to_mask(mask_p, param_p, s_idx, e_idx)) {
 927                        mlx5dr_err(matcher->tbl->dmn, "Rule outer parameters contains a value not specified by mask\n");
 928                        return false;
 929                }
 930        }
 931
 932        if (match_criteria & DR_MATCHER_CRITERIA_MISC) {
 933                s_idx = offsetof(struct mlx5dr_match_param, misc);
 934                e_idx = min(s_idx + sizeof(param->misc), value_size);
 935
 936                if (!dr_rule_cmp_value_to_mask(mask_p, param_p, s_idx, e_idx)) {
 937                        mlx5dr_err(matcher->tbl->dmn, "Rule misc parameters contains a value not specified by mask\n");
 938                        return false;
 939                }
 940        }
 941
 942        if (match_criteria & DR_MATCHER_CRITERIA_INNER) {
 943                s_idx = offsetof(struct mlx5dr_match_param, inner);
 944                e_idx = min(s_idx + sizeof(param->inner), value_size);
 945
 946                if (!dr_rule_cmp_value_to_mask(mask_p, param_p, s_idx, e_idx)) {
 947                        mlx5dr_err(matcher->tbl->dmn, "Rule inner parameters contains a value not specified by mask\n");
 948                        return false;
 949                }
 950        }
 951
 952        if (match_criteria & DR_MATCHER_CRITERIA_MISC2) {
 953                s_idx = offsetof(struct mlx5dr_match_param, misc2);
 954                e_idx = min(s_idx + sizeof(param->misc2), value_size);
 955
 956                if (!dr_rule_cmp_value_to_mask(mask_p, param_p, s_idx, e_idx)) {
 957                        mlx5dr_err(matcher->tbl->dmn, "Rule misc2 parameters contains a value not specified by mask\n");
 958                        return false;
 959                }
 960        }
 961
 962        if (match_criteria & DR_MATCHER_CRITERIA_MISC3) {
 963                s_idx = offsetof(struct mlx5dr_match_param, misc3);
 964                e_idx = min(s_idx + sizeof(param->misc3), value_size);
 965
 966                if (!dr_rule_cmp_value_to_mask(mask_p, param_p, s_idx, e_idx)) {
 967                        mlx5dr_err(matcher->tbl->dmn, "Rule misc3 parameters contains a value not specified by mask\n");
 968                        return false;
 969                }
 970        }
 971
 972        if (match_criteria & DR_MATCHER_CRITERIA_MISC4) {
 973                s_idx = offsetof(struct mlx5dr_match_param, misc4);
 974                e_idx = min(s_idx + sizeof(param->misc4), value_size);
 975
 976                if (!dr_rule_cmp_value_to_mask(mask_p, param_p, s_idx, e_idx)) {
 977                        mlx5dr_err(matcher->tbl->dmn,
 978                                   "Rule misc4 parameters contains a value not specified by mask\n");
 979                        return false;
 980                }
 981        }
 982        return true;
 983}
 984
 985static int dr_rule_destroy_rule_nic(struct mlx5dr_rule *rule,
 986                                    struct mlx5dr_rule_rx_tx *nic_rule)
 987{
 988        mlx5dr_domain_nic_lock(nic_rule->nic_matcher->nic_tbl->nic_dmn);
 989        dr_rule_clean_rule_members(rule, nic_rule);
 990        mlx5dr_domain_nic_unlock(nic_rule->nic_matcher->nic_tbl->nic_dmn);
 991
 992        return 0;
 993}
 994
 995static int dr_rule_destroy_rule_fdb(struct mlx5dr_rule *rule)
 996{
 997        dr_rule_destroy_rule_nic(rule, &rule->rx);
 998        dr_rule_destroy_rule_nic(rule, &rule->tx);
 999        return 0;
1000}
1001
1002static int dr_rule_destroy_rule(struct mlx5dr_rule *rule)
1003{
1004        struct mlx5dr_domain *dmn = rule->matcher->tbl->dmn;
1005
1006        switch (dmn->type) {
1007        case MLX5DR_DOMAIN_TYPE_NIC_RX:
1008                dr_rule_destroy_rule_nic(rule, &rule->rx);
1009                break;
1010        case MLX5DR_DOMAIN_TYPE_NIC_TX:
1011                dr_rule_destroy_rule_nic(rule, &rule->tx);
1012                break;
1013        case MLX5DR_DOMAIN_TYPE_FDB:
1014                dr_rule_destroy_rule_fdb(rule);
1015                break;
1016        default:
1017                return -EINVAL;
1018        }
1019
1020        dr_rule_remove_action_members(rule);
1021        kfree(rule);
1022        return 0;
1023}
1024
1025static enum mlx5dr_ipv dr_rule_get_ipv(struct mlx5dr_match_spec *spec)
1026{
1027        if (spec->ip_version == 6 || spec->ethertype == ETH_P_IPV6)
1028                return DR_RULE_IPV6;
1029
1030        return DR_RULE_IPV4;
1031}
1032
1033static bool dr_rule_skip(enum mlx5dr_domain_type domain,
1034                         enum mlx5dr_domain_nic_type nic_type,
1035                         struct mlx5dr_match_param *mask,
1036                         struct mlx5dr_match_param *value,
1037                         u32 flow_source)
1038{
1039        bool rx = nic_type == DR_DOMAIN_NIC_TYPE_RX;
1040
1041        if (domain != MLX5DR_DOMAIN_TYPE_FDB)
1042                return false;
1043
1044        if (mask->misc.source_port) {
1045                if (rx && value->misc.source_port != WIRE_PORT)
1046                        return true;
1047
1048                if (!rx && value->misc.source_port == WIRE_PORT)
1049                        return true;
1050        }
1051
1052        if (rx && flow_source == MLX5_FLOW_CONTEXT_FLOW_SOURCE_LOCAL_VPORT)
1053                return true;
1054
1055        if (!rx && flow_source == MLX5_FLOW_CONTEXT_FLOW_SOURCE_UPLINK)
1056                return true;
1057
1058        return false;
1059}
1060
1061static int
1062dr_rule_create_rule_nic(struct mlx5dr_rule *rule,
1063                        struct mlx5dr_rule_rx_tx *nic_rule,
1064                        struct mlx5dr_match_param *param,
1065                        size_t num_actions,
1066                        struct mlx5dr_action *actions[])
1067{
1068        struct mlx5dr_ste_send_info *ste_info, *tmp_ste_info;
1069        struct mlx5dr_matcher *matcher = rule->matcher;
1070        struct mlx5dr_domain *dmn = matcher->tbl->dmn;
1071        struct mlx5dr_matcher_rx_tx *nic_matcher;
1072        struct mlx5dr_domain_rx_tx *nic_dmn;
1073        struct mlx5dr_ste_htbl *htbl = NULL;
1074        struct mlx5dr_ste_htbl *cur_htbl;
1075        struct mlx5dr_ste *ste = NULL;
1076        LIST_HEAD(send_ste_list);
1077        u8 *hw_ste_arr = NULL;
1078        u32 new_hw_ste_arr_sz;
1079        int ret, i;
1080
1081        nic_matcher = nic_rule->nic_matcher;
1082        nic_dmn = nic_matcher->nic_tbl->nic_dmn;
1083
1084        if (dr_rule_skip(dmn->type, nic_dmn->type, &matcher->mask, param,
1085                         rule->flow_source))
1086                return 0;
1087
1088        hw_ste_arr = kzalloc(DR_RULE_MAX_STE_CHAIN * DR_STE_SIZE, GFP_KERNEL);
1089        if (!hw_ste_arr)
1090                return -ENOMEM;
1091
1092        mlx5dr_domain_nic_lock(nic_dmn);
1093
1094        ret = mlx5dr_matcher_select_builders(matcher,
1095                                             nic_matcher,
1096                                             dr_rule_get_ipv(&param->outer),
1097                                             dr_rule_get_ipv(&param->inner));
1098        if (ret)
1099                goto free_hw_ste;
1100
1101        /* Set the tag values inside the ste array */
1102        ret = mlx5dr_ste_build_ste_arr(matcher, nic_matcher, param, hw_ste_arr);
1103        if (ret)
1104                goto free_hw_ste;
1105
1106        /* Set the actions values/addresses inside the ste array */
1107        ret = mlx5dr_actions_build_ste_arr(matcher, nic_matcher, actions,
1108                                           num_actions, hw_ste_arr,
1109                                           &new_hw_ste_arr_sz);
1110        if (ret)
1111                goto free_hw_ste;
1112
1113        cur_htbl = nic_matcher->s_htbl;
1114
1115        /* Go over the array of STEs, and build dr_ste accordingly.
1116         * The loop is over only the builders which are equal or less to the
1117         * number of stes, in case we have actions that lives in other stes.
1118         */
1119        for (i = 0; i < nic_matcher->num_of_builders; i++) {
1120                /* Calculate CRC and keep new ste entry */
1121                u8 *cur_hw_ste_ent = hw_ste_arr + (i * DR_STE_SIZE);
1122
1123                ste = dr_rule_handle_ste_branch(rule,
1124                                                nic_rule,
1125                                                &send_ste_list,
1126                                                cur_htbl,
1127                                                cur_hw_ste_ent,
1128                                                i + 1,
1129                                                &htbl);
1130                if (!ste) {
1131                        mlx5dr_err(dmn, "Failed creating next branch\n");
1132                        ret = -ENOENT;
1133                        goto free_rule;
1134                }
1135
1136                cur_htbl = ste->next_htbl;
1137
1138                mlx5dr_ste_get(ste);
1139                mlx5dr_rule_set_last_member(nic_rule, ste, true);
1140        }
1141
1142        /* Connect actions */
1143        ret = dr_rule_handle_action_stes(rule, nic_rule, &send_ste_list,
1144                                         ste, hw_ste_arr, new_hw_ste_arr_sz);
1145        if (ret) {
1146                mlx5dr_dbg(dmn, "Failed apply actions\n");
1147                goto free_rule;
1148        }
1149        ret = dr_rule_send_update_list(&send_ste_list, dmn, true);
1150        if (ret) {
1151                mlx5dr_err(dmn, "Failed sending ste!\n");
1152                goto free_rule;
1153        }
1154
1155        if (htbl)
1156                mlx5dr_htbl_put(htbl);
1157
1158        mlx5dr_domain_nic_unlock(nic_dmn);
1159
1160        kfree(hw_ste_arr);
1161
1162        return 0;
1163
1164free_rule:
1165        dr_rule_clean_rule_members(rule, nic_rule);
1166        /* Clean all ste_info's */
1167        list_for_each_entry_safe(ste_info, tmp_ste_info, &send_ste_list, send_list) {
1168                list_del(&ste_info->send_list);
1169                kfree(ste_info);
1170        }
1171free_hw_ste:
1172        mlx5dr_domain_nic_unlock(nic_dmn);
1173        kfree(hw_ste_arr);
1174        return ret;
1175}
1176
1177static int
1178dr_rule_create_rule_fdb(struct mlx5dr_rule *rule,
1179                        struct mlx5dr_match_param *param,
1180                        size_t num_actions,
1181                        struct mlx5dr_action *actions[])
1182{
1183        struct mlx5dr_match_param copy_param = {};
1184        int ret;
1185
1186        /* Copy match_param since they will be consumed during the first
1187         * nic_rule insertion.
1188         */
1189        memcpy(&copy_param, param, sizeof(struct mlx5dr_match_param));
1190
1191        ret = dr_rule_create_rule_nic(rule, &rule->rx, param,
1192                                      num_actions, actions);
1193        if (ret)
1194                return ret;
1195
1196        ret = dr_rule_create_rule_nic(rule, &rule->tx, &copy_param,
1197                                      num_actions, actions);
1198        if (ret)
1199                goto destroy_rule_nic_rx;
1200
1201        return 0;
1202
1203destroy_rule_nic_rx:
1204        dr_rule_destroy_rule_nic(rule, &rule->rx);
1205        return ret;
1206}
1207
1208static struct mlx5dr_rule *
1209dr_rule_create_rule(struct mlx5dr_matcher *matcher,
1210                    struct mlx5dr_match_parameters *value,
1211                    size_t num_actions,
1212                    struct mlx5dr_action *actions[],
1213                    u32 flow_source)
1214{
1215        struct mlx5dr_domain *dmn = matcher->tbl->dmn;
1216        struct mlx5dr_match_param param = {};
1217        struct mlx5dr_rule *rule;
1218        int ret;
1219
1220        if (!dr_rule_verify(matcher, value, &param))
1221                return NULL;
1222
1223        rule = kzalloc(sizeof(*rule), GFP_KERNEL);
1224        if (!rule)
1225                return NULL;
1226
1227        rule->matcher = matcher;
1228        rule->flow_source = flow_source;
1229        INIT_LIST_HEAD(&rule->rule_actions_list);
1230
1231        ret = dr_rule_add_action_members(rule, num_actions, actions);
1232        if (ret)
1233                goto free_rule;
1234
1235        switch (dmn->type) {
1236        case MLX5DR_DOMAIN_TYPE_NIC_RX:
1237                rule->rx.nic_matcher = &matcher->rx;
1238                ret = dr_rule_create_rule_nic(rule, &rule->rx, &param,
1239                                              num_actions, actions);
1240                break;
1241        case MLX5DR_DOMAIN_TYPE_NIC_TX:
1242                rule->tx.nic_matcher = &matcher->tx;
1243                ret = dr_rule_create_rule_nic(rule, &rule->tx, &param,
1244                                              num_actions, actions);
1245                break;
1246        case MLX5DR_DOMAIN_TYPE_FDB:
1247                rule->rx.nic_matcher = &matcher->rx;
1248                rule->tx.nic_matcher = &matcher->tx;
1249                ret = dr_rule_create_rule_fdb(rule, &param,
1250                                              num_actions, actions);
1251                break;
1252        default:
1253                ret = -EINVAL;
1254                break;
1255        }
1256
1257        if (ret)
1258                goto remove_action_members;
1259
1260        return rule;
1261
1262remove_action_members:
1263        dr_rule_remove_action_members(rule);
1264free_rule:
1265        kfree(rule);
1266        mlx5dr_err(dmn, "Failed creating rule\n");
1267        return NULL;
1268}
1269
1270struct mlx5dr_rule *mlx5dr_rule_create(struct mlx5dr_matcher *matcher,
1271                                       struct mlx5dr_match_parameters *value,
1272                                       size_t num_actions,
1273                                       struct mlx5dr_action *actions[],
1274                                       u32 flow_source)
1275{
1276        struct mlx5dr_rule *rule;
1277
1278        refcount_inc(&matcher->refcount);
1279
1280        rule = dr_rule_create_rule(matcher, value, num_actions, actions, flow_source);
1281        if (!rule)
1282                refcount_dec(&matcher->refcount);
1283
1284        return rule;
1285}
1286
1287int mlx5dr_rule_destroy(struct mlx5dr_rule *rule)
1288{
1289        struct mlx5dr_matcher *matcher = rule->matcher;
1290        int ret;
1291
1292        ret = dr_rule_destroy_rule(rule);
1293        if (!ret)
1294                refcount_dec(&matcher->refcount);
1295
1296        return ret;
1297}
1298