1 // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
2 /* Copyright (c) 2019 Mellanox Technologies. */
3 
4 #include "dr_types.h"
5 
6 #define DR_RULE_MAX_STE_CHAIN (DR_RULE_MAX_STES + DR_ACTION_MAX_STES)
7 
8 struct mlx5dr_rule_action_member {
9 	struct mlx5dr_action *action;
10 	struct list_head list;
11 };
12 
13 static int dr_rule_append_to_miss_list(struct mlx5dr_ste_ctx *ste_ctx,
14 				       struct mlx5dr_ste *new_last_ste,
15 				       struct list_head *miss_list,
16 				       struct list_head *send_list)
17 {
18 	struct mlx5dr_ste_send_info *ste_info_last;
19 	struct mlx5dr_ste *last_ste;
20 
21 	/* The new entry will be inserted after the last */
22 	last_ste = list_last_entry(miss_list, struct mlx5dr_ste, miss_list_node);
23 	WARN_ON(!last_ste);
24 
25 	ste_info_last = kzalloc(sizeof(*ste_info_last), GFP_KERNEL);
26 	if (!ste_info_last)
27 		return -ENOMEM;
28 
29 	mlx5dr_ste_set_miss_addr(ste_ctx, last_ste->hw_ste,
30 				 mlx5dr_ste_get_icm_addr(new_last_ste));
31 	list_add_tail(&new_last_ste->miss_list_node, miss_list);
32 
33 	mlx5dr_send_fill_and_append_ste_send_info(last_ste, DR_STE_SIZE_CTRL,
34 						  0, last_ste->hw_ste,
35 						  ste_info_last, send_list, true);
36 
37 	return 0;
38 }
39 
40 static struct mlx5dr_ste *
41 dr_rule_create_collision_htbl(struct mlx5dr_matcher *matcher,
42 			      struct mlx5dr_matcher_rx_tx *nic_matcher,
43 			      u8 *hw_ste)
44 {
45 	struct mlx5dr_domain *dmn = matcher->tbl->dmn;
46 	struct mlx5dr_ste_ctx *ste_ctx = dmn->ste_ctx;
47 	struct mlx5dr_ste_htbl *new_htbl;
48 	struct mlx5dr_ste *ste;
49 
50 	/* Create new table for miss entry */
51 	new_htbl = mlx5dr_ste_htbl_alloc(dmn->ste_icm_pool,
52 					 DR_CHUNK_SIZE_1,
53 					 MLX5DR_STE_LU_TYPE_DONT_CARE,
54 					 0);
55 	if (!new_htbl) {
56 		mlx5dr_dbg(dmn, "Failed allocating collision table\n");
57 		return NULL;
58 	}
59 
60 	/* One and only entry, never grows */
61 	ste = new_htbl->ste_arr;
62 	mlx5dr_ste_set_miss_addr(ste_ctx, hw_ste,
63 				 nic_matcher->e_anchor->chunk->icm_addr);
64 	mlx5dr_htbl_get(new_htbl);
65 
66 	return ste;
67 }
68 
69 static struct mlx5dr_ste *
70 dr_rule_create_collision_entry(struct mlx5dr_matcher *matcher,
71 			       struct mlx5dr_matcher_rx_tx *nic_matcher,
72 			       u8 *hw_ste,
73 			       struct mlx5dr_ste *orig_ste)
74 {
75 	struct mlx5dr_ste *ste;
76 
77 	ste = dr_rule_create_collision_htbl(matcher, nic_matcher, hw_ste);
78 	if (!ste) {
79 		mlx5dr_dbg(matcher->tbl->dmn, "Failed creating collision entry\n");
80 		return NULL;
81 	}
82 
83 	ste->ste_chain_location = orig_ste->ste_chain_location;
84 	ste->htbl->pointing_ste = orig_ste->htbl->pointing_ste;
85 
86 	/* In collision entry, all members share the same miss_list_head */
87 	ste->htbl->miss_list = mlx5dr_ste_get_miss_list(orig_ste);
88 
89 	/* Next table */
90 	if (mlx5dr_ste_create_next_htbl(matcher, nic_matcher, ste, hw_ste,
91 					DR_CHUNK_SIZE_1)) {
92 		mlx5dr_dbg(matcher->tbl->dmn, "Failed allocating table\n");
93 		goto free_tbl;
94 	}
95 
96 	return ste;
97 
98 free_tbl:
99 	mlx5dr_ste_free(ste, matcher, nic_matcher);
100 	return NULL;
101 }
102 
103 static int
104 dr_rule_handle_one_ste_in_update_list(struct mlx5dr_ste_send_info *ste_info,
105 				      struct mlx5dr_domain *dmn)
106 {
107 	int ret;
108 
109 	list_del(&ste_info->send_list);
110 
111 	/* Copy data to ste, only reduced size or control, the last 16B (mask)
112 	 * is already written to the hw.
113 	 */
114 	if (ste_info->size == DR_STE_SIZE_CTRL)
115 		memcpy(ste_info->ste->hw_ste, ste_info->data, DR_STE_SIZE_CTRL);
116 	else
117 		memcpy(ste_info->ste->hw_ste, ste_info->data, DR_STE_SIZE_REDUCED);
118 
119 	ret = mlx5dr_send_postsend_ste(dmn, ste_info->ste, ste_info->data,
120 				       ste_info->size, ste_info->offset);
121 	if (ret)
122 		goto out;
123 
124 out:
125 	kfree(ste_info);
126 	return ret;
127 }
128 
129 static int dr_rule_send_update_list(struct list_head *send_ste_list,
130 				    struct mlx5dr_domain *dmn,
131 				    bool is_reverse)
132 {
133 	struct mlx5dr_ste_send_info *ste_info, *tmp_ste_info;
134 	int ret;
135 
136 	if (is_reverse) {
137 		list_for_each_entry_safe_reverse(ste_info, tmp_ste_info,
138 						 send_ste_list, send_list) {
139 			ret = dr_rule_handle_one_ste_in_update_list(ste_info,
140 								    dmn);
141 			if (ret)
142 				return ret;
143 		}
144 	} else {
145 		list_for_each_entry_safe(ste_info, tmp_ste_info,
146 					 send_ste_list, send_list) {
147 			ret = dr_rule_handle_one_ste_in_update_list(ste_info,
148 								    dmn);
149 			if (ret)
150 				return ret;
151 		}
152 	}
153 
154 	return 0;
155 }
156 
157 static struct mlx5dr_ste *
158 dr_rule_find_ste_in_miss_list(struct list_head *miss_list, u8 *hw_ste)
159 {
160 	struct mlx5dr_ste *ste;
161 
162 	if (list_empty(miss_list))
163 		return NULL;
164 
165 	/* Check if hw_ste is present in the list */
166 	list_for_each_entry(ste, miss_list, miss_list_node) {
167 		if (mlx5dr_ste_equal_tag(ste->hw_ste, hw_ste))
168 			return ste;
169 	}
170 
171 	return NULL;
172 }
173 
174 static struct mlx5dr_ste *
175 dr_rule_rehash_handle_collision(struct mlx5dr_matcher *matcher,
176 				struct mlx5dr_matcher_rx_tx *nic_matcher,
177 				struct list_head *update_list,
178 				struct mlx5dr_ste *col_ste,
179 				u8 *hw_ste)
180 {
181 	struct mlx5dr_domain *dmn = matcher->tbl->dmn;
182 	struct mlx5dr_ste *new_ste;
183 	int ret;
184 
185 	new_ste = dr_rule_create_collision_htbl(matcher, nic_matcher, hw_ste);
186 	if (!new_ste)
187 		return NULL;
188 
189 	/* Update collision pointing STE */
190 	new_ste->htbl->pointing_ste = col_ste->htbl->pointing_ste;
191 
192 	/* In collision entry, all members share the same miss_list_head */
193 	new_ste->htbl->miss_list = mlx5dr_ste_get_miss_list(col_ste);
194 
195 	/* Update the previous from the list */
196 	ret = dr_rule_append_to_miss_list(dmn->ste_ctx, new_ste,
197 					  mlx5dr_ste_get_miss_list(col_ste),
198 					  update_list);
199 	if (ret) {
200 		mlx5dr_dbg(dmn, "Failed update dup entry\n");
201 		goto err_exit;
202 	}
203 
204 	return new_ste;
205 
206 err_exit:
207 	mlx5dr_ste_free(new_ste, matcher, nic_matcher);
208 	return NULL;
209 }
210 
211 static void dr_rule_rehash_copy_ste_ctrl(struct mlx5dr_matcher *matcher,
212 					 struct mlx5dr_matcher_rx_tx *nic_matcher,
213 					 struct mlx5dr_ste *cur_ste,
214 					 struct mlx5dr_ste *new_ste)
215 {
216 	new_ste->next_htbl = cur_ste->next_htbl;
217 	new_ste->ste_chain_location = cur_ste->ste_chain_location;
218 
219 	if (new_ste->next_htbl)
220 		new_ste->next_htbl->pointing_ste = new_ste;
221 
222 	/* We need to copy the refcount since this ste
223 	 * may have been traversed several times
224 	 */
225 	new_ste->refcount = cur_ste->refcount;
226 
227 	/* Link old STEs rule to the new ste */
228 	mlx5dr_rule_set_last_member(cur_ste->rule_rx_tx, new_ste, false);
229 }
230 
231 static struct mlx5dr_ste *
232 dr_rule_rehash_copy_ste(struct mlx5dr_matcher *matcher,
233 			struct mlx5dr_matcher_rx_tx *nic_matcher,
234 			struct mlx5dr_ste *cur_ste,
235 			struct mlx5dr_ste_htbl *new_htbl,
236 			struct list_head *update_list)
237 {
238 	struct mlx5dr_domain *dmn = matcher->tbl->dmn;
239 	struct mlx5dr_ste_send_info *ste_info;
240 	bool use_update_list = false;
241 	u8 hw_ste[DR_STE_SIZE] = {};
242 	struct mlx5dr_ste *new_ste;
243 	int new_idx;
244 	u8 sb_idx;
245 
246 	/* Copy STE mask from the matcher */
247 	sb_idx = cur_ste->ste_chain_location - 1;
248 	mlx5dr_ste_set_bit_mask(hw_ste, nic_matcher->ste_builder[sb_idx].bit_mask);
249 
250 	/* Copy STE control and tag */
251 	memcpy(hw_ste, cur_ste->hw_ste, DR_STE_SIZE_REDUCED);
252 	mlx5dr_ste_set_miss_addr(dmn->ste_ctx, hw_ste,
253 				 nic_matcher->e_anchor->chunk->icm_addr);
254 
255 	new_idx = mlx5dr_ste_calc_hash_index(hw_ste, new_htbl);
256 	new_ste = &new_htbl->ste_arr[new_idx];
257 
258 	if (mlx5dr_ste_is_not_used(new_ste)) {
259 		mlx5dr_htbl_get(new_htbl);
260 		list_add_tail(&new_ste->miss_list_node,
261 			      mlx5dr_ste_get_miss_list(new_ste));
262 	} else {
263 		new_ste = dr_rule_rehash_handle_collision(matcher,
264 							  nic_matcher,
265 							  update_list,
266 							  new_ste,
267 							  hw_ste);
268 		if (!new_ste) {
269 			mlx5dr_dbg(dmn, "Failed adding collision entry, index: %d\n",
270 				   new_idx);
271 			return NULL;
272 		}
273 		new_htbl->ctrl.num_of_collisions++;
274 		use_update_list = true;
275 	}
276 
277 	memcpy(new_ste->hw_ste, hw_ste, DR_STE_SIZE_REDUCED);
278 
279 	new_htbl->ctrl.num_of_valid_entries++;
280 
281 	if (use_update_list) {
282 		ste_info = kzalloc(sizeof(*ste_info), GFP_KERNEL);
283 		if (!ste_info)
284 			goto err_exit;
285 
286 		mlx5dr_send_fill_and_append_ste_send_info(new_ste, DR_STE_SIZE, 0,
287 							  hw_ste, ste_info,
288 							  update_list, true);
289 	}
290 
291 	dr_rule_rehash_copy_ste_ctrl(matcher, nic_matcher, cur_ste, new_ste);
292 
293 	return new_ste;
294 
295 err_exit:
296 	mlx5dr_ste_free(new_ste, matcher, nic_matcher);
297 	return NULL;
298 }
299 
300 static int dr_rule_rehash_copy_miss_list(struct mlx5dr_matcher *matcher,
301 					 struct mlx5dr_matcher_rx_tx *nic_matcher,
302 					 struct list_head *cur_miss_list,
303 					 struct mlx5dr_ste_htbl *new_htbl,
304 					 struct list_head *update_list)
305 {
306 	struct mlx5dr_ste *tmp_ste, *cur_ste, *new_ste;
307 
308 	if (list_empty(cur_miss_list))
309 		return 0;
310 
311 	list_for_each_entry_safe(cur_ste, tmp_ste, cur_miss_list, miss_list_node) {
312 		new_ste = dr_rule_rehash_copy_ste(matcher,
313 						  nic_matcher,
314 						  cur_ste,
315 						  new_htbl,
316 						  update_list);
317 		if (!new_ste)
318 			goto err_insert;
319 
320 		list_del(&cur_ste->miss_list_node);
321 		mlx5dr_htbl_put(cur_ste->htbl);
322 	}
323 	return 0;
324 
325 err_insert:
326 	mlx5dr_err(matcher->tbl->dmn, "Fatal error during resize\n");
327 	WARN_ON(true);
328 	return -EINVAL;
329 }
330 
331 static int dr_rule_rehash_copy_htbl(struct mlx5dr_matcher *matcher,
332 				    struct mlx5dr_matcher_rx_tx *nic_matcher,
333 				    struct mlx5dr_ste_htbl *cur_htbl,
334 				    struct mlx5dr_ste_htbl *new_htbl,
335 				    struct list_head *update_list)
336 {
337 	struct mlx5dr_ste *cur_ste;
338 	int cur_entries;
339 	int err = 0;
340 	int i;
341 
342 	cur_entries = mlx5dr_icm_pool_chunk_size_to_entries(cur_htbl->chunk_size);
343 
344 	if (cur_entries < 1) {
345 		mlx5dr_dbg(matcher->tbl->dmn, "Invalid number of entries\n");
346 		return -EINVAL;
347 	}
348 
349 	for (i = 0; i < cur_entries; i++) {
350 		cur_ste = &cur_htbl->ste_arr[i];
351 		if (mlx5dr_ste_is_not_used(cur_ste)) /* Empty, nothing to copy */
352 			continue;
353 
354 		err = dr_rule_rehash_copy_miss_list(matcher,
355 						    nic_matcher,
356 						    mlx5dr_ste_get_miss_list(cur_ste),
357 						    new_htbl,
358 						    update_list);
359 		if (err)
360 			goto clean_copy;
361 	}
362 
363 clean_copy:
364 	return err;
365 }
366 
367 static struct mlx5dr_ste_htbl *
368 dr_rule_rehash_htbl(struct mlx5dr_rule *rule,
369 		    struct mlx5dr_rule_rx_tx *nic_rule,
370 		    struct mlx5dr_ste_htbl *cur_htbl,
371 		    u8 ste_location,
372 		    struct list_head *update_list,
373 		    enum mlx5dr_icm_chunk_size new_size)
374 {
375 	struct mlx5dr_ste_send_info *del_ste_info, *tmp_ste_info;
376 	struct mlx5dr_matcher *matcher = rule->matcher;
377 	struct mlx5dr_domain *dmn = matcher->tbl->dmn;
378 	struct mlx5dr_matcher_rx_tx *nic_matcher;
379 	struct mlx5dr_ste_send_info *ste_info;
380 	struct mlx5dr_htbl_connect_info info;
381 	struct mlx5dr_domain_rx_tx *nic_dmn;
382 	u8 formatted_ste[DR_STE_SIZE] = {};
383 	LIST_HEAD(rehash_table_send_list);
384 	struct mlx5dr_ste *ste_to_update;
385 	struct mlx5dr_ste_htbl *new_htbl;
386 	int err;
387 
388 	nic_matcher = nic_rule->nic_matcher;
389 	nic_dmn = nic_matcher->nic_tbl->nic_dmn;
390 
391 	ste_info = kzalloc(sizeof(*ste_info), GFP_KERNEL);
392 	if (!ste_info)
393 		return NULL;
394 
395 	new_htbl = mlx5dr_ste_htbl_alloc(dmn->ste_icm_pool,
396 					 new_size,
397 					 cur_htbl->lu_type,
398 					 cur_htbl->byte_mask);
399 	if (!new_htbl) {
400 		mlx5dr_err(dmn, "Failed to allocate new hash table\n");
401 		goto free_ste_info;
402 	}
403 
404 	/* Write new table to HW */
405 	info.type = CONNECT_MISS;
406 	info.miss_icm_addr = nic_matcher->e_anchor->chunk->icm_addr;
407 	mlx5dr_ste_set_formatted_ste(dmn->ste_ctx,
408 				     dmn->info.caps.gvmi,
409 				     nic_dmn->type,
410 				     new_htbl,
411 				     formatted_ste,
412 				     &info);
413 
414 	new_htbl->pointing_ste = cur_htbl->pointing_ste;
415 	new_htbl->pointing_ste->next_htbl = new_htbl;
416 	err = dr_rule_rehash_copy_htbl(matcher,
417 				       nic_matcher,
418 				       cur_htbl,
419 				       new_htbl,
420 				       &rehash_table_send_list);
421 	if (err)
422 		goto free_new_htbl;
423 
424 	if (mlx5dr_send_postsend_htbl(dmn, new_htbl, formatted_ste,
425 				      nic_matcher->ste_builder[ste_location - 1].bit_mask)) {
426 		mlx5dr_err(dmn, "Failed writing table to HW\n");
427 		goto free_new_htbl;
428 	}
429 
430 	/* Writing to the hw is done in regular order of rehash_table_send_list,
431 	 * in order to have the origin data written before the miss address of
432 	 * collision entries, if exists.
433 	 */
434 	if (dr_rule_send_update_list(&rehash_table_send_list, dmn, false)) {
435 		mlx5dr_err(dmn, "Failed updating table to HW\n");
436 		goto free_ste_list;
437 	}
438 
439 	/* Connect previous hash table to current */
440 	if (ste_location == 1) {
441 		/* The previous table is an anchor, anchors size is always one STE */
442 		struct mlx5dr_ste_htbl *prev_htbl = cur_htbl->pointing_ste->htbl;
443 
444 		/* On matcher s_anchor we keep an extra refcount */
445 		mlx5dr_htbl_get(new_htbl);
446 		mlx5dr_htbl_put(cur_htbl);
447 
448 		nic_matcher->s_htbl = new_htbl;
449 
450 		/* It is safe to operate dr_ste_set_hit_addr on the hw_ste here
451 		 * (48B len) which works only on first 32B
452 		 */
453 		mlx5dr_ste_set_hit_addr(dmn->ste_ctx,
454 					prev_htbl->ste_arr[0].hw_ste,
455 					new_htbl->chunk->icm_addr,
456 					new_htbl->chunk->num_of_entries);
457 
458 		ste_to_update = &prev_htbl->ste_arr[0];
459 	} else {
460 		mlx5dr_ste_set_hit_addr_by_next_htbl(dmn->ste_ctx,
461 						     cur_htbl->pointing_ste->hw_ste,
462 						     new_htbl);
463 		ste_to_update = cur_htbl->pointing_ste;
464 	}
465 
466 	mlx5dr_send_fill_and_append_ste_send_info(ste_to_update, DR_STE_SIZE_CTRL,
467 						  0, ste_to_update->hw_ste, ste_info,
468 						  update_list, false);
469 
470 	return new_htbl;
471 
472 free_ste_list:
473 	/* Clean all ste_info's from the new table */
474 	list_for_each_entry_safe(del_ste_info, tmp_ste_info,
475 				 &rehash_table_send_list, send_list) {
476 		list_del(&del_ste_info->send_list);
477 		kfree(del_ste_info);
478 	}
479 
480 free_new_htbl:
481 	mlx5dr_ste_htbl_free(new_htbl);
482 free_ste_info:
483 	kfree(ste_info);
484 	mlx5dr_info(dmn, "Failed creating rehash table\n");
485 	return NULL;
486 }
487 
488 static struct mlx5dr_ste_htbl *dr_rule_rehash(struct mlx5dr_rule *rule,
489 					      struct mlx5dr_rule_rx_tx *nic_rule,
490 					      struct mlx5dr_ste_htbl *cur_htbl,
491 					      u8 ste_location,
492 					      struct list_head *update_list)
493 {
494 	struct mlx5dr_domain *dmn = rule->matcher->tbl->dmn;
495 	enum mlx5dr_icm_chunk_size new_size;
496 
497 	new_size = mlx5dr_icm_next_higher_chunk(cur_htbl->chunk_size);
498 	new_size = min_t(u32, new_size, dmn->info.max_log_sw_icm_sz);
499 
500 	if (new_size == cur_htbl->chunk_size)
501 		return NULL; /* Skip rehash, we already at the max size */
502 
503 	return dr_rule_rehash_htbl(rule, nic_rule, cur_htbl, ste_location,
504 				   update_list, new_size);
505 }
506 
507 static struct mlx5dr_ste *
508 dr_rule_handle_collision(struct mlx5dr_matcher *matcher,
509 			 struct mlx5dr_matcher_rx_tx *nic_matcher,
510 			 struct mlx5dr_ste *ste,
511 			 u8 *hw_ste,
512 			 struct list_head *miss_list,
513 			 struct list_head *send_list)
514 {
515 	struct mlx5dr_domain *dmn = matcher->tbl->dmn;
516 	struct mlx5dr_ste_ctx *ste_ctx = dmn->ste_ctx;
517 	struct mlx5dr_ste_send_info *ste_info;
518 	struct mlx5dr_ste *new_ste;
519 
520 	ste_info = kzalloc(sizeof(*ste_info), GFP_KERNEL);
521 	if (!ste_info)
522 		return NULL;
523 
524 	new_ste = dr_rule_create_collision_entry(matcher, nic_matcher, hw_ste, ste);
525 	if (!new_ste)
526 		goto free_send_info;
527 
528 	if (dr_rule_append_to_miss_list(ste_ctx, new_ste,
529 					miss_list, send_list)) {
530 		mlx5dr_dbg(dmn, "Failed to update prev miss_list\n");
531 		goto err_exit;
532 	}
533 
534 	mlx5dr_send_fill_and_append_ste_send_info(new_ste, DR_STE_SIZE, 0, hw_ste,
535 						  ste_info, send_list, false);
536 
537 	ste->htbl->ctrl.num_of_collisions++;
538 	ste->htbl->ctrl.num_of_valid_entries++;
539 
540 	return new_ste;
541 
542 err_exit:
543 	mlx5dr_ste_free(new_ste, matcher, nic_matcher);
544 free_send_info:
545 	kfree(ste_info);
546 	return NULL;
547 }
548 
549 static void dr_rule_remove_action_members(struct mlx5dr_rule *rule)
550 {
551 	struct mlx5dr_rule_action_member *action_mem;
552 	struct mlx5dr_rule_action_member *tmp;
553 
554 	list_for_each_entry_safe(action_mem, tmp, &rule->rule_actions_list, list) {
555 		list_del(&action_mem->list);
556 		refcount_dec(&action_mem->action->refcount);
557 		kvfree(action_mem);
558 	}
559 }
560 
561 static int dr_rule_add_action_members(struct mlx5dr_rule *rule,
562 				      size_t num_actions,
563 				      struct mlx5dr_action *actions[])
564 {
565 	struct mlx5dr_rule_action_member *action_mem;
566 	int i;
567 
568 	for (i = 0; i < num_actions; i++) {
569 		action_mem = kvzalloc(sizeof(*action_mem), GFP_KERNEL);
570 		if (!action_mem)
571 			goto free_action_members;
572 
573 		action_mem->action = actions[i];
574 		INIT_LIST_HEAD(&action_mem->list);
575 		list_add_tail(&action_mem->list, &rule->rule_actions_list);
576 		refcount_inc(&action_mem->action->refcount);
577 	}
578 
579 	return 0;
580 
581 free_action_members:
582 	dr_rule_remove_action_members(rule);
583 	return -ENOMEM;
584 }
585 
586 void mlx5dr_rule_set_last_member(struct mlx5dr_rule_rx_tx *nic_rule,
587 				 struct mlx5dr_ste *ste,
588 				 bool force)
589 {
590 	/* Update rule member is usually done for the last STE or during rule
591 	 * creation to recover from mid-creation failure (for this peruse the
592 	 * force flag is used)
593 	 */
594 	if (ste->next_htbl && !force)
595 		return;
596 
597 	/* Update is required since each rule keeps track of its last STE */
598 	ste->rule_rx_tx = nic_rule;
599 	nic_rule->last_rule_ste = ste;
600 }
601 
602 static struct mlx5dr_ste *dr_rule_get_pointed_ste(struct mlx5dr_ste *curr_ste)
603 {
604 	struct mlx5dr_ste *first_ste;
605 
606 	first_ste = list_first_entry(mlx5dr_ste_get_miss_list(curr_ste),
607 				     struct mlx5dr_ste, miss_list_node);
608 
609 	return first_ste->htbl->pointing_ste;
610 }
611 
612 int mlx5dr_rule_get_reverse_rule_members(struct mlx5dr_ste **ste_arr,
613 					 struct mlx5dr_ste *curr_ste,
614 					 int *num_of_stes)
615 {
616 	bool first = false;
617 
618 	*num_of_stes = 0;
619 
620 	if (!curr_ste)
621 		return -ENOENT;
622 
623 	/* Iterate from last to first */
624 	while (!first) {
625 		first = curr_ste->ste_chain_location == 1;
626 		ste_arr[*num_of_stes] = curr_ste;
627 		*num_of_stes += 1;
628 		curr_ste = dr_rule_get_pointed_ste(curr_ste);
629 	}
630 
631 	return 0;
632 }
633 
634 static void dr_rule_clean_rule_members(struct mlx5dr_rule *rule,
635 				       struct mlx5dr_rule_rx_tx *nic_rule)
636 {
637 	struct mlx5dr_ste *ste_arr[DR_RULE_MAX_STES + DR_ACTION_MAX_STES];
638 	struct mlx5dr_ste *curr_ste = nic_rule->last_rule_ste;
639 	int i;
640 
641 	if (mlx5dr_rule_get_reverse_rule_members(ste_arr, curr_ste, &i))
642 		return;
643 
644 	while (i--)
645 		mlx5dr_ste_put(ste_arr[i], rule->matcher, nic_rule->nic_matcher);
646 }
647 
648 static u16 dr_get_bits_per_mask(u16 byte_mask)
649 {
650 	u16 bits = 0;
651 
652 	while (byte_mask) {
653 		byte_mask = byte_mask & (byte_mask - 1);
654 		bits++;
655 	}
656 
657 	return bits;
658 }
659 
660 static bool dr_rule_need_enlarge_hash(struct mlx5dr_ste_htbl *htbl,
661 				      struct mlx5dr_domain *dmn,
662 				      struct mlx5dr_domain_rx_tx *nic_dmn)
663 {
664 	struct mlx5dr_ste_htbl_ctrl *ctrl = &htbl->ctrl;
665 	int threshold;
666 
667 	if (dmn->info.max_log_sw_icm_sz <= htbl->chunk_size)
668 		return false;
669 
670 	if (!mlx5dr_ste_htbl_may_grow(htbl))
671 		return false;
672 
673 	if (dr_get_bits_per_mask(htbl->byte_mask) * BITS_PER_BYTE <= htbl->chunk_size)
674 		return false;
675 
676 	threshold = mlx5dr_ste_htbl_increase_threshold(htbl);
677 	if (ctrl->num_of_collisions >= threshold &&
678 	    (ctrl->num_of_valid_entries - ctrl->num_of_collisions) >= threshold)
679 		return true;
680 
681 	return false;
682 }
683 
684 static int dr_rule_handle_action_stes(struct mlx5dr_rule *rule,
685 				      struct mlx5dr_rule_rx_tx *nic_rule,
686 				      struct list_head *send_ste_list,
687 				      struct mlx5dr_ste *last_ste,
688 				      u8 *hw_ste_arr,
689 				      u32 new_hw_ste_arr_sz)
690 {
691 	struct mlx5dr_matcher_rx_tx *nic_matcher = nic_rule->nic_matcher;
692 	struct mlx5dr_ste_send_info *ste_info_arr[DR_ACTION_MAX_STES];
693 	u8 num_of_builders = nic_matcher->num_of_builders;
694 	struct mlx5dr_matcher *matcher = rule->matcher;
695 	struct mlx5dr_domain *dmn = matcher->tbl->dmn;
696 	u8 *curr_hw_ste, *prev_hw_ste;
697 	struct mlx5dr_ste *action_ste;
698 	int i, k;
699 
700 	/* Two cases:
701 	 * 1. num_of_builders is equal to new_hw_ste_arr_sz, the action in the ste
702 	 * 2. num_of_builders is less then new_hw_ste_arr_sz, new ste was added
703 	 *    to support the action.
704 	 */
705 
706 	for (i = num_of_builders, k = 0; i < new_hw_ste_arr_sz; i++, k++) {
707 		curr_hw_ste = hw_ste_arr + i * DR_STE_SIZE;
708 		prev_hw_ste = (i == 0) ? curr_hw_ste : hw_ste_arr + ((i - 1) * DR_STE_SIZE);
709 		action_ste = dr_rule_create_collision_htbl(matcher,
710 							   nic_matcher,
711 							   curr_hw_ste);
712 		if (!action_ste)
713 			return -ENOMEM;
714 
715 		mlx5dr_ste_get(action_ste);
716 
717 		action_ste->htbl->pointing_ste = last_ste;
718 		last_ste->next_htbl = action_ste->htbl;
719 		last_ste = action_ste;
720 
721 		/* While free ste we go over the miss list, so add this ste to the list */
722 		list_add_tail(&action_ste->miss_list_node,
723 			      mlx5dr_ste_get_miss_list(action_ste));
724 
725 		ste_info_arr[k] = kzalloc(sizeof(*ste_info_arr[k]),
726 					  GFP_KERNEL);
727 		if (!ste_info_arr[k])
728 			goto err_exit;
729 
730 		/* Point current ste to the new action */
731 		mlx5dr_ste_set_hit_addr_by_next_htbl(dmn->ste_ctx,
732 						     prev_hw_ste,
733 						     action_ste->htbl);
734 
735 		mlx5dr_rule_set_last_member(nic_rule, action_ste, true);
736 
737 		mlx5dr_send_fill_and_append_ste_send_info(action_ste, DR_STE_SIZE, 0,
738 							  curr_hw_ste,
739 							  ste_info_arr[k],
740 							  send_ste_list, false);
741 	}
742 
743 	last_ste->next_htbl = NULL;
744 
745 	return 0;
746 
747 err_exit:
748 	mlx5dr_ste_put(action_ste, matcher, nic_matcher);
749 	return -ENOMEM;
750 }
751 
752 static int dr_rule_handle_empty_entry(struct mlx5dr_matcher *matcher,
753 				      struct mlx5dr_matcher_rx_tx *nic_matcher,
754 				      struct mlx5dr_ste_htbl *cur_htbl,
755 				      struct mlx5dr_ste *ste,
756 				      u8 ste_location,
757 				      u8 *hw_ste,
758 				      struct list_head *miss_list,
759 				      struct list_head *send_list)
760 {
761 	struct mlx5dr_domain *dmn = matcher->tbl->dmn;
762 	struct mlx5dr_ste_send_info *ste_info;
763 
764 	/* Take ref on table, only on first time this ste is used */
765 	mlx5dr_htbl_get(cur_htbl);
766 
767 	/* new entry -> new branch */
768 	list_add_tail(&ste->miss_list_node, miss_list);
769 
770 	mlx5dr_ste_set_miss_addr(dmn->ste_ctx, hw_ste,
771 				 nic_matcher->e_anchor->chunk->icm_addr);
772 
773 	ste->ste_chain_location = ste_location;
774 
775 	ste_info = kzalloc(sizeof(*ste_info), GFP_KERNEL);
776 	if (!ste_info)
777 		goto clean_ste_setting;
778 
779 	if (mlx5dr_ste_create_next_htbl(matcher,
780 					nic_matcher,
781 					ste,
782 					hw_ste,
783 					DR_CHUNK_SIZE_1)) {
784 		mlx5dr_dbg(dmn, "Failed allocating table\n");
785 		goto clean_ste_info;
786 	}
787 
788 	cur_htbl->ctrl.num_of_valid_entries++;
789 
790 	mlx5dr_send_fill_and_append_ste_send_info(ste, DR_STE_SIZE, 0, hw_ste,
791 						  ste_info, send_list, false);
792 
793 	return 0;
794 
795 clean_ste_info:
796 	kfree(ste_info);
797 clean_ste_setting:
798 	list_del_init(&ste->miss_list_node);
799 	mlx5dr_htbl_put(cur_htbl);
800 
801 	return -ENOMEM;
802 }
803 
804 static struct mlx5dr_ste *
805 dr_rule_handle_ste_branch(struct mlx5dr_rule *rule,
806 			  struct mlx5dr_rule_rx_tx *nic_rule,
807 			  struct list_head *send_ste_list,
808 			  struct mlx5dr_ste_htbl *cur_htbl,
809 			  u8 *hw_ste,
810 			  u8 ste_location,
811 			  struct mlx5dr_ste_htbl **put_htbl)
812 {
813 	struct mlx5dr_matcher *matcher = rule->matcher;
814 	struct mlx5dr_domain *dmn = matcher->tbl->dmn;
815 	struct mlx5dr_matcher_rx_tx *nic_matcher;
816 	struct mlx5dr_domain_rx_tx *nic_dmn;
817 	struct mlx5dr_ste_htbl *new_htbl;
818 	struct mlx5dr_ste *matched_ste;
819 	struct list_head *miss_list;
820 	bool skip_rehash = false;
821 	struct mlx5dr_ste *ste;
822 	int index;
823 
824 	nic_matcher = nic_rule->nic_matcher;
825 	nic_dmn = nic_matcher->nic_tbl->nic_dmn;
826 
827 again:
828 	index = mlx5dr_ste_calc_hash_index(hw_ste, cur_htbl);
829 	miss_list = &cur_htbl->chunk->miss_list[index];
830 	ste = &cur_htbl->ste_arr[index];
831 
832 	if (mlx5dr_ste_is_not_used(ste)) {
833 		if (dr_rule_handle_empty_entry(matcher, nic_matcher, cur_htbl,
834 					       ste, ste_location,
835 					       hw_ste, miss_list,
836 					       send_ste_list))
837 			return NULL;
838 	} else {
839 		/* Hash table index in use, check if this ste is in the miss list */
840 		matched_ste = dr_rule_find_ste_in_miss_list(miss_list, hw_ste);
841 		if (matched_ste) {
842 			/* If it is last STE in the chain, and has the same tag
843 			 * it means that all the previous stes are the same,
844 			 * if so, this rule is duplicated.
845 			 */
846 			if (!mlx5dr_ste_is_last_in_rule(nic_matcher, ste_location))
847 				return matched_ste;
848 
849 			mlx5dr_dbg(dmn, "Duplicate rule inserted\n");
850 		}
851 
852 		if (!skip_rehash && dr_rule_need_enlarge_hash(cur_htbl, dmn, nic_dmn)) {
853 			/* Hash table index in use, try to resize of the hash */
854 			skip_rehash = true;
855 
856 			/* Hold the table till we update.
857 			 * Release in dr_rule_create_rule()
858 			 */
859 			*put_htbl = cur_htbl;
860 			mlx5dr_htbl_get(cur_htbl);
861 
862 			new_htbl = dr_rule_rehash(rule, nic_rule, cur_htbl,
863 						  ste_location, send_ste_list);
864 			if (!new_htbl) {
865 				mlx5dr_err(dmn, "Failed creating rehash table, htbl-log_size: %d\n",
866 					   cur_htbl->chunk_size);
867 				mlx5dr_htbl_put(cur_htbl);
868 			} else {
869 				cur_htbl = new_htbl;
870 			}
871 			goto again;
872 		} else {
873 			/* Hash table index in use, add another collision (miss) */
874 			ste = dr_rule_handle_collision(matcher,
875 						       nic_matcher,
876 						       ste,
877 						       hw_ste,
878 						       miss_list,
879 						       send_ste_list);
880 			if (!ste) {
881 				mlx5dr_dbg(dmn, "failed adding collision entry, index: %d\n",
882 					   index);
883 				return NULL;
884 			}
885 		}
886 	}
887 	return ste;
888 }
889 
890 static bool dr_rule_cmp_value_to_mask(u8 *mask, u8 *value,
891 				      u32 s_idx, u32 e_idx)
892 {
893 	u32 i;
894 
895 	for (i = s_idx; i < e_idx; i++) {
896 		if (value[i] & ~mask[i]) {
897 			pr_info("Rule parameters contains a value not specified by mask\n");
898 			return false;
899 		}
900 	}
901 	return true;
902 }
903 
904 static bool dr_rule_verify(struct mlx5dr_matcher *matcher,
905 			   struct mlx5dr_match_parameters *value,
906 			   struct mlx5dr_match_param *param)
907 {
908 	u8 match_criteria = matcher->match_criteria;
909 	size_t value_size = value->match_sz;
910 	u8 *mask_p = (u8 *)&matcher->mask;
911 	u8 *param_p = (u8 *)param;
912 	u32 s_idx, e_idx;
913 
914 	if (!value_size ||
915 	    (value_size > DR_SZ_MATCH_PARAM || (value_size % sizeof(u32)))) {
916 		mlx5dr_err(matcher->tbl->dmn, "Rule parameters length is incorrect\n");
917 		return false;
918 	}
919 
920 	mlx5dr_ste_copy_param(matcher->match_criteria, param, value, false);
921 
922 	if (match_criteria & DR_MATCHER_CRITERIA_OUTER) {
923 		s_idx = offsetof(struct mlx5dr_match_param, outer);
924 		e_idx = min(s_idx + sizeof(param->outer), value_size);
925 
926 		if (!dr_rule_cmp_value_to_mask(mask_p, param_p, s_idx, e_idx)) {
927 			mlx5dr_err(matcher->tbl->dmn, "Rule outer parameters contains a value not specified by mask\n");
928 			return false;
929 		}
930 	}
931 
932 	if (match_criteria & DR_MATCHER_CRITERIA_MISC) {
933 		s_idx = offsetof(struct mlx5dr_match_param, misc);
934 		e_idx = min(s_idx + sizeof(param->misc), value_size);
935 
936 		if (!dr_rule_cmp_value_to_mask(mask_p, param_p, s_idx, e_idx)) {
937 			mlx5dr_err(matcher->tbl->dmn, "Rule misc parameters contains a value not specified by mask\n");
938 			return false;
939 		}
940 	}
941 
942 	if (match_criteria & DR_MATCHER_CRITERIA_INNER) {
943 		s_idx = offsetof(struct mlx5dr_match_param, inner);
944 		e_idx = min(s_idx + sizeof(param->inner), value_size);
945 
946 		if (!dr_rule_cmp_value_to_mask(mask_p, param_p, s_idx, e_idx)) {
947 			mlx5dr_err(matcher->tbl->dmn, "Rule inner parameters contains a value not specified by mask\n");
948 			return false;
949 		}
950 	}
951 
952 	if (match_criteria & DR_MATCHER_CRITERIA_MISC2) {
953 		s_idx = offsetof(struct mlx5dr_match_param, misc2);
954 		e_idx = min(s_idx + sizeof(param->misc2), value_size);
955 
956 		if (!dr_rule_cmp_value_to_mask(mask_p, param_p, s_idx, e_idx)) {
957 			mlx5dr_err(matcher->tbl->dmn, "Rule misc2 parameters contains a value not specified by mask\n");
958 			return false;
959 		}
960 	}
961 
962 	if (match_criteria & DR_MATCHER_CRITERIA_MISC3) {
963 		s_idx = offsetof(struct mlx5dr_match_param, misc3);
964 		e_idx = min(s_idx + sizeof(param->misc3), value_size);
965 
966 		if (!dr_rule_cmp_value_to_mask(mask_p, param_p, s_idx, e_idx)) {
967 			mlx5dr_err(matcher->tbl->dmn, "Rule misc3 parameters contains a value not specified by mask\n");
968 			return false;
969 		}
970 	}
971 
972 	if (match_criteria & DR_MATCHER_CRITERIA_MISC4) {
973 		s_idx = offsetof(struct mlx5dr_match_param, misc4);
974 		e_idx = min(s_idx + sizeof(param->misc4), value_size);
975 
976 		if (!dr_rule_cmp_value_to_mask(mask_p, param_p, s_idx, e_idx)) {
977 			mlx5dr_err(matcher->tbl->dmn,
978 				   "Rule misc4 parameters contains a value not specified by mask\n");
979 			return false;
980 		}
981 	}
982 	return true;
983 }
984 
985 static int dr_rule_destroy_rule_nic(struct mlx5dr_rule *rule,
986 				    struct mlx5dr_rule_rx_tx *nic_rule)
987 {
988 	mlx5dr_domain_nic_lock(nic_rule->nic_matcher->nic_tbl->nic_dmn);
989 	dr_rule_clean_rule_members(rule, nic_rule);
990 	mlx5dr_domain_nic_unlock(nic_rule->nic_matcher->nic_tbl->nic_dmn);
991 
992 	return 0;
993 }
994 
995 static int dr_rule_destroy_rule_fdb(struct mlx5dr_rule *rule)
996 {
997 	dr_rule_destroy_rule_nic(rule, &rule->rx);
998 	dr_rule_destroy_rule_nic(rule, &rule->tx);
999 	return 0;
1000 }
1001 
1002 static int dr_rule_destroy_rule(struct mlx5dr_rule *rule)
1003 {
1004 	struct mlx5dr_domain *dmn = rule->matcher->tbl->dmn;
1005 
1006 	switch (dmn->type) {
1007 	case MLX5DR_DOMAIN_TYPE_NIC_RX:
1008 		dr_rule_destroy_rule_nic(rule, &rule->rx);
1009 		break;
1010 	case MLX5DR_DOMAIN_TYPE_NIC_TX:
1011 		dr_rule_destroy_rule_nic(rule, &rule->tx);
1012 		break;
1013 	case MLX5DR_DOMAIN_TYPE_FDB:
1014 		dr_rule_destroy_rule_fdb(rule);
1015 		break;
1016 	default:
1017 		return -EINVAL;
1018 	}
1019 
1020 	dr_rule_remove_action_members(rule);
1021 	kfree(rule);
1022 	return 0;
1023 }
1024 
1025 static enum mlx5dr_ipv dr_rule_get_ipv(struct mlx5dr_match_spec *spec)
1026 {
1027 	if (spec->ip_version == 6 || spec->ethertype == ETH_P_IPV6)
1028 		return DR_RULE_IPV6;
1029 
1030 	return DR_RULE_IPV4;
1031 }
1032 
1033 static bool dr_rule_skip(enum mlx5dr_domain_type domain,
1034 			 enum mlx5dr_domain_nic_type nic_type,
1035 			 struct mlx5dr_match_param *mask,
1036 			 struct mlx5dr_match_param *value,
1037 			 u32 flow_source)
1038 {
1039 	bool rx = nic_type == DR_DOMAIN_NIC_TYPE_RX;
1040 
1041 	if (domain != MLX5DR_DOMAIN_TYPE_FDB)
1042 		return false;
1043 
1044 	if (mask->misc.source_port) {
1045 		if (rx && value->misc.source_port != MLX5_VPORT_UPLINK)
1046 			return true;
1047 
1048 		if (!rx && value->misc.source_port == MLX5_VPORT_UPLINK)
1049 			return true;
1050 	}
1051 
1052 	if (rx && flow_source == MLX5_FLOW_CONTEXT_FLOW_SOURCE_LOCAL_VPORT)
1053 		return true;
1054 
1055 	if (!rx && flow_source == MLX5_FLOW_CONTEXT_FLOW_SOURCE_UPLINK)
1056 		return true;
1057 
1058 	return false;
1059 }
1060 
1061 static int
1062 dr_rule_create_rule_nic(struct mlx5dr_rule *rule,
1063 			struct mlx5dr_rule_rx_tx *nic_rule,
1064 			struct mlx5dr_match_param *param,
1065 			size_t num_actions,
1066 			struct mlx5dr_action *actions[])
1067 {
1068 	struct mlx5dr_ste_send_info *ste_info, *tmp_ste_info;
1069 	struct mlx5dr_matcher *matcher = rule->matcher;
1070 	struct mlx5dr_domain *dmn = matcher->tbl->dmn;
1071 	struct mlx5dr_matcher_rx_tx *nic_matcher;
1072 	struct mlx5dr_domain_rx_tx *nic_dmn;
1073 	struct mlx5dr_ste_htbl *htbl = NULL;
1074 	struct mlx5dr_ste_htbl *cur_htbl;
1075 	struct mlx5dr_ste *ste = NULL;
1076 	LIST_HEAD(send_ste_list);
1077 	u8 *hw_ste_arr = NULL;
1078 	u32 new_hw_ste_arr_sz;
1079 	int ret, i;
1080 
1081 	nic_matcher = nic_rule->nic_matcher;
1082 	nic_dmn = nic_matcher->nic_tbl->nic_dmn;
1083 
1084 	if (dr_rule_skip(dmn->type, nic_dmn->type, &matcher->mask, param,
1085 			 rule->flow_source))
1086 		return 0;
1087 
1088 	hw_ste_arr = kzalloc(DR_RULE_MAX_STE_CHAIN * DR_STE_SIZE, GFP_KERNEL);
1089 	if (!hw_ste_arr)
1090 		return -ENOMEM;
1091 
1092 	mlx5dr_domain_nic_lock(nic_dmn);
1093 
1094 	ret = mlx5dr_matcher_select_builders(matcher,
1095 					     nic_matcher,
1096 					     dr_rule_get_ipv(&param->outer),
1097 					     dr_rule_get_ipv(&param->inner));
1098 	if (ret)
1099 		goto free_hw_ste;
1100 
1101 	/* Set the tag values inside the ste array */
1102 	ret = mlx5dr_ste_build_ste_arr(matcher, nic_matcher, param, hw_ste_arr);
1103 	if (ret)
1104 		goto free_hw_ste;
1105 
1106 	/* Set the actions values/addresses inside the ste array */
1107 	ret = mlx5dr_actions_build_ste_arr(matcher, nic_matcher, actions,
1108 					   num_actions, hw_ste_arr,
1109 					   &new_hw_ste_arr_sz);
1110 	if (ret)
1111 		goto free_hw_ste;
1112 
1113 	cur_htbl = nic_matcher->s_htbl;
1114 
1115 	/* Go over the array of STEs, and build dr_ste accordingly.
1116 	 * The loop is over only the builders which are equal or less to the
1117 	 * number of stes, in case we have actions that lives in other stes.
1118 	 */
1119 	for (i = 0; i < nic_matcher->num_of_builders; i++) {
1120 		/* Calculate CRC and keep new ste entry */
1121 		u8 *cur_hw_ste_ent = hw_ste_arr + (i * DR_STE_SIZE);
1122 
1123 		ste = dr_rule_handle_ste_branch(rule,
1124 						nic_rule,
1125 						&send_ste_list,
1126 						cur_htbl,
1127 						cur_hw_ste_ent,
1128 						i + 1,
1129 						&htbl);
1130 		if (!ste) {
1131 			mlx5dr_err(dmn, "Failed creating next branch\n");
1132 			ret = -ENOENT;
1133 			goto free_rule;
1134 		}
1135 
1136 		cur_htbl = ste->next_htbl;
1137 
1138 		mlx5dr_ste_get(ste);
1139 		mlx5dr_rule_set_last_member(nic_rule, ste, true);
1140 	}
1141 
1142 	/* Connect actions */
1143 	ret = dr_rule_handle_action_stes(rule, nic_rule, &send_ste_list,
1144 					 ste, hw_ste_arr, new_hw_ste_arr_sz);
1145 	if (ret) {
1146 		mlx5dr_dbg(dmn, "Failed apply actions\n");
1147 		goto free_rule;
1148 	}
1149 	ret = dr_rule_send_update_list(&send_ste_list, dmn, true);
1150 	if (ret) {
1151 		mlx5dr_err(dmn, "Failed sending ste!\n");
1152 		goto free_rule;
1153 	}
1154 
1155 	if (htbl)
1156 		mlx5dr_htbl_put(htbl);
1157 
1158 	mlx5dr_domain_nic_unlock(nic_dmn);
1159 
1160 	kfree(hw_ste_arr);
1161 
1162 	return 0;
1163 
1164 free_rule:
1165 	dr_rule_clean_rule_members(rule, nic_rule);
1166 	/* Clean all ste_info's */
1167 	list_for_each_entry_safe(ste_info, tmp_ste_info, &send_ste_list, send_list) {
1168 		list_del(&ste_info->send_list);
1169 		kfree(ste_info);
1170 	}
1171 free_hw_ste:
1172 	mlx5dr_domain_nic_unlock(nic_dmn);
1173 	kfree(hw_ste_arr);
1174 	return ret;
1175 }
1176 
1177 static int
1178 dr_rule_create_rule_fdb(struct mlx5dr_rule *rule,
1179 			struct mlx5dr_match_param *param,
1180 			size_t num_actions,
1181 			struct mlx5dr_action *actions[])
1182 {
1183 	struct mlx5dr_match_param copy_param = {};
1184 	int ret;
1185 
1186 	/* Copy match_param since they will be consumed during the first
1187 	 * nic_rule insertion.
1188 	 */
1189 	memcpy(&copy_param, param, sizeof(struct mlx5dr_match_param));
1190 
1191 	ret = dr_rule_create_rule_nic(rule, &rule->rx, param,
1192 				      num_actions, actions);
1193 	if (ret)
1194 		return ret;
1195 
1196 	ret = dr_rule_create_rule_nic(rule, &rule->tx, &copy_param,
1197 				      num_actions, actions);
1198 	if (ret)
1199 		goto destroy_rule_nic_rx;
1200 
1201 	return 0;
1202 
1203 destroy_rule_nic_rx:
1204 	dr_rule_destroy_rule_nic(rule, &rule->rx);
1205 	return ret;
1206 }
1207 
1208 static struct mlx5dr_rule *
1209 dr_rule_create_rule(struct mlx5dr_matcher *matcher,
1210 		    struct mlx5dr_match_parameters *value,
1211 		    size_t num_actions,
1212 		    struct mlx5dr_action *actions[],
1213 		    u32 flow_source)
1214 {
1215 	struct mlx5dr_domain *dmn = matcher->tbl->dmn;
1216 	struct mlx5dr_match_param param = {};
1217 	struct mlx5dr_rule *rule;
1218 	int ret;
1219 
1220 	if (!dr_rule_verify(matcher, value, &param))
1221 		return NULL;
1222 
1223 	rule = kzalloc(sizeof(*rule), GFP_KERNEL);
1224 	if (!rule)
1225 		return NULL;
1226 
1227 	rule->matcher = matcher;
1228 	rule->flow_source = flow_source;
1229 	INIT_LIST_HEAD(&rule->rule_actions_list);
1230 
1231 	ret = dr_rule_add_action_members(rule, num_actions, actions);
1232 	if (ret)
1233 		goto free_rule;
1234 
1235 	switch (dmn->type) {
1236 	case MLX5DR_DOMAIN_TYPE_NIC_RX:
1237 		rule->rx.nic_matcher = &matcher->rx;
1238 		ret = dr_rule_create_rule_nic(rule, &rule->rx, &param,
1239 					      num_actions, actions);
1240 		break;
1241 	case MLX5DR_DOMAIN_TYPE_NIC_TX:
1242 		rule->tx.nic_matcher = &matcher->tx;
1243 		ret = dr_rule_create_rule_nic(rule, &rule->tx, &param,
1244 					      num_actions, actions);
1245 		break;
1246 	case MLX5DR_DOMAIN_TYPE_FDB:
1247 		rule->rx.nic_matcher = &matcher->rx;
1248 		rule->tx.nic_matcher = &matcher->tx;
1249 		ret = dr_rule_create_rule_fdb(rule, &param,
1250 					      num_actions, actions);
1251 		break;
1252 	default:
1253 		ret = -EINVAL;
1254 		break;
1255 	}
1256 
1257 	if (ret)
1258 		goto remove_action_members;
1259 
1260 	return rule;
1261 
1262 remove_action_members:
1263 	dr_rule_remove_action_members(rule);
1264 free_rule:
1265 	kfree(rule);
1266 	mlx5dr_err(dmn, "Failed creating rule\n");
1267 	return NULL;
1268 }
1269 
1270 struct mlx5dr_rule *mlx5dr_rule_create(struct mlx5dr_matcher *matcher,
1271 				       struct mlx5dr_match_parameters *value,
1272 				       size_t num_actions,
1273 				       struct mlx5dr_action *actions[],
1274 				       u32 flow_source)
1275 {
1276 	struct mlx5dr_rule *rule;
1277 
1278 	refcount_inc(&matcher->refcount);
1279 
1280 	rule = dr_rule_create_rule(matcher, value, num_actions, actions, flow_source);
1281 	if (!rule)
1282 		refcount_dec(&matcher->refcount);
1283 
1284 	return rule;
1285 }
1286 
1287 int mlx5dr_rule_destroy(struct mlx5dr_rule *rule)
1288 {
1289 	struct mlx5dr_matcher *matcher = rule->matcher;
1290 	int ret;
1291 
1292 	ret = dr_rule_destroy_rule(rule);
1293 	if (!ret)
1294 		refcount_dec(&matcher->refcount);
1295 
1296 	return ret;
1297 }
1298