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