]>
Commit | Line | Data |
---|---|---|
852f660b AV |
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 | ||
6e9e286e YK |
105 | static bool |
106 | dr_mask_is_misc3_vxlan_gpe_set(struct mlx5dr_match_misc3 *misc3) | |
852f660b AV |
107 | { |
108 | return (misc3->outer_vxlan_gpe_vni || | |
109 | misc3->outer_vxlan_gpe_next_protocol || | |
110 | misc3->outer_vxlan_gpe_flags); | |
111 | } | |
112 | ||
6e9e286e YK |
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 | ||
b6d12238 YK |
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 | ||
852f660b AV |
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 | ||
852f660b AV |
179 | int mlx5dr_matcher_select_builders(struct mlx5dr_matcher *matcher, |
180 | struct mlx5dr_matcher_rx_tx *nic_matcher, | |
667f2646 AV |
181 | enum mlx5dr_ipv outer_ipv, |
182 | enum mlx5dr_ipv inner_ipv) | |
852f660b | 183 | { |
667f2646 AV |
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]; | |
852f660b | 188 | |
86bb811b | 189 | if (!nic_matcher->num_of_builders) { |
852f660b AV |
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, | |
667f2646 AV |
200 | enum mlx5dr_ipv outer_ipv, |
201 | enum mlx5dr_ipv inner_ipv) | |
852f660b AV |
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; | |
852f660b AV |
208 | bool inner, rx; |
209 | int idx = 0; | |
210 | int ret, i; | |
211 | ||
667f2646 | 212 | sb = nic_matcher->ste_builder_arr[outer_ipv][inner_ipv]; |
852f660b AV |
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, | |
640bdb1f | 256 | dmn, inner, rx); |
852f660b AV |
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 | ||
667f2646 | 275 | if (outer_ipv == DR_RULE_IPV6) { |
852f660b AV |
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 | ||
6e9e286e YK |
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); | |
b6d12238 YK |
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); | |
852f660b AV |
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 | ||
667f2646 | 355 | if (inner_ipv == DR_RULE_IPV6) { |
852f660b AV |
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_dbg(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_info(dmn, "Mask contains unsupported parameters\n"); | |
399 | return -EOPNOTSUPP; | |
400 | } | |
401 | } | |
402 | ||
667f2646 AV |
403 | nic_matcher->ste_builder = sb; |
404 | nic_matcher->num_of_builders_arr[outer_ipv][inner_ipv] = idx; | |
852f660b AV |
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 | if (!list_empty(&tbl->matcher_list)) | |
478 | list_for_each_entry(tmp_matcher, &tbl->matcher_list, matcher_list) { | |
479 | if (tmp_matcher->prio >= matcher->prio) { | |
480 | next_matcher = tmp_matcher; | |
481 | break; | |
482 | } | |
483 | first = false; | |
484 | } | |
485 | ||
486 | prev_matcher = NULL; | |
487 | if (next_matcher && !first) | |
48cbde4b | 488 | prev_matcher = list_prev_entry(next_matcher, matcher_list); |
852f660b | 489 | else if (!first) |
48cbde4b AV |
490 | prev_matcher = list_last_entry(&tbl->matcher_list, |
491 | struct mlx5dr_matcher, | |
492 | matcher_list); | |
852f660b AV |
493 | |
494 | if (dmn->type == MLX5DR_DOMAIN_TYPE_FDB || | |
495 | dmn->type == MLX5DR_DOMAIN_TYPE_NIC_RX) { | |
496 | ret = dr_matcher_connect(dmn, &matcher->rx, | |
497 | next_matcher ? &next_matcher->rx : NULL, | |
498 | prev_matcher ? &prev_matcher->rx : NULL); | |
499 | if (ret) | |
500 | return ret; | |
501 | } | |
502 | ||
503 | if (dmn->type == MLX5DR_DOMAIN_TYPE_FDB || | |
504 | dmn->type == MLX5DR_DOMAIN_TYPE_NIC_TX) { | |
505 | ret = dr_matcher_connect(dmn, &matcher->tx, | |
506 | next_matcher ? &next_matcher->tx : NULL, | |
507 | prev_matcher ? &prev_matcher->tx : NULL); | |
508 | if (ret) | |
509 | return ret; | |
510 | } | |
511 | ||
512 | if (prev_matcher) | |
513 | list_add(&matcher->matcher_list, &prev_matcher->matcher_list); | |
514 | else if (next_matcher) | |
515 | list_add_tail(&matcher->matcher_list, | |
516 | &next_matcher->matcher_list); | |
517 | else | |
518 | list_add(&matcher->matcher_list, &tbl->matcher_list); | |
519 | ||
520 | return 0; | |
521 | } | |
522 | ||
523 | static void dr_matcher_uninit_nic(struct mlx5dr_matcher_rx_tx *nic_matcher) | |
524 | { | |
525 | mlx5dr_htbl_put(nic_matcher->s_htbl); | |
526 | mlx5dr_htbl_put(nic_matcher->e_anchor); | |
527 | } | |
528 | ||
529 | static void dr_matcher_uninit_fdb(struct mlx5dr_matcher *matcher) | |
530 | { | |
531 | dr_matcher_uninit_nic(&matcher->rx); | |
532 | dr_matcher_uninit_nic(&matcher->tx); | |
533 | } | |
534 | ||
535 | static void dr_matcher_uninit(struct mlx5dr_matcher *matcher) | |
536 | { | |
537 | struct mlx5dr_domain *dmn = matcher->tbl->dmn; | |
538 | ||
539 | switch (dmn->type) { | |
540 | case MLX5DR_DOMAIN_TYPE_NIC_RX: | |
541 | dr_matcher_uninit_nic(&matcher->rx); | |
542 | break; | |
543 | case MLX5DR_DOMAIN_TYPE_NIC_TX: | |
544 | dr_matcher_uninit_nic(&matcher->tx); | |
545 | break; | |
546 | case MLX5DR_DOMAIN_TYPE_FDB: | |
547 | dr_matcher_uninit_fdb(matcher); | |
548 | break; | |
549 | default: | |
550 | WARN_ON(true); | |
551 | break; | |
552 | } | |
553 | } | |
554 | ||
667f2646 AV |
555 | static int dr_matcher_set_all_ste_builders(struct mlx5dr_matcher *matcher, |
556 | struct mlx5dr_matcher_rx_tx *nic_matcher) | |
852f660b AV |
557 | { |
558 | struct mlx5dr_domain *dmn = matcher->tbl->dmn; | |
852f660b | 559 | |
667f2646 AV |
560 | dr_matcher_set_ste_builders(matcher, nic_matcher, DR_RULE_IPV4, DR_RULE_IPV4); |
561 | dr_matcher_set_ste_builders(matcher, nic_matcher, DR_RULE_IPV4, DR_RULE_IPV6); | |
562 | dr_matcher_set_ste_builders(matcher, nic_matcher, DR_RULE_IPV6, DR_RULE_IPV4); | |
563 | dr_matcher_set_ste_builders(matcher, nic_matcher, DR_RULE_IPV6, DR_RULE_IPV6); | |
852f660b | 564 | |
667f2646 | 565 | if (!nic_matcher->ste_builder) { |
852f660b AV |
566 | mlx5dr_dbg(dmn, "Cannot generate IPv4 or IPv6 rules with given mask\n"); |
567 | return -EINVAL; | |
568 | } | |
569 | ||
667f2646 AV |
570 | return 0; |
571 | } | |
572 | ||
573 | static int dr_matcher_init_nic(struct mlx5dr_matcher *matcher, | |
574 | struct mlx5dr_matcher_rx_tx *nic_matcher) | |
575 | { | |
576 | struct mlx5dr_domain *dmn = matcher->tbl->dmn; | |
577 | int ret; | |
578 | ||
579 | ret = dr_matcher_set_all_ste_builders(matcher, nic_matcher); | |
580 | if (ret) | |
581 | return ret; | |
852f660b AV |
582 | |
583 | nic_matcher->e_anchor = mlx5dr_ste_htbl_alloc(dmn->ste_icm_pool, | |
584 | DR_CHUNK_SIZE_1, | |
585 | MLX5DR_STE_LU_TYPE_DONT_CARE, | |
586 | 0); | |
587 | if (!nic_matcher->e_anchor) | |
588 | return -ENOMEM; | |
589 | ||
590 | nic_matcher->s_htbl = mlx5dr_ste_htbl_alloc(dmn->ste_icm_pool, | |
591 | DR_CHUNK_SIZE_1, | |
592 | nic_matcher->ste_builder[0].lu_type, | |
593 | nic_matcher->ste_builder[0].byte_mask); | |
594 | if (!nic_matcher->s_htbl) { | |
595 | ret = -ENOMEM; | |
596 | goto free_e_htbl; | |
597 | } | |
598 | ||
599 | /* make sure the tables exist while empty */ | |
600 | mlx5dr_htbl_get(nic_matcher->s_htbl); | |
601 | mlx5dr_htbl_get(nic_matcher->e_anchor); | |
602 | ||
603 | return 0; | |
604 | ||
605 | free_e_htbl: | |
606 | mlx5dr_ste_htbl_free(nic_matcher->e_anchor); | |
607 | return ret; | |
608 | } | |
609 | ||
610 | static int dr_matcher_init_fdb(struct mlx5dr_matcher *matcher) | |
611 | { | |
612 | int ret; | |
613 | ||
614 | ret = dr_matcher_init_nic(matcher, &matcher->rx); | |
615 | if (ret) | |
616 | return ret; | |
617 | ||
618 | ret = dr_matcher_init_nic(matcher, &matcher->tx); | |
619 | if (ret) | |
620 | goto uninit_nic_rx; | |
621 | ||
622 | return 0; | |
623 | ||
624 | uninit_nic_rx: | |
625 | dr_matcher_uninit_nic(&matcher->rx); | |
626 | return ret; | |
627 | } | |
628 | ||
629 | static int dr_matcher_init(struct mlx5dr_matcher *matcher, | |
630 | struct mlx5dr_match_parameters *mask) | |
631 | { | |
632 | struct mlx5dr_table *tbl = matcher->tbl; | |
633 | struct mlx5dr_domain *dmn = tbl->dmn; | |
634 | int ret; | |
635 | ||
636 | if (matcher->match_criteria >= DR_MATCHER_CRITERIA_MAX) { | |
637 | mlx5dr_info(dmn, "Invalid match criteria attribute\n"); | |
638 | return -EINVAL; | |
639 | } | |
640 | ||
641 | if (mask) { | |
642 | if (mask->match_sz > sizeof(struct mlx5dr_match_param)) { | |
643 | mlx5dr_info(dmn, "Invalid match size attribute\n"); | |
644 | return -EINVAL; | |
645 | } | |
646 | mlx5dr_ste_copy_param(matcher->match_criteria, | |
647 | &matcher->mask, mask); | |
648 | } | |
649 | ||
650 | switch (dmn->type) { | |
651 | case MLX5DR_DOMAIN_TYPE_NIC_RX: | |
652 | matcher->rx.nic_tbl = &tbl->rx; | |
653 | ret = dr_matcher_init_nic(matcher, &matcher->rx); | |
654 | break; | |
655 | case MLX5DR_DOMAIN_TYPE_NIC_TX: | |
656 | matcher->tx.nic_tbl = &tbl->tx; | |
657 | ret = dr_matcher_init_nic(matcher, &matcher->tx); | |
658 | break; | |
659 | case MLX5DR_DOMAIN_TYPE_FDB: | |
660 | matcher->rx.nic_tbl = &tbl->rx; | |
661 | matcher->tx.nic_tbl = &tbl->tx; | |
662 | ret = dr_matcher_init_fdb(matcher); | |
663 | break; | |
664 | default: | |
665 | WARN_ON(true); | |
666 | return -EINVAL; | |
667 | } | |
668 | ||
669 | return ret; | |
670 | } | |
671 | ||
672 | struct mlx5dr_matcher * | |
673 | mlx5dr_matcher_create(struct mlx5dr_table *tbl, | |
674 | u16 priority, | |
675 | u8 match_criteria_enable, | |
676 | struct mlx5dr_match_parameters *mask) | |
677 | { | |
678 | struct mlx5dr_matcher *matcher; | |
679 | int ret; | |
680 | ||
681 | refcount_inc(&tbl->refcount); | |
682 | ||
683 | matcher = kzalloc(sizeof(*matcher), GFP_KERNEL); | |
684 | if (!matcher) | |
685 | goto dec_ref; | |
686 | ||
687 | matcher->tbl = tbl; | |
688 | matcher->prio = priority; | |
689 | matcher->match_criteria = match_criteria_enable; | |
690 | refcount_set(&matcher->refcount, 1); | |
691 | INIT_LIST_HEAD(&matcher->matcher_list); | |
692 | ||
693 | mutex_lock(&tbl->dmn->mutex); | |
694 | ||
695 | ret = dr_matcher_init(matcher, mask); | |
696 | if (ret) | |
697 | goto free_matcher; | |
698 | ||
699 | ret = dr_matcher_add_to_tbl(matcher); | |
700 | if (ret) | |
701 | goto matcher_uninit; | |
702 | ||
703 | mutex_unlock(&tbl->dmn->mutex); | |
704 | ||
705 | return matcher; | |
706 | ||
707 | matcher_uninit: | |
708 | dr_matcher_uninit(matcher); | |
709 | free_matcher: | |
710 | mutex_unlock(&tbl->dmn->mutex); | |
711 | kfree(matcher); | |
712 | dec_ref: | |
713 | refcount_dec(&tbl->refcount); | |
714 | return NULL; | |
715 | } | |
716 | ||
717 | static int dr_matcher_disconnect(struct mlx5dr_domain *dmn, | |
718 | struct mlx5dr_table_rx_tx *nic_tbl, | |
719 | struct mlx5dr_matcher_rx_tx *next_nic_matcher, | |
720 | struct mlx5dr_matcher_rx_tx *prev_nic_matcher) | |
721 | { | |
722 | struct mlx5dr_domain_rx_tx *nic_dmn = nic_tbl->nic_dmn; | |
723 | struct mlx5dr_htbl_connect_info info; | |
724 | struct mlx5dr_ste_htbl *prev_anchor; | |
725 | ||
726 | if (prev_nic_matcher) | |
727 | prev_anchor = prev_nic_matcher->e_anchor; | |
728 | else | |
729 | prev_anchor = nic_tbl->s_anchor; | |
730 | ||
731 | /* Connect previous anchor hash table to next matcher or to the default address */ | |
732 | if (next_nic_matcher) { | |
733 | info.type = CONNECT_HIT; | |
734 | info.hit_next_htbl = next_nic_matcher->s_htbl; | |
735 | next_nic_matcher->s_htbl->pointing_ste = prev_anchor->ste_arr; | |
736 | prev_anchor->ste_arr[0].next_htbl = next_nic_matcher->s_htbl; | |
737 | } else { | |
738 | info.type = CONNECT_MISS; | |
739 | info.miss_icm_addr = nic_tbl->default_icm_addr; | |
740 | prev_anchor->ste_arr[0].next_htbl = NULL; | |
741 | } | |
742 | ||
743 | return mlx5dr_ste_htbl_init_and_postsend(dmn, nic_dmn, prev_anchor, | |
744 | &info, true); | |
745 | } | |
746 | ||
747 | static int dr_matcher_remove_from_tbl(struct mlx5dr_matcher *matcher) | |
748 | { | |
749 | struct mlx5dr_matcher *prev_matcher, *next_matcher; | |
750 | struct mlx5dr_table *tbl = matcher->tbl; | |
751 | struct mlx5dr_domain *dmn = tbl->dmn; | |
752 | int ret = 0; | |
753 | ||
754 | if (list_is_last(&matcher->matcher_list, &tbl->matcher_list)) | |
755 | next_matcher = NULL; | |
756 | else | |
757 | next_matcher = list_next_entry(matcher, matcher_list); | |
758 | ||
759 | if (matcher->matcher_list.prev == &tbl->matcher_list) | |
760 | prev_matcher = NULL; | |
761 | else | |
762 | prev_matcher = list_prev_entry(matcher, matcher_list); | |
763 | ||
764 | if (dmn->type == MLX5DR_DOMAIN_TYPE_FDB || | |
765 | dmn->type == MLX5DR_DOMAIN_TYPE_NIC_RX) { | |
766 | ret = dr_matcher_disconnect(dmn, &tbl->rx, | |
767 | next_matcher ? &next_matcher->rx : NULL, | |
768 | prev_matcher ? &prev_matcher->rx : NULL); | |
769 | if (ret) | |
770 | return ret; | |
771 | } | |
772 | ||
773 | if (dmn->type == MLX5DR_DOMAIN_TYPE_FDB || | |
774 | dmn->type == MLX5DR_DOMAIN_TYPE_NIC_TX) { | |
775 | ret = dr_matcher_disconnect(dmn, &tbl->tx, | |
776 | next_matcher ? &next_matcher->tx : NULL, | |
777 | prev_matcher ? &prev_matcher->tx : NULL); | |
778 | if (ret) | |
779 | return ret; | |
780 | } | |
781 | ||
782 | list_del(&matcher->matcher_list); | |
783 | ||
784 | return 0; | |
785 | } | |
786 | ||
787 | int mlx5dr_matcher_destroy(struct mlx5dr_matcher *matcher) | |
788 | { | |
789 | struct mlx5dr_table *tbl = matcher->tbl; | |
790 | ||
791 | if (refcount_read(&matcher->refcount) > 1) | |
792 | return -EBUSY; | |
793 | ||
794 | mutex_lock(&tbl->dmn->mutex); | |
795 | ||
796 | dr_matcher_remove_from_tbl(matcher); | |
797 | dr_matcher_uninit(matcher); | |
798 | refcount_dec(&matcher->tbl->refcount); | |
799 | ||
800 | mutex_unlock(&tbl->dmn->mutex); | |
801 | kfree(matcher); | |
802 | ||
803 | return 0; | |
804 | } |