Commit 45aad0b7 authored by Ido Schimmel's avatar Ido Schimmel Committed by David S. Miller

mlxsw: spectrum_acl: Offload FLOW_ACTION_SAMPLE

Implement support for action sample when used with a flower classifier
by implementing the required sampler_add() / sampler_del() callbacks and
registering an Rx listener for the sampled packets.

The sampler_add() callback returns an error for Spectrum-1 as the
functionality is not supported. In Spectrum-{2,3} the callback creates a
mirroring agent towards the CPU. The agent's identifier is used by the
policy engine code to mirror towards the CPU with probability.

The Rx listener for the sampled packet is registered with the 'policy
engine' mirroring reason and passes trapped packets to the psample
module after looking up their parameters (e.g., sampling group).
Signed-off-by: default avatarIdo Schimmel <idosch@nvidia.com>
Reviewed-by: default avatarJiri Pirko <jiri@nvidia.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent ca19ea63
...@@ -239,11 +239,12 @@ struct mlxsw_sp_port_pcpu_stats { ...@@ -239,11 +239,12 @@ struct mlxsw_sp_port_pcpu_stats {
enum mlxsw_sp_sample_trigger_type { enum mlxsw_sp_sample_trigger_type {
MLXSW_SP_SAMPLE_TRIGGER_TYPE_INGRESS, MLXSW_SP_SAMPLE_TRIGGER_TYPE_INGRESS,
MLXSW_SP_SAMPLE_TRIGGER_TYPE_EGRESS, MLXSW_SP_SAMPLE_TRIGGER_TYPE_EGRESS,
MLXSW_SP_SAMPLE_TRIGGER_TYPE_POLICY_ENGINE,
}; };
struct mlxsw_sp_sample_trigger { struct mlxsw_sp_sample_trigger {
enum mlxsw_sp_sample_trigger_type type; enum mlxsw_sp_sample_trigger_type type;
u8 local_port; u8 local_port; /* Reserved when trigger type is not ingress / egress. */
}; };
struct mlxsw_sp_sample_params { struct mlxsw_sp_sample_params {
...@@ -946,6 +947,12 @@ int mlxsw_sp_acl_rulei_act_count(struct mlxsw_sp *mlxsw_sp, ...@@ -946,6 +947,12 @@ int mlxsw_sp_acl_rulei_act_count(struct mlxsw_sp *mlxsw_sp,
int mlxsw_sp_acl_rulei_act_fid_set(struct mlxsw_sp *mlxsw_sp, int mlxsw_sp_acl_rulei_act_fid_set(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_acl_rule_info *rulei, struct mlxsw_sp_acl_rule_info *rulei,
u16 fid, struct netlink_ext_ack *extack); u16 fid, struct netlink_ext_ack *extack);
int mlxsw_sp_acl_rulei_act_sample(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_acl_rule_info *rulei,
struct mlxsw_sp_flow_block *block,
struct psample_group *psample_group, u32 rate,
u32 trunc_size, bool truncate,
struct netlink_ext_ack *extack);
struct mlxsw_sp_acl_rule; struct mlxsw_sp_acl_rule;
......
...@@ -688,6 +688,31 @@ int mlxsw_sp_acl_rulei_act_fid_set(struct mlxsw_sp *mlxsw_sp, ...@@ -688,6 +688,31 @@ int mlxsw_sp_acl_rulei_act_fid_set(struct mlxsw_sp *mlxsw_sp,
return mlxsw_afa_block_append_fid_set(rulei->act_block, fid, extack); return mlxsw_afa_block_append_fid_set(rulei->act_block, fid, extack);
} }
int mlxsw_sp_acl_rulei_act_sample(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_acl_rule_info *rulei,
struct mlxsw_sp_flow_block *block,
struct psample_group *psample_group, u32 rate,
u32 trunc_size, bool truncate,
struct netlink_ext_ack *extack)
{
struct mlxsw_sp_flow_block_binding *binding;
struct mlxsw_sp_port *mlxsw_sp_port;
if (!list_is_singular(&block->binding_list)) {
NL_SET_ERR_MSG_MOD(extack, "Only a single sampling source is allowed");
return -EOPNOTSUPP;
}
binding = list_first_entry(&block->binding_list,
struct mlxsw_sp_flow_block_binding, list);
mlxsw_sp_port = binding->mlxsw_sp_port;
return mlxsw_afa_block_append_sampler(rulei->act_block,
mlxsw_sp_port->local_port,
psample_group, rate, trunc_size,
truncate, binding->ingress,
extack);
}
struct mlxsw_sp_acl_rule * struct mlxsw_sp_acl_rule *
mlxsw_sp_acl_rule_create(struct mlxsw_sp *mlxsw_sp, mlxsw_sp_acl_rule_create(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_acl_ruleset *ruleset, struct mlxsw_sp_acl_ruleset *ruleset,
......
...@@ -192,6 +192,22 @@ static void mlxsw_sp_act_policer_del(void *priv, u16 policer_index) ...@@ -192,6 +192,22 @@ static void mlxsw_sp_act_policer_del(void *priv, u16 policer_index)
policer_index); policer_index);
} }
static int mlxsw_sp1_act_sampler_add(void *priv, u8 local_port,
struct psample_group *psample_group,
u32 rate, u32 trunc_size, bool truncate,
bool ingress, int *p_span_id,
struct netlink_ext_ack *extack)
{
NL_SET_ERR_MSG_MOD(extack, "Sampling action is not supported on Spectrum-1");
return -EOPNOTSUPP;
}
static void mlxsw_sp1_act_sampler_del(void *priv, u8 local_port, int span_id,
bool ingress)
{
WARN_ON_ONCE(1);
}
const struct mlxsw_afa_ops mlxsw_sp1_act_afa_ops = { const struct mlxsw_afa_ops mlxsw_sp1_act_afa_ops = {
.kvdl_set_add = mlxsw_sp1_act_kvdl_set_add, .kvdl_set_add = mlxsw_sp1_act_kvdl_set_add,
.kvdl_set_del = mlxsw_sp_act_kvdl_set_del, .kvdl_set_del = mlxsw_sp_act_kvdl_set_del,
...@@ -204,8 +220,73 @@ const struct mlxsw_afa_ops mlxsw_sp1_act_afa_ops = { ...@@ -204,8 +220,73 @@ const struct mlxsw_afa_ops mlxsw_sp1_act_afa_ops = {
.mirror_del = mlxsw_sp_act_mirror_del, .mirror_del = mlxsw_sp_act_mirror_del,
.policer_add = mlxsw_sp_act_policer_add, .policer_add = mlxsw_sp_act_policer_add,
.policer_del = mlxsw_sp_act_policer_del, .policer_del = mlxsw_sp_act_policer_del,
.sampler_add = mlxsw_sp1_act_sampler_add,
.sampler_del = mlxsw_sp1_act_sampler_del,
}; };
static int mlxsw_sp2_act_sampler_add(void *priv, u8 local_port,
struct psample_group *psample_group,
u32 rate, u32 trunc_size, bool truncate,
bool ingress, int *p_span_id,
struct netlink_ext_ack *extack)
{
struct mlxsw_sp_span_agent_parms agent_parms = {
.session_id = MLXSW_SP_SPAN_SESSION_ID_SAMPLING,
};
struct mlxsw_sp_sample_trigger trigger = {
.type = MLXSW_SP_SAMPLE_TRIGGER_TYPE_POLICY_ENGINE,
};
struct mlxsw_sp_sample_params params;
struct mlxsw_sp_port *mlxsw_sp_port;
struct mlxsw_sp *mlxsw_sp = priv;
int err;
params.psample_group = psample_group;
params.trunc_size = trunc_size;
params.rate = rate;
params.truncate = truncate;
err = mlxsw_sp_sample_trigger_params_set(mlxsw_sp, &trigger, &params,
extack);
if (err)
return err;
err = mlxsw_sp_span_agent_get(mlxsw_sp, p_span_id, &agent_parms);
if (err) {
NL_SET_ERR_MSG_MOD(extack, "Failed to get SPAN agent");
goto err_span_agent_get;
}
mlxsw_sp_port = mlxsw_sp->ports[local_port];
err = mlxsw_sp_span_analyzed_port_get(mlxsw_sp_port, ingress);
if (err) {
NL_SET_ERR_MSG_MOD(extack, "Failed to get analyzed port");
goto err_analyzed_port_get;
}
return 0;
err_analyzed_port_get:
mlxsw_sp_span_agent_put(mlxsw_sp, *p_span_id);
err_span_agent_get:
mlxsw_sp_sample_trigger_params_unset(mlxsw_sp, &trigger);
return err;
}
static void mlxsw_sp2_act_sampler_del(void *priv, u8 local_port, int span_id,
bool ingress)
{
struct mlxsw_sp_sample_trigger trigger = {
.type = MLXSW_SP_SAMPLE_TRIGGER_TYPE_POLICY_ENGINE,
};
struct mlxsw_sp_port *mlxsw_sp_port;
struct mlxsw_sp *mlxsw_sp = priv;
mlxsw_sp_port = mlxsw_sp->ports[local_port];
mlxsw_sp_span_analyzed_port_put(mlxsw_sp_port, ingress);
mlxsw_sp_span_agent_put(mlxsw_sp, span_id);
mlxsw_sp_sample_trigger_params_unset(mlxsw_sp, &trigger);
}
const struct mlxsw_afa_ops mlxsw_sp2_act_afa_ops = { const struct mlxsw_afa_ops mlxsw_sp2_act_afa_ops = {
.kvdl_set_add = mlxsw_sp2_act_kvdl_set_add, .kvdl_set_add = mlxsw_sp2_act_kvdl_set_add,
.kvdl_set_del = mlxsw_sp_act_kvdl_set_del, .kvdl_set_del = mlxsw_sp_act_kvdl_set_del,
...@@ -218,6 +299,8 @@ const struct mlxsw_afa_ops mlxsw_sp2_act_afa_ops = { ...@@ -218,6 +299,8 @@ const struct mlxsw_afa_ops mlxsw_sp2_act_afa_ops = {
.mirror_del = mlxsw_sp_act_mirror_del, .mirror_del = mlxsw_sp_act_mirror_del,
.policer_add = mlxsw_sp_act_policer_add, .policer_add = mlxsw_sp_act_policer_add,
.policer_del = mlxsw_sp_act_policer_del, .policer_del = mlxsw_sp_act_policer_del,
.sampler_add = mlxsw_sp2_act_sampler_add,
.sampler_del = mlxsw_sp2_act_sampler_del,
.dummy_first_set = true, .dummy_first_set = true,
}; };
......
...@@ -24,6 +24,7 @@ static int mlxsw_sp_flower_parse_actions(struct mlxsw_sp *mlxsw_sp, ...@@ -24,6 +24,7 @@ static int mlxsw_sp_flower_parse_actions(struct mlxsw_sp *mlxsw_sp,
const struct flow_action_entry *act; const struct flow_action_entry *act;
int mirror_act_count = 0; int mirror_act_count = 0;
int police_act_count = 0; int police_act_count = 0;
int sample_act_count = 0;
int err, i; int err, i;
if (!flow_action_has_entries(flow_action)) if (!flow_action_has_entries(flow_action))
...@@ -209,6 +210,23 @@ static int mlxsw_sp_flower_parse_actions(struct mlxsw_sp *mlxsw_sp, ...@@ -209,6 +210,23 @@ static int mlxsw_sp_flower_parse_actions(struct mlxsw_sp *mlxsw_sp,
return err; return err;
break; break;
} }
case FLOW_ACTION_SAMPLE: {
if (sample_act_count++) {
NL_SET_ERR_MSG_MOD(extack, "Multiple sample actions per rule are not supported");
return -EOPNOTSUPP;
}
err = mlxsw_sp_acl_rulei_act_sample(mlxsw_sp, rulei,
block,
act->sample.psample_group,
act->sample.rate,
act->sample.trunc_size,
act->sample.truncate,
extack);
if (err)
return err;
break;
}
default: default:
NL_SET_ERR_MSG_MOD(extack, "Unsupported action"); NL_SET_ERR_MSG_MOD(extack, "Unsupported action");
dev_err(mlxsw_sp->bus_info->dev, "Unsupported action\n"); dev_err(mlxsw_sp->bus_info->dev, "Unsupported action\n");
......
...@@ -51,6 +51,8 @@ enum { ...@@ -51,6 +51,8 @@ enum {
enum { enum {
/* Packet was mirrored from ingress. */ /* Packet was mirrored from ingress. */
MLXSW_SP_MIRROR_REASON_INGRESS = 1, MLXSW_SP_MIRROR_REASON_INGRESS = 1,
/* Packet was mirrored from policy engine. */
MLXSW_SP_MIRROR_REASON_POLICY_ENGINE = 2,
/* Packet was early dropped. */ /* Packet was early dropped. */
MLXSW_SP_MIRROR_REASON_INGRESS_WRED = 9, MLXSW_SP_MIRROR_REASON_INGRESS_WRED = 9,
/* Packet was mirrored from egress. */ /* Packet was mirrored from egress. */
...@@ -341,6 +343,42 @@ static void mlxsw_sp_rx_sample_tx_listener(struct sk_buff *skb, u8 local_port, ...@@ -341,6 +343,42 @@ static void mlxsw_sp_rx_sample_tx_listener(struct sk_buff *skb, u8 local_port,
consume_skb(skb); consume_skb(skb);
} }
static void mlxsw_sp_rx_sample_acl_listener(struct sk_buff *skb, u8 local_port,
void *trap_ctx)
{
struct mlxsw_sp *mlxsw_sp = devlink_trap_ctx_priv(trap_ctx);
struct mlxsw_sp_sample_trigger trigger = {
.type = MLXSW_SP_SAMPLE_TRIGGER_TYPE_POLICY_ENGINE,
};
struct mlxsw_sp_sample_params *params;
struct mlxsw_sp_port *mlxsw_sp_port;
struct psample_metadata md = {};
int err;
err = __mlxsw_sp_rx_no_mark_listener(skb, local_port, trap_ctx);
if (err)
return;
mlxsw_sp_port = mlxsw_sp->ports[local_port];
if (!mlxsw_sp_port)
goto out;
params = mlxsw_sp_sample_trigger_params_lookup(mlxsw_sp, &trigger);
if (!params)
goto out;
/* The psample module expects skb->data to point to the start of the
* Ethernet header.
*/
skb_push(skb, ETH_HLEN);
mlxsw_sp_psample_md_init(mlxsw_sp, &md, skb,
mlxsw_sp_port->dev->ifindex, params->truncate,
params->trunc_size);
psample_sample_packet(params->psample_group, skb, params->rate, &md);
out:
consume_skb(skb);
}
#define MLXSW_SP_TRAP_DROP(_id, _group_id) \ #define MLXSW_SP_TRAP_DROP(_id, _group_id) \
DEVLINK_TRAP_GENERIC(DROP, DROP, _id, \ DEVLINK_TRAP_GENERIC(DROP, DROP, _id, \
DEVLINK_TRAP_GROUP_GENERIC_ID_##_group_id, \ DEVLINK_TRAP_GROUP_GENERIC_ID_##_group_id, \
...@@ -1898,6 +1936,9 @@ mlxsw_sp2_trap_items_arr[] = { ...@@ -1898,6 +1936,9 @@ mlxsw_sp2_trap_items_arr[] = {
MLXSW_RXL_MIRROR(mlxsw_sp_rx_sample_tx_listener, 1, MLXSW_RXL_MIRROR(mlxsw_sp_rx_sample_tx_listener, 1,
SP_PKT_SAMPLE, SP_PKT_SAMPLE,
MLXSW_SP_MIRROR_REASON_EGRESS), MLXSW_SP_MIRROR_REASON_EGRESS),
MLXSW_RXL_MIRROR(mlxsw_sp_rx_sample_acl_listener, 1,
SP_PKT_SAMPLE,
MLXSW_SP_MIRROR_REASON_POLICY_ENGINE),
}, },
}, },
}; };
......
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