linux/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_matcher.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
   6static bool dr_mask_is_smac_set(struct mlx5dr_match_spec *spec)
   7{
   8        return (spec->smac_47_16 || spec->smac_15_0);
   9}
  10
  11static bool dr_mask_is_dmac_set(struct mlx5dr_match_spec *spec)
  12{
  13        return (spec->dmac_47_16 || spec->dmac_15_0);
  14}
  15
  16static bool dr_mask_is_src_addr_set(struct mlx5dr_match_spec *spec)
  17{
  18        return (spec->src_ip_127_96 || spec->src_ip_95_64 ||
  19                spec->src_ip_63_32 || spec->src_ip_31_0);
  20}
  21
  22static bool dr_mask_is_dst_addr_set(struct mlx5dr_match_spec *spec)
  23{
  24        return (spec->dst_ip_127_96 || spec->dst_ip_95_64 ||
  25                spec->dst_ip_63_32 || spec->dst_ip_31_0);
  26}
  27
  28static bool dr_mask_is_l3_base_set(struct mlx5dr_match_spec *spec)
  29{
  30        return (spec->ip_protocol || spec->frag || spec->tcp_flags ||
  31                spec->ip_ecn || spec->ip_dscp);
  32}
  33
  34static bool dr_mask_is_tcp_udp_base_set(struct mlx5dr_match_spec *spec)
  35{
  36        return (spec->tcp_sport || spec->tcp_dport ||
  37                spec->udp_sport || spec->udp_dport);
  38}
  39
  40static bool dr_mask_is_ipv4_set(struct mlx5dr_match_spec *spec)
  41{
  42        return (spec->dst_ip_31_0 || spec->src_ip_31_0);
  43}
  44
  45static bool dr_mask_is_ipv4_5_tuple_set(struct mlx5dr_match_spec *spec)
  46{
  47        return (dr_mask_is_l3_base_set(spec) ||
  48                dr_mask_is_tcp_udp_base_set(spec) ||
  49                dr_mask_is_ipv4_set(spec));
  50}
  51
  52static bool dr_mask_is_eth_l2_tnl_set(struct mlx5dr_match_misc *misc)
  53{
  54        return misc->vxlan_vni;
  55}
  56
  57static bool dr_mask_is_ttl_set(struct mlx5dr_match_spec *spec)
  58{
  59        return spec->ttl_hoplimit;
  60}
  61
  62#define DR_MASK_IS_L2_DST(_spec, _misc, _inner_outer) (_spec.first_vid || \
  63        (_spec).first_cfi || (_spec).first_prio || (_spec).cvlan_tag || \
  64        (_spec).svlan_tag || (_spec).dmac_47_16 || (_spec).dmac_15_0 || \
  65        (_spec).ethertype || (_spec).ip_version || \
  66        (_misc)._inner_outer##_second_vid || \
  67        (_misc)._inner_outer##_second_cfi || \
  68        (_misc)._inner_outer##_second_prio || \
  69        (_misc)._inner_outer##_second_cvlan_tag || \
  70        (_misc)._inner_outer##_second_svlan_tag)
  71
  72#define DR_MASK_IS_ETH_L4_SET(_spec, _misc, _inner_outer) ( \
  73        dr_mask_is_l3_base_set(&(_spec)) || \
  74        dr_mask_is_tcp_udp_base_set(&(_spec)) || \
  75        dr_mask_is_ttl_set(&(_spec)) || \
  76        (_misc)._inner_outer##_ipv6_flow_label)
  77
  78#define DR_MASK_IS_ETH_L4_MISC_SET(_misc3, _inner_outer) ( \
  79        (_misc3)._inner_outer##_tcp_seq_num || \
  80        (_misc3)._inner_outer##_tcp_ack_num)
  81
  82#define DR_MASK_IS_FIRST_MPLS_SET(_misc2, _inner_outer) ( \
  83        (_misc2)._inner_outer##_first_mpls_label || \
  84        (_misc2)._inner_outer##_first_mpls_exp || \
  85        (_misc2)._inner_outer##_first_mpls_s_bos || \
  86        (_misc2)._inner_outer##_first_mpls_ttl)
  87
  88static bool dr_mask_is_gre_set(struct mlx5dr_match_misc *misc)
  89{
  90        return (misc->gre_key_h || misc->gre_key_l ||
  91                misc->gre_protocol || misc->gre_c_present ||
  92                misc->gre_k_present || misc->gre_s_present);
  93}
  94
  95#define DR_MASK_IS_OUTER_MPLS_OVER_GRE_UDP_SET(_misc2, gre_udp) ( \
  96        (_misc2).outer_first_mpls_over_##gre_udp##_label || \
  97        (_misc2).outer_first_mpls_over_##gre_udp##_exp || \
  98        (_misc2).outer_first_mpls_over_##gre_udp##_s_bos || \
  99        (_misc2).outer_first_mpls_over_##gre_udp##_ttl)
 100
 101#define DR_MASK_IS_FLEX_PARSER_0_SET(_misc2) ( \
 102        DR_MASK_IS_OUTER_MPLS_OVER_GRE_UDP_SET((_misc2), gre) || \
 103        DR_MASK_IS_OUTER_MPLS_OVER_GRE_UDP_SET((_misc2), udp))
 104
 105static bool dr_mask_is_flex_parser_tnl_set(struct mlx5dr_match_misc3 *misc3)
 106{
 107        return (misc3->outer_vxlan_gpe_vni ||
 108                misc3->outer_vxlan_gpe_next_protocol ||
 109                misc3->outer_vxlan_gpe_flags);
 110}
 111
 112static bool dr_mask_is_flex_parser_icmpv6_set(struct mlx5dr_match_misc3 *misc3)
 113{
 114        return (misc3->icmpv6_type || misc3->icmpv6_code ||
 115                misc3->icmpv6_header_data);
 116}
 117
 118static bool dr_mask_is_wqe_metadata_set(struct mlx5dr_match_misc2 *misc2)
 119{
 120        return misc2->metadata_reg_a;
 121}
 122
 123static bool dr_mask_is_reg_c_0_3_set(struct mlx5dr_match_misc2 *misc2)
 124{
 125        return (misc2->metadata_reg_c_0 || misc2->metadata_reg_c_1 ||
 126                misc2->metadata_reg_c_2 || misc2->metadata_reg_c_3);
 127}
 128
 129static bool dr_mask_is_reg_c_4_7_set(struct mlx5dr_match_misc2 *misc2)
 130{
 131        return (misc2->metadata_reg_c_4 || misc2->metadata_reg_c_5 ||
 132                misc2->metadata_reg_c_6 || misc2->metadata_reg_c_7);
 133}
 134
 135static bool dr_mask_is_gvmi_or_qpn_set(struct mlx5dr_match_misc *misc)
 136{
 137        return (misc->source_sqn || misc->source_port);
 138}
 139
 140static bool
 141dr_matcher_supp_flex_parser_vxlan_gpe(struct mlx5dr_domain *dmn)
 142{
 143        return dmn->info.caps.flex_protocols &
 144               MLX5_FLEX_PARSER_VXLAN_GPE_ENABLED;
 145}
 146
 147int mlx5dr_matcher_select_builders(struct mlx5dr_matcher *matcher,
 148                                   struct mlx5dr_matcher_rx_tx *nic_matcher,
 149                                   bool ipv6)
 150{
 151        if (ipv6) {
 152                nic_matcher->ste_builder = nic_matcher->ste_builder6;
 153                nic_matcher->num_of_builders = nic_matcher->num_of_builders6;
 154        } else {
 155                nic_matcher->ste_builder = nic_matcher->ste_builder4;
 156                nic_matcher->num_of_builders = nic_matcher->num_of_builders4;
 157        }
 158
 159        if (!nic_matcher->num_of_builders) {
 160                mlx5dr_dbg(matcher->tbl->dmn,
 161                           "Rule not supported on this matcher due to IP related fields\n");
 162                return -EINVAL;
 163        }
 164
 165        return 0;
 166}
 167
 168static int dr_matcher_set_ste_builders(struct mlx5dr_matcher *matcher,
 169                                       struct mlx5dr_matcher_rx_tx *nic_matcher,
 170                                       bool ipv6)
 171{
 172        struct mlx5dr_domain_rx_tx *nic_dmn = nic_matcher->nic_tbl->nic_dmn;
 173        struct mlx5dr_domain *dmn = matcher->tbl->dmn;
 174        struct mlx5dr_match_param mask = {};
 175        struct mlx5dr_match_misc3 *misc3;
 176        struct mlx5dr_ste_build *sb;
 177        u8 *num_of_builders;
 178        bool inner, rx;
 179        int idx = 0;
 180        int ret, i;
 181
 182        if (ipv6) {
 183                sb = nic_matcher->ste_builder6;
 184                num_of_builders = &nic_matcher->num_of_builders6;
 185        } else {
 186                sb = nic_matcher->ste_builder4;
 187                num_of_builders = &nic_matcher->num_of_builders4;
 188        }
 189
 190        rx = nic_dmn->ste_type == MLX5DR_STE_TYPE_RX;
 191
 192        /* Create a temporary mask to track and clear used mask fields */
 193        if (matcher->match_criteria & DR_MATCHER_CRITERIA_OUTER)
 194                mask.outer = matcher->mask.outer;
 195
 196        if (matcher->match_criteria & DR_MATCHER_CRITERIA_MISC)
 197                mask.misc = matcher->mask.misc;
 198
 199        if (matcher->match_criteria & DR_MATCHER_CRITERIA_INNER)
 200                mask.inner = matcher->mask.inner;
 201
 202        if (matcher->match_criteria & DR_MATCHER_CRITERIA_MISC2)
 203                mask.misc2 = matcher->mask.misc2;
 204
 205        if (matcher->match_criteria & DR_MATCHER_CRITERIA_MISC3)
 206                mask.misc3 = matcher->mask.misc3;
 207
 208        ret = mlx5dr_ste_build_pre_check(dmn, matcher->match_criteria,
 209                                         &matcher->mask, NULL);
 210        if (ret)
 211                return ret;
 212
 213        /* Outer */
 214        if (matcher->match_criteria & (DR_MATCHER_CRITERIA_OUTER |
 215                                       DR_MATCHER_CRITERIA_MISC |
 216                                       DR_MATCHER_CRITERIA_MISC2 |
 217                                       DR_MATCHER_CRITERIA_MISC3)) {
 218                inner = false;
 219
 220                if (dr_mask_is_wqe_metadata_set(&mask.misc2))
 221                        mlx5dr_ste_build_general_purpose(&sb[idx++], &mask, inner, rx);
 222
 223                if (dr_mask_is_reg_c_0_3_set(&mask.misc2))
 224                        mlx5dr_ste_build_register_0(&sb[idx++], &mask, inner, rx);
 225
 226                if (dr_mask_is_reg_c_4_7_set(&mask.misc2))
 227                        mlx5dr_ste_build_register_1(&sb[idx++], &mask, inner, rx);
 228
 229                if (dr_mask_is_gvmi_or_qpn_set(&mask.misc) &&
 230                    (dmn->type == MLX5DR_DOMAIN_TYPE_FDB ||
 231                     dmn->type == MLX5DR_DOMAIN_TYPE_NIC_RX)) {
 232                        ret = mlx5dr_ste_build_src_gvmi_qpn(&sb[idx++], &mask,
 233                                                            dmn, inner, rx);
 234                        if (ret)
 235                                return ret;
 236                }
 237
 238                if (dr_mask_is_smac_set(&mask.outer) &&
 239                    dr_mask_is_dmac_set(&mask.outer)) {
 240                        ret = mlx5dr_ste_build_eth_l2_src_des(&sb[idx++], &mask,
 241                                                              inner, rx);
 242                        if (ret)
 243                                return ret;
 244                }
 245
 246                if (dr_mask_is_smac_set(&mask.outer))
 247                        mlx5dr_ste_build_eth_l2_src(&sb[idx++], &mask, inner, rx);
 248
 249                if (DR_MASK_IS_L2_DST(mask.outer, mask.misc, outer))
 250                        mlx5dr_ste_build_eth_l2_dst(&sb[idx++], &mask, inner, rx);
 251
 252                if (ipv6) {
 253                        if (dr_mask_is_dst_addr_set(&mask.outer))
 254                                mlx5dr_ste_build_eth_l3_ipv6_dst(&sb[idx++], &mask,
 255                                                                 inner, rx);
 256
 257                        if (dr_mask_is_src_addr_set(&mask.outer))
 258                                mlx5dr_ste_build_eth_l3_ipv6_src(&sb[idx++], &mask,
 259                                                                 inner, rx);
 260
 261                        if (DR_MASK_IS_ETH_L4_SET(mask.outer, mask.misc, outer))
 262                                mlx5dr_ste_build_ipv6_l3_l4(&sb[idx++], &mask,
 263                                                            inner, rx);
 264                } else {
 265                        if (dr_mask_is_ipv4_5_tuple_set(&mask.outer))
 266                                mlx5dr_ste_build_eth_l3_ipv4_5_tuple(&sb[idx++], &mask,
 267                                                                     inner, rx);
 268
 269                        if (dr_mask_is_ttl_set(&mask.outer))
 270                                mlx5dr_ste_build_eth_l3_ipv4_misc(&sb[idx++], &mask,
 271                                                                  inner, rx);
 272                }
 273
 274                if (dr_mask_is_flex_parser_tnl_set(&mask.misc3) &&
 275                    dr_matcher_supp_flex_parser_vxlan_gpe(dmn))
 276                        mlx5dr_ste_build_flex_parser_tnl(&sb[idx++], &mask,
 277                                                         inner, rx);
 278
 279                if (DR_MASK_IS_ETH_L4_MISC_SET(mask.misc3, outer))
 280                        mlx5dr_ste_build_eth_l4_misc(&sb[idx++], &mask, inner, rx);
 281
 282                if (DR_MASK_IS_FIRST_MPLS_SET(mask.misc2, outer))
 283                        mlx5dr_ste_build_mpls(&sb[idx++], &mask, inner, rx);
 284
 285                if (DR_MASK_IS_FLEX_PARSER_0_SET(mask.misc2))
 286                        mlx5dr_ste_build_flex_parser_0(&sb[idx++], &mask,
 287                                                       inner, rx);
 288
 289                misc3 = &mask.misc3;
 290                if ((DR_MASK_IS_FLEX_PARSER_ICMPV4_SET(misc3) &&
 291                     mlx5dr_matcher_supp_flex_parser_icmp_v4(&dmn->info.caps)) ||
 292                    (dr_mask_is_flex_parser_icmpv6_set(&mask.misc3) &&
 293                     mlx5dr_matcher_supp_flex_parser_icmp_v6(&dmn->info.caps))) {
 294                        ret = mlx5dr_ste_build_flex_parser_1(&sb[idx++],
 295                                                             &mask, &dmn->info.caps,
 296                                                             inner, rx);
 297                        if (ret)
 298                                return ret;
 299                }
 300                if (dr_mask_is_gre_set(&mask.misc))
 301                        mlx5dr_ste_build_gre(&sb[idx++], &mask, inner, rx);
 302        }
 303
 304        /* Inner */
 305        if (matcher->match_criteria & (DR_MATCHER_CRITERIA_INNER |
 306                                       DR_MATCHER_CRITERIA_MISC |
 307                                       DR_MATCHER_CRITERIA_MISC2 |
 308                                       DR_MATCHER_CRITERIA_MISC3)) {
 309                inner = true;
 310
 311                if (dr_mask_is_eth_l2_tnl_set(&mask.misc))
 312                        mlx5dr_ste_build_eth_l2_tnl(&sb[idx++], &mask, inner, rx);
 313
 314                if (dr_mask_is_smac_set(&mask.inner) &&
 315                    dr_mask_is_dmac_set(&mask.inner)) {
 316                        ret = mlx5dr_ste_build_eth_l2_src_des(&sb[idx++],
 317                                                              &mask, inner, rx);
 318                        if (ret)
 319                                return ret;
 320                }
 321
 322                if (dr_mask_is_smac_set(&mask.inner))
 323                        mlx5dr_ste_build_eth_l2_src(&sb[idx++], &mask, inner, rx);
 324
 325                if (DR_MASK_IS_L2_DST(mask.inner, mask.misc, inner))
 326                        mlx5dr_ste_build_eth_l2_dst(&sb[idx++], &mask, inner, rx);
 327
 328                if (ipv6) {
 329                        if (dr_mask_is_dst_addr_set(&mask.inner))
 330                                mlx5dr_ste_build_eth_l3_ipv6_dst(&sb[idx++], &mask,
 331                                                                 inner, rx);
 332
 333                        if (dr_mask_is_src_addr_set(&mask.inner))
 334                                mlx5dr_ste_build_eth_l3_ipv6_src(&sb[idx++], &mask,
 335                                                                 inner, rx);
 336
 337                        if (DR_MASK_IS_ETH_L4_SET(mask.inner, mask.misc, inner))
 338                                mlx5dr_ste_build_ipv6_l3_l4(&sb[idx++], &mask,
 339                                                            inner, rx);
 340                } else {
 341                        if (dr_mask_is_ipv4_5_tuple_set(&mask.inner))
 342                                mlx5dr_ste_build_eth_l3_ipv4_5_tuple(&sb[idx++], &mask,
 343                                                                     inner, rx);
 344
 345                        if (dr_mask_is_ttl_set(&mask.inner))
 346                                mlx5dr_ste_build_eth_l3_ipv4_misc(&sb[idx++], &mask,
 347                                                                  inner, rx);
 348                }
 349
 350                if (DR_MASK_IS_ETH_L4_MISC_SET(mask.misc3, inner))
 351                        mlx5dr_ste_build_eth_l4_misc(&sb[idx++], &mask, inner, rx);
 352
 353                if (DR_MASK_IS_FIRST_MPLS_SET(mask.misc2, inner))
 354                        mlx5dr_ste_build_mpls(&sb[idx++], &mask, inner, rx);
 355
 356                if (DR_MASK_IS_FLEX_PARSER_0_SET(mask.misc2))
 357                        mlx5dr_ste_build_flex_parser_0(&sb[idx++], &mask, inner, rx);
 358        }
 359        /* Empty matcher, takes all */
 360        if (matcher->match_criteria == DR_MATCHER_CRITERIA_EMPTY)
 361                mlx5dr_ste_build_empty_always_hit(&sb[idx++], rx);
 362
 363        if (idx == 0) {
 364                mlx5dr_dbg(dmn, "Cannot generate any valid rules from mask\n");
 365                return -EINVAL;
 366        }
 367
 368        /* Check that all mask fields were consumed */
 369        for (i = 0; i < sizeof(struct mlx5dr_match_param); i++) {
 370                if (((u8 *)&mask)[i] != 0) {
 371                        mlx5dr_info(dmn, "Mask contains unsupported parameters\n");
 372                        return -EOPNOTSUPP;
 373                }
 374        }
 375
 376        *num_of_builders = idx;
 377
 378        return 0;
 379}
 380
 381static int dr_matcher_connect(struct mlx5dr_domain *dmn,
 382                              struct mlx5dr_matcher_rx_tx *curr_nic_matcher,
 383                              struct mlx5dr_matcher_rx_tx *next_nic_matcher,
 384                              struct mlx5dr_matcher_rx_tx *prev_nic_matcher)
 385{
 386        struct mlx5dr_table_rx_tx *nic_tbl = curr_nic_matcher->nic_tbl;
 387        struct mlx5dr_domain_rx_tx *nic_dmn = nic_tbl->nic_dmn;
 388        struct mlx5dr_htbl_connect_info info;
 389        struct mlx5dr_ste_htbl *prev_htbl;
 390        int ret;
 391
 392        /* Connect end anchor hash table to next_htbl or to the default address */
 393        if (next_nic_matcher) {
 394                info.type = CONNECT_HIT;
 395                info.hit_next_htbl = next_nic_matcher->s_htbl;
 396        } else {
 397                info.type = CONNECT_MISS;
 398                info.miss_icm_addr = nic_tbl->default_icm_addr;
 399        }
 400        ret = mlx5dr_ste_htbl_init_and_postsend(dmn, nic_dmn,
 401                                                curr_nic_matcher->e_anchor,
 402                                                &info, info.type == CONNECT_HIT);
 403        if (ret)
 404                return ret;
 405
 406        /* Connect start hash table to end anchor */
 407        info.type = CONNECT_MISS;
 408        info.miss_icm_addr = curr_nic_matcher->e_anchor->chunk->icm_addr;
 409        ret = mlx5dr_ste_htbl_init_and_postsend(dmn, nic_dmn,
 410                                                curr_nic_matcher->s_htbl,
 411                                                &info, false);
 412        if (ret)
 413                return ret;
 414
 415        /* Connect previous hash table to matcher start hash table */
 416        if (prev_nic_matcher)
 417                prev_htbl = prev_nic_matcher->e_anchor;
 418        else
 419                prev_htbl = nic_tbl->s_anchor;
 420
 421        info.type = CONNECT_HIT;
 422        info.hit_next_htbl = curr_nic_matcher->s_htbl;
 423        ret = mlx5dr_ste_htbl_init_and_postsend(dmn, nic_dmn, prev_htbl,
 424                                                &info, true);
 425        if (ret)
 426                return ret;
 427
 428        /* Update the pointing ste and next hash table */
 429        curr_nic_matcher->s_htbl->pointing_ste = prev_htbl->ste_arr;
 430        prev_htbl->ste_arr[0].next_htbl = curr_nic_matcher->s_htbl;
 431
 432        if (next_nic_matcher) {
 433                next_nic_matcher->s_htbl->pointing_ste = curr_nic_matcher->e_anchor->ste_arr;
 434                curr_nic_matcher->e_anchor->ste_arr[0].next_htbl = next_nic_matcher->s_htbl;
 435        }
 436
 437        return 0;
 438}
 439
 440static int dr_matcher_add_to_tbl(struct mlx5dr_matcher *matcher)
 441{
 442        struct mlx5dr_matcher *next_matcher, *prev_matcher, *tmp_matcher;
 443        struct mlx5dr_table *tbl = matcher->tbl;
 444        struct mlx5dr_domain *dmn = tbl->dmn;
 445        bool first = true;
 446        int ret;
 447
 448        next_matcher = NULL;
 449        if (!list_empty(&tbl->matcher_list))
 450                list_for_each_entry(tmp_matcher, &tbl->matcher_list, matcher_list) {
 451                        if (tmp_matcher->prio >= matcher->prio) {
 452                                next_matcher = tmp_matcher;
 453                                break;
 454                        }
 455                        first = false;
 456                }
 457
 458        prev_matcher = NULL;
 459        if (next_matcher && !first)
 460                prev_matcher = list_prev_entry(next_matcher, matcher_list);
 461        else if (!first)
 462                prev_matcher = list_last_entry(&tbl->matcher_list,
 463                                               struct mlx5dr_matcher,
 464                                               matcher_list);
 465
 466        if (dmn->type == MLX5DR_DOMAIN_TYPE_FDB ||
 467            dmn->type == MLX5DR_DOMAIN_TYPE_NIC_RX) {
 468                ret = dr_matcher_connect(dmn, &matcher->rx,
 469                                         next_matcher ? &next_matcher->rx : NULL,
 470                                         prev_matcher ? &prev_matcher->rx : NULL);
 471                if (ret)
 472                        return ret;
 473        }
 474
 475        if (dmn->type == MLX5DR_DOMAIN_TYPE_FDB ||
 476            dmn->type == MLX5DR_DOMAIN_TYPE_NIC_TX) {
 477                ret = dr_matcher_connect(dmn, &matcher->tx,
 478                                         next_matcher ? &next_matcher->tx : NULL,
 479                                         prev_matcher ? &prev_matcher->tx : NULL);
 480                if (ret)
 481                        return ret;
 482        }
 483
 484        if (prev_matcher)
 485                list_add(&matcher->matcher_list, &prev_matcher->matcher_list);
 486        else if (next_matcher)
 487                list_add_tail(&matcher->matcher_list,
 488                              &next_matcher->matcher_list);
 489        else
 490                list_add(&matcher->matcher_list, &tbl->matcher_list);
 491
 492        return 0;
 493}
 494
 495static void dr_matcher_uninit_nic(struct mlx5dr_matcher_rx_tx *nic_matcher)
 496{
 497        mlx5dr_htbl_put(nic_matcher->s_htbl);
 498        mlx5dr_htbl_put(nic_matcher->e_anchor);
 499}
 500
 501static void dr_matcher_uninit_fdb(struct mlx5dr_matcher *matcher)
 502{
 503        dr_matcher_uninit_nic(&matcher->rx);
 504        dr_matcher_uninit_nic(&matcher->tx);
 505}
 506
 507static void dr_matcher_uninit(struct mlx5dr_matcher *matcher)
 508{
 509        struct mlx5dr_domain *dmn = matcher->tbl->dmn;
 510
 511        switch (dmn->type) {
 512        case MLX5DR_DOMAIN_TYPE_NIC_RX:
 513                dr_matcher_uninit_nic(&matcher->rx);
 514                break;
 515        case MLX5DR_DOMAIN_TYPE_NIC_TX:
 516                dr_matcher_uninit_nic(&matcher->tx);
 517                break;
 518        case MLX5DR_DOMAIN_TYPE_FDB:
 519                dr_matcher_uninit_fdb(matcher);
 520                break;
 521        default:
 522                WARN_ON(true);
 523                break;
 524        }
 525}
 526
 527static int dr_matcher_init_nic(struct mlx5dr_matcher *matcher,
 528                               struct mlx5dr_matcher_rx_tx *nic_matcher)
 529{
 530        struct mlx5dr_domain *dmn = matcher->tbl->dmn;
 531        int ret, ret_v4, ret_v6;
 532
 533        ret_v4 = dr_matcher_set_ste_builders(matcher, nic_matcher, false);
 534        ret_v6 = dr_matcher_set_ste_builders(matcher, nic_matcher, true);
 535
 536        if (ret_v4 && ret_v6) {
 537                mlx5dr_dbg(dmn, "Cannot generate IPv4 or IPv6 rules with given mask\n");
 538                return -EINVAL;
 539        }
 540
 541        if (!ret_v4)
 542                nic_matcher->ste_builder = nic_matcher->ste_builder4;
 543        else
 544                nic_matcher->ste_builder = nic_matcher->ste_builder6;
 545
 546        nic_matcher->e_anchor = mlx5dr_ste_htbl_alloc(dmn->ste_icm_pool,
 547                                                      DR_CHUNK_SIZE_1,
 548                                                      MLX5DR_STE_LU_TYPE_DONT_CARE,
 549                                                      0);
 550        if (!nic_matcher->e_anchor)
 551                return -ENOMEM;
 552
 553        nic_matcher->s_htbl = mlx5dr_ste_htbl_alloc(dmn->ste_icm_pool,
 554                                                    DR_CHUNK_SIZE_1,
 555                                                    nic_matcher->ste_builder[0].lu_type,
 556                                                    nic_matcher->ste_builder[0].byte_mask);
 557        if (!nic_matcher->s_htbl) {
 558                ret = -ENOMEM;
 559                goto free_e_htbl;
 560        }
 561
 562        /* make sure the tables exist while empty */
 563        mlx5dr_htbl_get(nic_matcher->s_htbl);
 564        mlx5dr_htbl_get(nic_matcher->e_anchor);
 565
 566        return 0;
 567
 568free_e_htbl:
 569        mlx5dr_ste_htbl_free(nic_matcher->e_anchor);
 570        return ret;
 571}
 572
 573static int dr_matcher_init_fdb(struct mlx5dr_matcher *matcher)
 574{
 575        int ret;
 576
 577        ret = dr_matcher_init_nic(matcher, &matcher->rx);
 578        if (ret)
 579                return ret;
 580
 581        ret = dr_matcher_init_nic(matcher, &matcher->tx);
 582        if (ret)
 583                goto uninit_nic_rx;
 584
 585        return 0;
 586
 587uninit_nic_rx:
 588        dr_matcher_uninit_nic(&matcher->rx);
 589        return ret;
 590}
 591
 592static int dr_matcher_init(struct mlx5dr_matcher *matcher,
 593                           struct mlx5dr_match_parameters *mask)
 594{
 595        struct mlx5dr_table *tbl = matcher->tbl;
 596        struct mlx5dr_domain *dmn = tbl->dmn;
 597        int ret;
 598
 599        if (matcher->match_criteria >= DR_MATCHER_CRITERIA_MAX) {
 600                mlx5dr_info(dmn, "Invalid match criteria attribute\n");
 601                return -EINVAL;
 602        }
 603
 604        if (mask) {
 605                if (mask->match_sz > sizeof(struct mlx5dr_match_param)) {
 606                        mlx5dr_info(dmn, "Invalid match size attribute\n");
 607                        return -EINVAL;
 608                }
 609                mlx5dr_ste_copy_param(matcher->match_criteria,
 610                                      &matcher->mask, mask);
 611        }
 612
 613        switch (dmn->type) {
 614        case MLX5DR_DOMAIN_TYPE_NIC_RX:
 615                matcher->rx.nic_tbl = &tbl->rx;
 616                ret = dr_matcher_init_nic(matcher, &matcher->rx);
 617                break;
 618        case MLX5DR_DOMAIN_TYPE_NIC_TX:
 619                matcher->tx.nic_tbl = &tbl->tx;
 620                ret = dr_matcher_init_nic(matcher, &matcher->tx);
 621                break;
 622        case MLX5DR_DOMAIN_TYPE_FDB:
 623                matcher->rx.nic_tbl = &tbl->rx;
 624                matcher->tx.nic_tbl = &tbl->tx;
 625                ret = dr_matcher_init_fdb(matcher);
 626                break;
 627        default:
 628                WARN_ON(true);
 629                return -EINVAL;
 630        }
 631
 632        return ret;
 633}
 634
 635struct mlx5dr_matcher *
 636mlx5dr_matcher_create(struct mlx5dr_table *tbl,
 637                      u16 priority,
 638                      u8 match_criteria_enable,
 639                      struct mlx5dr_match_parameters *mask)
 640{
 641        struct mlx5dr_matcher *matcher;
 642        int ret;
 643
 644        refcount_inc(&tbl->refcount);
 645
 646        matcher = kzalloc(sizeof(*matcher), GFP_KERNEL);
 647        if (!matcher)
 648                goto dec_ref;
 649
 650        matcher->tbl = tbl;
 651        matcher->prio = priority;
 652        matcher->match_criteria = match_criteria_enable;
 653        refcount_set(&matcher->refcount, 1);
 654        INIT_LIST_HEAD(&matcher->matcher_list);
 655
 656        mutex_lock(&tbl->dmn->mutex);
 657
 658        ret = dr_matcher_init(matcher, mask);
 659        if (ret)
 660                goto free_matcher;
 661
 662        ret = dr_matcher_add_to_tbl(matcher);
 663        if (ret)
 664                goto matcher_uninit;
 665
 666        mutex_unlock(&tbl->dmn->mutex);
 667
 668        return matcher;
 669
 670matcher_uninit:
 671        dr_matcher_uninit(matcher);
 672free_matcher:
 673        mutex_unlock(&tbl->dmn->mutex);
 674        kfree(matcher);
 675dec_ref:
 676        refcount_dec(&tbl->refcount);
 677        return NULL;
 678}
 679
 680static int dr_matcher_disconnect(struct mlx5dr_domain *dmn,
 681                                 struct mlx5dr_table_rx_tx *nic_tbl,
 682                                 struct mlx5dr_matcher_rx_tx *next_nic_matcher,
 683                                 struct mlx5dr_matcher_rx_tx *prev_nic_matcher)
 684{
 685        struct mlx5dr_domain_rx_tx *nic_dmn = nic_tbl->nic_dmn;
 686        struct mlx5dr_htbl_connect_info info;
 687        struct mlx5dr_ste_htbl *prev_anchor;
 688
 689        if (prev_nic_matcher)
 690                prev_anchor = prev_nic_matcher->e_anchor;
 691        else
 692                prev_anchor = nic_tbl->s_anchor;
 693
 694        /* Connect previous anchor hash table to next matcher or to the default address */
 695        if (next_nic_matcher) {
 696                info.type = CONNECT_HIT;
 697                info.hit_next_htbl = next_nic_matcher->s_htbl;
 698                next_nic_matcher->s_htbl->pointing_ste = prev_anchor->ste_arr;
 699                prev_anchor->ste_arr[0].next_htbl = next_nic_matcher->s_htbl;
 700        } else {
 701                info.type = CONNECT_MISS;
 702                info.miss_icm_addr = nic_tbl->default_icm_addr;
 703                prev_anchor->ste_arr[0].next_htbl = NULL;
 704        }
 705
 706        return mlx5dr_ste_htbl_init_and_postsend(dmn, nic_dmn, prev_anchor,
 707                                                 &info, true);
 708}
 709
 710static int dr_matcher_remove_from_tbl(struct mlx5dr_matcher *matcher)
 711{
 712        struct mlx5dr_matcher *prev_matcher, *next_matcher;
 713        struct mlx5dr_table *tbl = matcher->tbl;
 714        struct mlx5dr_domain *dmn = tbl->dmn;
 715        int ret = 0;
 716
 717        if (list_is_last(&matcher->matcher_list, &tbl->matcher_list))
 718                next_matcher = NULL;
 719        else
 720                next_matcher = list_next_entry(matcher, matcher_list);
 721
 722        if (matcher->matcher_list.prev == &tbl->matcher_list)
 723                prev_matcher = NULL;
 724        else
 725                prev_matcher = list_prev_entry(matcher, matcher_list);
 726
 727        if (dmn->type == MLX5DR_DOMAIN_TYPE_FDB ||
 728            dmn->type == MLX5DR_DOMAIN_TYPE_NIC_RX) {
 729                ret = dr_matcher_disconnect(dmn, &tbl->rx,
 730                                            next_matcher ? &next_matcher->rx : NULL,
 731                                            prev_matcher ? &prev_matcher->rx : NULL);
 732                if (ret)
 733                        return ret;
 734        }
 735
 736        if (dmn->type == MLX5DR_DOMAIN_TYPE_FDB ||
 737            dmn->type == MLX5DR_DOMAIN_TYPE_NIC_TX) {
 738                ret = dr_matcher_disconnect(dmn, &tbl->tx,
 739                                            next_matcher ? &next_matcher->tx : NULL,
 740                                            prev_matcher ? &prev_matcher->tx : NULL);
 741                if (ret)
 742                        return ret;
 743        }
 744
 745        list_del(&matcher->matcher_list);
 746
 747        return 0;
 748}
 749
 750int mlx5dr_matcher_destroy(struct mlx5dr_matcher *matcher)
 751{
 752        struct mlx5dr_table *tbl = matcher->tbl;
 753
 754        if (refcount_read(&matcher->refcount) > 1)
 755                return -EBUSY;
 756
 757        mutex_lock(&tbl->dmn->mutex);
 758
 759        dr_matcher_remove_from_tbl(matcher);
 760        dr_matcher_uninit(matcher);
 761        refcount_dec(&matcher->tbl->refcount);
 762
 763        mutex_unlock(&tbl->dmn->mutex);
 764        kfree(matcher);
 765
 766        return 0;
 767}
 768