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