Commit b3638e1a authored by Maor Gottlieb's avatar Maor Gottlieb Committed by Doug Ledford

net/mlx5_core: Introduce forward to next priority action

Add support to create flow rule that forward packets
to the first flow table in the next priority (next priority
could be the first priority in the next namespace or the
next priority in the same namespace).
This feature could be used for DONT_TRAP rules or rules
that only want to mark the packet with flow tag.

In order to do it optimally, each flow table has list
of all rules that point to this flow table,
when a flow table is destroyed/created, we update the list
head correspondingly.

This kind of rule is created when destination is NULL and
action is MLX5_FLOW_CONTEXT_ACTION_FWD_NEXT_PRIO.
Signed-off-by: default avatarMaor Gottlieb <maorg@mellanox.com>
Reviewed-by: default avatarMatan Barak <matanb@mellanox.com>
Signed-off-by: default avatarDoug Ledford <dledford@redhat.com>
parent 153fefbf
......@@ -201,8 +201,10 @@ static void tree_put_node(struct fs_node *node)
static int tree_remove_node(struct fs_node *node)
{
if (atomic_read(&node->refcount) > 1)
return -EPERM;
if (atomic_read(&node->refcount) > 1) {
atomic_dec(&node->refcount);
return -EEXIST;
}
tree_put_node(node);
return 0;
}
......@@ -365,6 +367,11 @@ static void del_rule(struct fs_node *node)
memcpy(match_value, fte->val, sizeof(fte->val));
fs_get_obj(ft, fg->node.parent);
list_del(&rule->node.list);
if (rule->sw_action == MLX5_FLOW_CONTEXT_ACTION_FWD_NEXT_PRIO) {
mutex_lock(&rule->dest_attr.ft->lock);
list_del(&rule->next_ft);
mutex_unlock(&rule->dest_attr.ft->lock);
}
fte->dests_size--;
if (fte->dests_size) {
err = mlx5_cmd_update_fte(dev, ft,
......@@ -470,6 +477,8 @@ static struct mlx5_flow_table *alloc_flow_table(int level, int max_fte,
ft->node.type = FS_TYPE_FLOW_TABLE;
ft->type = table_type;
ft->max_fte = max_fte;
INIT_LIST_HEAD(&ft->fwd_rules);
mutex_init(&ft->lock);
return ft;
}
......@@ -606,9 +615,63 @@ 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)
{
struct mlx5_flow_table *ft;
struct mlx5_flow_group *fg;
struct fs_fte *fte;
int err = 0;
fs_get_obj(fte, rule->node.parent);
if (!(fte->action & MLX5_FLOW_CONTEXT_ACTION_FWD_DEST))
return -EINVAL;
lock_ref_node(&fte->node);
fs_get_obj(fg, fte->node.parent);
fs_get_obj(ft, fg->node.parent);
memcpy(&rule->dest_attr, dest, sizeof(*dest));
err = mlx5_cmd_update_fte(get_dev(&ft->node),
ft, fg->id, fte);
unlock_ref_node(&fte->node);
return err;
}
/* Modify/set FWD rules that point on old_next_ft to point on new_next_ft */
static int connect_fwd_rules(struct mlx5_core_dev *dev,
struct mlx5_flow_table *new_next_ft,
struct mlx5_flow_table *old_next_ft)
{
struct mlx5_flow_destination dest;
struct mlx5_flow_rule *iter;
int err = 0;
/* new_next_ft and old_next_ft could be NULL only
* when we create/destroy the anchor flow table.
*/
if (!new_next_ft || !old_next_ft)
return 0;
dest.type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE;
dest.ft = new_next_ft;
mutex_lock(&old_next_ft->lock);
list_splice_init(&old_next_ft->fwd_rules, &new_next_ft->fwd_rules);
mutex_unlock(&old_next_ft->lock);
list_for_each_entry(iter, &new_next_ft->fwd_rules, next_ft) {
err = mlx5_modify_rule_destination(iter, &dest);
if (err)
pr_err("mlx5_core: failed to modify rule to point on flow table %d\n",
new_next_ft->id);
}
return 0;
}
static int connect_flow_table(struct mlx5_core_dev *dev, struct mlx5_flow_table *ft,
struct fs_prio *prio)
{
struct mlx5_flow_table *next_ft;
int err = 0;
/* Connect_prev_fts and update_root_ft_create are mutually exclusive */
......@@ -617,6 +680,11 @@ static int connect_flow_table(struct mlx5_core_dev *dev, struct mlx5_flow_table
err = connect_prev_fts(dev, ft, prio);
if (err)
return err;
next_ft = find_next_chained_ft(prio);
err = connect_fwd_rules(dev, ft, next_ft);
if (err)
return err;
}
if (MLX5_CAP_FLOWTABLE(dev,
......@@ -767,6 +835,7 @@ static struct mlx5_flow_rule *alloc_rule(struct mlx5_flow_destination *dest)
if (!rule)
return NULL;
INIT_LIST_HEAD(&rule->next_ft);
rule->node.type = FS_TYPE_FLOW_DEST;
memcpy(&rule->dest_attr, dest, sizeof(*dest));
......@@ -787,8 +856,13 @@ static struct mlx5_flow_rule *add_rule_fte(struct fs_fte *fte,
return ERR_PTR(-ENOMEM);
fs_get_obj(ft, fg->node.parent);
/* Add dest to dests list- added as first element after the head */
/* Add dest to dests list- we need flow tables to be in the
* end of the list for forward to next prio rules.
*/
tree_init_node(&rule->node, 1, del_rule);
if (dest && dest->type != MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE)
list_add(&rule->node.list, &fte->node.children);
else
list_add_tail(&rule->node.list, &fte->node.children);
fte->dests_size++;
if (fte->dests_size == 1)
......@@ -908,6 +982,25 @@ static struct mlx5_flow_group *create_autogroup(struct mlx5_flow_table *ft,
return fg;
}
static struct mlx5_flow_rule *find_flow_rule(struct fs_fte *fte,
struct mlx5_flow_destination *dest)
{
struct mlx5_flow_rule *rule;
list_for_each_entry(rule, &fte->node.children, node.list) {
if (rule->dest_attr.type == dest->type) {
if ((dest->type == MLX5_FLOW_DESTINATION_TYPE_VPORT &&
dest->vport_num == rule->dest_attr.vport_num) ||
(dest->type == MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE &&
dest->ft == rule->dest_attr.ft) ||
(dest->type == MLX5_FLOW_DESTINATION_TYPE_TIR &&
dest->tir_num == rule->dest_attr.tir_num))
return rule;
}
}
return NULL;
}
static struct mlx5_flow_rule *add_rule_fg(struct mlx5_flow_group *fg,
u32 *match_value,
u8 action,
......@@ -924,6 +1017,13 @@ static struct mlx5_flow_rule *add_rule_fg(struct mlx5_flow_group *fg,
nested_lock_ref_node(&fte->node, FS_MUTEX_CHILD);
if (compare_match_value(&fg->mask, match_value, &fte->val) &&
action == fte->action && flow_tag == fte->flow_tag) {
rule = find_flow_rule(fte, dest);
if (rule) {
atomic_inc(&rule->node.refcount);
unlock_ref_node(&fte->node);
unlock_ref_node(&fg->node);
return rule;
}
rule = add_rule_fte(fte, fg, dest);
unlock_ref_node(&fte->node);
if (IS_ERR(rule))
......@@ -989,8 +1089,8 @@ static struct mlx5_flow_rule *add_rule_to_auto_fg(struct mlx5_flow_table *ft,
return rule;
}
struct mlx5_flow_rule *
mlx5_add_flow_rule(struct mlx5_flow_table *ft,
static struct mlx5_flow_rule *
_mlx5_add_flow_rule(struct mlx5_flow_table *ft,
u8 match_criteria_enable,
u32 *match_criteria,
u32 *match_value,
......@@ -1019,6 +1119,63 @@ mlx5_add_flow_rule(struct mlx5_flow_table *ft,
unlock_ref_node(&ft->node);
return rule;
}
static bool fwd_next_prio_supported(struct mlx5_flow_table *ft)
{
return ((ft->type == FS_FT_NIC_RX) &&
(MLX5_CAP_FLOWTABLE(get_dev(&ft->node), nic_rx_multi_path_tirs)));
}
struct mlx5_flow_rule *
mlx5_add_flow_rule(struct mlx5_flow_table *ft,
u8 match_criteria_enable,
u32 *match_criteria,
u32 *match_value,
u32 action,
u32 flow_tag,
struct mlx5_flow_destination *dest)
{
struct mlx5_flow_root_namespace *root = find_root(&ft->node);
struct mlx5_flow_destination gen_dest;
struct mlx5_flow_table *next_ft = NULL;
struct mlx5_flow_rule *rule = NULL;
u32 sw_action = action;
struct fs_prio *prio;
fs_get_obj(prio, ft->node.parent);
if (action == MLX5_FLOW_CONTEXT_ACTION_FWD_NEXT_PRIO) {
if (!fwd_next_prio_supported(ft))
return ERR_PTR(-EOPNOTSUPP);
if (dest)
return ERR_PTR(-EINVAL);
mutex_lock(&root->chain_lock);
next_ft = find_next_chained_ft(prio);
if (next_ft) {
gen_dest.type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE;
gen_dest.ft = next_ft;
dest = &gen_dest;
action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
} else {
mutex_unlock(&root->chain_lock);
return ERR_PTR(-EOPNOTSUPP);
}
}
rule = _mlx5_add_flow_rule(ft, match_criteria_enable, match_criteria,
match_value, action, flow_tag, dest);
if (sw_action == MLX5_FLOW_CONTEXT_ACTION_FWD_NEXT_PRIO) {
if (!IS_ERR_OR_NULL(rule) &&
(list_empty(&rule->next_ft))) {
mutex_lock(&next_ft->lock);
list_add(&rule->next_ft, &next_ft->fwd_rules);
mutex_unlock(&next_ft->lock);
rule->sw_action = MLX5_FLOW_CONTEXT_ACTION_FWD_NEXT_PRIO;
}
mutex_unlock(&root->chain_lock);
}
return rule;
}
EXPORT_SYMBOL(mlx5_add_flow_rule);
void mlx5_del_flow_rule(struct mlx5_flow_rule *rule)
......@@ -1082,6 +1239,10 @@ static int disconnect_flow_table(struct mlx5_flow_table *ft)
return 0;
next_ft = find_next_chained_ft(prio);
err = connect_fwd_rules(dev, next_ft, ft);
if (err)
return err;
err = connect_prev_fts(dev, next_ft, prio);
if (err)
mlx5_core_warn(dev, "Failed to disconnect flow table %d\n",
......
......@@ -68,6 +68,11 @@ struct fs_node {
struct mlx5_flow_rule {
struct fs_node node;
struct mlx5_flow_destination dest_attr;
/* next_ft should be accessed under chain_lock and only of
* destination type is FWD_NEXT_fT.
*/
struct list_head next_ft;
u32 sw_action;
};
/* Type of children is mlx5_flow_group */
......@@ -82,6 +87,10 @@ struct mlx5_flow_table {
unsigned int required_groups;
unsigned int num_groups;
} autogroup;
/* Protect fwd_rules */
struct mutex lock;
/* FWD rules that point on this flow table */
struct list_head fwd_rules;
};
/* Type of children is mlx5_flow_rule */
......
......@@ -38,6 +38,10 @@
#define MLX5_FS_DEFAULT_FLOW_TAG 0x0
enum {
MLX5_FLOW_CONTEXT_ACTION_FWD_NEXT_PRIO = 1 << 16,
};
#define LEFTOVERS_RULE_NUM 2
static inline void build_leftovers_ft_param(int *priority,
int *n_ent,
......
......@@ -458,7 +458,8 @@ struct mlx5_ifc_ads_bits {
};
struct mlx5_ifc_flow_table_nic_cap_bits {
u8 reserved_at_0[0x200];
u8 nic_rx_multi_path_tirs[0x1];
u8 reserved_at_1[0x1ff];
struct mlx5_ifc_flow_table_prop_layout_bits flow_table_properties_nic_receive;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment