1 // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
2 /* Copyright (c) 2019 Mellanox Technologies. */
3 
4 #include "dr_types.h"
5 
dr_mask_is_smac_set(struct mlx5dr_match_spec * spec)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 
dr_mask_is_dmac_set(struct mlx5dr_match_spec * spec)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 
dr_mask_is_src_addr_set(struct mlx5dr_match_spec * spec)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 
dr_mask_is_dst_addr_set(struct mlx5dr_match_spec * spec)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 
dr_mask_is_l3_base_set(struct mlx5dr_match_spec * spec)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 
dr_mask_is_tcp_udp_base_set(struct mlx5dr_match_spec * spec)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 
dr_mask_is_ipv4_set(struct mlx5dr_match_spec * spec)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 
dr_mask_is_ipv4_5_tuple_set(struct mlx5dr_match_spec * spec)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 
dr_mask_is_eth_l2_tnl_set(struct mlx5dr_match_misc * misc)52 static bool dr_mask_is_eth_l2_tnl_set(struct mlx5dr_match_misc *misc)
53 {
54 	return misc->vxlan_vni;
55 }
56 
dr_mask_is_ttl_set(struct mlx5dr_match_spec * spec)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 
dr_mask_is_gre_set(struct mlx5dr_match_misc * misc)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 
dr_mask_is_flex_parser_tnl_set(struct mlx5dr_match_misc3 * misc3)105 static 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 
dr_mask_is_flex_parser_icmpv6_set(struct mlx5dr_match_misc3 * misc3)112 static 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 
dr_mask_is_wqe_metadata_set(struct mlx5dr_match_misc2 * misc2)118 static bool dr_mask_is_wqe_metadata_set(struct mlx5dr_match_misc2 *misc2)
119 {
120 	return misc2->metadata_reg_a;
121 }
122 
dr_mask_is_reg_c_0_3_set(struct mlx5dr_match_misc2 * misc2)123 static 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 
dr_mask_is_reg_c_4_7_set(struct mlx5dr_match_misc2 * misc2)129 static 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 
dr_mask_is_gvmi_or_qpn_set(struct mlx5dr_match_misc * misc)135 static bool dr_mask_is_gvmi_or_qpn_set(struct mlx5dr_match_misc *misc)
136 {
137 	return (misc->source_sqn || misc->source_port);
138 }
139 
140 static bool
dr_matcher_supp_flex_parser_vxlan_gpe(struct mlx5dr_domain * dmn)141 dr_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 
mlx5dr_matcher_select_builders(struct mlx5dr_matcher * matcher,struct mlx5dr_matcher_rx_tx * nic_matcher,bool ipv6)147 int 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 
dr_matcher_set_ste_builders(struct mlx5dr_matcher * matcher,struct mlx5dr_matcher_rx_tx * nic_matcher,bool ipv6)168 static 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 
dr_matcher_connect(struct mlx5dr_domain * dmn,struct mlx5dr_matcher_rx_tx * curr_nic_matcher,struct mlx5dr_matcher_rx_tx * next_nic_matcher,struct mlx5dr_matcher_rx_tx * prev_nic_matcher)381 static 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 
dr_matcher_add_to_tbl(struct mlx5dr_matcher * matcher)440 static 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 
dr_matcher_uninit_nic(struct mlx5dr_matcher_rx_tx * nic_matcher)495 static 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 
dr_matcher_uninit_fdb(struct mlx5dr_matcher * matcher)501 static 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 
dr_matcher_uninit(struct mlx5dr_matcher * matcher)507 static 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 
dr_matcher_init_nic(struct mlx5dr_matcher * matcher,struct mlx5dr_matcher_rx_tx * nic_matcher)527 static 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 
568 free_e_htbl:
569 	mlx5dr_ste_htbl_free(nic_matcher->e_anchor);
570 	return ret;
571 }
572 
dr_matcher_init_fdb(struct mlx5dr_matcher * matcher)573 static 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 
587 uninit_nic_rx:
588 	dr_matcher_uninit_nic(&matcher->rx);
589 	return ret;
590 }
591 
dr_matcher_init(struct mlx5dr_matcher * matcher,struct mlx5dr_match_parameters * mask)592 static 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 
635 struct mlx5dr_matcher *
mlx5dr_matcher_create(struct mlx5dr_table * tbl,u16 priority,u8 match_criteria_enable,struct mlx5dr_match_parameters * mask)636 mlx5dr_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 
670 matcher_uninit:
671 	dr_matcher_uninit(matcher);
672 free_matcher:
673 	mutex_unlock(&tbl->dmn->mutex);
674 	kfree(matcher);
675 dec_ref:
676 	refcount_dec(&tbl->refcount);
677 	return NULL;
678 }
679 
dr_matcher_disconnect(struct mlx5dr_domain * dmn,struct mlx5dr_table_rx_tx * nic_tbl,struct mlx5dr_matcher_rx_tx * next_nic_matcher,struct mlx5dr_matcher_rx_tx * prev_nic_matcher)680 static 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 
dr_matcher_remove_from_tbl(struct mlx5dr_matcher * matcher)710 static 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 
mlx5dr_matcher_destroy(struct mlx5dr_matcher * matcher)750 int 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