net/mlx5e: Split the main flow steering table
[cascardo/linux.git] / drivers / net / ethernet / mellanox / mlx5 / core / fs_core.c
index 5121be4..83fe864 100644 (file)
 #define INIT_TREE_NODE_ARRAY_SIZE(...) (sizeof((struct init_tree_node[]){__VA_ARGS__}) /\
                                         sizeof(struct init_tree_node))
 
-#define ADD_PRIO(num_prios_val, min_level_val, max_ft_val, caps_val,\
+#define ADD_PRIO(num_prios_val, min_level_val, num_levels_val, caps_val,\
                 ...) {.type = FS_TYPE_PRIO,\
        .min_ft_level = min_level_val,\
-       .max_ft = max_ft_val,\
+       .num_levels = num_levels_val,\
        .num_leaf_prios = num_prios_val,\
        .caps = caps_val,\
        .children = (struct init_tree_node[]) {__VA_ARGS__},\
        .ar_size = INIT_TREE_NODE_ARRAY_SIZE(__VA_ARGS__) \
 }
 
-#define ADD_MULTIPLE_PRIO(num_prios_val, max_ft_val, ...)\
-       ADD_PRIO(num_prios_val, 0, max_ft_val, {},\
+#define ADD_MULTIPLE_PRIO(num_prios_val, num_levels_val, ...)\
+       ADD_PRIO(num_prios_val, 0, num_levels_val, {},\
                 __VA_ARGS__)\
 
 #define ADD_NS(...) {.type = FS_TYPE_NAMESPACE,\
 #define FS_REQUIRED_CAPS(...) {.arr_sz = INIT_CAPS_ARRAY_SIZE(__VA_ARGS__), \
                               .caps = (long[]) {__VA_ARGS__} }
 
-#define LEFTOVERS_MAX_FT 1
+#define LEFTOVERS_NUM_LEVELS 1
 #define LEFTOVERS_NUM_PRIOS 1
-#define BY_PASS_PRIO_MAX_FT 1
-#define BY_PASS_MIN_LEVEL (KENREL_MIN_LEVEL + MLX5_BY_PASS_NUM_PRIOS +\
-                          LEFTOVERS_MAX_FT)
 
-#define KERNEL_MAX_FT 3
-#define KERNEL_NUM_PRIOS 2
-#define KENREL_MIN_LEVEL 2
+#define BY_PASS_PRIO_NUM_LEVELS 1
+#define BY_PASS_MIN_LEVEL (KERNEL_MIN_LEVEL + MLX5_BY_PASS_NUM_PRIOS +\
+                          LEFTOVERS_NUM_PRIOS)
 
