Merge tag 'mlx5-next-2020-12-02' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux-block.git] / drivers / net / ethernet / mellanox / mlx5 / core / steering / dr_matcher.c
1 // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
2 /* Copyright (c) 2019 Mellanox Technologies. */
3
4 #include "dr_types.h"
5
6 static bool dr_mask_is_smac_set(struct mlx5dr_match_spec *spec)
7 {
8         return (spec->smac_47_16 || spec->smac_15_0);
9 }
10
11 static bool dr_mask_is_dmac_set(struct mlx5dr_match_spec *spec)
12 {
13         return (spec->dmac_47_16 || spec->dmac_15_0);
14 }
15
16 static 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
22 static 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
28 static 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
34 static 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
40 static 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
45 static 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
52 static bool dr_mask_is_eth_l2_tnl_set(struct mlx5dr_match_misc *misc)
53 {
54         return misc->vxlan_vni;
55 }
56
57 static 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
88 static bool dr_mask_is_tnl_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_TNL_MPLS_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
105 static bool
106 dr_mask_is_vxlan_gpe_set(struct mlx5dr_match_misc3 *misc3)
107 {
108         return (misc3->outer_vxlan_gpe_vni ||
109                 misc3->outer_vxlan_gpe_next_protocol ||
110                 misc3->outer_vxlan_gpe_flags);
111 }
112
113 static bool
114 dr_matcher_supp_vxlan_gpe(struct mlx5dr_cmd_caps *caps)
115 {
116         return caps->flex_protocols & MLX5_FLEX_PARSER_VXLAN_GPE_ENABLED;
117 }
118
119 static bool
120 dr_mask_is_tnl_vxlan_gpe(struct mlx5dr_match_param *mask,
121                          struct mlx5dr_domain *dmn)
122 {
123         return dr_mask_is_vxlan_gpe_set(&mask->misc3) &&
124                dr_matcher_supp_vxlan_gpe(&dmn->info.caps);
125 }
126
127 static bool dr_mask_is_tnl_geneve_set(struct mlx5dr_match_misc *misc)
128 {
129         return misc->geneve_vni ||
130                misc->geneve_oam ||
131                misc->geneve_protocol_type ||
132                misc->geneve_opt_len;
133 }
134
135 static bool
136 dr_matcher_supp_tnl_geneve(struct mlx5dr_cmd_caps *caps)
137 {
138         return caps->flex_protocols & MLX5_FLEX_PARSER_GENEVE_ENABLED;
139 }
140
141 static bool
142 dr_mask_is_tnl_geneve(struct mlx5dr_match_param *mask,
143                       struct mlx5dr_domain *dmn)
144 {
145         return dr_mask_is_tnl_geneve_set(&mask->misc) &&
146                dr_matcher_supp_tnl_geneve(&dmn->info.caps);
147 }
148
149 static int dr_matcher_supp_icmp_v4(struct mlx5dr_cmd_caps *caps)
150 {
151         return caps->flex_protocols & MLX5_FLEX_PARSER_ICMP_V4_ENABLED;
152 }
153
154 static int dr_matcher_supp_icmp_v6(struct mlx5dr_cmd_caps *caps)
155 {
156         return caps->flex_protocols & MLX5_FLEX_PARSER_ICMP_V6_ENABLED;
157 }
158
159 static bool dr_mask_is_icmpv6_set(struct mlx5dr_match_misc3 *misc3)
160 {
161         return (misc3->icmpv6_type || misc3->icmpv6_code ||
162                 misc3->icmpv6_header_data);
163 }
164
165 static bool dr_mask_is_icmp(struct mlx5dr_match_param *mask,
166                             struct mlx5dr_domain *dmn)
167 {
168         if (DR_MASK_IS_ICMPV4_SET(&mask->misc3))
169                 return dr_matcher_supp_icmp_v4(&dmn->info.caps);
170         else if (dr_mask_is_icmpv6_set(&mask->misc3))
171                 return dr_matcher_supp_icmp_v6(&dmn->info.caps);
172
173         return false;
174 }
175
176 static bool dr_mask_is_wqe_metadata_set(struct mlx5dr_match_misc2 *misc2)
177 {
178         return misc2->metadata_reg_a;
179 }
180
181 static bool dr_mask_is_reg_c_0_3_set(struct mlx5dr_match_misc2 *misc2)
182 {
183         return (misc2->metadata_reg_c_0 || misc2->metadata_reg_c_1 ||
184                 misc2->metadata_reg_c_2 || misc2->metadata_reg_c_3);
185 }
186
187 static bool dr_mask_is_reg_c_4_7_set(struct mlx5dr_match_misc2 *misc2)
188 {
189         return (misc2->metadata_reg_c_4 || misc2->metadata_reg_c_5 ||
190                 misc2->metadata_reg_c_6 || misc2->metadata_reg_c_7);
191 }
192
193 static bool dr_mask_is_gvmi_or_qpn_set(struct mlx5dr_match_misc *misc)
194 {
195         return (misc->source_sqn || misc->source_port);
196 }
197
198 int mlx5dr_matcher_select_builders(struct mlx5dr_matcher *matcher,
199                                    struct mlx5dr_matcher_rx_tx *nic_matcher,
200                                    enum mlx5dr_ipv outer_ipv,
201                                    enum mlx5dr_ipv inner_ipv)
202 {
203         nic_matcher->ste_builder =
204                 nic_matcher->ste_builder_arr[outer_ipv][inner_ipv];
205         nic_matcher->num_of_builders =
206                 nic_matcher->num_of_builders_arr[outer_ipv][inner_ipv];
207
208         if (!nic_matcher->num_of_builders) {
209                 mlx5dr_dbg(matcher->tbl->dmn,
210                            "Rule not supported on this matcher due to IP related fields\n");
211                 return -EINVAL;
212         }
213
214         return 0;
215 }
216
217 static int dr_matcher_set_ste_builders(struct mlx5dr_matcher *matcher,
218                                        struct mlx5dr_matcher_rx_tx *nic_matcher,
219                                        enum mlx5dr_ipv outer_ipv,
220                                        enum mlx5dr_ipv inner_ipv)
221 {
222         struct mlx5dr_domain_rx_tx *nic_dmn = nic_matcher->nic_tbl->nic_dmn;
223         struct mlx5dr_domain *dmn = matcher->tbl->dmn;
224         struct mlx5dr_match_param mask = {};
225         struct mlx5dr_ste_build *sb;
226         bool inner, rx;
227         int idx = 0;
228         int ret, i;
229
230         sb = nic_matcher->ste_builder_arr[outer_ipv][inner_ipv];
231         rx = nic_dmn->ste_type == MLX5DR_STE_TYPE_RX;
232
233         /* Create a temporary mask to track and clear used mask fields */
234         if (matcher->match_criteria & DR_MATCHER_CRITERIA_OUTER)
235                 mask.outer = matcher->mask.outer;
236
237         if (matcher->match_criteria & DR_MATCHER_CRITERIA_MISC)
238                 mask.misc = matcher->mask.misc;
239
240         if (matcher->match_criteria & DR_MATCHER_CRITERIA_INNER)
241                 mask.inner = matcher->mask.inner;
242
243         if (matcher->match_criteria & DR_MATCHER_CRITERIA_MISC2)
244                 mask.misc2 = matcher->mask.misc2;
245
246         if (matcher->match_criteria & DR_MATCHER_CRITERIA_MISC3)
247                 mask.misc3 = matcher->mask.misc3;
248
249         ret = mlx5dr_ste_build_pre_check(dmn, matcher->match_criteria,
250                                          &matcher->mask, NULL);
251         if (ret)
252                 return ret;
253
254         /* Outer */
255         if (matcher->match_criteria & (DR_MATCHER_CRITERIA_OUTER |
256                                        DR_MATCHER_CRITERIA_MISC |
257                                        DR_MATCHER_CRITERIA_MISC2 |
258                                        DR_MATCHER_CRITERIA_MISC3)) {
259                 inner = false;
260
261                 if (dr_mask_is_wqe_metadata_set(&mask.misc2))
262                         mlx5dr_ste_build_general_purpose(&sb[idx++], &mask, inner, rx);
263
264                 if (dr_mask_is_reg_c_0_3_set(&mask.misc2))
265                         mlx5dr_ste_build_register_0(&sb[idx++], &mask, inner, rx);
266
267                 if (dr_mask_is_reg_c_4_7_set(&mask.misc2))
268                         mlx5dr_ste_build_register_1(&sb[idx++], &mask, inner, rx);
269
270                 if (dr_mask_is_gvmi_or_qpn_set(&mask.misc) &&
271                     (dmn->type == MLX5DR_DOMAIN_TYPE_FDB ||
272                      dmn->type == MLX5DR_DOMAIN_TYPE_NIC_RX)) {
273                         mlx5dr_ste_build_src_gvmi_qpn(&sb[idx++], &mask,
274                                                       dmn, inner, rx);
275                 }
276
277                 if (dr_mask_is_smac_set(&mask.outer) &&
278                     dr_mask_is_dmac_set(&mask.outer)) {
279                         mlx5dr_ste_build_eth_l2_src_dst(&sb[idx++], &mask,
280                                                         inner, rx);
281                 }
282
283                 if (dr_mask_is_smac_set(&mask.outer))
284                         mlx5dr_ste_build_eth_l2_src(&sb[idx++], &mask, inner, rx);
285
286                 if (DR_MASK_IS_L2_DST(mask.outer, mask.misc, outer))
287                         mlx5dr_ste_build_eth_l2_dst(&sb[idx++], &mask, inner, rx);
288
289                 if (outer_ipv == DR_RULE_IPV6) {
290                         if (dr_mask_is_dst_addr_set(&mask.outer))
291                                 mlx5dr_ste_build_eth_l3_ipv6_dst(&sb[idx++], &mask,
292                                                                  inner, rx);
293
294                         if (dr_mask_is_src_addr_set(&mask.outer))
295                                 mlx5dr_ste_build_eth_l3_ipv6_src(&sb[idx++], &mask,
296                                                                  inner, rx);
297
298                         if (DR_MASK_IS_ETH_L4_SET(mask.outer, mask.misc, outer))
299                                 mlx5dr_ste_build_eth_ipv6_l3_l4(&sb[idx++], &mask,
300                                                                 inner, rx);
301                 } else {
302                         if (dr_mask_is_ipv4_5_tuple_set(&mask.outer))
303                                 mlx5dr_ste_build_eth_l3_ipv4_5_tuple(&sb[idx++], &mask,
304                                                                      inner, rx);
305
306                         if (dr_mask_is_ttl_set(&mask.outer))
307                                 mlx5dr_ste_build_eth_l3_ipv4_misc(&sb[idx++], &mask,
308                                                                   inner, rx);
309                 }
310
311                 if (dr_mask_is_tnl_vxlan_gpe(&mask, dmn))
312                         mlx5dr_ste_build_tnl_vxlan_gpe(&sb[idx++], &mask,
313                                                        inner, rx);
314                 else if (dr_mask_is_tnl_geneve(&mask, dmn))
315                         mlx5dr_ste_build_tnl_geneve(&sb[idx++], &mask,
316                                                     inner, rx);
317
318                 if (DR_MASK_IS_ETH_L4_MISC_SET(mask.misc3, outer))
319                         mlx5dr_ste_build_eth_l4_misc(&sb[idx++], &mask, inner, rx);
320
321                 if (DR_MASK_IS_FIRST_MPLS_SET(mask.misc2, outer))
322                         mlx5dr_ste_build_mpls(&sb[idx++], &mask, inner, rx);
323
324                 if (DR_MASK_IS_TNL_MPLS_SET(mask.misc2))
325                         mlx5dr_ste_build_tnl_mpls(&sb[idx++], &mask, inner, rx);
326
327                 if (dr_mask_is_icmp(&mask, dmn)) {
328                         ret = mlx5dr_ste_build_icmp(&sb[idx++],
329                                                     &mask, &dmn->info.caps,
330                                                     inner, rx);
331                         if (ret)
332                                 return ret;
333                 }
334                 if (dr_mask_is_tnl_gre_set(&mask.misc))
335                         mlx5dr_ste_build_tnl_gre(&sb[idx++], &mask, inner, rx);
336         }
337
338         /* Inner */
339         if (matcher->match_criteria & (DR_MATCHER_CRITERIA_INNER |
340                                        DR_MATCHER_CRITERIA_MISC |
341                                        DR_MATCHER_CRITERIA_MISC2 |
342                                        DR_MATCHER_CRITERIA_MISC3)) {
343                 inner = true;
344
345                 if (dr_mask_is_eth_l2_tnl_set(&mask.misc))
346                         mlx5dr_ste_build_eth_l2_tnl(&sb[idx++], &mask, inner, rx);
347
348                 if (dr_mask_is_smac_set(&mask.inner) &&
349                     dr_mask_is_dmac_set(&mask.inner)) {
350                         mlx5dr_ste_build_eth_l2_src_dst(&sb[idx++],
351                                                         &mask, inner, rx);
352                 }
353
354                 if (dr_mask_is_smac_set(&mask.inner))
355                         mlx5dr_ste_build_eth_l2_src(&sb[idx++], &mask, inner, rx);
356
357                 if (DR_MASK_IS_L2_DST(mask.inner, mask.misc, inner))
358                         mlx5dr_ste_build_eth_l2_dst(&sb[idx++], &mask, inner, rx);
359
360                 if (inner_ipv == DR_RULE_IPV6) {
361                         if (dr_mask_is_dst_addr_set(&mask.inner))
362                                 mlx5dr_ste_build_eth_l3_ipv6_dst(&sb[idx++], &mask,
363                                                                  inner, rx);
364
365                         if (dr_mask_is_src_addr_set(&mask.inner))
366                                 mlx5dr_ste_build_eth_l3_ipv6_src(&sb[idx++], &mask,
367                                                                  inner, rx);
368
369                         if (DR_MASK_IS_ETH_L4_SET(mask.inner, mask.misc, inner))
370                                 mlx5dr_ste_build_eth_ipv6_l3_l4(&sb[idx++], &mask,
371                                                                 inner, rx);
372                 } else {
373                         if (dr_mask_is_ipv4_5_tuple_set(&mask.inner))
374                                 mlx5dr_ste_build_eth_l3_ipv4_5_tuple(&sb[idx++], &mask,
375                                                                      inner, rx);
376
377                         if (dr_mask_is_ttl_set(&mask.inner))
378                                 mlx5dr_ste_build_eth_l3_ipv4_misc(&sb[idx++], &mask,
379                                                                   inner, rx);
380                 }
381
382                 if (DR_MASK_IS_ETH_L4_MISC_SET(mask.misc3, inner))
383                         mlx5dr_ste_build_eth_l4_misc(&sb[idx++], &mask, inner, rx);
384
385                 if (DR_MASK_IS_FIRST_MPLS_SET(mask.misc2, inner))
386                         mlx5dr_ste_build_mpls(&sb[idx++], &mask, inner, rx);
387
388                 if (DR_MASK_IS_TNL_MPLS_SET(mask.misc2))
389                         mlx5dr_ste_build_tnl_mpls(&sb[idx++], &mask, inner, rx);
390         }
391         /* Empty matcher, takes all */
392         if (matcher->match_criteria == DR_MATCHER_CRITERIA_EMPTY)
393                 mlx5dr_ste_build_empty_always_hit(&sb[idx++], rx);
394
395         if (idx == 0) {
396                 mlx5dr_err(dmn, "Cannot generate any valid rules from mask\n");
397                 return -EINVAL;
398         }
399
400         /* Check that all mask fields were consumed */
401         for (i = 0; i < sizeof(struct mlx5dr_match_param); i++) {
402                 if (((u8 *)&mask)[i] != 0) {
403                         mlx5dr_dbg(dmn, "Mask contains unsupported parameters\n");
404                         return -EOPNOTSUPP;
405                 }
406         }
407
408         nic_matcher->ste_builder = sb;
409         nic_matcher->num_of_builders_arr[outer_ipv][inner_ipv] = idx;
410
411         return 0;
412 }
413
414 static int dr_matcher_connect(struct mlx5dr_domain *dmn,
415                               struct mlx5dr_matcher_rx_tx *curr_nic_matcher,
416                               struct mlx5dr_matcher_rx_tx *next_nic_matcher,
417                               struct mlx5dr_matcher_rx_tx *prev_nic_matcher)
418 {
419         struct mlx5dr_table_rx_tx *nic_tbl = curr_nic_matcher->nic_tbl;
420         struct mlx5dr_domain_rx_tx *nic_dmn = nic_tbl->nic_dmn;
421         struct mlx5dr_htbl_connect_info info;
422         struct mlx5dr_ste_htbl *prev_htbl;
423         int ret;
424
425         /* Connect end anchor hash table to next_htbl or to the default address */
426         if (next_nic_matcher) {
427                 info.type = CONNECT_HIT;
428                 info.hit_next_htbl = next_nic_matcher->s_htbl;
429         } else {
430                 info.type = CONNECT_MISS;
431                 info.miss_icm_addr = nic_tbl->default_icm_addr;
432         }
433         ret = mlx5dr_ste_htbl_init_and_postsend(dmn, nic_dmn,
434                                                 curr_nic_matcher->e_anchor,
435                                                 &info, info.type == CONNECT_HIT);
436         if (ret)
437                 return ret;
438
439         /* Connect start hash table to end anchor */
440         info.type = CONNECT_MISS;
441         info.miss_icm_addr = curr_nic_matcher->e_anchor->chunk->icm_addr;
442         ret = mlx5dr_ste_htbl_init_and_postsend(dmn, nic_dmn,
443                                                 curr_nic_matcher->s_htbl,
444                                                 &info, false);
445         if (ret)
446                 return ret;
447
448         /* Connect previous hash table to matcher start hash table */
449         if (prev_nic_matcher)
450                 prev_htbl = prev_nic_matcher->e_anchor;
451         else
452                 prev_htbl = nic_tbl->s_anchor;
453
454         info.type = CONNECT_HIT;
455         info.hit_next_htbl = curr_nic_matcher->s_htbl;
456         ret = mlx5dr_ste_htbl_init_and_postsend(dmn, nic_dmn, prev_htbl,
457                                                 &info, true);
458         if (ret)
459                 return ret;
460
461         /* Update the pointing ste and next hash table */
462         curr_nic_matcher->s_htbl->pointing_ste = prev_htbl->ste_arr;
463         prev_htbl->ste_arr[0].next_htbl = curr_nic_matcher->s_htbl;
464
465         if (next_nic_matcher) {
466                 next_nic_matcher->s_htbl->pointing_ste = curr_nic_matcher->e_anchor->ste_arr;
467                 curr_nic_matcher->e_anchor->ste_arr[0].next_htbl = next_nic_matcher->s_htbl;
468         }
469
470         return 0;
471 }
472
473 static int dr_matcher_add_to_tbl(struct mlx5dr_matcher *matcher)
474 {
475         struct mlx5dr_matcher *next_matcher, *prev_matcher, *tmp_matcher;
476         struct mlx5dr_table *tbl = matcher->tbl;
477         struct mlx5dr_domain *dmn = tbl->dmn;
478         bool first = true;
479         int ret;
480
481         next_matcher = NULL;
482         list_for_each_entry(tmp_matcher, &tbl->matcher_list, matcher_list) {
483                 if (tmp_matcher->prio >= matcher->prio) {
484                         next_matcher = tmp_matcher;
485                         break;
486                 }
487                 first = false;
488         }
489
490         prev_matcher = NULL;
491         if (next_matcher && !first)
492                 prev_matcher = list_prev_entry(next_matcher, matcher_list);
493         else if (!first)
494                 prev_matcher = list_last_entry(&tbl->matcher_list,
495                                                struct mlx5dr_matcher,
496                                                matcher_list);
497
498         if (dmn->type == MLX5DR_DOMAIN_TYPE_FDB ||
499             dmn->type == MLX5DR_DOMAIN_TYPE_NIC_RX) {
500                 ret = dr_matcher_connect(dmn, &matcher->rx,
501                                          next_matcher ? &next_matcher->rx : NULL,
502                                          prev_matcher ? &prev_matcher->rx : NULL);
503                 if (ret)
504                         return ret;
505         }
506
507         if (dmn->type == MLX5DR_DOMAIN_TYPE_FDB ||
508             dmn->type == MLX5DR_DOMAIN_TYPE_NIC_TX) {
509                 ret = dr_matcher_connect(dmn, &matcher->tx,
510                                          next_matcher ? &next_matcher->tx : NULL,
511                                          prev_matcher ? &prev_matcher->tx : NULL);
512                 if (ret)
513                         return ret;
514         }
515
516         if (prev_matcher)
517                 list_add(&matcher->matcher_list, &prev_matcher->matcher_list);
518         else if (next_matcher)
519                 list_add_tail(&matcher->matcher_list,
520                               &next_matcher->matcher_list);
521         else
522                 list_add(&matcher->matcher_list, &tbl->matcher_list);
523
524         return 0;
525 }
526
527 static void dr_matcher_uninit_nic(struct mlx5dr_matcher_rx_tx *nic_matcher)
528 {
529         mlx5dr_htbl_put(nic_matcher->s_htbl);
530         mlx5dr_htbl_put(nic_matcher->e_anchor);
531 }
532
533 static void dr_matcher_uninit_fdb(struct mlx5dr_matcher *matcher)
534 {
535         dr_matcher_uninit_nic(&matcher->rx);
536         dr_matcher_uninit_nic(&matcher->tx);
537 }
538
539 static void dr_matcher_uninit(struct mlx5dr_matcher *matcher)
540 {
541         struct mlx5dr_domain *dmn = matcher->tbl->dmn;
542
543         switch (dmn->type) {
544         case MLX5DR_DOMAIN_TYPE_NIC_RX:
545                 dr_matcher_uninit_nic(&matcher->rx);
546                 break;
547         case MLX5DR_DOMAIN_TYPE_NIC_TX:
548                 dr_matcher_uninit_nic(&matcher->tx);
549                 break;
550         case MLX5DR_DOMAIN_TYPE_FDB:
551                 dr_matcher_uninit_fdb(matcher);
552                 break;
553         default:
554                 WARN_ON(true);
555                 break;
556         }
557 }
558
559 static int dr_matcher_set_all_ste_builders(struct mlx5dr_matcher *matcher,
560                                            struct mlx5dr_matcher_rx_tx *nic_matcher)
561 {
562         struct mlx5dr_domain *dmn = matcher->tbl->dmn;
563
564         dr_matcher_set_ste_builders(matcher, nic_matcher, DR_RULE_IPV4, DR_RULE_IPV4);
565         dr_matcher_set_ste_builders(matcher, nic_matcher, DR_RULE_IPV4, DR_RULE_IPV6);
566         dr_matcher_set_ste_builders(matcher, nic_matcher, DR_RULE_IPV6, DR_RULE_IPV4);
567         dr_matcher_set_ste_builders(matcher, nic_matcher, DR_RULE_IPV6, DR_RULE_IPV6);
568
569         if (!nic_matcher->ste_builder) {
570                 mlx5dr_err(dmn, "Cannot generate IPv4 or IPv6 rules with given mask\n");
571                 return -EINVAL;
572         }
573
574         return 0;
575 }
576
577 static int dr_matcher_init_nic(struct mlx5dr_matcher *matcher,
578                                struct mlx5dr_matcher_rx_tx *nic_matcher)
579 {
580         struct mlx5dr_domain *dmn = matcher->tbl->dmn;
581         int ret;
582
583         ret = dr_matcher_set_all_ste_builders(matcher, nic_matcher);
584         if (ret)
585                 return ret;
586
587         nic_matcher->e_anchor = mlx5dr_ste_htbl_alloc(dmn->ste_icm_pool,
588                                                       DR_CHUNK_SIZE_1,
589                                                       MLX5DR_STE_LU_TYPE_DONT_CARE,
590                                                       0);
591         if (!nic_matcher->e_anchor)
592                 return -ENOMEM;
593
594         nic_matcher->s_htbl = mlx5dr_ste_htbl_alloc(dmn->ste_icm_pool,
595                                                     DR_CHUNK_SIZE_1,
596                                                     nic_matcher->ste_builder[0].lu_type,
597                                                     nic_matcher->ste_builder[0].byte_mask);
598         if (!nic_matcher->s_htbl) {
599                 ret = -ENOMEM;
600                 goto free_e_htbl;
601         }
602
603         /* make sure the tables exist while empty */
604         mlx5dr_htbl_get(nic_matcher->s_htbl);
605         mlx5dr_htbl_get(nic_matcher->e_anchor);
606
607         return 0;
608
609 free_e_htbl:
610         mlx5dr_ste_htbl_free(nic_matcher->e_anchor);
611         return ret;
612 }
613
614 static int dr_matcher_init_fdb(struct mlx5dr_matcher *matcher)
615 {
616         int ret;
617
618         ret = dr_matcher_init_nic(matcher, &matcher->rx);
619         if (ret)
620                 return ret;
621
622         ret = dr_matcher_init_nic(matcher, &matcher->tx);
623         if (ret)
624                 goto uninit_nic_rx;
625
626         return 0;
627
628 uninit_nic_rx:
629         dr_matcher_uninit_nic(&matcher->rx);
630         return ret;
631 }
632
633 static int dr_matcher_init(struct mlx5dr_matcher *matcher,
634                            struct mlx5dr_match_parameters *mask)
635 {
636         struct mlx5dr_table *tbl = matcher->tbl;
637         struct mlx5dr_domain *dmn = tbl->dmn;
638         int ret;
639
640         if (matcher->match_criteria >= DR_MATCHER_CRITERIA_MAX) {
641                 mlx5dr_err(dmn, "Invalid match criteria attribute\n");
642                 return -EINVAL;
643         }
644
645         if (mask) {
646                 if (mask->match_sz > DR_SZ_MATCH_PARAM) {
647                         mlx5dr_err(dmn, "Invalid match size attribute\n");
648                         return -EINVAL;
649                 }
650                 mlx5dr_ste_copy_param(matcher->match_criteria,
651                                       &matcher->mask, mask);
652         }
653
654         switch (dmn->type) {
655         case MLX5DR_DOMAIN_TYPE_NIC_RX:
656                 matcher->rx.nic_tbl = &tbl->rx;
657                 ret = dr_matcher_init_nic(matcher, &matcher->rx);
658                 break;
659         case MLX5DR_DOMAIN_TYPE_NIC_TX:
660                 matcher->tx.nic_tbl = &tbl->tx;
661                 ret = dr_matcher_init_nic(matcher, &matcher->tx);
662                 break;
663         case MLX5DR_DOMAIN_TYPE_FDB:
664                 matcher->rx.nic_tbl = &tbl->rx;
665                 matcher->tx.nic_tbl = &tbl->tx;
666                 ret = dr_matcher_init_fdb(matcher);
667                 break;
668         default:
669                 WARN_ON(true);
670                 return -EINVAL;
671         }
672
673         return ret;
674 }
675
676 struct mlx5dr_matcher *
677 mlx5dr_matcher_create(struct mlx5dr_table *tbl,
678                       u32 priority,
679                       u8 match_criteria_enable,
680                       struct mlx5dr_match_parameters *mask)
681 {
682         struct mlx5dr_matcher *matcher;
683         int ret;
684
685         refcount_inc(&tbl->refcount);
686
687         matcher = kzalloc(sizeof(*matcher), GFP_KERNEL);
688         if (!matcher)
689                 goto dec_ref;
690
691         matcher->tbl = tbl;
692         matcher->prio = priority;
693         matcher->match_criteria = match_criteria_enable;
694         refcount_set(&matcher->refcount, 1);
695         INIT_LIST_HEAD(&matcher->matcher_list);
696
697         mlx5dr_domain_lock(tbl->dmn);
698
699         ret = dr_matcher_init(matcher, mask);
700         if (ret)
701                 goto free_matcher;
702
703         ret = dr_matcher_add_to_tbl(matcher);
704         if (ret)
705                 goto matcher_uninit;
706
707         mlx5dr_domain_unlock(tbl->dmn);
708
709         return matcher;
710
711 matcher_uninit:
712         dr_matcher_uninit(matcher);
713 free_matcher:
714         mlx5dr_domain_unlock(tbl->dmn);
715         kfree(matcher);
716 dec_ref:
717         refcount_dec(&tbl->refcount);
718         return NULL;
719 }
720
721 static int dr_matcher_disconnect(struct mlx5dr_domain *dmn,
722                                  struct mlx5dr_table_rx_tx *nic_tbl,
723                                  struct mlx5dr_matcher_rx_tx *next_nic_matcher,
724                                  struct mlx5dr_matcher_rx_tx *prev_nic_matcher)
725 {
726         struct mlx5dr_domain_rx_tx *nic_dmn = nic_tbl->nic_dmn;
727         struct mlx5dr_htbl_connect_info info;
728         struct mlx5dr_ste_htbl *prev_anchor;
729
730         if (prev_nic_matcher)
731                 prev_anchor = prev_nic_matcher->e_anchor;
732         else
733                 prev_anchor = nic_tbl->s_anchor;
734
735         /* Connect previous anchor hash table to next matcher or to the default address */
736         if (next_nic_matcher) {
737                 info.type = CONNECT_HIT;
738                 info.hit_next_htbl = next_nic_matcher->s_htbl;
739                 next_nic_matcher->s_htbl->pointing_ste = prev_anchor->ste_arr;
740                 prev_anchor->ste_arr[0].next_htbl = next_nic_matcher->s_htbl;
741         } else {
742                 info.type = CONNECT_MISS;
743                 info.miss_icm_addr = nic_tbl->default_icm_addr;
744                 prev_anchor->ste_arr[0].next_htbl = NULL;
745         }
746
747         return mlx5dr_ste_htbl_init_and_postsend(dmn, nic_dmn, prev_anchor,
748                                                  &info, true);
749 }
750
751 static int dr_matcher_remove_from_tbl(struct mlx5dr_matcher *matcher)
752 {
753         struct mlx5dr_matcher *prev_matcher, *next_matcher;
754         struct mlx5dr_table *tbl = matcher->tbl;
755         struct mlx5dr_domain *dmn = tbl->dmn;
756         int ret = 0;
757
758         if (list_is_last(&matcher->matcher_list, &tbl->matcher_list))
759                 next_matcher = NULL;
760         else
761                 next_matcher = list_next_entry(matcher, matcher_list);
762
763         if (matcher->matcher_list.prev == &tbl->matcher_list)
764                 prev_matcher = NULL;
765         else
766                 prev_matcher = list_prev_entry(matcher, matcher_list);
767
768         if (dmn->type == MLX5DR_DOMAIN_TYPE_FDB ||
769             dmn->type == MLX5DR_DOMAIN_TYPE_NIC_RX) {
770                 ret = dr_matcher_disconnect(dmn, &tbl->rx,
771                                             next_matcher ? &next_matcher->rx : NULL,
772                                             prev_matcher ? &prev_matcher->rx : NULL);
773                 if (ret)
774                         return ret;
775         }
776
777         if (dmn->type == MLX5DR_DOMAIN_TYPE_FDB ||
778             dmn->type == MLX5DR_DOMAIN_TYPE_NIC_TX) {
779                 ret = dr_matcher_disconnect(dmn, &tbl->tx,
780                                             next_matcher ? &next_matcher->tx : NULL,
781                                             prev_matcher ? &prev_matcher->tx : NULL);
782                 if (ret)
783                         return ret;
784         }
785
786         list_del(&matcher->matcher_list);
787
788         return 0;
789 }
790
791 int mlx5dr_matcher_destroy(struct mlx5dr_matcher *matcher)
792 {
793         struct mlx5dr_table *tbl = matcher->tbl;
794
795         if (refcount_read(&matcher->refcount) > 1)
796                 return -EBUSY;
797
798         mlx5dr_domain_lock(tbl->dmn);
799
800         dr_matcher_remove_from_tbl(matcher);
801         dr_matcher_uninit(matcher);
802         refcount_dec(&matcher->tbl->refcount);
803
804         mlx5dr_domain_unlock(tbl->dmn);
805         kfree(matcher);
806
807         return 0;
808 }