net/mlx5_core: Introduce flow steering API
[cascardo/linux.git] / drivers / net / ethernet / mellanox / mlx5 / core / fs_core.c
1 /*
2  * Copyright (c) 2015, Mellanox Technologies. All rights reserved.
3  *
4  * This software is available to you under a choice of one of two
5  * licenses.  You may choose to be licensed under the terms of the GNU
6  * General Public License (GPL) Version 2, available from the file
7  * COPYING in the main directory of this source tree, or the
8  * OpenIB.org BSD license below:
9  *
10  *     Redistribution and use in source and binary forms, with or
11  *     without modification, are permitted provided that the following
12  *     conditions are met:
13  *
14  *      - Redistributions of source code must retain the above
15  *        copyright notice, this list of conditions and the following
16  *        disclaimer.
17  *
18  *      - Redistributions in binary form must reproduce the above
19  *        copyright notice, this list of conditions and the following
20  *        disclaimer in the documentation and/or other materials
21  *        provided with the distribution.
22  *
23  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
27  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
28  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
29  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30  * SOFTWARE.
31  */
32
33 #include <linux/mutex.h>
34 #include <linux/mlx5/driver.h>
35
36 #include "mlx5_core.h"
37 #include "fs_core.h"
38 #include "fs_cmd.h"
39
40 static void del_rule(struct fs_node *node);
41 static void del_flow_table(struct fs_node *node);
42 static void del_flow_group(struct fs_node *node);
43 static void del_fte(struct fs_node *node);
44
45 static void tree_init_node(struct fs_node *node,
46                            unsigned int refcount,
47                            void (*remove_func)(struct fs_node *))
48 {
49         atomic_set(&node->refcount, refcount);
50         INIT_LIST_HEAD(&node->list);
51         INIT_LIST_HEAD(&node->children);
52         mutex_init(&node->lock);
53         node->remove_func = remove_func;
54 }
55
56 static void tree_add_node(struct fs_node *node, struct fs_node *parent)
57 {
58         if (parent)
59                 atomic_inc(&parent->refcount);
60         node->parent = parent;
61
62         /* Parent is the root */
63         if (!parent)
64                 node->root = node;
65         else
66                 node->root = parent->root;
67 }
68
69 static void tree_get_node(struct fs_node *node)
70 {
71         atomic_inc(&node->refcount);
72 }
73
74 static void nested_lock_ref_node(struct fs_node *node)
75 {
76         if (node) {
77                 mutex_lock_nested(&node->lock, SINGLE_DEPTH_NESTING);
78                 atomic_inc(&node->refcount);
79         }
80 }
81
82 static void lock_ref_node(struct fs_node *node)
83 {
84         if (node) {
85                 mutex_lock(&node->lock);
86                 atomic_inc(&node->refcount);
87         }
88 }
89
90 static void unlock_ref_node(struct fs_node *node)
91 {
92         if (node) {
93                 atomic_dec(&node->refcount);
94                 mutex_unlock(&node->lock);
95         }
96 }
97
98 static void tree_put_node(struct fs_node *node)
99 {
100         struct fs_node *parent_node = node->parent;
101
102         lock_ref_node(parent_node);
103         if (atomic_dec_and_test(&node->refcount)) {
104                 if (parent_node)
105                         list_del_init(&node->list);
106                 if (node->remove_func)
107                         node->remove_func(node);
108                 kfree(node);
109                 node = NULL;
110         }
111         unlock_ref_node(parent_node);
112         if (!node && parent_node)
113                 tree_put_node(parent_node);
114 }
115
116 static int tree_remove_node(struct fs_node *node)
117 {
118         if (atomic_read(&node->refcount) > 1)
119                 return -EPERM;
120         tree_put_node(node);
121         return 0;
122 }
123
124 static struct fs_prio *find_prio(struct mlx5_flow_namespace *ns,
125                                  unsigned int prio)
126 {
127         struct fs_prio *iter_prio;
128
129         fs_for_each_prio(iter_prio, ns) {
130                 if (iter_prio->prio == prio)
131                         return iter_prio;
132         }
133
134         return NULL;
135 }
136
137 static unsigned int find_next_free_level(struct fs_prio *prio)
138 {
139         if (!list_empty(&prio->node.children)) {
140                 struct mlx5_flow_table *ft;
141
142                 ft = list_last_entry(&prio->node.children,
143                                      struct mlx5_flow_table,
144                                      node.list);
145                 return ft->level + 1;
146         }
147         return prio->start_level;
148 }
149
150 static bool masked_memcmp(void *mask, void *val1, void *val2, size_t size)
151 {
152         unsigned int i;
153
154         for (i = 0; i < size; i++, mask++, val1++, val2++)
155                 if ((*((u8 *)val1) & (*(u8 *)mask)) !=
156                     ((*(u8 *)val2) & (*(u8 *)mask)))
157                         return false;
158
159         return true;
160 }
161
162 static bool compare_match_value(struct mlx5_flow_group_mask *mask,
163                                 void *fte_param1, void *fte_param2)
164 {
165         if (mask->match_criteria_enable &
166             1 << MLX5_CREATE_FLOW_GROUP_IN_MATCH_CRITERIA_ENABLE_OUTER_HEADERS) {
167                 void *fte_match1 = MLX5_ADDR_OF(fte_match_param,
168                                                 fte_param1, outer_headers);
169                 void *fte_match2 = MLX5_ADDR_OF(fte_match_param,
170                                                 fte_param2, outer_headers);
171                 void *fte_mask = MLX5_ADDR_OF(fte_match_param,
172                                               mask->match_criteria, outer_headers);
173
174                 if (!masked_memcmp(fte_mask, fte_match1, fte_match2,
175                                    MLX5_ST_SZ_BYTES(fte_match_set_lyr_2_4)))
176                         return false;
177         }
178
179         if (mask->match_criteria_enable &
180             1 << MLX5_CREATE_FLOW_GROUP_IN_MATCH_CRITERIA_ENABLE_MISC_PARAMETERS) {
181                 void *fte_match1 = MLX5_ADDR_OF(fte_match_param,
182                                                 fte_param1, misc_parameters);
183                 void *fte_match2 = MLX5_ADDR_OF(fte_match_param,
184                                                 fte_param2, misc_parameters);
185                 void *fte_mask = MLX5_ADDR_OF(fte_match_param,
186                                           mask->match_criteria, misc_parameters);
187
188                 if (!masked_memcmp(fte_mask, fte_match1, fte_match2,
189                                    MLX5_ST_SZ_BYTES(fte_match_set_misc)))
190                         return false;
191         }
192
193         if (mask->match_criteria_enable &
194             1 << MLX5_CREATE_FLOW_GROUP_IN_MATCH_CRITERIA_ENABLE_INNER_HEADERS) {
195                 void *fte_match1 = MLX5_ADDR_OF(fte_match_param,
196                                                 fte_param1, inner_headers);
197                 void *fte_match2 = MLX5_ADDR_OF(fte_match_param,
198                                                 fte_param2, inner_headers);
199                 void *fte_mask = MLX5_ADDR_OF(fte_match_param,
200                                           mask->match_criteria, inner_headers);
201
202                 if (!masked_memcmp(fte_mask, fte_match1, fte_match2,
203                                    MLX5_ST_SZ_BYTES(fte_match_set_lyr_2_4)))
204                         return false;
205         }
206         return true;
207 }
208
209 static bool compare_match_criteria(u8 match_criteria_enable1,
210                                    u8 match_criteria_enable2,
211                                    void *mask1, void *mask2)
212 {
213         return match_criteria_enable1 == match_criteria_enable2 &&
214                 !memcmp(mask1, mask2, MLX5_ST_SZ_BYTES(fte_match_param));
215 }
216
217 static struct mlx5_flow_root_namespace *find_root(struct fs_node *node)
218 {
219         struct fs_node *root;
220         struct mlx5_flow_namespace *ns;
221
222         root = node->root;
223
224         if (WARN_ON(root->type != FS_TYPE_NAMESPACE)) {
225                 pr_warn("mlx5: flow steering node is not in tree or garbaged\n");
226                 return NULL;
227         }
228
229         ns = container_of(root, struct mlx5_flow_namespace, node);
230         return container_of(ns, struct mlx5_flow_root_namespace, ns);
231 }
232
233 static inline struct mlx5_core_dev *get_dev(struct fs_node *node)
234 {
235         struct mlx5_flow_root_namespace *root = find_root(node);
236
237         if (root)
238                 return root->dev;
239         return NULL;
240 }
241
242 static void del_flow_table(struct fs_node *node)
243 {
244         struct mlx5_flow_table *ft;
245         struct mlx5_core_dev *dev;
246         struct fs_prio *prio;
247         int err;
248
249         fs_get_obj(ft, node);
250         dev = get_dev(&ft->node);
251
252         err = mlx5_cmd_destroy_flow_table(dev, ft);
253         if (err)
254                 pr_warn("flow steering can't destroy ft\n");
255         fs_get_obj(prio, ft->node.parent);
256         prio->num_ft--;
257 }
258
259 static void del_rule(struct fs_node *node)
260 {
261         struct mlx5_flow_rule *rule;
262         struct mlx5_flow_table *ft;
263         struct mlx5_flow_group *fg;
264         struct fs_fte *fte;
265         u32     *match_value;
266         struct mlx5_core_dev *dev = get_dev(node);
267         int match_len = MLX5_ST_SZ_BYTES(fte_match_param);
268         int err;
269
270         match_value = mlx5_vzalloc(match_len);
271         if (!match_value) {
272                 pr_warn("failed to allocate inbox\n");
273                 return;
274         }
275
276         fs_get_obj(rule, node);
277         fs_get_obj(fte, rule->node.parent);
278         fs_get_obj(fg, fte->node.parent);
279         memcpy(match_value, fte->val, sizeof(fte->val));
280         fs_get_obj(ft, fg->node.parent);
281         list_del(&rule->node.list);
282         fte->dests_size--;
283         if (fte->dests_size) {
284                 err = mlx5_cmd_update_fte(dev, ft,
285                                           fg->id, fte);
286                 if (err)
287                         pr_warn("%s can't del rule fg id=%d fte_index=%d\n",
288                                 __func__, fg->id, fte->index);
289         }
290         kvfree(match_value);
291 }
292
293 static void del_fte(struct fs_node *node)
294 {
295         struct mlx5_flow_table *ft;
296         struct mlx5_flow_group *fg;
297         struct mlx5_core_dev *dev;
298         struct fs_fte *fte;
299         int err;
300
301         fs_get_obj(fte, node);
302         fs_get_obj(fg, fte->node.parent);
303         fs_get_obj(ft, fg->node.parent);
304
305         dev = get_dev(&ft->node);
306         err = mlx5_cmd_delete_fte(dev, ft,
307                                   fte->index);
308         if (err)
309                 pr_warn("flow steering can't delete fte in index %d of flow group id %d\n",
310                         fte->index, fg->id);
311
312         fte->status = 0;
313         fg->num_ftes--;
314 }
315
316 static void del_flow_group(struct fs_node *node)
317 {
318         struct mlx5_flow_group *fg;
319         struct mlx5_flow_table *ft;
320         struct mlx5_core_dev *dev;
321
322         fs_get_obj(fg, node);
323         fs_get_obj(ft, fg->node.parent);
324         dev = get_dev(&ft->node);
325
326         if (mlx5_cmd_destroy_flow_group(dev, ft, fg->id))
327                 pr_warn("flow steering can't destroy fg %d of ft %d\n",
328                         fg->id, ft->id);
329 }
330
331 static struct fs_fte *alloc_fte(u8 action,
332                                 u32 flow_tag,
333                                 u32 *match_value,
334                                 unsigned int index)
335 {
336         struct fs_fte *fte;
337
338         fte = kzalloc(sizeof(*fte), GFP_KERNEL);
339         if (!fte)
340                 return ERR_PTR(-ENOMEM);
341
342         memcpy(fte->val, match_value, sizeof(fte->val));
343         fte->node.type =  FS_TYPE_FLOW_ENTRY;
344         fte->flow_tag = flow_tag;
345         fte->index = index;
346         fte->action = action;
347
348         return fte;
349 }
350
351 static struct mlx5_flow_group *alloc_flow_group(u32 *create_fg_in)
352 {
353         struct mlx5_flow_group *fg;
354         void *match_criteria = MLX5_ADDR_OF(create_flow_group_in,
355                                             create_fg_in, match_criteria);
356         u8 match_criteria_enable = MLX5_GET(create_flow_group_in,
357                                             create_fg_in,
358                                             match_criteria_enable);
359         fg = kzalloc(sizeof(*fg), GFP_KERNEL);
360         if (!fg)
361                 return ERR_PTR(-ENOMEM);
362
363         fg->mask.match_criteria_enable = match_criteria_enable;
364         memcpy(&fg->mask.match_criteria, match_criteria,
365                sizeof(fg->mask.match_criteria));
366         fg->node.type =  FS_TYPE_FLOW_GROUP;
367         fg->start_index = MLX5_GET(create_flow_group_in, create_fg_in,
368                                    start_flow_index);
369         fg->max_ftes = MLX5_GET(create_flow_group_in, create_fg_in,
370                                 end_flow_index) - fg->start_index + 1;
371         return fg;
372 }
373
374 static struct mlx5_flow_table *alloc_flow_table(int level, int max_fte,
375                                                 enum fs_flow_table_type table_type)
376 {
377         struct mlx5_flow_table *ft;
378
379         ft  = kzalloc(sizeof(*ft), GFP_KERNEL);
380         if (!ft)
381                 return NULL;
382
383         ft->level = level;
384         ft->node.type = FS_TYPE_FLOW_TABLE;
385         ft->type = table_type;
386         ft->max_fte = max_fte;
387
388         return ft;
389 }
390
391 static struct mlx5_flow_table *mlx5_create_flow_table(struct mlx5_flow_namespace *ns,
392                                                       int prio,
393                                                       int max_fte)
394 {
395         struct mlx5_flow_table *ft;
396         int err;
397         int log_table_sz;
398         struct mlx5_flow_root_namespace *root =
399                 find_root(&ns->node);
400         struct fs_prio *fs_prio = NULL;
401
402         if (!root) {
403                 pr_err("mlx5: flow steering failed to find root of namespace\n");
404                 return ERR_PTR(-ENODEV);
405         }
406
407         fs_prio = find_prio(ns, prio);
408         if (!fs_prio)
409                 return ERR_PTR(-EINVAL);
410
411         lock_ref_node(&fs_prio->node);
412         if (fs_prio->num_ft == fs_prio->max_ft) {
413                 err = -ENOSPC;
414                 goto unlock_prio;
415         }
416
417         ft = alloc_flow_table(find_next_free_level(fs_prio),
418                               roundup_pow_of_two(max_fte),
419                               root->table_type);
420         if (!ft) {
421                 err = -ENOMEM;
422                 goto unlock_prio;
423         }
424
425         tree_init_node(&ft->node, 1, del_flow_table);
426         log_table_sz = ilog2(ft->max_fte);
427         err = mlx5_cmd_create_flow_table(root->dev, ft->type, ft->level,
428                                          log_table_sz, &ft->id);
429         if (err)
430                 goto free_ft;
431
432         tree_add_node(&ft->node, &fs_prio->node);
433         list_add_tail(&ft->node.list, &fs_prio->node.children);
434         fs_prio->num_ft++;
435         unlock_ref_node(&fs_prio->node);
436
437         return ft;
438
439 free_ft:
440         kfree(ft);
441 unlock_prio:
442         unlock_ref_node(&fs_prio->node);
443         return ERR_PTR(err);
444 }
445
446 static struct mlx5_flow_group *mlx5_create_flow_group(struct mlx5_flow_table *ft,
447                                                       u32 *fg_in)
448 {
449         struct mlx5_flow_group *fg;
450         struct mlx5_core_dev *dev = get_dev(&ft->node);
451         int err;
452
453         if (!dev)
454                 return ERR_PTR(-ENODEV);
455
456         fg = alloc_flow_group(fg_in);
457         if (IS_ERR(fg))
458                 return fg;
459
460         lock_ref_node(&ft->node);
461         err = mlx5_cmd_create_flow_group(dev, ft, fg_in, &fg->id);
462         if (err) {
463                 kfree(fg);
464                 unlock_ref_node(&ft->node);
465                 return ERR_PTR(err);
466         }
467         /* Add node to tree */
468         tree_init_node(&fg->node, 1, del_flow_group);
469         tree_add_node(&fg->node, &ft->node);
470         /* Add node to group list */
471         list_add(&fg->node.list, ft->node.children.prev);
472         unlock_ref_node(&ft->node);
473
474         return fg;
475 }
476
477 static struct mlx5_flow_rule *alloc_rule(struct mlx5_flow_destination *dest)
478 {
479         struct mlx5_flow_rule *rule;
480
481         rule = kzalloc(sizeof(*rule), GFP_KERNEL);
482         if (!rule)
483                 return NULL;
484
485         rule->node.type = FS_TYPE_FLOW_DEST;
486         memcpy(&rule->dest_attr, dest, sizeof(*dest));
487
488         return rule;
489 }
490
491 /* fte should not be deleted while calling this function */
492 static struct mlx5_flow_rule *add_rule_fte(struct fs_fte *fte,
493                                            struct mlx5_flow_group *fg,
494                                            struct mlx5_flow_destination *dest)
495 {
496         struct mlx5_flow_table *ft;
497         struct mlx5_flow_rule *rule;
498         int err;
499
500         rule = alloc_rule(dest);
501         if (!rule)
502                 return ERR_PTR(-ENOMEM);
503
504         fs_get_obj(ft, fg->node.parent);
505         /* Add dest to dests list- added as first element after the head */
506         tree_init_node(&rule->node, 1, del_rule);
507         list_add_tail(&rule->node.list, &fte->node.children);
508         fte->dests_size++;
509         if (fte->dests_size == 1)
510                 err = mlx5_cmd_create_fte(get_dev(&ft->node),
511                                           ft, fg->id, fte);
512         else
513                 err = mlx5_cmd_update_fte(get_dev(&ft->node),
514                                           ft, fg->id, fte);
515         if (err)
516                 goto free_rule;
517
518         fte->status |= FS_FTE_STATUS_EXISTING;
519
520         return rule;
521
522 free_rule:
523         list_del(&rule->node.list);
524         kfree(rule);
525         fte->dests_size--;
526         return ERR_PTR(err);
527 }
528
529 /* Assumed fg is locked */
530 static unsigned int get_free_fte_index(struct mlx5_flow_group *fg,
531                                        struct list_head **prev)
532 {
533         struct fs_fte *fte;
534         unsigned int start = fg->start_index;
535
536         if (prev)
537                 *prev = &fg->node.children;
538
539         /* assumed list is sorted by index */
540         fs_for_each_fte(fte, fg) {
541                 if (fte->index != start)
542                         return start;
543                 start++;
544                 if (prev)
545                         *prev = &fte->node.list;
546         }
547
548         return start;
549 }
550
551 /* prev is output, prev->next = new_fte */
552 static struct fs_fte *create_fte(struct mlx5_flow_group *fg,
553                                  u32 *match_value,
554                                  u8 action,
555                                  u32 flow_tag,
556                                  struct list_head **prev)
557 {
558         struct fs_fte *fte;
559         int index;
560
561         index = get_free_fte_index(fg, prev);
562         fte = alloc_fte(action, flow_tag, match_value, index);
563         if (IS_ERR(fte))
564                 return fte;
565
566         return fte;
567 }
568
569 /* Assuming parent fg(flow table) is locked */
570 static struct mlx5_flow_rule *add_rule_fg(struct mlx5_flow_group *fg,
571                                           u32 *match_value,
572                                           u8 action,
573                                           u32 flow_tag,
574                                           struct mlx5_flow_destination *dest)
575 {
576         struct fs_fte *fte;
577         struct mlx5_flow_rule *rule;
578         struct mlx5_flow_table *ft;
579         struct list_head *prev;
580
581         lock_ref_node(&fg->node);
582         fs_for_each_fte(fte, fg) {
583                 nested_lock_ref_node(&fte->node);
584                 if (compare_match_value(&fg->mask, match_value, &fte->val) &&
585                     action == fte->action && flow_tag == fte->flow_tag) {
586                         rule = add_rule_fte(fte, fg, dest);
587                         unlock_ref_node(&fte->node);
588                         if (IS_ERR(rule))
589                                 goto unlock_fg;
590                         else
591                                 goto add_rule;
592                 }
593                 unlock_ref_node(&fte->node);
594         }
595         fs_get_obj(ft, fg->node.parent);
596         if (fg->num_ftes >= fg->max_ftes) {
597                 rule = ERR_PTR(-ENOSPC);
598                 goto unlock_fg;
599         }
600
601         fte = create_fte(fg, match_value, action, flow_tag, &prev);
602         if (IS_ERR(fte)) {
603                 rule = (void *)fte;
604                 goto unlock_fg;
605         }
606         tree_init_node(&fte->node, 0, del_fte);
607         rule = add_rule_fte(fte, fg, dest);
608         if (IS_ERR(rule)) {
609                 kfree(fte);
610                 goto unlock_fg;
611         }
612
613         fg->num_ftes++;
614
615         tree_add_node(&fte->node, &fg->node);
616         list_add(&fte->node.list, prev);
617 add_rule:
618         tree_add_node(&rule->node, &fte->node);
619 unlock_fg:
620         unlock_ref_node(&fg->node);
621         return rule;
622 }
623
624 static struct mlx5_flow_rule *
625 mlx5_add_flow_rule(struct mlx5_flow_table *ft,
626                    u8 match_criteria_enable,
627                    u32 *match_criteria,
628                    u32 *match_value,
629                    u32 action,
630                    u32 flow_tag,
631                    struct mlx5_flow_destination *dest)
632 {
633         struct mlx5_flow_group *g;
634         struct mlx5_flow_rule *rule = ERR_PTR(-EINVAL);
635
636         tree_get_node(&ft->node);
637         lock_ref_node(&ft->node);
638         fs_for_each_fg(g, ft)
639                 if (compare_match_criteria(g->mask.match_criteria_enable,
640                                            match_criteria_enable,
641                                            g->mask.match_criteria,
642                                            match_criteria)) {
643                         unlock_ref_node(&ft->node);
644                         rule = add_rule_fg(g, match_value,
645                                            action, flow_tag, dest);
646                         goto put;
647                 }
648         unlock_ref_node(&ft->node);
649 put:
650         tree_put_node(&ft->node);
651         return rule;
652 }
653
654 static void mlx5_del_flow_rule(struct mlx5_flow_rule *rule)
655 {
656         tree_remove_node(&rule->node);
657 }
658
659 static int mlx5_destroy_flow_table(struct mlx5_flow_table *ft)
660 {
661         if (tree_remove_node(&ft->node))
662                 mlx5_core_warn(get_dev(&ft->node), "Flow table %d wasn't destroyed, refcount > 1\n",
663                                ft->id);
664
665         return 0;
666 }
667
668 static void mlx5_destroy_flow_group(struct mlx5_flow_group *fg)
669 {
670         if (tree_remove_node(&fg->node))
671                 mlx5_core_warn(get_dev(&fg->node), "Flow group %d wasn't destroyed, refcount > 1\n",
672                                fg->id);
673 }