-#define ANCHOR_MAX_FT 1
+#define KERNEL_NIC_PRIO_NUM_LEVELS 3
+#define KERNEL_NIC_NUM_PRIOS 1
+/* One more level for tc */
+#define KERNEL_MIN_LEVEL (KERNEL_NIC_PRIO_NUM_LEVELS + 1)
+
+#define ANCHOR_NUM_LEVELS 1
 #define ANCHOR_NUM_PRIOS 1
 #define ANCHOR_MIN_LEVEL (BY_PASS_MIN_LEVEL + 1)
 struct node_caps {
@@ -92,7 +94,7 @@ static struct init_tree_node {
        int min_ft_level;
        int num_leaf_prios;
        int prio;
-       int max_ft;
+       int num_levels;
 } root_fs = {
        .type = FS_TYPE_NAMESPACE,
        .ar_size = 4,
@@ -102,17 +104,20 @@ static struct init_tree_node {
                                          FS_CAP(flow_table_properties_nic_receive.modify_root),
                                          FS_CAP(flow_table_properties_nic_receive.identified_miss_table_mode),
                                          FS_CAP(flow_table_properties_nic_receive.flow_table_modify)),
-                        ADD_NS(ADD_MULTIPLE_PRIO(MLX5_BY_PASS_NUM_PRIOS, BY_PASS_PRIO_MAX_FT))),
-               ADD_PRIO(0, KENREL_MIN_LEVEL, 0, {},
-                        ADD_NS(ADD_MULTIPLE_PRIO(KERNEL_NUM_PRIOS, KERNEL_MAX_FT))),
+                        ADD_NS(ADD_MULTIPLE_PRIO(MLX5_BY_PASS_NUM_PRIOS,
+                                                 BY_PASS_PRIO_NUM_LEVELS))),
+               ADD_PRIO(0, KERNEL_MIN_LEVEL, 0, {},
+                        ADD_NS(ADD_MULTIPLE_PRIO(1, 1),
+                               ADD_MULTIPLE_PRIO(KERNEL_NIC_NUM_PRIOS,
+                                                 KERNEL_NIC_PRIO_NUM_LEVELS))),
                ADD_PRIO(0, BY_PASS_MIN_LEVEL, 0,
                         FS_REQUIRED_CAPS(FS_CAP(flow_table_properties_nic_receive.flow_modify_en),
                                          FS_CAP(flow_table_properties_nic_receive.modify_root),
                                          FS_CAP(flow_table_properties_nic_receive.identified_miss_table_mode),
                                          FS_CAP(flow_table_properties_nic_receive.flow_table_modify)),
-                        ADD_NS(ADD_MULTIPLE_PRIO(LEFTOVERS_NUM_PRIOS, LEFTOVERS_MAX_FT))),
+                        ADD_NS(ADD_MULTIPLE_PRIO(LEFTOVERS_NUM_PRIOS, LEFTOVERS_NUM_LEVELS))),
                ADD_PRIO(0, ANCHOR_MIN_LEVEL, 0, {},
-                        ADD_NS(ADD_MULTIPLE_PRIO(ANCHOR_NUM_PRIOS, ANCHOR_MAX_FT))),
+                        ADD_NS(ADD_MULTIPLE_PRIO(ANCHOR_NUM_PRIOS, ANCHOR_NUM_LEVELS))),
        }
 };
 
@@ -222,19 +227,6 @@ static struct fs_prio *find_prio(struct mlx5_flow_namespace *ns,
        return NULL;
 }
 
