net/mlx5_core: Flow steering tree initialization
authorMaor Gottlieb <maorg@mellanox.com>
Thu, 10 Dec 2015 15:12:43 +0000 (17:12 +0200)
committerDavid S. Miller <davem@davemloft.net>
Sat, 12 Dec 2015 05:15:24 +0000 (00:15 -0500)
Flow steering initialization is based on static tree which
illustrates the flow steering tree when the driver is loaded. The
initialization considers the max supported flow table level of the device,
a minimum of 2 kernel flow tables(vlan and mac) are required to have
kernel flow table functionality.

The tree structures when the driver is loaded:

root_namespace(receive nic)
  |
priority-0 (kernel priority)
  |
namespace(kernel namespace)
  |
priority-0 (flow tables priority)

In the following patches, When the EN driver will use the flow steering
API, it create two flow tables and their flow groups under
priority-0(flow tables priority).

Signed-off-by: Maor Gottlieb <maorg@mellanox.com>
Signed-off-by: Moni Shoua <monis@mellanox.com>
Signed-off-by: Matan Barak <matanb@mellanox.com>
Signed-off-by: Saeed Mahameed <saeedm@mellanox.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/mellanox/mlx5/core/fs_core.c
drivers/net/ethernet/mellanox/mlx5/core/fs_core.h
include/linux/mlx5/driver.h
include/linux/mlx5/fs.h

index 1828351..4264e8b 100644 (file)
 #include "fs_core.h"
 #include "fs_cmd.h"
 
+#define INIT_TREE_NODE_ARRAY_SIZE(...) (sizeof((struct init_tree_node[]){__VA_ARGS__}) /\
+                                        sizeof(struct init_tree_node))
+
+#define INIT_PRIO(min_level_val, max_ft_val,\
+                 start_level_val, ...) {.type = FS_TYPE_PRIO,\
+       .min_ft_level = min_level_val,\
+       .start_level = start_level_val,\
+       .max_ft = max_ft_val,\
+       .children = (struct init_tree_node[]) {__VA_ARGS__},\
+       .ar_size = INIT_TREE_NODE_ARRAY_SIZE(__VA_ARGS__) \
+}
+
+#define ADD_PRIO(min_level_val, max_ft_val, start_level_val, ...)\
+       INIT_PRIO(min_level_val, max_ft_val, start_level_val,\
+                 __VA_ARGS__)\
+
+#define ADD_FT_PRIO(max_ft_val, start_level_val, ...)\
+       INIT_PRIO(0, max_ft_val, start_level_val,\
+                 __VA_ARGS__)\
+
+#define ADD_NS(...) {.type = FS_TYPE_NAMESPACE,\
+       .children = (struct init_tree_node[]) {__VA_ARGS__},\
+       .ar_size = INIT_TREE_NODE_ARRAY_SIZE(__VA_ARGS__) \
+}
+
+#define KERNEL_START_LEVEL 0
+#define KERNEL_P0_START_LEVEL KERNEL_START_LEVEL
+#define KERNEL_MAX_FT 2
+#define KENREL_MIN_LEVEL 2
+static struct init_tree_node {
+       enum fs_node_type       type;
+       struct init_tree_node *children;
+       int ar_size;
+       int min_ft_level;
+       int prio;
+       int max_ft;
+       int start_level;
+} root_fs = {
+       .type = FS_TYPE_NAMESPACE,
+       .ar_size = 1,
+       .children = (struct init_tree_node[]) {
+               ADD_PRIO(KENREL_MIN_LEVEL, KERNEL_MAX_FT,
+                        KERNEL_START_LEVEL,
+                        ADD_NS(ADD_FT_PRIO(KERNEL_MAX_FT,
+                                           KERNEL_P0_START_LEVEL))),
+       }
+};
+
 static void del_rule(struct fs_node *node);
 static void del_flow_table(struct fs_node *node);
 static void del_flow_group(struct fs_node *node);
@@ -671,3 +719,329 @@ static void mlx5_destroy_flow_group(struct mlx5_flow_group *fg)
                mlx5_core_warn(get_dev(&fg->node), "Flow group %d wasn't destroyed, refcount > 1\n",
                               fg->id);
 }
