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