net/mlx5_core: Managing root flow table
[cascardo/linux.git] / drivers / net / ethernet / mellanox / mlx5 / core / fs_core.c
index c5a96e6..64bdb54 100644 (file)
@@ -510,6 +510,29 @@ static struct mlx5_flow_table *find_prev_chained_ft(struct fs_prio *prio)
        return find_closest_ft(prio, true);
 }
 
+static int update_root_ft_create(struct mlx5_flow_table *ft, struct fs_prio
+                                *prio)
+{
+       struct mlx5_flow_root_namespace *root = find_root(&prio->node);
+       int min_level = INT_MAX;
+       int err;
+
+       if (root->root_ft)
+               min_level = root->root_ft->level;
+
+       if (ft->level >= min_level)
+               return 0;
+
+       err = mlx5_cmd_update_root_ft(root->dev, ft);
+       if (err)
+               mlx5_core_warn(root->dev, "Update root flow table of id=%u failed\n",
+                              ft->id);
+       else
+               root->root_ft = ft;
+
+       return err;
+}
+
 struct mlx5_flow_table *mlx5_create_flow_table(struct mlx5_flow_namespace *ns,
                                               int prio,
                                               int max_fte)
@@ -526,14 +549,15 @@ struct mlx5_flow_table *mlx5_create_flow_table(struct mlx5_flow_namespace *ns,
                return ERR_PTR(-ENODEV);
        }
 
+       mutex_lock(&root->chain_lock);
        fs_prio = find_prio(ns, prio);
-       if (!fs_prio)
-               return ERR_PTR(-EINVAL);
-
-       lock_ref_node(&fs_prio->node);
+       if (!fs_prio) {
+               err = -EINVAL;
+               goto unlock_root;
+       }
        if (fs_prio->num_ft == fs_prio->max_ft) {
                err = -ENOSPC;
-               goto unlock_prio;
+               goto unlock_root;
        }
 
        ft = alloc_flow_table(find_next_free_level(fs_prio),
@@ -541,7 +565,7 @@ struct mlx5_flow_table *mlx5_create_flow_table(struct mlx5_flow_namespace *ns,
                              root->table_type);
        if (!ft) {
                err = -ENOMEM;
-               goto unlock_prio;
+               goto unlock_root;
        }
 
        tree_init_node(&ft->node, 1, del_flow_table);
@@ -551,15 +575,25 @@ struct mlx5_flow_table *mlx5_create_flow_table(struct mlx5_flow_namespace *ns,
        if (err)
                goto free_ft;
 
+       if (MLX5_CAP_FLOWTABLE(root->dev,
+                              flow_table_properties_nic_receive.modify_root)) {
+               err = update_root_ft_create(ft, fs_prio);
+               if (err)
+                       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);
        fs_prio->num_ft++;
        unlock_ref_node(&fs_prio->node);
+       mutex_unlock(&root->chain_lock);
        return ft;
+destroy_ft:
+       mlx5_cmd_destroy_flow_table(root->dev, ft);
 free_ft:
        kfree(ft);
-unlock_prio:
-       unlock_ref_node(&fs_prio->node);
+unlock_root:
+       mutex_unlock(&root->chain_lock);
        return ERR_PTR(err);
 }
 
@@ -899,13 +933,57 @@ void mlx5_del_flow_rule(struct mlx5_flow_rule *rule)
        tree_remove_node(&rule->node);
 }
 
+/* Assuming prio->node.children(flow tables) is sorted by level */
+static struct mlx5_flow_table *find_next_ft(struct mlx5_flow_table *ft)
+{
+       struct fs_prio *prio;
+
+       fs_get_obj(prio, ft->node.parent);
+
+       if (!list_is_last(&ft->node.list, &prio->node.children))
+               return list_next_entry(ft, node.list);
+       return find_next_chained_ft(prio);
+}
+
+static int update_root_ft_destroy(struct mlx5_flow_table *ft)
+{
+       struct mlx5_flow_root_namespace *root = find_root(&ft->node);
+       struct mlx5_flow_table *new_root_ft = NULL;
+
+       if (root->root_ft != ft)
+               return 0;
+
+       new_root_ft = find_next_ft(ft);
+       if (new_root_ft) {
+               int err = mlx5_cmd_update_root_ft(root->dev, new_root_ft);
+
+               if (err) {
+                       mlx5_core_warn(root->dev, "Update root flow table of id=%u failed\n",
+                                      ft->id);
+                       return err;
+               }
+               root->root_ft = new_root_ft;
+       }
+       return 0;
+}
+
 int mlx5_destroy_flow_table(struct mlx5_flow_table *ft)
 {
+       struct mlx5_flow_root_namespace *root = find_root(&ft->node);
+       int err = 0;
+
+       mutex_lock(&root->chain_lock);
+       err = update_root_ft_destroy(ft);
+       if (err) {
+               mutex_unlock(&root->chain_lock);
+               return err;
+       }
        if (tree_remove_node(&ft->node))
                mlx5_core_warn(get_dev(&ft->node), "Flow table %d wasn't destroyed, refcount > 1\n",
                               ft->id);
+       mutex_unlock(&root->chain_lock);
 
-       return 0;
+       return err;
 }
 
 void mlx5_destroy_flow_group(struct mlx5_flow_group *fg)
@@ -1072,6 +1150,7 @@ static struct mlx5_flow_root_namespace *create_root_ns(struct mlx5_core_dev *dev
 
        ns = &root_ns->ns;
        fs_init_namespace(ns);
+       mutex_init(&root_ns->chain_lock);
        tree_init_node(&ns->node, 1, NULL);
        tree_add_node(&ns->node, NULL);