+
+static struct mlx5_flow_namespace *mlx5_get_flow_namespace(struct mlx5_core_dev *dev,
+                                                          enum mlx5_flow_namespace_type type)
+{
+       struct mlx5_flow_root_namespace *root_ns = dev->priv.root_ns;
+       int prio;
+       static struct fs_prio *fs_prio;
+       struct mlx5_flow_namespace *ns;
+
+       if (!root_ns)
+               return NULL;
+
+       switch (type) {
+       case MLX5_FLOW_NAMESPACE_KERNEL:
+               prio = 0;
+               break;
+       case MLX5_FLOW_NAMESPACE_FDB:
+               if (dev->priv.fdb_root_ns)
+                       return &dev->priv.fdb_root_ns->ns;
+               else
+                       return NULL;
+       default:
+               return NULL;
+       }
+
+       fs_prio = find_prio(&root_ns->ns, prio);
+       if (!fs_prio)
+               return NULL;
+
+       ns = list_first_entry(&fs_prio->node.children,
+                             typeof(*ns),
+                             node.list);
+
+       return ns;
+}
+
+static struct fs_prio *fs_create_prio(struct mlx5_flow_namespace *ns,
+                                     unsigned prio, int max_ft,
+                                     int start_level)
+{
+       struct fs_prio *fs_prio;
+
+       fs_prio = kzalloc(sizeof(*fs_prio), GFP_KERNEL);
+       if (!fs_prio)
+               return ERR_PTR(-ENOMEM);
+
+       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->prio = prio;
+       fs_prio->start_level = start_level;
+       list_add_tail(&fs_prio->node.list, &ns->node.children);
+
+       return fs_prio;
+}
+
+static struct mlx5_flow_namespace *fs_init_namespace(struct mlx5_flow_namespace
+                                                    *ns)
+{
+       ns->node.type = FS_TYPE_NAMESPACE;
+
+       return ns;
+}
+
+static struct mlx5_flow_namespace *fs_create_namespace(struct fs_prio *prio)
+{
+       struct mlx5_flow_namespace      *ns;
+
+       ns = kzalloc(sizeof(*ns), GFP_KERNEL);
+       if (!ns)
+               return ERR_PTR(-ENOMEM);
+
+       fs_init_namespace(ns);
+       tree_init_node(&ns->node, 1, NULL);
+       tree_add_node(&ns->node, &prio->node);
+       list_add_tail(&ns->node.list, &prio->node.children);
+
+       return ns;
+}
+
+static int init_root_tree_recursive(int max_ft_level, struct init_tree_node *init_node,
+                                   struct fs_node *fs_parent_node,
+                                   struct init_tree_node *init_parent_node,
+                                   int index)
+{
+       struct mlx5_flow_namespace *fs_ns;
+       struct fs_prio *fs_prio;
+       struct fs_node *base;
+       int i;
+       int err;
+
+       if (init_node->type == FS_TYPE_PRIO) {
+               if (init_node->min_ft_level > max_ft_level)
+                       return -ENOTSUPP;
+
+               fs_get_obj(fs_ns, fs_parent_node);
+               fs_prio = fs_create_prio(fs_ns, index, init_node->max_ft,
+                                        init_node->start_level);
+               if (IS_ERR(fs_prio))
+                       return PTR_ERR(fs_prio);
+               base = &fs_prio->node;
+       } else if (init_node->type == FS_TYPE_NAMESPACE) {
+               fs_get_obj(fs_prio, fs_parent_node);
+               fs_ns = fs_create_namespace(fs_prio);
+               if (IS_ERR(fs_ns))
+                       return PTR_ERR(fs_ns);
+               base = &fs_ns->node;
+       } else {
+               return -EINVAL;
+       }
+       for (i = 0; i < init_node->ar_size; i++) {
+               err = init_root_tree_recursive(max_ft_level,
+                                              &init_node->children[i], base,
+                                              init_node, i);
+               if (err)
+                       return err;
+       }
+
+       return 0;
+}
+
+static int init_root_tree(int max_ft_level, struct init_tree_node *init_node,
+                         struct fs_node *fs_parent_node)
+{
+       int i;
+       struct mlx5_flow_namespace *fs_ns;
+       int err;
+
+       fs_get_obj(fs_ns, fs_parent_node);
+       for (i = 0; i < init_node->ar_size; i++) {
+               err = init_root_tree_recursive(max_ft_level,
+                                              &init_node->children[i],
+                                              &fs_ns->node,
+                                              init_node, i);
+               if (err)
+                       return err;
+       }
+       return 0;
+}
+
+static struct mlx5_flow_root_namespace *create_root_ns(struct mlx5_core_dev *dev,
+                                                      enum fs_flow_table_type
+                                                      table_type)
+{
+       struct mlx5_flow_root_namespace *root_ns;
+       struct mlx5_flow_namespace *ns;
+
+       /* create the root namespace */
+       root_ns = mlx5_vzalloc(sizeof(*root_ns));
+       if (!root_ns)
+               return NULL;
+
+       root_ns->dev = dev;
+       root_ns->table_type = table_type;
+
+       ns = &root_ns->ns;
+       fs_init_namespace(ns);
+       tree_init_node(&ns->node, 1, NULL);
+       tree_add_node(&ns->node, NULL);
+
+       return root_ns;
+}
+
+static int init_root_ns(struct mlx5_core_dev *dev)
+{
+       int max_ft_level = MLX5_CAP_FLOWTABLE(dev,
+                                             flow_table_properties_nic_receive.
+                                             max_ft_level);
+
+       dev->priv.root_ns = create_root_ns(dev, FS_FT_NIC_RX);
+       if (IS_ERR_OR_NULL(dev->priv.root_ns))
+               goto cleanup;
+
+       if (init_root_tree(max_ft_level, &root_fs, &dev->priv.root_ns->ns.node))
+               goto cleanup;
+
+       return 0;
+
+cleanup:
+       mlx5_cleanup_fs(dev);
+       return -ENOMEM;
+}
+
+static void cleanup_single_prio_root_ns(struct mlx5_core_dev *dev,
+                                       struct mlx5_flow_root_namespace *root_ns)
+{
+       struct fs_node *prio;
+
+       if (!root_ns)
+               return;
+
+       if (!list_empty(&root_ns->ns.node.children)) {
+               prio = list_first_entry(&root_ns->ns.node.children,
+                                       struct fs_node,
+                                list);
+               if (tree_remove_node(prio))
+                       mlx5_core_warn(dev,
+                                      "Flow steering priority wasn't destroyed, refcount > 1\n");
+       }
+       if (tree_remove_node(&root_ns->ns.node))
+               mlx5_core_warn(dev,
+                              "Flow steering namespace wasn't destroyed, refcount > 1\n");
+       root_ns = NULL;
+}
+
+static void cleanup_root_ns(struct mlx5_core_dev *dev)
+{
+       struct mlx5_flow_root_namespace *root_ns = dev->priv.root_ns;
+       struct fs_prio *iter_prio;
+
+       if (!MLX5_CAP_GEN(dev, nic_flow_table))
+               return;
+
+       if (!root_ns)
+               return;
+
+       /* stage 1 */
+       fs_for_each_prio(iter_prio, &root_ns->ns) {
+               struct fs_node *node;
+               struct mlx5_flow_namespace *iter_ns;
+
+               fs_for_each_ns_or_ft(node, iter_prio) {
+                       if (node->type == FS_TYPE_FLOW_TABLE)
+                               continue;
+                       fs_get_obj(iter_ns, node);
+                       while (!list_empty(&iter_ns->node.children)) {
+                               struct fs_prio *obj_iter_prio2;
+                               struct fs_node *iter_prio2 =
+                                       list_first_entry(&iter_ns->node.children,
+                                                        struct fs_node,
+                                                        list);
+
+                               fs_get_obj(obj_iter_prio2, iter_prio2);
+                               if (tree_remove_node(iter_prio2)) {
+                                       mlx5_core_warn(dev,
+                                                      "Priority %d wasn't destroyed, refcount > 1\n",
+                                                      obj_iter_prio2->prio);
+                                       return;
+                               }
+                       }
+               }
+       }
+
+       /* stage 2 */
+       fs_for_each_prio(iter_prio, &root_ns->ns) {
+               while (!list_empty(&iter_prio->node.children)) {
+                       struct fs_node *iter_ns =
+                               list_first_entry(&iter_prio->node.children,
+                                                struct fs_node,
+                                                list);
+                       if (tree_remove_node(iter_ns)) {
+                               mlx5_core_warn(dev,
+                                              "Namespace wasn't destroyed, refcount > 1\n");
+                               return;
+                       }
+               }
+       }
+
+       /* stage 3 */
+       while (!list_empty(&root_ns->ns.node.children)) {
+               struct fs_prio *obj_prio_node;
+               struct fs_node *prio_node =
+                       list_first_entry(&root_ns->ns.node.children,
+                                        struct fs_node,
+                                        list);
+
+               fs_get_obj(obj_prio_node, prio_node);
+               if (tree_remove_node(prio_node)) {
+                       mlx5_core_warn(dev,
+                                      "Priority %d wasn't destroyed, refcount > 1\n",
+                                      obj_prio_node->prio);
+                       return;
+               }
+       }
+
+       if (tree_remove_node(&root_ns->ns.node)) {
+               mlx5_core_warn(dev,
+                              "root namespace wasn't destroyed, refcount > 1\n");
+               return;
+       }
+
+       dev->priv.root_ns = NULL;
+}
+
+void mlx5_cleanup_fs(struct mlx5_core_dev *dev)
+{
+       cleanup_root_ns(dev);
+       cleanup_single_prio_root_ns(dev, dev->priv.fdb_root_ns);
+}
+
+static int init_fdb_root_ns(struct mlx5_core_dev *dev)
+{
+       struct fs_prio *prio;
+
+       dev->priv.fdb_root_ns = create_root_ns(dev, FS_FT_FDB);
+       if (!dev->priv.fdb_root_ns)
+               return -ENOMEM;
+
+       /* create 1 prio*/
+       prio = fs_create_prio(&dev->priv.fdb_root_ns->ns, 0, 1, 0);
+       if (IS_ERR(prio)) {
+               cleanup_single_prio_root_ns(dev, dev->priv.fdb_root_ns);
+               return PTR_ERR(prio);
+       } else {
+               return 0;
+       }
+}
+
+int mlx5_init_fs(struct mlx5_core_dev *dev)
+{
+       int err = 0;
+
+       if (MLX5_CAP_GEN(dev, nic_flow_table)) {
+               err = init_root_ns(dev);
+               if (err)
+                       return err;
+       }
+       if (MLX5_CAP_GEN(dev, eswitch_flow_table)) {
+               err = init_fdb_root_ns(dev);
+               if (err)
+                       cleanup_root_ns(dev);
+       }
+
+       return err;
+}
index 6c27b8e..4ebb97f 100644 (file)
@@ -46,6 +46,7 @@ enum fs_node_type {
 
 enum fs_flow_table_type {
        FS_FT_NIC_RX     = 0x0,
+       FS_FT_FDB        = 0X4,
 };
 
 enum fs_fte_status {
@@ -125,6 +126,9 @@ struct mlx5_flow_root_namespace {
        struct mlx5_core_dev            *dev;
 };
 
+int mlx5_init_fs(struct mlx5_core_dev *dev);
+void mlx5_cleanup_fs(struct mlx5_core_dev *dev);
+
 #define fs_get_obj(v, _node)  {v = container_of((_node), typeof(*v), node); }
 
 #define fs_list_for_each_entry(pos, root)              \
index ac098b6..2fd7019 100644 (file)
@@ -502,6 +502,8 @@ struct mlx5_priv {
        struct mlx5_eswitch     *eswitch;
        struct mlx5_core_sriov  sriov;
        unsigned long           pci_dev_data;
+       struct mlx5_flow_root_namespace *root_ns;
+       struct mlx5_flow_root_namespace *fdb_root_ns;
 };
 
 enum mlx5_device_state {
index 34fd8dc..16ae523 100644 (file)
 
 #include <linux/mlx5/mlx5_ifc.h>
 
+#define MLX5_FS_DEFAULT_FLOW_TAG 0x0
+
+enum mlx5_flow_namespace_type {
+       MLX5_FLOW_NAMESPACE_KERNEL,
+       MLX5_FLOW_NAMESPACE_FDB,
+};
+
 struct mlx5_flow_table;
 
 struct mlx5_flow_destination {
@@ -42,6 +49,7 @@ struct mlx5_flow_destination {
        union {
                u32                     tir_num;
                struct mlx5_flow_table  *ft;
+               u32                     vport_num;
        };
 };
 #endif