-static unsigned int find_next_free_level(struct fs_prio *prio)
-{
-       if (!list_empty(&prio->node.children)) {
-               struct mlx5_flow_table *ft;
-
-               ft = list_last_entry(&prio->node.children,
-                                    struct mlx5_flow_table,
-                                    node.list);
-               return ft->level + 1;
-       }
-       return prio->start_level;
-}
-
 static bool masked_memcmp(void *mask, void *val1, void *val2, size_t size)
 {
        unsigned int i;
@@ -615,8 +607,8 @@ static int update_root_ft_create(struct mlx5_flow_table *ft, struct fs_prio
        return err;
 }
 
-static int mlx5_modify_rule_destination(struct mlx5_flow_rule *rule,
-                                       struct mlx5_flow_destination *dest)
+int mlx5_modify_rule_destination(struct mlx5_flow_rule *rule,
+                                struct mlx5_flow_destination *dest)
 {
        struct mlx5_flow_table *ft;
        struct mlx5_flow_group *fg;
@@ -693,9 +685,23 @@ static int connect_flow_table(struct mlx5_core_dev *dev, struct mlx5_flow_table
        return err;
 }
 
+static void list_add_flow_table(struct mlx5_flow_table *ft,
+                               struct fs_prio *prio)
+{
+       struct list_head *prev = &prio->node.children;
+       struct mlx5_flow_table *iter;
+
+       fs_for_each_ft(iter, prio) {
+               if (iter->level > ft->level)
+                       break;
+               prev = &iter->node.list;
+       }
+       list_add(&ft->node.list, prev);
+}
+
 struct mlx5_flow_table *mlx5_create_flow_table(struct mlx5_flow_namespace *ns,
-                                              int prio,
-                                              int max_fte)
+                                              int prio, int max_fte,
+                                              u32 level)
 {
        struct mlx5_flow_table *next_ft = NULL;
        struct mlx5_flow_table *ft;
@@ -716,12 +722,15 @@ struct mlx5_flow_table *mlx5_create_flow_table(struct mlx5_flow_namespace *ns,
                err = -EINVAL;
                goto unlock_root;
        }
-       if (fs_prio->num_ft == fs_prio->max_ft) {
+       if (level >= fs_prio->num_levels) {
                err = -ENOSPC;
                goto unlock_root;
        }
-
-       ft = alloc_flow_table(find_next_free_level(fs_prio),
+       /* The level is related to the
+        * priority level range.
+        */
+       level += fs_prio->start_level;
+       ft = alloc_flow_table(level,
                              roundup_pow_of_two(max_fte),
                              root->table_type);
        if (!ft) {
@@ -742,7 +751,7 @@ struct mlx5_flow_table *mlx5_create_flow_table(struct mlx5_flow_namespace *ns,
                goto destroy_ft;
        lock_ref_node(&fs_prio->node);
        tree_add_node(&ft->node, &fs_prio->node);
-       list_add_tail(&ft->node.list, &fs_prio->node.children);
+       list_add_flow_table(ft, fs_prio);
        fs_prio->num_ft++;
        unlock_ref_node(&fs_prio->node);
        mutex_unlock(&root->chain_lock);
@@ -759,14 +768,15 @@ unlock_root:
 struct mlx5_flow_table *mlx5_create_auto_grouped_flow_table(struct mlx5_flow_namespace *ns,
                                                            int prio,
                                                            int num_flow_table_entries,
-                                                           int max_num_groups)
+                                                           int max_num_groups,
+                                                           u32 level)
 {
        struct mlx5_flow_table *ft;
 
        if (max_num_groups > num_flow_table_entries)
                return ERR_PTR(-EINVAL);
 
-       ft = mlx5_create_flow_table(ns, prio, num_flow_table_entries);
+       ft = mlx5_create_flow_table(ns, prio, num_flow_table_entries, level);
        if (IS_ERR(ft))
                return ft;
 
@@ -1065,31 +1075,18 @@ unlock_fg:
        return rule;
 }
 
-static struct mlx5_flow_rule *add_rule_to_auto_fg(struct mlx5_flow_table *ft,
-                                                 u8 match_criteria_enable,
-                                                 u32 *match_criteria,
-                                                 u32 *match_value,
-                                                 u8 action,
-                                                 u32 flow_tag,
-                                                 struct mlx5_flow_destination *dest)
+static bool dest_is_valid(struct mlx5_flow_destination *dest,
+                         u32 action,
+                         struct mlx5_flow_table *ft)
 {
-       struct mlx5_flow_rule *rule;
-       struct mlx5_flow_group *g;
+       if (!(action & MLX5_FLOW_CONTEXT_ACTION_FWD_DEST))
+               return true;
 
-       g = create_autogroup(ft, match_criteria_enable, match_criteria);
-       if (IS_ERR(g))
-               return (void *)g;
-
-       rule = add_rule_fg(g, match_value,
-                          action, flow_tag, dest);
-       if (IS_ERR(rule)) {
-               /* Remove assumes refcount > 0 and autogroup creates a group
-                * with a refcount = 0.
-                */
-               tree_get_node(&g->node);
-               tree_remove_node(&g->node);
-       }
-       return rule;
+       if (!dest || ((dest->type ==
+           MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE) &&
+           (dest->ft->level <= ft->level)))
+               return false;
+       return true;
 }
 
 static struct mlx5_flow_rule *
@@ -1104,7 +1101,7 @@ _mlx5_add_flow_rule(struct mlx5_flow_table *ft,
        struct mlx5_flow_group *g;
        struct mlx5_flow_rule *rule;
 
-       if ((action & MLX5_FLOW_CONTEXT_ACTION_FWD_DEST) && !dest)
+       if (!dest_is_valid(dest, action, ft))
                return ERR_PTR(-EINVAL);
 
        nested_lock_ref_node(&ft->node, FS_MUTEX_GRANDPARENT);
@@ -1119,8 +1116,23 @@ _mlx5_add_flow_rule(struct mlx5_flow_table *ft,
                                goto unlock;
                }
 
-       rule = add_rule_to_auto_fg(ft, match_criteria_enable, match_criteria,
-                                  match_value, action, flow_tag, dest);
+       g = create_autogroup(ft, match_criteria_enable, match_criteria);
+       if (IS_ERR(g)) {
+               rule = (void *)g;
+               goto unlock;
+       }
+
+       rule = add_rule_fg(g, match_value,
+                          action, flow_tag, dest);
+       if (IS_ERR(rule)) {
+               /* Remove assumes refcount > 0 and autogroup creates a group
+                * with a refcount = 0.
+                */
+               unlock_ref_node(&ft->node);
+               tree_get_node(&g->node);
+               tree_remove_node(&g->node);
+               return rule;
+       }
 unlock:
        unlock_ref_node(&ft->node);
        return rule;
@@ -1288,7 +1300,7 @@ struct mlx5_flow_namespace *mlx5_get_flow_namespace(struct mlx5_core_dev *dev,
 {
        struct mlx5_flow_root_namespace *root_ns = dev->priv.root_ns;
        int prio;
-       static struct fs_prio *fs_prio;
+       struct fs_prio *fs_prio;
        struct mlx5_flow_namespace *ns;
 
        if (!root_ns)
@@ -1323,7 +1335,7 @@ struct mlx5_flow_namespace *mlx5_get_flow_namespace(struct mlx5_core_dev *dev,
 EXPORT_SYMBOL(mlx5_get_flow_namespace);
 
 static struct fs_prio *fs_create_prio(struct mlx5_flow_namespace *ns,
-                                     unsigned prio, int max_ft)
+                                     unsigned int prio, int num_levels)
 {
        struct fs_prio *fs_prio;
 
@@ -1334,7 +1346,7 @@ static struct fs_prio *fs_create_prio(struct mlx5_flow_namespace *ns,
        fs_prio->node.type = FS_TYPE_PRIO;
        tree_init_node(&fs_prio->node, 1, NULL);
        tree_add_node(&fs_prio->node, &ns->node);
-       fs_prio->max_ft = max_ft;
+       fs_prio->num_levels = num_levels;
        fs_prio->prio = prio;
        list_add_tail(&fs_prio->node.list, &ns->node.children);
 
@@ -1365,14 +1377,14 @@ static struct mlx5_flow_namespace *fs_create_namespace(struct fs_prio *prio)
        return ns;
 }
 
-static int create_leaf_prios(struct mlx5_flow_namespace *ns, struct init_tree_node
-                            *prio_metadata)
+static int create_leaf_prios(struct mlx5_flow_namespace *ns, int prio,
+                            struct init_tree_node *prio_metadata)
 {
        struct fs_prio *fs_prio;
        int i;
 
        for (i = 0; i < prio_metadata->num_leaf_prios; i++) {
-               fs_prio = fs_create_prio(ns, i, prio_metadata->max_ft);
+               fs_prio = fs_create_prio(ns, prio++, prio_metadata->num_levels);
                if (IS_ERR(fs_prio))
                        return PTR_ERR(fs_prio);
        }
@@ -1399,7 +1411,7 @@ static int init_root_tree_recursive(struct mlx5_core_dev *dev,
                                    struct init_tree_node *init_node,
                                    struct fs_node *fs_parent_node,
                                    struct init_tree_node *init_parent_node,
-                                   int index)
+                                   int prio)
 {
        int max_ft_level = MLX5_CAP_FLOWTABLE(dev,
                                              flow_table_properties_nic_receive.
@@ -1417,8 +1429,8 @@ static int init_root_tree_recursive(struct mlx5_core_dev *dev,
 
                fs_get_obj(fs_ns, fs_parent_node);
                if (init_node->num_leaf_prios)
-                       return create_leaf_prios(fs_ns, init_node);
-               fs_prio = fs_create_prio(fs_ns, index, init_node->max_ft);
+                       return create_leaf_prios(fs_ns, prio, init_node);
+               fs_prio = fs_create_prio(fs_ns, prio, init_node->num_levels);
                if (IS_ERR(fs_prio))
                        return PTR_ERR(fs_prio);
                base = &fs_prio->node;
@@ -1431,11 +1443,16 @@ static int init_root_tree_recursive(struct mlx5_core_dev *dev,
        } else {
                return -EINVAL;
        }
+       prio = 0;
        for (i = 0; i < init_node->ar_size; i++) {
                err = init_root_tree_recursive(dev, &init_node->children[i],
-                                              base, init_node, i);
+                                              base, init_node, prio);
                if (err)
                        return err;
+               if (init_node->children[i].type == FS_TYPE_PRIO &&
+                   init_node->children[i].num_leaf_prios) {
+                       prio += init_node->children[i].num_leaf_prios;
+               }
        }
 
        return 0;
@@ -1491,9 +1508,9 @@ static int set_prio_attrs_in_ns(struct mlx5_flow_namespace *ns, int acc_level)
        struct fs_prio *prio;
 
        fs_for_each_prio(prio, ns) {
-                /* This updates prio start_level and max_ft */
+                /* This updates prio start_level and num_levels */
                set_prio_attrs_in_prio(prio, acc_level);
-               acc_level += prio->max_ft;
+               acc_level += prio->num_levels;
        }
        return acc_level;
 }
@@ -1505,11 +1522,11 @@ static void set_prio_attrs_in_prio(struct fs_prio *prio, int acc_level)
 
        prio->start_level = acc_level;
        fs_for_each_ns(ns, prio)
-               /* This updates start_level and max_ft of ns's priority descendants */
+               /* This updates start_level and num_levels of ns's priority descendants */
                acc_level_ns = set_prio_attrs_in_ns(ns, acc_level);
-       if (!prio->max_ft)
-               prio->max_ft = acc_level_ns - prio->start_level;
-       WARN_ON(prio->max_ft < acc_level_ns - prio->start_level);
+       if (!prio->num_levels)
+               prio->num_levels = acc_level_ns - prio->start_level;
+       WARN_ON(prio->num_levels < acc_level_ns - prio->start_level);
 }
 
 static void set_prio_attrs(struct mlx5_flow_root_namespace *root_ns)
@@ -1520,12 +1537,13 @@ static void set_prio_attrs(struct mlx5_flow_root_namespace *root_ns)
 
        fs_for_each_prio(prio, ns) {
                set_prio_attrs_in_prio(prio, start_level);
-               start_level += prio->max_ft;
+               start_level += prio->num_levels;
        }
 }
 
 #define ANCHOR_PRIO 0
 #define ANCHOR_SIZE 1
+#define ANCHOR_LEVEL 0
 static int create_anchor_flow_table(struct mlx5_core_dev
                                                        *dev)
 {
@@ -1535,7 +1553,7 @@ static int create_anchor_flow_table(struct mlx5_core_dev
        ns = mlx5_get_flow_namespace(dev, MLX5_FLOW_NAMESPACE_ANCHOR);
        if (!ns)
                return -EINVAL;
-       ft = mlx5_create_flow_table(ns, ANCHOR_PRIO, ANCHOR_SIZE);
+       ft = mlx5_create_flow_table(ns, ANCHOR_PRIO, ANCHOR_SIZE, ANCHOR_LEVEL);
        if (IS_ERR(ft)) {
                mlx5_core_err(dev, "Failed to create last anchor flow table");
                return PTR_ERR(ft);