Merge branch 'mlx5-Support-tc-police-jump-conform-exceed-attribute'

Saeed Mahameed says:

====================
Support tc police jump conform-exceed attribute

The tc police action conform-exceed option defines how to handle
packets which exceed or conform to the configured bandwidth limit.
One of the possible conform-exceed values is jump, which skips over
a specified number of actions.
This series adds support for conform-exceed jump action.

The series adds platform support for branching actions by providing
true/false flow attributes to the branching action.
This is necessary for supporting police jump, as each branch may
execute a different action list.

The first five patches are preparation patches:
- Patches 1 and 2 add support for actions with no destinations (e.g. drop)
- Patch 3 refactor the code for subsequent function reuse
- Patch 4 defines an abstract way for identifying terminating actions
- Patch 5 updates action list validations logic considering branching actions

The following three patches introduce an interface for abstracting branching
actions:
- Patch 6 introduces an abstract api for defining branching actions
- Patch 7 generically instantiates the branching flow attributes using
  the abstract API

Patch 8 adds the platform support for jump actions, by executing the following
sequence:
  a. Store the jumping flow attr
  b. Identify the jump target action while iterating the actions list.
  c. Instantiate a new flow attribute after the jump target action.
     This is the flow attribute that the branching action should jump to.
  d. Set the target post action id on:
    d.1. The jumping attribute, thus realizing the jump functionality.
    d.2. The attribute preceding the target jump attr, if not terminating.

The next patches apply the platform's branching attributes to the police
action:
- Patch 9 is a refactor patch
- Patch 10 initializes the post meter table with the red/green flow attributes,
           as were initialized by the platform
- Patch 11 enables the offload of meter actions using jump conform-exceed
           value.
====================

Link: https://lore.kernel.org/all/20221203221337.29267-1-saeed@kernel.org/
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2022-12-08 13:04:57 -08:00
commit ddda6326ee
17 changed files with 496 additions and 175 deletions

View file

