|
@@ -36,6 +36,7 @@
|
|
#include "mlx5_core.h"
|
|
#include "mlx5_core.h"
|
|
#include "fs_core.h"
|
|
#include "fs_core.h"
|
|
#include "fs_cmd.h"
|
|
#include "fs_cmd.h"
|
|
|
|
+#include "diag/fs_tracepoint.h"
|
|
|
|
|
|
#define INIT_TREE_NODE_ARRAY_SIZE(...) (sizeof((struct init_tree_node[]){__VA_ARGS__}) /\
|
|
#define INIT_TREE_NODE_ARRAY_SIZE(...) (sizeof((struct init_tree_node[]){__VA_ARGS__}) /\
|
|
sizeof(struct init_tree_node))
|
|
sizeof(struct init_tree_node))
|
|
@@ -150,6 +151,23 @@ enum fs_i_mutex_lock_class {
|
|
FS_MUTEX_CHILD
|
|
FS_MUTEX_CHILD
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+static const struct rhashtable_params rhash_fte = {
|
|
|
|
+ .key_len = FIELD_SIZEOF(struct fs_fte, val),
|
|
|
|
+ .key_offset = offsetof(struct fs_fte, val),
|
|
|
|
+ .head_offset = offsetof(struct fs_fte, hash),
|
|
|
|
+ .automatic_shrinking = true,
|
|
|
|
+ .min_size = 1,
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static const struct rhashtable_params rhash_fg = {
|
|
|
|
+ .key_len = FIELD_SIZEOF(struct mlx5_flow_group, mask),
|
|
|
|
+ .key_offset = offsetof(struct mlx5_flow_group, mask),
|
|
|
|
+ .head_offset = offsetof(struct mlx5_flow_group, hash),
|
|
|
|
+ .automatic_shrinking = true,
|
|
|
|
+ .min_size = 1,
|
|
|
|
+
|
|
|
|
+};
|
|
|
|
+
|
|
static void del_rule(struct fs_node *node);
|
|
static void del_rule(struct fs_node *node);
|
|
static void del_flow_table(struct fs_node *node);
|
|
static void del_flow_table(struct fs_node *node);
|
|
static void del_flow_group(struct fs_node *node);
|
|
static void del_flow_group(struct fs_node *node);
|
|
@@ -255,71 +273,77 @@ static struct fs_prio *find_prio(struct mlx5_flow_namespace *ns,
|
|
return NULL;
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
|
|
-static bool masked_memcmp(void *mask, void *val1, void *val2, size_t size)
|
|
|
|
|
|
+static bool check_last_reserved(const u32 *match_criteria)
|
|
{
|
|
{
|
|
- unsigned int i;
|
|
|
|
|
|
+ char *match_criteria_reserved =
|
|
|
|
+ MLX5_ADDR_OF(fte_match_param, match_criteria, MLX5_FTE_MATCH_PARAM_RESERVED);
|
|
|
|
|
|
- for (i = 0; i < size; i++, mask++, val1++, val2++)
|
|
|
|
- if ((*((u8 *)val1) & (*(u8 *)mask)) !=
|
|
|
|
- ((*(u8 *)val2) & (*(u8 *)mask)))
|
|
|
|
- return false;
|
|
|
|
-
|
|
|
|
- return true;
|
|
|
|
|
|
+ return !match_criteria_reserved[0] &&
|
|
|
|
+ !memcmp(match_criteria_reserved, match_criteria_reserved + 1,
|
|
|
|
+ MLX5_FLD_SZ_BYTES(fte_match_param,
|
|
|
|
+ MLX5_FTE_MATCH_PARAM_RESERVED) - 1);
|
|
}
|
|
}
|
|
|
|
|
|
-static bool compare_match_value(struct mlx5_flow_group_mask *mask,
|
|
|
|
- void *fte_param1, void *fte_param2)
|
|
|
|
|
|
+static bool check_valid_mask(u8 match_criteria_enable, const u32 *match_criteria)
|
|
{
|
|
{
|
|
- if (mask->match_criteria_enable &
|
|
|
|
- 1 << MLX5_CREATE_FLOW_GROUP_IN_MATCH_CRITERIA_ENABLE_OUTER_HEADERS) {
|
|
|
|
- void *fte_match1 = MLX5_ADDR_OF(fte_match_param,
|
|
|
|
- fte_param1, outer_headers);
|
|
|
|
- void *fte_match2 = MLX5_ADDR_OF(fte_match_param,
|
|
|
|
- fte_param2, outer_headers);
|
|
|
|
- void *fte_mask = MLX5_ADDR_OF(fte_match_param,
|
|
|
|
- mask->match_criteria, outer_headers);
|
|
|
|
|
|
+ if (match_criteria_enable & ~(
|
|
|
|
+ (1 << MLX5_CREATE_FLOW_GROUP_IN_MATCH_CRITERIA_ENABLE_OUTER_HEADERS) |
|
|
|
|
+ (1 << MLX5_CREATE_FLOW_GROUP_IN_MATCH_CRITERIA_ENABLE_MISC_PARAMETERS) |
|
|
|
|
+ (1 << MLX5_CREATE_FLOW_GROUP_IN_MATCH_CRITERIA_ENABLE_INNER_HEADERS)))
|
|
|
|
+ return false;
|
|
|
|
|
|
- if (!masked_memcmp(fte_mask, fte_match1, fte_match2,
|
|
|
|
- MLX5_ST_SZ_BYTES(fte_match_set_lyr_2_4)))
|
|
|
|
|
|
+ if (!(match_criteria_enable &
|
|
|
|
+ 1 << MLX5_CREATE_FLOW_GROUP_IN_MATCH_CRITERIA_ENABLE_OUTER_HEADERS)) {
|
|
|
|
+ char *fg_type_mask = MLX5_ADDR_OF(fte_match_param,
|
|
|
|
+ match_criteria, outer_headers);
|
|
|
|
+
|
|
|
|
+ if (fg_type_mask[0] ||
|
|
|
|
+ memcmp(fg_type_mask, fg_type_mask + 1,
|
|
|
|
+ MLX5_ST_SZ_BYTES(fte_match_set_lyr_2_4) - 1))
|
|
return false;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
- if (mask->match_criteria_enable &
|
|
|
|
- 1 << MLX5_CREATE_FLOW_GROUP_IN_MATCH_CRITERIA_ENABLE_MISC_PARAMETERS) {
|
|
|
|
- void *fte_match1 = MLX5_ADDR_OF(fte_match_param,
|
|
|
|
- fte_param1, misc_parameters);
|
|
|
|
- void *fte_match2 = MLX5_ADDR_OF(fte_match_param,
|
|
|
|
- fte_param2, misc_parameters);
|
|
|
|
- void *fte_mask = MLX5_ADDR_OF(fte_match_param,
|
|
|
|
- mask->match_criteria, misc_parameters);
|
|
|
|
|
|
+ if (!(match_criteria_enable &
|
|
|
|
+ 1 << MLX5_CREATE_FLOW_GROUP_IN_MATCH_CRITERIA_ENABLE_MISC_PARAMETERS)) {
|
|
|
|
+ char *fg_type_mask = MLX5_ADDR_OF(fte_match_param,
|
|
|
|
+ match_criteria, misc_parameters);
|
|
|
|
|
|
- if (!masked_memcmp(fte_mask, fte_match1, fte_match2,
|
|
|
|
- MLX5_ST_SZ_BYTES(fte_match_set_misc)))
|
|
|
|
|
|
+ if (fg_type_mask[0] ||
|
|
|
|
+ memcmp(fg_type_mask, fg_type_mask + 1,
|
|
|
|
+ MLX5_ST_SZ_BYTES(fte_match_set_misc) - 1))
|
|
return false;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
- if (mask->match_criteria_enable &
|
|
|
|
- 1 << MLX5_CREATE_FLOW_GROUP_IN_MATCH_CRITERIA_ENABLE_INNER_HEADERS) {
|
|
|
|
- void *fte_match1 = MLX5_ADDR_OF(fte_match_param,
|
|
|
|
- fte_param1, inner_headers);
|
|
|
|
- void *fte_match2 = MLX5_ADDR_OF(fte_match_param,
|
|
|
|
- fte_param2, inner_headers);
|
|
|
|
- void *fte_mask = MLX5_ADDR_OF(fte_match_param,
|
|
|
|
- mask->match_criteria, inner_headers);
|
|
|
|
|
|
+ if (!(match_criteria_enable &
|
|
|
|
+ 1 << MLX5_CREATE_FLOW_GROUP_IN_MATCH_CRITERIA_ENABLE_INNER_HEADERS)) {
|
|
|
|
+ char *fg_type_mask = MLX5_ADDR_OF(fte_match_param,
|
|
|
|
+ match_criteria, inner_headers);
|
|
|
|
|
|
- if (!masked_memcmp(fte_mask, fte_match1, fte_match2,
|
|
|
|
- MLX5_ST_SZ_BYTES(fte_match_set_lyr_2_4)))
|
|
|
|
|
|
+ if (fg_type_mask[0] ||
|
|
|
|
+ memcmp(fg_type_mask, fg_type_mask + 1,
|
|
|
|
+ MLX5_ST_SZ_BYTES(fte_match_set_lyr_2_4) - 1))
|
|
return false;
|
|
return false;
|
|
}
|
|
}
|
|
- return true;
|
|
|
|
|
|
+
|
|
|
|
+ return check_last_reserved(match_criteria);
|
|
}
|
|
}
|
|
|
|
|
|
-static bool compare_match_criteria(u8 match_criteria_enable1,
|
|
|
|
- u8 match_criteria_enable2,
|
|
|
|
- void *mask1, void *mask2)
|
|
|
|
|
|
+static bool check_valid_spec(const struct mlx5_flow_spec *spec)
|
|
{
|
|
{
|
|
- return match_criteria_enable1 == match_criteria_enable2 &&
|
|
|
|
- !memcmp(mask1, mask2, MLX5_ST_SZ_BYTES(fte_match_param));
|
|
|
|
|
|
+ int i;
|
|
|
|
+
|
|
|
|
+ if (!check_valid_mask(spec->match_criteria_enable, spec->match_criteria)) {
|
|
|
|
+ pr_warn("mlx5_core: Match criteria given mismatches match_criteria_enable\n");
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ for (i = 0; i < MLX5_ST_SZ_DW_MATCH_PARAM; i++)
|
|
|
|
+ if (spec->match_value[i] & ~spec->match_criteria[i]) {
|
|
|
|
+ pr_warn("mlx5_core: match_value differs from match_criteria\n");
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return check_last_reserved(spec->match_value);
|
|
}
|
|
}
|
|
|
|
|
|
static struct mlx5_flow_root_namespace *find_root(struct fs_node *node)
|
|
static struct mlx5_flow_root_namespace *find_root(struct fs_node *node)
|
|
@@ -360,6 +384,8 @@ static void del_flow_table(struct fs_node *node)
|
|
err = mlx5_cmd_destroy_flow_table(dev, ft);
|
|
err = mlx5_cmd_destroy_flow_table(dev, ft);
|
|
if (err)
|
|
if (err)
|
|
mlx5_core_warn(dev, "flow steering can't destroy ft\n");
|
|
mlx5_core_warn(dev, "flow steering can't destroy ft\n");
|
|
|
|
+ ida_destroy(&ft->fte_allocator);
|
|
|
|
+ rhltable_destroy(&ft->fgs_hash);
|
|
fs_get_obj(prio, ft->node.parent);
|
|
fs_get_obj(prio, ft->node.parent);
|
|
prio->num_ft--;
|
|
prio->num_ft--;
|
|
}
|
|
}
|
|
@@ -370,22 +396,16 @@ static void del_rule(struct fs_node *node)
|
|
struct mlx5_flow_table *ft;
|
|
struct mlx5_flow_table *ft;
|
|
struct mlx5_flow_group *fg;
|
|
struct mlx5_flow_group *fg;
|
|
struct fs_fte *fte;
|
|
struct fs_fte *fte;
|
|
- u32 *match_value;
|
|
|
|
int modify_mask;
|
|
int modify_mask;
|
|
struct mlx5_core_dev *dev = get_dev(node);
|
|
struct mlx5_core_dev *dev = get_dev(node);
|
|
- int match_len = MLX5_ST_SZ_BYTES(fte_match_param);
|
|
|
|
int err;
|
|
int err;
|
|
bool update_fte = false;
|
|
bool update_fte = false;
|
|
|
|
|
|
- match_value = kvzalloc(match_len, GFP_KERNEL);
|
|
|
|
- if (!match_value)
|
|
|
|
- return;
|
|
|
|
-
|
|
|
|
fs_get_obj(rule, node);
|
|
fs_get_obj(rule, node);
|
|
fs_get_obj(fte, rule->node.parent);
|
|
fs_get_obj(fte, rule->node.parent);
|
|
fs_get_obj(fg, fte->node.parent);
|
|
fs_get_obj(fg, fte->node.parent);
|
|
- memcpy(match_value, fte->val, sizeof(fte->val));
|
|
|
|
fs_get_obj(ft, fg->node.parent);
|
|
fs_get_obj(ft, fg->node.parent);
|
|
|
|
+ trace_mlx5_fs_del_rule(rule);
|
|
list_del(&rule->node.list);
|
|
list_del(&rule->node.list);
|
|
if (rule->sw_action == MLX5_FLOW_CONTEXT_ACTION_FWD_NEXT_PRIO) {
|
|
if (rule->sw_action == MLX5_FLOW_CONTEXT_ACTION_FWD_NEXT_PRIO) {
|
|
mutex_lock(&rule->dest_attr.ft->lock);
|
|
mutex_lock(&rule->dest_attr.ft->lock);
|
|
@@ -414,7 +434,18 @@ out:
|
|
"%s can't del rule fg id=%d fte_index=%d\n",
|
|
"%s can't del rule fg id=%d fte_index=%d\n",
|
|
__func__, fg->id, fte->index);
|
|
__func__, fg->id, fte->index);
|
|
}
|
|
}
|
|
- kvfree(match_value);
|
|
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void destroy_fte(struct fs_fte *fte, struct mlx5_flow_group *fg)
|
|
|
|
+{
|
|
|
|
+ struct mlx5_flow_table *ft;
|
|
|
|
+ int ret;
|
|
|
|
+
|
|
|
|
+ ret = rhashtable_remove_fast(&fg->ftes_hash, &fte->hash, rhash_fte);
|
|
|
|
+ WARN_ON(ret);
|
|
|
|
+ fte->status = 0;
|
|
|
|
+ fs_get_obj(ft, fg->node.parent);
|
|
|
|
+ ida_simple_remove(&ft->fte_allocator, fte->index);
|
|
}
|
|
}
|
|
|
|
|
|
static void del_fte(struct fs_node *node)
|
|
static void del_fte(struct fs_node *node)
|
|
@@ -428,6 +459,7 @@ static void del_fte(struct fs_node *node)
|
|
fs_get_obj(fte, node);
|
|
fs_get_obj(fte, node);
|
|
fs_get_obj(fg, fte->node.parent);
|
|
fs_get_obj(fg, fte->node.parent);
|
|
fs_get_obj(ft, fg->node.parent);
|
|
fs_get_obj(ft, fg->node.parent);
|
|
|
|
+ trace_mlx5_fs_del_fte(fte);
|
|
|
|
|
|
dev = get_dev(&ft->node);
|
|
dev = get_dev(&ft->node);
|
|
err = mlx5_cmd_delete_fte(dev, ft,
|
|
err = mlx5_cmd_delete_fte(dev, ft,
|
|
@@ -437,8 +469,7 @@ static void del_fte(struct fs_node *node)
|
|
"flow steering can't delete fte in index %d of flow group id %d\n",
|
|
"flow steering can't delete fte in index %d of flow group id %d\n",
|
|
fte->index, fg->id);
|
|
fte->index, fg->id);
|
|
|
|
|
|
- fte->status = 0;
|
|
|
|
- fg->num_ftes--;
|
|
|
|
|
|
+ destroy_fte(fte, fg);
|
|
}
|
|
}
|
|
|
|
|
|
static void del_flow_group(struct fs_node *node)
|
|
static void del_flow_group(struct fs_node *node)
|
|
@@ -446,14 +477,21 @@ static void del_flow_group(struct fs_node *node)
|
|
struct mlx5_flow_group *fg;
|
|
struct mlx5_flow_group *fg;
|
|
struct mlx5_flow_table *ft;
|
|
struct mlx5_flow_table *ft;
|
|
struct mlx5_core_dev *dev;
|
|
struct mlx5_core_dev *dev;
|
|
|
|
+ int err;
|
|
|
|
|
|
fs_get_obj(fg, node);
|
|
fs_get_obj(fg, node);
|
|
fs_get_obj(ft, fg->node.parent);
|
|
fs_get_obj(ft, fg->node.parent);
|
|
dev = get_dev(&ft->node);
|
|
dev = get_dev(&ft->node);
|
|
|
|
+ trace_mlx5_fs_del_fg(fg);
|
|
|
|
|
|
if (ft->autogroup.active)
|
|
if (ft->autogroup.active)
|
|
ft->autogroup.num_groups--;
|
|
ft->autogroup.num_groups--;
|
|
|
|
|
|
|
|
+ rhashtable_destroy(&fg->ftes_hash);
|
|
|
|
+ err = rhltable_remove(&ft->fgs_hash,
|
|
|
|
+ &fg->hash,
|
|
|
|
+ rhash_fg);
|
|
|
|
+ WARN_ON(err);
|
|
if (mlx5_cmd_destroy_flow_group(dev, ft, fg->id))
|
|
if (mlx5_cmd_destroy_flow_group(dev, ft, fg->id))
|
|
mlx5_core_warn(dev, "flow steering can't destroy fg %d of ft %d\n",
|
|
mlx5_core_warn(dev, "flow steering can't destroy fg %d of ft %d\n",
|
|
fg->id, ft->id);
|
|
fg->id, ft->id);
|
|
@@ -488,10 +526,17 @@ static struct mlx5_flow_group *alloc_flow_group(u32 *create_fg_in)
|
|
u8 match_criteria_enable = MLX5_GET(create_flow_group_in,
|
|
u8 match_criteria_enable = MLX5_GET(create_flow_group_in,
|
|
create_fg_in,
|
|
create_fg_in,
|
|
match_criteria_enable);
|
|
match_criteria_enable);
|
|
|
|
+ int ret;
|
|
|
|
+
|
|
fg = kzalloc(sizeof(*fg), GFP_KERNEL);
|
|
fg = kzalloc(sizeof(*fg), GFP_KERNEL);
|
|
if (!fg)
|
|
if (!fg)
|
|
return ERR_PTR(-ENOMEM);
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
|
|
|
|
+ ret = rhashtable_init(&fg->ftes_hash, &rhash_fte);
|
|
|
|
+ if (ret) {
|
|
|
|
+ kfree(fg);
|
|
|
|
+ return ERR_PTR(ret);
|
|
|
|
+ }
|
|
fg->mask.match_criteria_enable = match_criteria_enable;
|
|
fg->mask.match_criteria_enable = match_criteria_enable;
|
|
memcpy(&fg->mask.match_criteria, match_criteria,
|
|
memcpy(&fg->mask.match_criteria, match_criteria,
|
|
sizeof(fg->mask.match_criteria));
|
|
sizeof(fg->mask.match_criteria));
|
|
@@ -509,10 +554,17 @@ static struct mlx5_flow_table *alloc_flow_table(int level, u16 vport, int max_ft
|
|
u32 flags)
|
|
u32 flags)
|
|
{
|
|
{
|
|
struct mlx5_flow_table *ft;
|
|
struct mlx5_flow_table *ft;
|
|
|
|
+ int ret;
|
|
|
|
|
|
ft = kzalloc(sizeof(*ft), GFP_KERNEL);
|
|
ft = kzalloc(sizeof(*ft), GFP_KERNEL);
|
|
if (!ft)
|
|
if (!ft)
|
|
- return NULL;
|
|
|
|
|
|
+ return ERR_PTR(-ENOMEM);
|
|
|
|
+
|
|
|
|
+ ret = rhltable_init(&ft->fgs_hash, &rhash_fg);
|
|
|
|
+ if (ret) {
|
|
|
|
+ kfree(ft);
|
|
|
|
+ return ERR_PTR(ret);
|
|
|
|
+ }
|
|
|
|
|
|
ft->level = level;
|
|
ft->level = level;
|
|
ft->node.type = FS_TYPE_FLOW_TABLE;
|
|
ft->node.type = FS_TYPE_FLOW_TABLE;
|
|
@@ -523,6 +575,7 @@ static struct mlx5_flow_table *alloc_flow_table(int level, u16 vport, int max_ft
|
|
ft->flags = flags;
|
|
ft->flags = flags;
|
|
INIT_LIST_HEAD(&ft->fwd_rules);
|
|
INIT_LIST_HEAD(&ft->fwd_rules);
|
|
mutex_init(&ft->lock);
|
|
mutex_init(&ft->lock);
|
|
|
|
+ ida_init(&ft->fte_allocator);
|
|
|
|
|
|
return ft;
|
|
return ft;
|
|
}
|
|
}
|
|
@@ -812,8 +865,8 @@ static struct mlx5_flow_table *__mlx5_create_flow_table(struct mlx5_flow_namespa
|
|
ft_attr->max_fte ? roundup_pow_of_two(ft_attr->max_fte) : 0,
|
|
ft_attr->max_fte ? roundup_pow_of_two(ft_attr->max_fte) : 0,
|
|
root->table_type,
|
|
root->table_type,
|
|
op_mod, ft_attr->flags);
|
|
op_mod, ft_attr->flags);
|
|
- if (!ft) {
|
|
|
|
- err = -ENOMEM;
|
|
|
|
|
|
+ if (IS_ERR(ft)) {
|
|
|
|
+ err = PTR_ERR(ft);
|
|
goto unlock_root;
|
|
goto unlock_root;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -839,6 +892,7 @@ static struct mlx5_flow_table *__mlx5_create_flow_table(struct mlx5_flow_namespa
|
|
destroy_ft:
|
|
destroy_ft:
|
|
mlx5_cmd_destroy_flow_table(root->dev, ft);
|
|
mlx5_cmd_destroy_flow_table(root->dev, ft);
|
|
free_ft:
|
|
free_ft:
|
|
|
|
+ ida_destroy(&ft->fte_allocator);
|
|
kfree(ft);
|
|
kfree(ft);
|
|
unlock_root:
|
|
unlock_root:
|
|
mutex_unlock(&root->chain_lock);
|
|
mutex_unlock(&root->chain_lock);
|
|
@@ -924,11 +978,13 @@ static struct mlx5_flow_group *create_flow_group_common(struct mlx5_flow_table *
|
|
if (IS_ERR(fg))
|
|
if (IS_ERR(fg))
|
|
return fg;
|
|
return fg;
|
|
|
|
|
|
|
|
+ err = rhltable_insert(&ft->fgs_hash, &fg->hash, rhash_fg);
|
|
|
|
+ if (err)
|
|
|
|
+ goto err_free_fg;
|
|
|
|
+
|
|
err = mlx5_cmd_create_flow_group(dev, ft, fg_in, &fg->id);
|
|
err = mlx5_cmd_create_flow_group(dev, ft, fg_in, &fg->id);
|
|
- if (err) {
|
|
|
|
- kfree(fg);
|
|
|
|
- return ERR_PTR(err);
|
|
|
|
- }
|
|
|
|
|
|
+ if (err)
|
|
|
|
+ goto err_remove_fg;
|
|
|
|
|
|
if (ft->autogroup.active)
|
|
if (ft->autogroup.active)
|
|
ft->autogroup.num_groups++;
|
|
ft->autogroup.num_groups++;
|
|
@@ -938,14 +994,33 @@ static struct mlx5_flow_group *create_flow_group_common(struct mlx5_flow_table *
|
|
/* Add node to group list */
|
|
/* Add node to group list */
|
|
list_add(&fg->node.list, prev_fg);
|
|
list_add(&fg->node.list, prev_fg);
|
|
|
|
|
|
|
|
+ trace_mlx5_fs_add_fg(fg);
|
|
return fg;
|
|
return fg;
|
|
|
|
+
|
|
|
|
+err_remove_fg:
|
|
|
|
+ WARN_ON(rhltable_remove(&ft->fgs_hash,
|
|
|
|
+ &fg->hash,
|
|
|
|
+ rhash_fg));
|
|
|
|
+err_free_fg:
|
|
|
|
+ rhashtable_destroy(&fg->ftes_hash);
|
|
|
|
+ kfree(fg);
|
|
|
|
+
|
|
|
|
+ return ERR_PTR(err);
|
|
}
|
|
}
|
|
|
|
|
|
struct mlx5_flow_group *mlx5_create_flow_group(struct mlx5_flow_table *ft,
|
|
struct mlx5_flow_group *mlx5_create_flow_group(struct mlx5_flow_table *ft,
|
|
u32 *fg_in)
|
|
u32 *fg_in)
|
|
{
|
|
{
|
|
|
|
+ void *match_criteria = MLX5_ADDR_OF(create_flow_group_in,
|
|
|
|
+ fg_in, match_criteria);
|
|
|
|
+ u8 match_criteria_enable = MLX5_GET(create_flow_group_in,
|
|
|
|
+ fg_in,
|
|
|
|
+ match_criteria_enable);
|
|
struct mlx5_flow_group *fg;
|
|
struct mlx5_flow_group *fg;
|
|
|
|
|
|
|
|
+ if (!check_valid_mask(match_criteria_enable, match_criteria))
|
|
|
|
+ return ERR_PTR(-EINVAL);
|
|
|
|
+
|
|
if (ft->autogroup.active)
|
|
if (ft->autogroup.active)
|
|
return ERR_PTR(-EPERM);
|
|
return ERR_PTR(-EPERM);
|
|
|
|
|
|
@@ -1102,43 +1177,38 @@ free_handle:
|
|
return ERR_PTR(err);
|
|
return ERR_PTR(err);
|
|
}
|
|
}
|
|
|
|
|
|
-/* Assumed fg is locked */
|
|
|
|
-static unsigned int get_free_fte_index(struct mlx5_flow_group *fg,
|
|
|
|
- struct list_head **prev)
|
|
|
|
-{
|
|
|
|
- struct fs_fte *fte;
|
|
|
|
- unsigned int start = fg->start_index;
|
|
|
|
-
|
|
|
|
- if (prev)
|
|
|
|
- *prev = &fg->node.children;
|
|
|
|
-
|
|
|
|
- /* assumed list is sorted by index */
|
|
|
|
- fs_for_each_fte(fte, fg) {
|
|
|
|
- if (fte->index != start)
|
|
|
|
- return start;
|
|
|
|
- start++;
|
|
|
|
- if (prev)
|
|
|
|
- *prev = &fte->node.list;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return start;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/* prev is output, prev->next = new_fte */
|
|
|
|
static struct fs_fte *create_fte(struct mlx5_flow_group *fg,
|
|
static struct fs_fte *create_fte(struct mlx5_flow_group *fg,
|
|
u32 *match_value,
|
|
u32 *match_value,
|
|
- struct mlx5_flow_act *flow_act,
|
|
|
|
- struct list_head **prev)
|
|
|
|
|
|
+ struct mlx5_flow_act *flow_act)
|
|
{
|
|
{
|
|
|
|
+ struct mlx5_flow_table *ft;
|
|
struct fs_fte *fte;
|
|
struct fs_fte *fte;
|
|
int index;
|
|
int index;
|
|
|
|
+ int ret;
|
|
|
|
+
|
|
|
|
+ fs_get_obj(ft, fg->node.parent);
|
|
|
|
+ index = ida_simple_get(&ft->fte_allocator, fg->start_index,
|
|
|
|
+ fg->start_index + fg->max_ftes,
|
|
|
|
+ GFP_KERNEL);
|
|
|
|
+ if (index < 0)
|
|
|
|
+ return ERR_PTR(index);
|
|
|
|
|
|
- index = get_free_fte_index(fg, prev);
|
|
|
|
fte = alloc_fte(flow_act, match_value, index);
|
|
fte = alloc_fte(flow_act, match_value, index);
|
|
- if (IS_ERR(fte))
|
|
|
|
- return fte;
|
|
|
|
|
|
+ if (IS_ERR(fte)) {
|
|
|
|
+ ret = PTR_ERR(fte);
|
|
|
|
+ goto err_alloc;
|
|
|
|
+ }
|
|
|
|
+ ret = rhashtable_insert_fast(&fg->ftes_hash, &fte->hash, rhash_fte);
|
|
|
|
+ if (ret)
|
|
|
|
+ goto err_hash;
|
|
|
|
|
|
return fte;
|
|
return fte;
|
|
|
|
+
|
|
|
|
+err_hash:
|
|
|
|
+ kfree(fte);
|
|
|
|
+err_alloc:
|
|
|
|
+ ida_simple_remove(&ft->fte_allocator, index);
|
|
|
|
+ return ERR_PTR(ret);
|
|
}
|
|
}
|
|
|
|
|
|
static struct mlx5_flow_group *create_autogroup(struct mlx5_flow_table *ft,
|
|
static struct mlx5_flow_group *create_autogroup(struct mlx5_flow_table *ft,
|
|
@@ -1226,79 +1296,104 @@ static struct mlx5_flow_rule *find_flow_rule(struct fs_fte *fte,
|
|
return NULL;
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static bool check_conflicting_actions(u32 action1, u32 action2)
|
|
|
|
+{
|
|
|
|
+ u32 xored_actions = action1 ^ action2;
|
|
|
|
+
|
|
|
|
+ /* if one rule only wants to count, it's ok */
|
|
|
|
+ if (action1 == MLX5_FLOW_CONTEXT_ACTION_COUNT ||
|
|
|
|
+ action2 == MLX5_FLOW_CONTEXT_ACTION_COUNT)
|
|
|
|
+ return false;
|
|
|
|
+
|
|
|
|
+ if (xored_actions & (MLX5_FLOW_CONTEXT_ACTION_DROP |
|
|
|
|
+ MLX5_FLOW_CONTEXT_ACTION_ENCAP |
|
|
|
|
+ MLX5_FLOW_CONTEXT_ACTION_DECAP))
|
|
|
|
+ return true;
|
|
|
|
+
|
|
|
|
+ return false;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int check_conflicting_ftes(struct fs_fte *fte, const struct mlx5_flow_act *flow_act)
|
|
|
|
+{
|
|
|
|
+ if (check_conflicting_actions(flow_act->action, fte->action)) {
|
|
|
|
+ mlx5_core_warn(get_dev(&fte->node),
|
|
|
|
+ "Found two FTEs with conflicting actions\n");
|
|
|
|
+ return -EEXIST;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (fte->flow_tag != flow_act->flow_tag) {
|
|
|
|
+ mlx5_core_warn(get_dev(&fte->node),
|
|
|
|
+ "FTE flow tag %u already exists with different flow tag %u\n",
|
|
|
|
+ fte->flow_tag,
|
|
|
|
+ flow_act->flow_tag);
|
|
|
|
+ return -EEXIST;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
static struct mlx5_flow_handle *add_rule_fg(struct mlx5_flow_group *fg,
|
|
static struct mlx5_flow_handle *add_rule_fg(struct mlx5_flow_group *fg,
|
|
u32 *match_value,
|
|
u32 *match_value,
|
|
struct mlx5_flow_act *flow_act,
|
|
struct mlx5_flow_act *flow_act,
|
|
struct mlx5_flow_destination *dest,
|
|
struct mlx5_flow_destination *dest,
|
|
- int dest_num)
|
|
|
|
|
|
+ int dest_num,
|
|
|
|
+ struct fs_fte *fte)
|
|
{
|
|
{
|
|
struct mlx5_flow_handle *handle;
|
|
struct mlx5_flow_handle *handle;
|
|
struct mlx5_flow_table *ft;
|
|
struct mlx5_flow_table *ft;
|
|
- struct list_head *prev;
|
|
|
|
- struct fs_fte *fte;
|
|
|
|
int i;
|
|
int i;
|
|
|
|
|
|
- nested_lock_ref_node(&fg->node, FS_MUTEX_PARENT);
|
|
|
|
- fs_for_each_fte(fte, fg) {
|
|
|
|
|
|
+ if (fte) {
|
|
|
|
+ int old_action;
|
|
|
|
+ int ret;
|
|
|
|
+
|
|
nested_lock_ref_node(&fte->node, FS_MUTEX_CHILD);
|
|
nested_lock_ref_node(&fte->node, FS_MUTEX_CHILD);
|
|
- if (compare_match_value(&fg->mask, match_value, &fte->val) &&
|
|
|
|
- (flow_act->action & fte->action)) {
|
|
|
|
- int old_action = fte->action;
|
|
|
|
-
|
|
|
|
- if (fte->flow_tag != flow_act->flow_tag) {
|
|
|
|
- mlx5_core_warn(get_dev(&fte->node),
|
|
|
|
- "FTE flow tag %u already exists with different flow tag %u\n",
|
|
|
|
- fte->flow_tag,
|
|
|
|
- flow_act->flow_tag);
|
|
|
|
- handle = ERR_PTR(-EEXIST);
|
|
|
|
- goto unlock_fte;
|
|
|
|
- }
|
|
|
|
|
|
+ ret = check_conflicting_ftes(fte, flow_act);
|
|
|
|
+ if (ret) {
|
|
|
|
+ handle = ERR_PTR(ret);
|
|
|
|
+ goto unlock_fte;
|
|
|
|
+ }
|
|
|
|
|
|
- fte->action |= flow_act->action;
|
|
|
|
- handle = add_rule_fte(fte, fg, dest, dest_num,
|
|
|
|
- old_action != flow_act->action);
|
|
|
|
- if (IS_ERR(handle)) {
|
|
|
|
- fte->action = old_action;
|
|
|
|
- goto unlock_fte;
|
|
|
|
- } else {
|
|
|
|
- goto add_rules;
|
|
|
|
- }
|
|
|
|
|
|
+ old_action = fte->action;
|
|
|
|
+ fte->action |= flow_act->action;
|
|
|
|
+ handle = add_rule_fte(fte, fg, dest, dest_num,
|
|
|
|
+ old_action != flow_act->action);
|
|
|
|
+ if (IS_ERR(handle)) {
|
|
|
|
+ fte->action = old_action;
|
|
|
|
+ goto unlock_fte;
|
|
|
|
+ } else {
|
|
|
|
+ trace_mlx5_fs_set_fte(fte, false);
|
|
|
|
+ goto add_rules;
|
|
}
|
|
}
|
|
- unlock_ref_node(&fte->node);
|
|
|
|
}
|
|
}
|
|
fs_get_obj(ft, fg->node.parent);
|
|
fs_get_obj(ft, fg->node.parent);
|
|
- if (fg->num_ftes >= fg->max_ftes) {
|
|
|
|
- handle = ERR_PTR(-ENOSPC);
|
|
|
|
- goto unlock_fg;
|
|
|
|
- }
|
|
|
|
|
|
|
|
- fte = create_fte(fg, match_value, flow_act, &prev);
|
|
|
|
- if (IS_ERR(fte)) {
|
|
|
|
- handle = (void *)fte;
|
|
|
|
- goto unlock_fg;
|
|
|
|
- }
|
|
|
|
|
|
+ fte = create_fte(fg, match_value, flow_act);
|
|
|
|
+ if (IS_ERR(fte))
|
|
|
|
+ return (void *)fte;
|
|
tree_init_node(&fte->node, 0, del_fte);
|
|
tree_init_node(&fte->node, 0, del_fte);
|
|
nested_lock_ref_node(&fte->node, FS_MUTEX_CHILD);
|
|
nested_lock_ref_node(&fte->node, FS_MUTEX_CHILD);
|
|
handle = add_rule_fte(fte, fg, dest, dest_num, false);
|
|
handle = add_rule_fte(fte, fg, dest, dest_num, false);
|
|
if (IS_ERR(handle)) {
|
|
if (IS_ERR(handle)) {
|
|
unlock_ref_node(&fte->node);
|
|
unlock_ref_node(&fte->node);
|
|
|
|
+ destroy_fte(fte, fg);
|
|
kfree(fte);
|
|
kfree(fte);
|
|
- goto unlock_fg;
|
|
|
|
|
|
+ return handle;
|
|
}
|
|
}
|
|
|
|
|
|
- fg->num_ftes++;
|
|
|
|
-
|
|
|
|
tree_add_node(&fte->node, &fg->node);
|
|
tree_add_node(&fte->node, &fg->node);
|
|
- list_add(&fte->node.list, prev);
|
|
|
|
|
|
+ /* fte list isn't sorted */
|
|
|
|
+ list_add_tail(&fte->node.list, &fg->node.children);
|
|
|
|
+ trace_mlx5_fs_set_fte(fte, true);
|
|
add_rules:
|
|
add_rules:
|
|
for (i = 0; i < handle->num_rules; i++) {
|
|
for (i = 0; i < handle->num_rules; i++) {
|
|
- if (atomic_read(&handle->rule[i]->node.refcount) == 1)
|
|
|
|
|
|
+ if (atomic_read(&handle->rule[i]->node.refcount) == 1) {
|
|
tree_add_node(&handle->rule[i]->node, &fte->node);
|
|
tree_add_node(&handle->rule[i]->node, &fte->node);
|
|
|
|
+ trace_mlx5_fs_add_rule(handle->rule[i]);
|
|
|
|
+ }
|
|
}
|
|
}
|
|
unlock_fte:
|
|
unlock_fte:
|
|
unlock_ref_node(&fte->node);
|
|
unlock_ref_node(&fte->node);
|
|
-unlock_fg:
|
|
|
|
- unlock_ref_node(&fg->node);
|
|
|
|
return handle;
|
|
return handle;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -1346,6 +1441,96 @@ static bool dest_is_valid(struct mlx5_flow_destination *dest,
|
|
return true;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static struct mlx5_flow_handle *
|
|
|
|
+try_add_to_existing_fg(struct mlx5_flow_table *ft,
|
|
|
|
+ struct mlx5_flow_spec *spec,
|
|
|
|
+ struct mlx5_flow_act *flow_act,
|
|
|
|
+ struct mlx5_flow_destination *dest,
|
|
|
|
+ int dest_num)
|
|
|
|
+{
|
|
|
|
+ struct mlx5_flow_group *g;
|
|
|
|
+ struct mlx5_flow_handle *rule = ERR_PTR(-ENOENT);
|
|
|
|
+ struct rhlist_head *tmp, *list;
|
|
|
|
+ struct match_list {
|
|
|
|
+ struct list_head list;
|
|
|
|
+ struct mlx5_flow_group *g;
|
|
|
|
+ } match_list, *iter;
|
|
|
|
+ LIST_HEAD(match_head);
|
|
|
|
+
|
|
|
|
+ rcu_read_lock();
|
|
|
|
+ /* Collect all fgs which has a matching match_criteria */
|
|
|
|
+ list = rhltable_lookup(&ft->fgs_hash, spec, rhash_fg);
|
|
|
|
+ rhl_for_each_entry_rcu(g, tmp, list, hash) {
|
|
|
|
+ struct match_list *curr_match;
|
|
|
|
+
|
|
|
|
+ if (likely(list_empty(&match_head))) {
|
|
|
|
+ match_list.g = g;
|
|
|
|
+ list_add_tail(&match_list.list, &match_head);
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
+ curr_match = kmalloc(sizeof(*curr_match), GFP_ATOMIC);
|
|
|
|
+
|
|
|
|
+ if (!curr_match) {
|
|
|
|
+ rcu_read_unlock();
|
|
|
|
+ rule = ERR_PTR(-ENOMEM);
|
|
|
|
+ goto free_list;
|
|
|
|
+ }
|
|
|
|
+ curr_match->g = g;
|
|
|
|
+ list_add_tail(&curr_match->list, &match_head);
|
|
|
|
+ }
|
|
|
|
+ rcu_read_unlock();
|
|
|
|
+
|
|
|
|
+ /* Try to find a fg that already contains a matching fte */
|
|
|
|
+ list_for_each_entry(iter, &match_head, list) {
|
|
|
|
+ struct fs_fte *fte;
|
|
|
|
+
|
|
|
|
+ g = iter->g;
|
|
|
|
+ nested_lock_ref_node(&g->node, FS_MUTEX_PARENT);
|
|
|
|
+ fte = rhashtable_lookup_fast(&g->ftes_hash, spec->match_value,
|
|
|
|
+ rhash_fte);
|
|
|
|
+ if (fte) {
|
|
|
|
+ rule = add_rule_fg(g, spec->match_value,
|
|
|
|
+ flow_act, dest, dest_num, fte);
|
|
|
|
+ unlock_ref_node(&g->node);
|
|
|
|
+ goto free_list;
|
|
|
|
+ }
|
|
|
|
+ unlock_ref_node(&g->node);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* No group with matching fte found. Try to add a new fte to any
|
|
|
|
+ * matching fg.
|
|
|
|
+ */
|
|
|
|
+ list_for_each_entry(iter, &match_head, list) {
|
|
|
|
+ g = iter->g;
|
|
|
|
+
|
|
|
|
+ nested_lock_ref_node(&g->node, FS_MUTEX_PARENT);
|
|
|
|
+ rule = add_rule_fg(g, spec->match_value,
|
|
|
|
+ flow_act, dest, dest_num, NULL);
|
|
|
|
+ if (!IS_ERR(rule) || PTR_ERR(rule) != -ENOSPC) {
|
|
|
|
+ unlock_ref_node(&g->node);
|
|
|
|
+ goto free_list;
|
|
|
|
+ }
|
|
|
|
+ unlock_ref_node(&g->node);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+free_list:
|
|
|
|
+ if (!list_empty(&match_head)) {
|
|
|
|
+ struct match_list *match_tmp;
|
|
|
|
+
|
|
|
|
+ /* The most common case is having one FG. Since we want to
|
|
|
|
+ * optimize this case, we save the first on the stack.
|
|
|
|
+ * Therefore, no need to free it.
|
|
|
|
+ */
|
|
|
|
+ list_del(&list_first_entry(&match_head, typeof(*iter), list)->list);
|
|
|
|
+ list_for_each_entry_safe(iter, match_tmp, &match_head, list) {
|
|
|
|
+ list_del(&iter->list);
|
|
|
|
+ kfree(iter);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return rule;
|
|
|
|
+}
|
|
|
|
+
|
|
static struct mlx5_flow_handle *
|
|
static struct mlx5_flow_handle *
|
|
_mlx5_add_flow_rules(struct mlx5_flow_table *ft,
|
|
_mlx5_add_flow_rules(struct mlx5_flow_table *ft,
|
|
struct mlx5_flow_spec *spec,
|
|
struct mlx5_flow_spec *spec,
|
|
@@ -1358,22 +1543,18 @@ _mlx5_add_flow_rules(struct mlx5_flow_table *ft,
|
|
struct mlx5_flow_handle *rule;
|
|
struct mlx5_flow_handle *rule;
|
|
int i;
|
|
int i;
|
|
|
|
|
|
|
|
+ if (!check_valid_spec(spec))
|
|
|
|
+ return ERR_PTR(-EINVAL);
|
|
|
|
+
|
|
for (i = 0; i < dest_num; i++) {
|
|
for (i = 0; i < dest_num; i++) {
|
|
if (!dest_is_valid(&dest[i], flow_act->action, ft))
|
|
if (!dest_is_valid(&dest[i], flow_act->action, ft))
|
|
return ERR_PTR(-EINVAL);
|
|
return ERR_PTR(-EINVAL);
|
|
}
|
|
}
|
|
|
|
|
|
nested_lock_ref_node(&ft->node, FS_MUTEX_GRANDPARENT);
|
|
nested_lock_ref_node(&ft->node, FS_MUTEX_GRANDPARENT);
|
|
- fs_for_each_fg(g, ft)
|
|
|
|
- if (compare_match_criteria(g->mask.match_criteria_enable,
|
|
|
|
- spec->match_criteria_enable,
|
|
|
|
- g->mask.match_criteria,
|
|
|
|
- spec->match_criteria)) {
|
|
|
|
- rule = add_rule_fg(g, spec->match_value,
|
|
|
|
- flow_act, dest, dest_num);
|
|
|
|
- if (!IS_ERR(rule) || PTR_ERR(rule) != -ENOSPC)
|
|
|
|
- goto unlock;
|
|
|
|
- }
|
|
|
|
|
|
+ rule = try_add_to_existing_fg(ft, spec, flow_act, dest, dest_num);
|
|
|
|
+ if (!IS_ERR(rule))
|
|
|
|
+ goto unlock;
|
|
|
|
|
|
g = create_autogroup(ft, spec->match_criteria_enable,
|
|
g = create_autogroup(ft, spec->match_criteria_enable,
|
|
spec->match_criteria);
|
|
spec->match_criteria);
|
|
@@ -1382,7 +1563,8 @@ _mlx5_add_flow_rules(struct mlx5_flow_table *ft,
|
|
goto unlock;
|
|
goto unlock;
|
|
}
|
|
}
|
|
|
|
|
|
- rule = add_rule_fg(g, spec->match_value, flow_act, dest, dest_num);
|
|
|
|
|
|
+ rule = add_rule_fg(g, spec->match_value, flow_act, dest,
|
|
|
|
+ dest_num, NULL);
|
|
if (IS_ERR(rule)) {
|
|
if (IS_ERR(rule)) {
|
|
/* Remove assumes refcount > 0 and autogroup creates a group
|
|
/* Remove assumes refcount > 0 and autogroup creates a group
|
|
* with a refcount = 0.
|
|
* with a refcount = 0.
|