@ -28,4 +28,5 @@ tc_act_parse_accept(struct mlx5e_tc_act_parse_state *parse_state,
struct mlx5e_tc_act mlx5e_tc_act_accept = {
.can_offload = tc_act_can_offload_accept,
.parse_action = tc_act_parse_accept,
.is_terminating_action = true,
};

View file

@ -11,7 +11,7 @@ static struct mlx5e_tc_act *tc_acts_fdb[NUM_FLOW_ACTIONS] = {
[FLOW_ACTION_DROP] = &mlx5e_tc_act_drop,
[FLOW_ACTION_TRAP] = &mlx5e_tc_act_trap,
[FLOW_ACTION_GOTO] = &mlx5e_tc_act_goto,
[FLOW_ACTION_REDIRECT] = &mlx5e_tc_act_mirred,
[FLOW_ACTION_REDIRECT] = &mlx5e_tc_act_redirect,
[FLOW_ACTION_MIRRED] = &mlx5e_tc_act_mirred,
[FLOW_ACTION_REDIRECT_INGRESS] = &mlx5e_tc_act_redirect_ingress,
[FLOW_ACTION_VLAN_PUSH] = &mlx5e_tc_act_vlan,

View file

@ -32,6 +32,11 @@ struct mlx5e_tc_act_parse_state {
struct mlx5_tc_ct_priv *ct_priv;
};
struct mlx5e_tc_act_branch_ctrl {
enum flow_action_id act_id;
u32 extval;
};
struct mlx5e_tc_act {
bool (*can_offload)(struct mlx5e_tc_act_parse_state *parse_state,
const struct flow_action_entry *act,
@ -60,6 +65,12 @@ struct mlx5e_tc_act {
int (*stats_action)(struct mlx5e_priv *priv,
struct flow_offload_action *fl_act);
bool (*get_branch_ctrl)(const struct flow_action_entry *act,
struct mlx5e_tc_act_branch_ctrl *cond_true,
struct mlx5e_tc_act_branch_ctrl *cond_false);
bool is_terminating_action;
};
struct mlx5e_tc_flow_action {
@ -81,6 +92,7 @@ extern struct mlx5e_tc_act mlx5e_tc_act_vlan_mangle;
extern struct mlx5e_tc_act mlx5e_tc_act_mpls_push;
extern struct mlx5e_tc_act mlx5e_tc_act_mpls_pop;
extern struct mlx5e_tc_act mlx5e_tc_act_mirred;
extern struct mlx5e_tc_act mlx5e_tc_act_redirect;
extern struct mlx5e_tc_act mlx5e_tc_act_mirred_nic;
extern struct mlx5e_tc_act mlx5e_tc_act_ct;
extern struct mlx5e_tc_act mlx5e_tc_act_sample;

View file

@ -27,4 +27,5 @@ tc_act_parse_drop(struct mlx5e_tc_act_parse_state *parse_state,
struct mlx5e_tc_act mlx5e_tc_act_drop = {
.can_offload = tc_act_can_offload_drop,
.parse_action = tc_act_parse_drop,
.is_terminating_action = true,
};

View file

@ -121,4 +121,5 @@ struct mlx5e_tc_act mlx5e_tc_act_goto = {
.can_offload = tc_act_can_offload_goto,
.parse_action = tc_act_parse_goto,
.post_parse = tc_act_post_parse_goto,
.is_terminating_action = true,
};

View file

@ -334,4 +334,11 @@ tc_act_parse_mirred(struct mlx5e_tc_act_parse_state *parse_state,
struct mlx5e_tc_act mlx5e_tc_act_mirred = {
.can_offload = tc_act_can_offload_mirred,
.parse_action = tc_act_parse_mirred,
.is_terminating_action = false,
};
struct mlx5e_tc_act mlx5e_tc_act_redirect = {
.can_offload = tc_act_can_offload_mirred,
.parse_action = tc_act_parse_mirred,
.is_terminating_action = true,
};

View file

@ -48,4 +48,5 @@ tc_act_parse_mirred_nic(struct mlx5e_tc_act_parse_state *parse_state,
struct mlx5e_tc_act mlx5e_tc_act_mirred_nic = {
.can_offload = tc_act_can_offload_mirred_nic,
.parse_action = tc_act_parse_mirred_nic,
.is_terminating_action = true,
};

View file

@ -4,20 +4,54 @@
#include "act.h"
#include "en/tc_priv.h"
static bool police_act_validate_control(enum flow_action_id act_id,
struct netlink_ext_ack *extack)
{
if (act_id != FLOW_ACTION_PIPE &&
act_id != FLOW_ACTION_ACCEPT &&
act_id != FLOW_ACTION_JUMP &&
act_id != FLOW_ACTION_DROP) {
NL_SET_ERR_MSG_MOD(extack,
"Offload not supported when conform-exceed action is not pipe, ok, jump or drop");
return false;
}
return true;
}
static int police_act_validate(const struct flow_action_entry *act,
struct netlink_ext_ack *extack)
{
if (!police_act_validate_control(act->police.exceed.act_id, extack) ||
!police_act_validate_control(act->police.notexceed.act_id, extack))
return -EOPNOTSUPP;
if (act->police.peakrate_bytes_ps ||
act->police.avrate || act->police.overhead) {
NL_SET_ERR_MSG_MOD(extack,
"Offload not supported when peakrate/avrate/overhead is configured");
return -EOPNOTSUPP;
}
if (act->police.rate_pkt_ps) {
NL_SET_ERR_MSG_MOD(extack,
"QoS offload not support packets per second");
return -EOPNOTSUPP;
}
return 0;
}
static bool
tc_act_can_offload_police(struct mlx5e_tc_act_parse_state *parse_state,
const struct flow_action_entry *act,
int act_index,
struct mlx5_flow_attr *attr)
{
if (act->police.notexceed.act_id != FLOW_ACTION_PIPE &&
act->police.notexceed.act_id != FLOW_ACTION_ACCEPT) {
NL_SET_ERR_MSG_MOD(parse_state->extack,
"Offload not supported when conform action is not pipe or ok");
return false;
}
if (mlx5e_policer_validate(parse_state->flow_action, act,
parse_state->extack))
int err;
err = police_act_validate(act, parse_state->extack);
if (err)
return false;
return !!mlx5e_get_flow_meters(parse_state->flow->priv->mdev);
@ -79,7 +113,7 @@ tc_act_police_offload(struct mlx5e_priv *priv,
struct mlx5e_flow_meter_handle *meter;
int err = 0;
err = mlx5e_policer_validate(&fl_act->action, act, fl_act->extack);
err = police_act_validate(act, fl_act->extack);
if (err)
return err;
@ -147,6 +181,19 @@ tc_act_police_stats(struct mlx5e_priv *priv,
return 0;
}
static bool
tc_act_police_get_branch_ctrl(const struct flow_action_entry *act,
struct mlx5e_tc_act_branch_ctrl *cond_true,
struct mlx5e_tc_act_branch_ctrl *cond_false)
{
cond_true->act_id = act->police.notexceed.act_id;
cond_true->extval = act->police.notexceed.extval;
cond_false->act_id = act->police.exceed.act_id;
cond_false->extval = act->police.exceed.extval;
return true;
}
struct mlx5e_tc_act mlx5e_tc_act_police = {
.can_offload = tc_act_can_offload_police,
.parse_action = tc_act_parse_police,
@ -154,4 +201,5 @@ struct mlx5e_tc_act mlx5e_tc_act_police = {
.offload_action = tc_act_police_offload,
.destroy_action = tc_act_police_destroy,
.stats_action = tc_act_police_stats,
.get_branch_ctrl = tc_act_police_get_branch_ctrl,
};

View file

@ -257,16 +257,16 @@ __mlx5e_flow_meter_alloc(struct mlx5e_flow_meters *flow_meters)
counter = mlx5_fc_create(mdev, true);
if (IS_ERR(counter)) {
err = PTR_ERR(counter);
goto err_red_counter;
goto err_drop_counter;
}
meter->red_counter = counter;
meter->drop_counter = counter;
counter = mlx5_fc_create(mdev, true);
if (IS_ERR(counter)) {
err = PTR_ERR(counter);
goto err_green_counter;
goto err_act_counter;
}
meter->green_counter = counter;
meter->act_counter = counter;
meters_obj = list_first_entry_or_null(&flow_meters->partial_list,
struct mlx5e_flow_meter_aso_obj,
@ -313,10 +313,10 @@ __mlx5e_flow_meter_alloc(struct mlx5e_flow_meters *flow_meters)
err_mem:
mlx5e_flow_meter_destroy_aso_obj(mdev, id);
err_create:
mlx5_fc_destroy(mdev, meter->green_counter);
err_green_counter:
mlx5_fc_destroy(mdev, meter->red_counter);
err_red_counter:
mlx5_fc_destroy(mdev, meter->act_counter);
err_act_counter:
mlx5_fc_destroy(mdev, meter->drop_counter);
err_drop_counter:
kfree(meter);
return ERR_PTR(err);
}
@ -329,8 +329,8 @@ __mlx5e_flow_meter_free(struct mlx5e_flow_meter_handle *meter)
struct mlx5e_flow_meter_aso_obj *meters_obj;
int n, pos;
mlx5_fc_destroy(mdev, meter->green_counter);
mlx5_fc_destroy(mdev, meter->red_counter);
mlx5_fc_destroy(mdev, meter->act_counter);
mlx5_fc_destroy(mdev, meter->drop_counter);
meters_obj = meter->meters_obj;
pos = (meter->obj_id - meters_obj->base_id) * 2 + meter->idx;
@ -575,8 +575,8 @@ mlx5e_tc_meter_get_stats(struct mlx5e_flow_meter_handle *meter,
u64 bytes1, packets1, lastuse1;
u64 bytes2, packets2, lastuse2;
mlx5_fc_query_cached(meter->green_counter, &bytes1, &packets1, &lastuse1);
mlx5_fc_query_cached(meter->red_counter, &bytes2, &packets2, &lastuse2);
mlx5_fc_query_cached(meter->act_counter, &bytes1, &packets1, &lastuse1);
mlx5_fc_query_cached(meter->drop_counter, &bytes2, &packets2, &lastuse2);
*bytes = bytes1 + bytes2;
*packets = packets1 + packets2;

View file

@ -32,8 +32,8 @@ struct mlx5e_flow_meter_handle {
struct hlist_node hlist;
struct mlx5e_flow_meter_params params;
struct mlx5_fc *green_counter;
struct mlx5_fc *red_counter;
struct mlx5_fc *act_counter;
struct mlx5_fc *drop_counter;
};
struct mlx5e_meter_attr {

View file

@ -11,8 +11,10 @@
struct mlx5e_post_meter_priv {
struct mlx5_flow_table *ft;
struct mlx5_flow_group *fg;
struct mlx5_flow_handle *fwd_green_rule;
struct mlx5_flow_handle *drop_red_rule;
struct mlx5_flow_handle *green_rule;
struct mlx5_flow_attr *green_attr;
struct mlx5_flow_handle *red_rule;
struct mlx5_flow_attr *red_attr;
};
struct mlx5_flow_table *
@ -81,15 +83,48 @@ mlx5e_post_meter_fg_create(struct mlx5e_priv *priv,
return err;
}
static struct mlx5_flow_handle *
mlx5e_post_meter_add_rule(struct mlx5e_priv *priv,
struct mlx5e_post_meter_priv *post_meter,
struct mlx5_flow_spec *spec,
struct mlx5_flow_attr *attr,
struct mlx5_fc *act_counter,
struct mlx5_fc *drop_counter)
{
struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
struct mlx5_flow_handle *ret;
attr->action |= MLX5_FLOW_CONTEXT_ACTION_COUNT;
if (attr->action & MLX5_FLOW_CONTEXT_ACTION_DROP)
attr->counter = drop_counter;
else
attr->counter = act_counter;
attr->ft = post_meter->ft;
attr->flags |= MLX5_ATTR_FLAG_NO_IN_PORT;
attr->outer_match_level = MLX5_MATCH_NONE;
attr->chain = 0;
attr->prio = 0;
ret = mlx5_eswitch_add_offloaded_rule(esw, spec, attr);
/* We did not create the counter, so we can't delete it.
* Avoid freeing the counter when the attr is deleted in free_branching_attr
*/
attr->action &= ~MLX5_FLOW_CONTEXT_ACTION_COUNT;
return ret;
}
static int
mlx5e_post_meter_rules_create(struct mlx5e_priv *priv,
struct mlx5e_post_meter_priv *post_meter,
struct mlx5e_post_act *post_act,
struct mlx5_fc *green_counter,
struct mlx5_fc *red_counter)
struct mlx5_fc *act_counter,
struct mlx5_fc *drop_counter,
struct mlx5_flow_attr *green_attr,
struct mlx5_flow_attr *red_attr)
{
struct mlx5_flow_destination dest[2] = {};
struct mlx5_flow_act flow_act = {};
struct mlx5_flow_handle *rule;
struct mlx5_flow_spec *spec;
int err;
@ -100,52 +135,45 @@ mlx5e_post_meter_rules_create(struct mlx5e_priv *priv,
mlx5e_tc_match_to_reg_match(spec, PACKET_COLOR_TO_REG,
MLX5_FLOW_METER_COLOR_RED, MLX5_PACKET_COLOR_MASK);
flow_act.action = MLX5_FLOW_CONTEXT_ACTION_DROP |
MLX5_FLOW_CONTEXT_ACTION_COUNT;
flow_act.flags |= FLOW_ACT_IGNORE_FLOW_LEVEL;
dest[0].type = MLX5_FLOW_DESTINATION_TYPE_COUNTER;
dest[0].counter_id = mlx5_fc_id(red_counter);
rule = mlx5_add_flow_rules(post_meter->ft, spec, &flow_act, dest, 1);
rule = mlx5e_post_meter_add_rule(priv, post_meter, spec, red_attr,
act_counter, drop_counter);
if (IS_ERR(rule)) {
mlx5_core_warn(priv->mdev, "Failed to create post_meter flow drop rule\n");
mlx5_core_warn(priv->mdev, "Failed to create post_meter exceed rule\n");
err = PTR_ERR(rule);
goto err_red;
}
post_meter->drop_red_rule = rule;
post_meter->red_rule = rule;
post_meter->red_attr = red_attr;
mlx5e_tc_match_to_reg_match(spec, PACKET_COLOR_TO_REG,
MLX5_FLOW_METER_COLOR_GREEN, MLX5_PACKET_COLOR_MASK);
flow_act.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST |
MLX5_FLOW_CONTEXT_ACTION_COUNT;
dest[0].type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE;
dest[0].ft = mlx5e_tc_post_act_get_ft(post_act);
dest[1].type = MLX5_FLOW_DESTINATION_TYPE_COUNTER;
dest[1].counter_id = mlx5_fc_id(green_counter);
rule = mlx5_add_flow_rules(post_meter->ft, spec, &flow_act, dest, 2);
rule = mlx5e_post_meter_add_rule(priv, post_meter, spec, green_attr,
act_counter, drop_counter);
if (IS_ERR(rule)) {
mlx5_core_warn(priv->mdev, "Failed to create post_meter flow fwd rule\n");
mlx5_core_warn(priv->mdev, "Failed to create post_meter notexceed rule\n");
err = PTR_ERR(rule);
goto err_green;
}
post_meter->fwd_green_rule = rule;
post_meter->green_rule = rule;
post_meter->green_attr = green_attr;
kvfree(spec);
return 0;
err_green:
mlx5_del_flow_rules(post_meter->drop_red_rule);
mlx5_del_flow_rules(post_meter->red_rule);
err_red:
kvfree(spec);
return err;
}
static void
mlx5e_post_meter_rules_destroy(struct mlx5e_post_meter_priv *post_meter)
mlx5e_post_meter_rules_destroy(struct mlx5_eswitch *esw,
struct mlx5e_post_meter_priv *post_meter)
{
mlx5_del_flow_rules(post_meter->drop_red_rule);
mlx5_del_flow_rules(post_meter->fwd_green_rule);
mlx5_eswitch_del_offloaded_rule(esw, post_meter->red_rule, post_meter->red_attr);
mlx5_eswitch_del_offloaded_rule(esw, post_meter->green_rule, post_meter->green_attr);
}
static void
@ -164,8 +192,10 @@ struct mlx5e_post_meter_priv *
mlx5e_post_meter_init(struct mlx5e_priv *priv,
enum mlx5_flow_namespace_type ns_type,
struct mlx5e_post_act *post_act,
struct mlx5_fc *green_counter,
struct mlx5_fc *red_counter)
struct mlx5_fc *act_counter,
struct mlx5_fc *drop_counter,
struct mlx5_flow_attr *branch_true,
struct mlx5_flow_attr *branch_false)
{
struct mlx5e_post_meter_priv *post_meter;
int err;
@ -182,8 +212,8 @@ mlx5e_post_meter_init(struct mlx5e_priv *priv,
if (err)
goto err_fg;
err = mlx5e_post_meter_rules_create(priv, post_meter, post_act, green_counter,
red_counter);
err = mlx5e_post_meter_rules_create(priv, post_meter, post_act, act_counter,
drop_counter, branch_true, branch_false);
if (err)
goto err_rules;
@ -199,9 +229,9 @@ mlx5e_post_meter_init(struct mlx5e_priv *priv,
}
void
mlx5e_post_meter_cleanup(struct mlx5e_post_meter_priv *post_meter)
mlx5e_post_meter_cleanup(struct mlx5_eswitch *esw, struct mlx5e_post_meter_priv *post_meter)
{
mlx5e_post_meter_rules_destroy(post_meter);
mlx5e_post_meter_rules_destroy(esw, post_meter);
mlx5e_post_meter_fg_destroy(post_meter);
mlx5e_post_meter_table_destroy(post_meter);
kfree(post_meter);

View file

@ -21,9 +21,11 @@ struct mlx5e_post_meter_priv *
mlx5e_post_meter_init(struct mlx5e_priv *priv,
enum mlx5_flow_namespace_type ns_type,
struct mlx5e_post_act *post_act,
struct mlx5_fc *green_counter,
struct mlx5_fc *red_counter);
struct mlx5_fc *act_counter,
struct mlx5_fc *drop_counter,
struct mlx5_flow_attr *branch_true,
struct mlx5_flow_attr *branch_false);
void
mlx5e_post_meter_cleanup(struct mlx5e_post_meter_priv *post_meter);
mlx5e_post_meter_cleanup(struct mlx5_eswitch *esw, struct mlx5e_post_meter_priv *post_meter);
#endif /* __MLX5_EN_POST_METER_H__ */

View file

@ -211,8 +211,4 @@ struct mlx5e_flow_meters *mlx5e_get_flow_meters(struct mlx5_core_dev *dev);
void *mlx5e_get_match_headers_value(u32 flags, struct mlx5_flow_spec *spec);
void *mlx5e_get_match_headers_criteria(u32 flags, struct mlx5_flow_spec *spec);
int mlx5e_policer_validate(const struct flow_action *action,
const struct flow_action_entry *act,
struct netlink_ext_ack *extack);
#endif /* __MLX5_EN_TC_PRIV_H__ */

View file

@ -132,6 +132,15 @@ struct mlx5e_tc_attr_to_reg_mapping mlx5e_tc_attr_to_reg_mappings[] = {
[PACKET_COLOR_TO_REG] = packet_color_to_reg,
};
struct mlx5e_tc_jump_state {
u32 jump_count;
bool jump_target;
struct mlx5_flow_attr *jumping_attr;
enum flow_action_id last_id;
u32 last_index;
};
struct mlx5e_tc_table *mlx5e_tc_table_alloc(void)
{
struct mlx5e_tc_table *tc;
@ -160,6 +169,7 @@ static struct lock_class_key tc_ht_lock_key;
static void mlx5e_put_flow_tunnel_id(struct mlx5e_tc_flow *flow);
static void free_flow_post_acts(struct mlx5e_tc_flow *flow);
static void mlx5_free_flow_attr(struct mlx5e_tc_flow *flow, struct mlx5_flow_attr *attr);
void
mlx5e_tc_match_to_reg_match(struct mlx5_flow_spec *spec,
@ -412,8 +422,9 @@ mlx5e_tc_add_flow_meter(struct mlx5e_priv *priv,
}
ns_type = mlx5e_tc_meter_get_namespace(meter->flow_meters);
post_meter = mlx5e_post_meter_init(priv, ns_type, post_act, meter->green_counter,
meter->red_counter);
post_meter = mlx5e_post_meter_init(priv, ns_type, post_act,
meter->act_counter, meter->drop_counter,
attr->branch_true, attr->branch_false);
if (IS_ERR(post_meter)) {
mlx5_core_err(priv->mdev, "Failed to init post meter\n");
goto err_meter_init;
@ -432,9 +443,9 @@ mlx5e_tc_add_flow_meter(struct mlx5e_priv *priv,
}
static void
mlx5e_tc_del_flow_meter(struct mlx5_flow_attr *attr)
mlx5e_tc_del_flow_meter(struct mlx5_eswitch *esw, struct mlx5_flow_attr *attr)
{
mlx5e_post_meter_cleanup(attr->meter_attr.post_meter);
mlx5e_post_meter_cleanup(esw, attr->meter_attr.post_meter);
mlx5e_tc_meter_put(attr->meter_attr.meter);
}
@ -495,7 +506,7 @@ mlx5e_tc_rule_unoffload(struct mlx5e_priv *priv,
mlx5_eswitch_del_offloaded_rule(esw, rule, attr);
if (attr->meter_attr.meter)
mlx5e_tc_del_flow_meter(attr);
mlx5e_tc_del_flow_meter(esw, attr);
}
int
@ -606,6 +617,12 @@ int mlx5e_get_flow_namespace(struct mlx5e_tc_flow *flow)
MLX5_FLOW_NAMESPACE_FDB : MLX5_FLOW_NAMESPACE_KERNEL;
}
static struct mlx5_core_dev *
get_flow_counter_dev(struct mlx5e_tc_flow *flow)
{
return mlx5e_is_eswitch_flow(flow) ? flow->attr->esw_attr->counter_dev : flow->priv->mdev;
}
static struct mod_hdr_tbl *
get_mod_hdr_table(struct mlx5e_priv *priv, struct mlx5e_tc_flow *flow)
{
@ -1718,6 +1735,90 @@ clean_encap_dests(struct mlx5e_priv *priv,
}
}
static int
verify_attr_actions(u32 actions, struct netlink_ext_ack *extack)
{
if (!(actions &
(MLX5_FLOW_CONTEXT_ACTION_FWD_DEST | MLX5_FLOW_CONTEXT_ACTION_DROP))) {
NL_SET_ERR_MSG_MOD(extack, "Rule must have at least one forward/drop action");
return -EOPNOTSUPP;
}
if (!(~actions &
(MLX5_FLOW_CONTEXT_ACTION_FWD_DEST | MLX5_FLOW_CONTEXT_ACTION_DROP))) {
NL_SET_ERR_MSG_MOD(extack, "Rule cannot support forward+drop action");
return -EOPNOTSUPP;
}
if (actions & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR &&
actions & MLX5_FLOW_CONTEXT_ACTION_DROP) {
NL_SET_ERR_MSG_MOD(extack, "Drop with modify header action is not supported");
return -EOPNOTSUPP;
}
return 0;
}
static int
post_process_attr(struct mlx5e_tc_flow *flow,
struct mlx5_flow_attr *attr,
bool is_post_act_attr,
struct netlink_ext_ack *extack)
{
struct mlx5_eswitch *esw = flow->priv->mdev->priv.eswitch;
bool vf_tun;
int err = 0;
err = verify_attr_actions(attr->action, extack);
if (err)
goto err_out;
err = set_encap_dests(flow->priv, flow, attr, extack, &vf_tun);
if (err)
goto err_out;
if (mlx5e_is_eswitch_flow(flow)) {
err = mlx5_eswitch_add_vlan_action(esw, attr);
if (err)
goto err_out;
}
if (attr->action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR) {
if (vf_tun || is_post_act_attr) {
err = mlx5e_tc_add_flow_mod_hdr(flow->priv, flow, attr);
if (err)
goto err_out;
} else {
err = mlx5e_attach_mod_hdr(flow->priv, flow, attr->parse_attr);
if (err)
goto err_out;
}
}
if (attr->branch_true &&
attr->branch_true->action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR) {
err = mlx5e_tc_add_flow_mod_hdr(flow->priv, flow, attr->branch_true);
if (err)
goto err_out;
}
if (attr->branch_false &&
attr->branch_false->action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR) {
err = mlx5e_tc_add_flow_mod_hdr(flow->priv, flow, attr->branch_false);
if (err)
goto err_out;
}
if (attr->action & MLX5_FLOW_CONTEXT_ACTION_COUNT) {
err = alloc_flow_attr_counter(get_flow_counter_dev(flow), attr);
if (err)
goto err_out;
}
err_out:
return err;
}
static int
mlx5e_tc_add_fdb_flow(struct mlx5e_priv *priv,
struct mlx5e_tc_flow *flow,
@ -1728,7 +1829,6 @@ mlx5e_tc_add_fdb_flow(struct mlx5e_priv *priv,
struct mlx5_flow_attr *attr = flow->attr;
struct mlx5_esw_flow_attr *esw_attr;
u32 max_prio, max_chain;
bool vf_tun;
int err = 0;
parse_attr = attr->parse_attr;
@ -1818,32 +1918,10 @@ mlx5e_tc_add_fdb_flow(struct mlx5e_priv *priv,
esw_attr->int_port = int_port;
}
err = set_encap_dests(priv, flow, attr, extack, &vf_tun);
err = post_process_attr(flow, attr, false, extack);
if (err)
goto err_out;
err = mlx5_eswitch_add_vlan_action(esw, attr);
if (err)
goto err_out;
if (attr->action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR) {
if (vf_tun) {
err = mlx5e_tc_add_flow_mod_hdr(priv, flow, attr);
if (err)
goto err_out;
} else {
err = mlx5e_attach_mod_hdr(priv, flow, parse_attr);
if (err)
goto err_out;
}
}
if (attr->action & MLX5_FLOW_CONTEXT_ACTION_COUNT) {
err = alloc_flow_attr_counter(esw_attr->counter_dev, attr);
if (err)
goto err_out;
}
/* we get here if one of the following takes place:
* (1) there's no error
* (2) there's an encap action and we don't have valid neigh
@ -1879,6 +1957,16 @@ static bool mlx5_flow_has_geneve_opt(struct mlx5e_tc_flow *flow)
return !!geneve_tlv_opt_0_data;
}
static void free_branch_attr(struct mlx5e_tc_flow *flow, struct mlx5_flow_attr *attr)
{
if (!attr)
return;
mlx5_free_flow_attr(flow, attr);
kvfree(attr->parse_attr);
kfree(attr);
}
static void mlx5e_tc_del_fdb_flow(struct mlx5e_priv *priv,
struct mlx5e_tc_flow *flow)
{
@ -1934,6 +2022,8 @@ static void mlx5e_tc_del_fdb_flow(struct mlx5e_priv *priv,
mlx5e_detach_decap(priv, flow);
free_flow_post_acts(flow);
free_branch_attr(flow, attr->branch_true);
free_branch_attr(flow, attr->branch_false);
if (flow->attr->lag.count)
mlx5_lag_del_mpesw_rule(esw->dev);
@ -3507,36 +3597,6 @@ actions_match_supported(struct mlx5e_priv *priv,
ct_clear = flow->attr->ct_attr.ct_action & TCA_CT_ACT_CLEAR;
ct_flow = flow_flag_test(flow, CT) && !ct_clear;
if (!(actions &
(MLX5_FLOW_CONTEXT_ACTION_FWD_DEST | MLX5_FLOW_CONTEXT_ACTION_DROP))) {
NL_SET_ERR_MSG_MOD(extack, "Rule must have at least one forward/drop action");
return false;
}
if (!(~actions &
(MLX5_FLOW_CONTEXT_ACTION_FWD_DEST | MLX5_FLOW_CONTEXT_ACTION_DROP))) {
NL_SET_ERR_MSG_MOD(extack, "Rule cannot support forward+drop action");
return false;
}
if (actions & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR &&
actions & MLX5_FLOW_CONTEXT_ACTION_DROP) {
NL_SET_ERR_MSG_MOD(extack, "Drop with modify header action is not supported");
return false;
}
if (!(~actions &
(MLX5_FLOW_CONTEXT_ACTION_FWD_DEST | MLX5_FLOW_CONTEXT_ACTION_DROP))) {
NL_SET_ERR_MSG_MOD(extack, "Rule cannot support forward+drop action");
return false;
}
if (actions & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR &&
actions & MLX5_FLOW_CONTEXT_ACTION_DROP) {
NL_SET_ERR_MSG_MOD(extack, "Drop with modify header action is not supported");
return false;
}
if (actions & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR &&
!modify_header_match_supported(priv, &parse_attr->spec, flow_action,
actions, ct_flow, ct_clear, extack))
@ -3636,15 +3696,12 @@ mlx5e_clone_flow_attr_for_post_act(struct mlx5_flow_attr *attr,
attr2->esw_attr->split_count = 0;
}
attr2->branch_true = NULL;
attr2->branch_false = NULL;
attr2->jumping_attr = NULL;
return attr2;
}
static struct mlx5_core_dev *
get_flow_counter_dev(struct mlx5e_tc_flow *flow)
{
return mlx5e_is_eswitch_flow(flow) ? flow->attr->esw_attr->counter_dev : flow->priv->mdev;
}
struct mlx5_flow_attr *
mlx5e_tc_get_encap_attr(struct mlx5e_tc_flow *flow)
{
@ -3680,28 +3737,15 @@ mlx5e_tc_unoffload_flow_post_acts(struct mlx5e_tc_flow *flow)
static void
free_flow_post_acts(struct mlx5e_tc_flow *flow)
{
struct mlx5_core_dev *counter_dev = get_flow_counter_dev(flow);
struct mlx5e_post_act *post_act = get_post_action(flow->priv);
struct mlx5_flow_attr *attr, *tmp;
bool vf_tun;
list_for_each_entry_safe(attr, tmp, &flow->attrs, list) {
if (list_is_last(&attr->list, &flow->attrs))
break;
if (attr->post_act_handle)
mlx5e_tc_post_act_del(post_act, attr->post_act_handle);
clean_encap_dests(flow->priv, flow, attr, &vf_tun);
if (attr->action & MLX5_FLOW_CONTEXT_ACTION_COUNT)
mlx5_fc_destroy(counter_dev, attr->counter);
if (attr->action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR) {
mlx5e_mod_hdr_dealloc(&attr->parse_attr->mod_hdr_acts);
if (attr->modify_hdr)
mlx5_modify_header_dealloc(flow->priv->mdev, attr->modify_hdr);
}
mlx5_free_flow_attr(flow, attr);
free_branch_attr(flow, attr->branch_true);
free_branch_attr(flow, attr->branch_false);
list_del(&attr->list);
kvfree(attr->parse_attr);
@ -3754,7 +3798,6 @@ alloc_flow_post_acts(struct mlx5e_tc_flow *flow, struct netlink_ext_ack *extack)
struct mlx5e_post_act *post_act = get_post_action(flow->priv);
struct mlx5_flow_attr *attr, *next_attr = NULL;
struct mlx5e_post_act_handle *handle;
bool vf_tun;
int err;
/* This is going in reverse order as needed.
@ -3764,7 +3807,9 @@ alloc_flow_post_acts(struct mlx5e_tc_flow *flow, struct netlink_ext_ack *extack)
if (!next_attr) {
/* Set counter action on last post act rule. */
attr->action |= MLX5_FLOW_CONTEXT_ACTION_COUNT;
} else {
}
if (next_attr && !(attr->flags & MLX5_ATTR_FLAG_TERMINATING)) {
err = mlx5e_tc_act_set_next_post_act(flow, attr, next_attr);
if (err)
goto out_free;
@ -3776,25 +3821,13 @@ alloc_flow_post_acts(struct mlx5e_tc_flow *flow, struct netlink_ext_ack *extack)
if (list_is_last(&attr->list, &flow->attrs))
break;
err = set_encap_dests(flow->priv, flow, attr, extack, &vf_tun);
if (err)
goto out_free;
err = actions_prepare_mod_hdr_actions(flow->priv, flow, attr, extack);
if (err)
goto out_free;
if (attr->action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR) {
err = mlx5e_tc_add_flow_mod_hdr(flow->priv, flow, attr);
if (err)
goto out_free;
}
if (attr->action & MLX5_FLOW_CONTEXT_ACTION_COUNT) {
err = alloc_flow_attr_counter(get_flow_counter_dev(flow), attr);
if (err)
goto out_free;
}
err = post_process_attr(flow, attr, true, extack);
if (err)
goto out_free;
handle = mlx5e_tc_post_act_add(post_act, attr);
if (IS_ERR(handle)) {
@ -3803,6 +3836,13 @@ alloc_flow_post_acts(struct mlx5e_tc_flow *flow, struct netlink_ext_ack *extack)
}
attr->post_act_handle = handle;
if (attr->jumping_attr) {
err = mlx5e_tc_act_set_next_post_act(flow, attr->jumping_attr, attr);
if (err)
goto out_free;
}
next_attr = attr;
}
@ -3821,6 +3861,138 @@ alloc_flow_post_acts(struct mlx5e_tc_flow *flow, struct netlink_ext_ack *extack)
return err;
}
static int
alloc_branch_attr(struct mlx5e_tc_flow *flow,
struct mlx5e_tc_act_branch_ctrl *cond,
struct mlx5_flow_attr **cond_attr,
u32 *jump_count,
struct netlink_ext_ack *extack)
{
struct mlx5_flow_attr *attr;
int err = 0;
*cond_attr = mlx5e_clone_flow_attr_for_post_act(flow->attr,
mlx5e_get_flow_namespace(flow));
if (!(*cond_attr))
return -ENOMEM;
attr = *cond_attr;
switch (cond->act_id) {
case FLOW_ACTION_DROP:
attr->action |= MLX5_FLOW_CONTEXT_ACTION_DROP;
break;
case FLOW_ACTION_ACCEPT:
case FLOW_ACTION_PIPE:
attr->action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
attr->dest_ft = mlx5e_tc_post_act_get_ft(get_post_action(flow->priv));
break;
case FLOW_ACTION_JUMP:
if (*jump_count) {
NL_SET_ERR_MSG_MOD(extack, "Cannot offload flows with nested jumps");
err = -EOPNOTSUPP;
goto out_err;
}
*jump_count = cond->extval;
attr->action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
attr->dest_ft = mlx5e_tc_post_act_get_ft(get_post_action(flow->priv));
break;
default:
err = -EOPNOTSUPP;
goto out_err;
}
return err;
out_err:
kfree(*cond_attr);
*cond_attr = NULL;
return err;
}
static void
dec_jump_count(struct flow_action_entry *act, struct mlx5e_tc_act *tc_act,
struct mlx5_flow_attr *attr, struct mlx5e_priv *priv,
struct mlx5e_tc_jump_state *jump_state)
{
if (!jump_state->jump_count)
return;
/* Single tc action can instantiate multiple offload actions (e.g. pedit)
* Jump only over a tc action
*/
if (act->id == jump_state->last_id && act->hw_index == jump_state->last_index)
return;
jump_state->last_id = act->id;
jump_state->last_index = act->hw_index;
/* nothing to do for intermediate actions */
if (--jump_state->jump_count > 1)
return;
if (jump_state->jump_count == 1) { /* last action in the jump action list */
/* create a new attribute after this action */
jump_state->jump_target = true;
if (tc_act->is_terminating_action) { /* the branch ends here */
attr->flags |= MLX5_ATTR_FLAG_TERMINATING;
attr->action |= MLX5_FLOW_CONTEXT_ACTION_COUNT;
} else { /* the branch continues executing the rest of the actions */
struct mlx5e_post_act *post_act;
attr->action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
post_act = get_post_action(priv);
attr->dest_ft = mlx5e_tc_post_act_get_ft(post_act);
}
} else if (jump_state->jump_count == 0) { /* first attr after the jump action list */
/* This is the post action for the jumping attribute (either red or green)
* Use the stored jumping_attr to set the post act id on the jumping attribute
*/
attr->jumping_attr = jump_state->jumping_attr;
}
}
static int
parse_branch_ctrl(struct flow_action_entry *act, struct mlx5e_tc_act *tc_act,
struct mlx5e_tc_flow *flow, struct mlx5_flow_attr *attr,
struct mlx5e_tc_jump_state *jump_state,
struct netlink_ext_ack *extack)
{
struct mlx5e_tc_act_branch_ctrl cond_true, cond_false;
u32 jump_count = jump_state->jump_count;
int err;
if (!tc_act->get_branch_ctrl)
return 0;
tc_act->get_branch_ctrl(act, &cond_true, &cond_false);
err = alloc_branch_attr(flow, &cond_true,
&attr->branch_true, &jump_count, extack);
if (err)
goto out_err;
if (jump_count)
jump_state->jumping_attr = attr->branch_true;
err = alloc_branch_attr(flow, &cond_false,
&attr->branch_false, &jump_count, extack);
if (err)
goto err_branch_false;
if (jump_count && !jump_state->jumping_attr)
jump_state->jumping_attr = attr->branch_false;
jump_state->jump_count = jump_count;
return 0;
err_branch_false:
free_branch_attr(flow, attr->branch_true);
out_err:
return err;
}
static int
parse_tc_actions(struct mlx5e_tc_act_parse_state *parse_state,
struct flow_action *flow_action)
@ -3828,6 +4000,7 @@ parse_tc_actions(struct mlx5e_tc_act_parse_state *parse_state,
struct netlink_ext_ack *extack = parse_state->extack;
struct mlx5e_tc_flow_action flow_action_reorder;
struct mlx5e_tc_flow *flow = parse_state->flow;
struct mlx5e_tc_jump_state jump_state = {};
struct mlx5_flow_attr *attr = flow->attr;
enum mlx5_flow_namespace_type ns_type;
struct mlx5e_priv *priv = flow->priv;
@ -3847,6 +4020,7 @@ parse_tc_actions(struct mlx5e_tc_act_parse_state *parse_state,
list_add(&attr->list, &flow->attrs);
flow_action_for_each(i, _act, &flow_action_reorder) {
jump_state.jump_target = false;
act = *_act;
tc_act = mlx5e_tc_act_get(act->id, ns_type);
if (!tc_act) {
@ -3864,12 +4038,19 @@ parse_tc_actions(struct mlx5e_tc_act_parse_state *parse_state,
if (err)
goto out_free;
dec_jump_count(act, tc_act, attr, priv, &jump_state);
err = parse_branch_ctrl(act, tc_act, flow, attr, &jump_state, extack);
if (err)
goto out_free;
parse_state->actions |= attr->action;
/* Split attr for multi table act if not the last act. */
if (tc_act->is_multi_table_act &&
if (jump_state.jump_target ||
(tc_act->is_multi_table_act &&
tc_act->is_multi_table_act(priv, act, attr) &&
i < flow_action_reorder.num_entries - 1) {
i < flow_action_reorder.num_entries - 1)) {
err = mlx5e_tc_act_post_parse(parse_state, flow_action, attr, ns_type);
if (err)
goto out_free;
@ -3951,6 +4132,10 @@ parse_tc_nic_actions(struct mlx5e_priv *priv,
if (err)
return err;
err = verify_attr_actions(attr->action, extack);
if (err)
return err;
if (!actions_match_supported(priv, flow_action, parse_state->actions,
parse_attr, flow, extack))
return -EOPNOTSUPP;
@ -4188,6 +4373,30 @@ mlx5_alloc_flow_attr(enum mlx5_flow_namespace_type type)
return attr;
}
static void
mlx5_free_flow_attr(struct mlx5e_tc_flow *flow, struct mlx5_flow_attr *attr)
{
struct mlx5_core_dev *counter_dev = get_flow_counter_dev(flow);
bool vf_tun;
if (!attr)
return;
if (attr->post_act_handle)
mlx5e_tc_post_act_del(get_post_action(flow->priv), attr->post_act_handle);
clean_encap_dests(flow->priv, flow, attr, &vf_tun);
if (attr->action & MLX5_FLOW_CONTEXT_ACTION_COUNT)
mlx5_fc_destroy(counter_dev, attr->counter);
if (attr->action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR) {
mlx5e_mod_hdr_dealloc(&attr->parse_attr->mod_hdr_acts);
if (attr->modify_hdr)
mlx5_modify_header_dealloc(flow->priv->mdev, attr->modify_hdr);
}
}
static int
mlx5e_alloc_flow(struct mlx5e_priv *priv, int attr_size,
struct flow_cls_offload *f, unsigned long flow_flags,
@ -4730,10 +4939,17 @@ static int apply_police_params(struct mlx5e_priv *priv, u64 rate,
return err;
}
int mlx5e_policer_validate(const struct flow_action *action,
const struct flow_action_entry *act,
struct netlink_ext_ack *extack)
static int
tc_matchall_police_validate(const struct flow_action *action,
const struct flow_action_entry *act,
struct netlink_ext_ack *extack)
{
if (act->police.notexceed.act_id != FLOW_ACTION_CONTINUE) {
NL_SET_ERR_MSG_MOD(extack,
"Offload not supported when conform action is not continue");
return -EOPNOTSUPP;
}
if (act->police.exceed.act_id != FLOW_ACTION_DROP) {
NL_SET_ERR_MSG_MOD(extack,
"Offload not supported when exceed action is not drop");
@ -4784,13 +5000,7 @@ static int scan_tc_matchall_fdb_actions(struct mlx5e_priv *priv,
flow_action_for_each(i, act, flow_action) {
switch (act->id) {
case FLOW_ACTION_POLICE:
if (act->police.notexceed.act_id != FLOW_ACTION_CONTINUE) {
NL_SET_ERR_MSG_MOD(extack,
"Offload not supported when conform action is not continue");
return -EOPNOTSUPP;
}
err = mlx5e_policer_validate(flow_action, act, extack);
err = tc_matchall_police_validate(flow_action, act, extack);
if (err)
return err;

View file

@ -95,6 +95,9 @@ struct mlx5_flow_attr {
*/
bool count;
} lag;
struct mlx5_flow_attr *branch_true;
struct mlx5_flow_attr *branch_false;
struct mlx5_flow_attr *jumping_attr;
/* keep this union last */
union {
DECLARE_FLEX_ARRAY(struct mlx5_esw_flow_attr, esw_attr);
@ -110,6 +113,7 @@ enum {
MLX5_ATTR_FLAG_SAMPLE = BIT(4),
MLX5_ATTR_FLAG_ACCEPT = BIT(5),
MLX5_ATTR_FLAG_CT = BIT(6),
MLX5_ATTR_FLAG_TERMINATING = BIT(7),
};
/* Returns true if any of the flags that require skipping further TC/NF processing are set. */

View file

@ -640,6 +640,11 @@ mlx5_eswitch_add_offloaded_rule(struct mlx5_eswitch *esw,
goto err_esw_get;
}
if (!i) {
kfree(dest);
dest = NULL;
}
if (mlx5_eswitch_termtbl_required(esw, attr, &flow_act, spec))
rule = mlx5_eswitch_add_termtbl_rule(esw, fdb, spec, esw_attr,
&flow_act, dest, i);

View file

@ -1962,6 +1962,9 @@ _mlx5_add_flow_rules(struct mlx5_flow_table *ft,
if (flow_act->fg && ft->autogroup.active)
return ERR_PTR(-EINVAL);
if (dest && dest_num <= 0)
return ERR_PTR(-EINVAL);
for (i = 0; i < dest_num; i++) {
if (!dest_is_valid(&dest[i], flow_act, ft))
return ERR_PTR(-EINVAL);