Commit 46bb5a9c authored by David S. Miller's avatar David S. Miller

Merge branch 'mlxsw-Add-support-for-egress-and-policy-based-sampling'

Ido Schimmel says:

====================
mlxsw: Add support for egress and policy-based sampling

So far mlxsw only supported ingress sampling using matchall classifier.
This series adds support for egress sampling and policy-based sampling
using flower classifier on Spectrum-2 and newer ASICs. As such, it is
now possible to issue these commands:

 # tc filter add dev swp1 egress pref 1 proto all matchall action sample rate 100 group 1

 # tc filter add dev swp2 ingress pref 1 proto ip flower dst_ip 198.51.100.1 action sample rate 100 group 2

When performing egress sampling (using either matchall or flower) the
ASIC is able to report the end-to-end latency which is passed to the
psample module.

Series overview:

Patches #1-#3 are preparations without any functional changes

Patch #4 generalizes the idea of sampling triggers and creates a hash
table to track active sampling triggers in preparation for egress and
policy-based triggers. The motivation is explained in the changelog

Patch #5 flips mlxsw to start using this hash table instead of storing
ingress sampling triggers as an attribute of the sampled port

Patch #6 finally adds support for egress sampling using matchall
classifier

Patches #7-#8 add support for policy-based sampling using flower
classifier

Patches #9 extends the mlxsw sampling selftest to cover the new triggers

Patch #10 makes sure that egress sampling configuration only fails on
Spectrum-1
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 5bdbdb82 0f967d9e
......@@ -2007,3 +2007,134 @@ int mlxsw_afa_block_append_l4port(struct mlxsw_afa_block *block, bool is_dport,
return 0;
}
EXPORT_SYMBOL(mlxsw_afa_block_append_l4port);
/* Mirror Sampler Action
* ---------------------
* The SAMPLER_ACTION is used to mirror packets with a probability (sampling).
*/
#define MLXSW_AFA_SAMPLER_CODE 0x13
#define MLXSW_AFA_SAMPLER_SIZE 1
/* afa_sampler_mirror_agent
* Mirror (SPAN) agent.
*/
MLXSW_ITEM32(afa, sampler, mirror_agent, 0x04, 0, 3);
#define MLXSW_AFA_SAMPLER_RATE_MAX (BIT(24) - 1)
/* afa_sampler_mirror_probability_rate
* Mirroring probability.
* Valid values are 1 to 2^24 - 1
*/
MLXSW_ITEM32(afa, sampler, mirror_probability_rate, 0x08, 0, 24);
static void mlxsw_afa_sampler_pack(char *payload, u8 mirror_agent, u32 rate)
{
mlxsw_afa_sampler_mirror_agent_set(payload, mirror_agent);
mlxsw_afa_sampler_mirror_probability_rate_set(payload, rate);
}
struct mlxsw_afa_sampler {
struct mlxsw_afa_resource resource;
int span_id;
u8 local_port;
bool ingress;
};
static void mlxsw_afa_sampler_destroy(struct mlxsw_afa_block *block,
struct mlxsw_afa_sampler *sampler)
{
mlxsw_afa_resource_del(&sampler->resource);
block->afa->ops->sampler_del(block->afa->ops_priv, sampler->local_port,
sampler->span_id, sampler->ingress);
kfree(sampler);
}
static void mlxsw_afa_sampler_destructor(struct mlxsw_afa_block *block,
struct mlxsw_afa_resource *resource)
{
struct mlxsw_afa_sampler *sampler;
sampler = container_of(resource, struct mlxsw_afa_sampler, resource);
mlxsw_afa_sampler_destroy(block, sampler);
}
static struct mlxsw_afa_sampler *
mlxsw_afa_sampler_create(struct mlxsw_afa_block *block, u8 local_port,
struct psample_group *psample_group, u32 rate,
u32 trunc_size, bool truncate, bool ingress,
struct netlink_ext_ack *extack)
{
struct mlxsw_afa_sampler *sampler;
int err;
sampler = kzalloc(sizeof(*sampler), GFP_KERNEL);
if (!sampler)
return ERR_PTR(-ENOMEM);
err = block->afa->ops->sampler_add(block->afa->ops_priv, local_port,
psample_group, rate, trunc_size,
truncate, ingress, &sampler->span_id,
extack);
if (err)
goto err_sampler_add;
sampler->ingress = ingress;
sampler->local_port = local_port;
sampler->resource.destructor = mlxsw_afa_sampler_destructor;
mlxsw_afa_resource_add(block, &sampler->resource);
return sampler;
err_sampler_add:
kfree(sampler);
return ERR_PTR(err);
}
static int
mlxsw_afa_block_append_allocated_sampler(struct mlxsw_afa_block *block,
u8 mirror_agent, u32 rate)
{
char *act = mlxsw_afa_block_append_action(block, MLXSW_AFA_SAMPLER_CODE,
MLXSW_AFA_SAMPLER_SIZE);
if (IS_ERR(act))
return PTR_ERR(act);
mlxsw_afa_sampler_pack(act, mirror_agent, rate);
return 0;
}
int mlxsw_afa_block_append_sampler(struct mlxsw_afa_block *block, u8 local_port,
struct psample_group *psample_group,
u32 rate, u32 trunc_size, bool truncate,
bool ingress,
struct netlink_ext_ack *extack)
{
struct mlxsw_afa_sampler *sampler;
int err;
if (rate > MLXSW_AFA_SAMPLER_RATE_MAX) {
NL_SET_ERR_MSG_MOD(extack, "Sampling rate is too high");
return -EINVAL;
}
sampler = mlxsw_afa_sampler_create(block, local_port, psample_group,
rate, trunc_size, truncate, ingress,
extack);
if (IS_ERR(sampler))
return PTR_ERR(sampler);
err = mlxsw_afa_block_append_allocated_sampler(block, sampler->span_id,
rate);
if (err) {
NL_SET_ERR_MSG_MOD(extack, "Cannot append sampler action");
goto err_append_allocated_sampler;
}
return 0;
err_append_allocated_sampler:
mlxsw_afa_sampler_destroy(block, sampler);
return err;
}
EXPORT_SYMBOL(mlxsw_afa_block_append_sampler);
......@@ -30,6 +30,12 @@ struct mlxsw_afa_ops {
u16 *p_policer_index,
struct netlink_ext_ack *extack);
void (*policer_del)(void *priv, u16 policer_index);
int (*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);
void (*sampler_del)(void *priv, u8 local_port, int span_id,
bool ingress);
bool dummy_first_set;
};
......@@ -92,5 +98,10 @@ int mlxsw_afa_block_append_police(struct mlxsw_afa_block *block,
u32 fa_index, u64 rate_bytes_ps, u32 burst,
u16 *p_policer_index,
struct netlink_ext_ack *extack);
int mlxsw_afa_block_append_sampler(struct mlxsw_afa_block *block, u8 local_port,
struct psample_group *psample_group,
u32 rate, u32 trunc_size, bool truncate,
bool ingress,
struct netlink_ext_ack *extack);
#endif
......@@ -23,6 +23,8 @@
#include <linux/netlink.h>
#include <linux/jhash.h>
#include <linux/log2.h>
#include <linux/refcount.h>
#include <linux/rhashtable.h>
#include <net/switchdev.h>
#include <net/pkt_cls.h>
#include <net/netevent.h>
......@@ -2550,6 +2552,142 @@ static const struct mlxsw_sp_ptp_ops mlxsw_sp2_ptp_ops = {
.get_stats = mlxsw_sp2_get_stats,
};
struct mlxsw_sp_sample_trigger_node {
struct mlxsw_sp_sample_trigger trigger;
struct mlxsw_sp_sample_params params;
struct rhash_head ht_node;
struct rcu_head rcu;
refcount_t refcount;
};
static const struct rhashtable_params mlxsw_sp_sample_trigger_ht_params = {
.key_offset = offsetof(struct mlxsw_sp_sample_trigger_node, trigger),
.head_offset = offsetof(struct mlxsw_sp_sample_trigger_node, ht_node),
.key_len = sizeof(struct mlxsw_sp_sample_trigger),
.automatic_shrinking = true,
};
static void
mlxsw_sp_sample_trigger_key_init(struct mlxsw_sp_sample_trigger *key,
const struct mlxsw_sp_sample_trigger *trigger)
{
memset(key, 0, sizeof(*key));
key->type = trigger->type;
key->local_port = trigger->local_port;
}
/* RCU read lock must be held */
struct mlxsw_sp_sample_params *
mlxsw_sp_sample_trigger_params_lookup(struct mlxsw_sp *mlxsw_sp,
const struct mlxsw_sp_sample_trigger *trigger)
{
struct mlxsw_sp_sample_trigger_node *trigger_node;
struct mlxsw_sp_sample_trigger key;
mlxsw_sp_sample_trigger_key_init(&key, trigger);
trigger_node = rhashtable_lookup(&mlxsw_sp->sample_trigger_ht, &key,
mlxsw_sp_sample_trigger_ht_params);
if (!trigger_node)
return NULL;
return &trigger_node->params;
}
static int
mlxsw_sp_sample_trigger_node_init(struct mlxsw_sp *mlxsw_sp,
const struct mlxsw_sp_sample_trigger *trigger,
const struct mlxsw_sp_sample_params *params)
{
struct mlxsw_sp_sample_trigger_node *trigger_node;
int err;
trigger_node = kzalloc(sizeof(*trigger_node), GFP_KERNEL);
if (!trigger_node)
return -ENOMEM;
trigger_node->trigger = *trigger;
trigger_node->params = *params;
refcount_set(&trigger_node->refcount, 1);
err = rhashtable_insert_fast(&mlxsw_sp->sample_trigger_ht,
&trigger_node->ht_node,
mlxsw_sp_sample_trigger_ht_params);
if (err)
goto err_rhashtable_insert;
return 0;
err_rhashtable_insert:
kfree(trigger_node);
return err;
}
static void
mlxsw_sp_sample_trigger_node_fini(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_sample_trigger_node *trigger_node)
{
rhashtable_remove_fast(&mlxsw_sp->sample_trigger_ht,
&trigger_node->ht_node,
mlxsw_sp_sample_trigger_ht_params);
kfree_rcu(trigger_node, rcu);
}
int
mlxsw_sp_sample_trigger_params_set(struct mlxsw_sp *mlxsw_sp,
const struct mlxsw_sp_sample_trigger *trigger,
const struct mlxsw_sp_sample_params *params,
struct netlink_ext_ack *extack)
{
struct mlxsw_sp_sample_trigger_node *trigger_node;
struct mlxsw_sp_sample_trigger key;
ASSERT_RTNL();
mlxsw_sp_sample_trigger_key_init(&key, trigger);
trigger_node = rhashtable_lookup_fast(&mlxsw_sp->sample_trigger_ht,
&key,
mlxsw_sp_sample_trigger_ht_params);
if (!trigger_node)
return mlxsw_sp_sample_trigger_node_init(mlxsw_sp, &key,
params);
if (trigger_node->params.psample_group != params->psample_group ||
trigger_node->params.truncate != params->truncate ||
trigger_node->params.rate != params->rate ||
trigger_node->params.trunc_size != params->trunc_size) {
NL_SET_ERR_MSG_MOD(extack, "Sampling parameters do not match for an existing sampling trigger");
return -EINVAL;
}
refcount_inc(&trigger_node->refcount);
return 0;
}
void
mlxsw_sp_sample_trigger_params_unset(struct mlxsw_sp *mlxsw_sp,
const struct mlxsw_sp_sample_trigger *trigger)
{
struct mlxsw_sp_sample_trigger_node *trigger_node;
struct mlxsw_sp_sample_trigger key;
ASSERT_RTNL();
mlxsw_sp_sample_trigger_key_init(&key, trigger);
trigger_node = rhashtable_lookup_fast(&mlxsw_sp->sample_trigger_ht,
&key,
mlxsw_sp_sample_trigger_ht_params);
if (!trigger_node)
return;
if (!refcount_dec_and_test(&trigger_node->refcount))
return;
mlxsw_sp_sample_trigger_node_fini(mlxsw_sp, trigger_node);
}
static int mlxsw_sp_netdevice_event(struct notifier_block *unused,
unsigned long event, void *ptr);
......@@ -2704,6 +2842,13 @@ static int mlxsw_sp_init(struct mlxsw_core *mlxsw_core,
goto err_port_module_info_init;
}
err = rhashtable_init(&mlxsw_sp->sample_trigger_ht,
&mlxsw_sp_sample_trigger_ht_params);
if (err) {
dev_err(mlxsw_sp->bus_info->dev, "Failed to init sampling trigger hashtable\n");
goto err_sample_trigger_init;
}
err = mlxsw_sp_ports_create(mlxsw_sp);
if (err) {
dev_err(mlxsw_sp->bus_info->dev, "Failed to create ports\n");
......@@ -2713,6 +2858,8 @@ static int mlxsw_sp_init(struct mlxsw_core *mlxsw_core,
return 0;
err_ports_create:
rhashtable_destroy(&mlxsw_sp->sample_trigger_ht);
err_sample_trigger_init:
mlxsw_sp_port_module_info_fini(mlxsw_sp);
err_port_module_info_init:
mlxsw_sp_dpipe_fini(mlxsw_sp);
......@@ -2847,6 +2994,7 @@ static void mlxsw_sp_fini(struct mlxsw_core *mlxsw_core)
struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core);
mlxsw_sp_ports_remove(mlxsw_sp);
rhashtable_destroy(&mlxsw_sp->sample_trigger_ht);
mlxsw_sp_port_module_info_fini(mlxsw_sp);
mlxsw_sp_dpipe_fini(mlxsw_sp);
unregister_netdevice_notifier_net(mlxsw_sp_net(mlxsw_sp),
......
......@@ -16,6 +16,7 @@
#include <linux/in6.h>
#include <linux/notifier.h>
#include <linux/net_namespace.h>
#include <linux/spinlock.h>
#include <net/psample.h>
#include <net/pkt_cls.h>
#include <net/red.h>
......@@ -133,6 +134,7 @@ struct mlxsw_sp_ptp_state;
struct mlxsw_sp_ptp_ops;
struct mlxsw_sp_span_ops;
struct mlxsw_sp_qdisc_state;
struct mlxsw_sp_mall_entry;
struct mlxsw_sp_port_mapping {
u8 module;
......@@ -148,6 +150,7 @@ struct mlxsw_sp {
const unsigned char *mac_mask;
struct mlxsw_sp_upper *lags;
struct mlxsw_sp_port_mapping **port_mapping;
struct rhashtable sample_trigger_ht;
struct mlxsw_sp_sb *sb;
struct mlxsw_sp_bridge *bridge;
struct mlxsw_sp_router *router;
......@@ -233,12 +236,22 @@ struct mlxsw_sp_port_pcpu_stats {
u32 tx_dropped;
};
struct mlxsw_sp_port_sample {
enum mlxsw_sp_sample_trigger_type {
MLXSW_SP_SAMPLE_TRIGGER_TYPE_INGRESS,
MLXSW_SP_SAMPLE_TRIGGER_TYPE_EGRESS,
MLXSW_SP_SAMPLE_TRIGGER_TYPE_POLICY_ENGINE,
};
struct mlxsw_sp_sample_trigger {
enum mlxsw_sp_sample_trigger_type type;
u8 local_port; /* Reserved when trigger type is not ingress / egress. */
};
struct mlxsw_sp_sample_params {
struct psample_group *psample_group;
u32 trunc_size;
u32 rate;
bool truncate;
int span_id; /* Relevant for Spectrum-2 onwards. */
};
struct mlxsw_sp_bridge_port;
......@@ -304,7 +317,6 @@ struct mlxsw_sp_port {
struct mlxsw_sp_port_xstats xstats;
struct delayed_work update_dw;
} periodic_hw_stats;
struct mlxsw_sp_port_sample __rcu *sample;
struct list_head vlans_list;
struct mlxsw_sp_port_vlan *default_vlan;
struct mlxsw_sp_qdisc_state *qdisc;
......@@ -533,6 +545,17 @@ void mlxsw_sp_hdroom_bufs_reset_sizes(struct mlxsw_sp_port *mlxsw_sp_port,
struct mlxsw_sp_hdroom *hdroom);
int mlxsw_sp_hdroom_configure(struct mlxsw_sp_port *mlxsw_sp_port,
const struct mlxsw_sp_hdroom *hdroom);
struct mlxsw_sp_sample_params *
mlxsw_sp_sample_trigger_params_lookup(struct mlxsw_sp *mlxsw_sp,
const struct mlxsw_sp_sample_trigger *trigger);
int
mlxsw_sp_sample_trigger_params_set(struct mlxsw_sp *mlxsw_sp,
const struct mlxsw_sp_sample_trigger *trigger,
const struct mlxsw_sp_sample_params *params,
struct netlink_ext_ack *extack);
void
mlxsw_sp_sample_trigger_params_unset(struct mlxsw_sp *mlxsw_sp,
const struct mlxsw_sp_sample_trigger *trigger);
extern const struct mlxsw_sp_sb_vals mlxsw_sp1_sb_vals;
extern const struct mlxsw_sp_sb_vals mlxsw_sp2_sb_vals;
......@@ -924,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,
struct mlxsw_sp_acl_rule_info *rulei,
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;
......@@ -1035,9 +1064,12 @@ extern const struct mlxsw_afk_ops mlxsw_sp2_afk_ops;
/* spectrum_matchall.c */
struct mlxsw_sp_mall_ops {
int (*sample_add)(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_port *mlxsw_sp_port, u32 rate);
struct mlxsw_sp_port *mlxsw_sp_port,
struct mlxsw_sp_mall_entry *mall_entry,
struct netlink_ext_ack *extack);
void (*sample_del)(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_port *mlxsw_sp_port);
struct mlxsw_sp_port *mlxsw_sp_port,
struct mlxsw_sp_mall_entry *mall_entry);
};
extern const struct mlxsw_sp_mall_ops mlxsw_sp1_mall_ops;
......@@ -1058,6 +1090,11 @@ struct mlxsw_sp_mall_trap_entry {
int span_id;
};
struct mlxsw_sp_mall_sample_entry {
struct mlxsw_sp_sample_params params;
int span_id; /* Relevant for Spectrum-2 onwards. */
};
struct mlxsw_sp_mall_entry {
struct list_head list;
unsigned long cookie;
......@@ -1067,7 +1104,7 @@ struct mlxsw_sp_mall_entry {
union {
struct mlxsw_sp_mall_mirror_entry mirror;
struct mlxsw_sp_mall_trap_entry trap;
struct mlxsw_sp_port_sample sample;
struct mlxsw_sp_mall_sample_entry sample;
};
struct rcu_head rcu;
};
......@@ -1078,7 +1115,8 @@ int mlxsw_sp_mall_replace(struct mlxsw_sp *mlxsw_sp,
void mlxsw_sp_mall_destroy(struct mlxsw_sp_flow_block *block,
struct tc_cls_matchall_offload *f);
int mlxsw_sp_mall_port_bind(struct mlxsw_sp_flow_block *block,
struct mlxsw_sp_port *mlxsw_sp_port);
struct mlxsw_sp_port *mlxsw_sp_port,
struct netlink_ext_ack *extack);
void mlxsw_sp_mall_port_unbind(struct mlxsw_sp_flow_block *block,
struct mlxsw_sp_port *mlxsw_sp_port);
int mlxsw_sp_mall_prio_get(struct mlxsw_sp_flow_block *block, u32 chain_index,
......
......@@ -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);
}
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 *
mlxsw_sp_acl_rule_create(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_acl_ruleset *ruleset,
......
......@@ -192,6 +192,22 @@ static void mlxsw_sp_act_policer_del(void *priv, u16 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 = {
.kvdl_set_add = mlxsw_sp1_act_kvdl_set_add,
.kvdl_set_del = mlxsw_sp_act_kvdl_set_del,
......@@ -204,8 +220,73 @@ const struct mlxsw_afa_ops mlxsw_sp1_act_afa_ops = {
.mirror_del = mlxsw_sp_act_mirror_del,
.policer_add = mlxsw_sp_act_policer_add,
.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 = {
.kvdl_set_add = mlxsw_sp2_act_kvdl_set_add,
.kvdl_set_del = mlxsw_sp_act_kvdl_set_del,
......@@ -218,6 +299,8 @@ const struct mlxsw_afa_ops mlxsw_sp2_act_afa_ops = {
.mirror_del = mlxsw_sp_act_mirror_del,
.policer_add = mlxsw_sp_act_policer_add,
.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,
};
......
......@@ -71,7 +71,7 @@ static int mlxsw_sp_flow_block_bind(struct mlxsw_sp *mlxsw_sp,
return -EOPNOTSUPP;
}
err = mlxsw_sp_mall_port_bind(block, mlxsw_sp_port);
err = mlxsw_sp_mall_port_bind(block, mlxsw_sp_port, extack);
if (err)
return err;
......
......@@ -24,6 +24,7 @@ static int mlxsw_sp_flower_parse_actions(struct mlxsw_sp *mlxsw_sp,
const struct flow_action_entry *act;
int mirror_act_count = 0;
int police_act_count = 0;
int sample_act_count = 0;
int err, i;
if (!flow_action_has_entries(flow_action))
......@@ -209,6 +210,23 @@ static int mlxsw_sp_flower_parse_actions(struct mlxsw_sp *mlxsw_sp,
return err;
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:
NL_SET_ERR_MSG_MOD(extack, "Unsupported action");
dev_err(mlxsw_sp->bus_info->dev, "Unsupported action\n");
......
......@@ -51,8 +51,12 @@ enum {
enum {
/* Packet was mirrored from ingress. */
MLXSW_SP_MIRROR_REASON_INGRESS = 1,
/* Packet was mirrored from policy engine. */
MLXSW_SP_MIRROR_REASON_POLICY_ENGINE = 2,
/* Packet was early dropped. */
MLXSW_SP_MIRROR_REASON_INGRESS_WRED = 9,
/* Packet was mirrored from egress. */
MLXSW_SP_MIRROR_REASON_EGRESS = 14,
};
static int mlxsw_sp_rx_listener(struct mlxsw_sp *mlxsw_sp, struct sk_buff *skb,
......@@ -257,8 +261,9 @@ static void mlxsw_sp_rx_sample_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;
struct mlxsw_sp_sample_params *params;
struct mlxsw_sp_port *mlxsw_sp_port;
struct mlxsw_sp_port_sample *sample;
struct psample_metadata md = {};
int err;
......@@ -270,8 +275,10 @@ static void mlxsw_sp_rx_sample_listener(struct sk_buff *skb, u8 local_port,
if (!mlxsw_sp_port)
goto out;
sample = rcu_dereference(mlxsw_sp_port->sample);
if (!sample)
trigger.type = MLXSW_SP_SAMPLE_TRIGGER_TYPE_INGRESS;
trigger.local_port = local_port;
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
......@@ -279,9 +286,95 @@ static void mlxsw_sp_rx_sample_listener(struct sk_buff *skb, u8 local_port,
*/
skb_push(skb, ETH_HLEN);
mlxsw_sp_psample_md_init(mlxsw_sp, &md, skb,
mlxsw_sp_port->dev->ifindex, sample->truncate,
sample->trunc_size);
psample_sample_packet(sample->psample_group, skb, sample->rate, &md);
mlxsw_sp_port->dev->ifindex, params->truncate,
params->trunc_size);
psample_sample_packet(params->psample_group, skb, params->rate, &md);
out:
consume_skb(skb);
}
static void mlxsw_sp_rx_sample_tx_listener(struct sk_buff *skb, u8 local_port,
void *trap_ctx)
{
struct mlxsw_rx_md_info *rx_md_info = &mlxsw_skb_cb(skb)->rx_md_info;
struct mlxsw_sp *mlxsw_sp = devlink_trap_ctx_priv(trap_ctx);
struct mlxsw_sp_port *mlxsw_sp_port, *mlxsw_sp_port_tx;
struct mlxsw_sp_sample_trigger trigger;
struct mlxsw_sp_sample_params *params;
struct psample_metadata md = {};
int err;
/* Locally generated packets are not reported from the policy engine
* trigger, so do not report them from the egress trigger as well.
*/
if (local_port == MLXSW_PORT_CPU_PORT)
goto out;
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;
/* Packet was sampled from Tx, so we need to retrieve the sample
* parameters based on the Tx port and not the Rx port.
*/
mlxsw_sp_port_tx = mlxsw_sp_sample_tx_port_get(mlxsw_sp, rx_md_info);
if (!mlxsw_sp_port_tx)
goto out;
trigger.type = MLXSW_SP_SAMPLE_TRIGGER_TYPE_EGRESS;
trigger.local_port = mlxsw_sp_port_tx->local_port;
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);
}
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);
}
......@@ -1840,6 +1933,12 @@ mlxsw_sp2_trap_items_arr[] = {
MLXSW_RXL_MIRROR(mlxsw_sp_rx_sample_listener, 1,
SP_PKT_SAMPLE,
MLXSW_SP_MIRROR_REASON_INGRESS),
MLXSW_RXL_MIRROR(mlxsw_sp_rx_sample_tx_listener, 1,
SP_PKT_SAMPLE,
MLXSW_SP_MIRROR_REASON_EGRESS),
MLXSW_RXL_MIRROR(mlxsw_sp_rx_sample_acl_listener, 1,
SP_PKT_SAMPLE,
MLXSW_SP_MIRROR_REASON_POLICY_ENGINE),
},
},
};
......
......@@ -18,6 +18,7 @@ NUM_NETIFS=2
source $lib_dir/tc_common.sh
source $lib_dir/lib.sh
source $lib_dir/devlink_lib.sh
switch_create()
{
......@@ -166,7 +167,8 @@ matchall_sample_egress_test()
RET=0
# It is forbidden in mlxsw driver to have matchall with sample action
# bound on egress
# bound on egress. Spectrum-1 specific restriction
[[ "$DEVLINK_VIDDID" != "15b3:cb84" ]] && return
tc qdisc add dev $swp1 clsact
......
......@@ -41,6 +41,10 @@ ALL_TESTS="
tc_sample_md_lag_oif_test
tc_sample_md_out_tc_test
tc_sample_md_out_tc_occ_test
tc_sample_md_latency_test
tc_sample_acl_group_conflict_test
tc_sample_acl_rate_test
tc_sample_acl_max_rate_test
"
NUM_NETIFS=8
CAPTURE_FILE=$(mktemp)
......@@ -482,6 +486,137 @@ tc_sample_md_out_tc_occ_test()
tc filter del dev $rp1 ingress protocol all pref 1 handle 101 matchall
}
tc_sample_md_latency_test()
{
RET=0
# Egress sampling not supported on Spectrum-1.
[[ "$DEVLINK_VIDDID" == "15b3:cb84" ]] && return
tc filter add dev $rp2 egress protocol all pref 1 handle 101 matchall \
skip_sw action sample rate 5 group 1
check_err $? "Failed to configure sampling rule"
psample_capture_start
ip vrf exec v$h1 $MZ $h1 -c 3200 -d 1msec -p 64 -A 192.0.2.1 \
-B 198.51.100.1 -t udp dp=52768,sp=42768 -q
psample_capture_stop
grep -q -e "latency " $CAPTURE_FILE
check_err $? "Sampled packets do not have latency attribute"
log_test "tc sample latency"
tc filter del dev $rp2 egress protocol all pref 1 handle 101 matchall
}
tc_sample_acl_group_conflict_test()
{
RET=0
# Test that two flower sampling rules cannot be configured on the same
# port with different groups.
# Policy-based sampling is not supported on Spectrum-1.
[[ "$DEVLINK_VIDDID" == "15b3:cb84" ]] && return
tc filter add dev $rp1 ingress protocol ip pref 1 handle 101 flower \
skip_sw action sample rate 1024 group 1
check_err $? "Failed to configure sampling rule"
tc filter add dev $rp1 ingress protocol ip pref 2 handle 102 flower \
skip_sw action sample rate 1024 group 1
check_err $? "Failed to configure sampling rule with same group"
tc filter add dev $rp1 ingress protocol ip pref 3 handle 103 flower \
skip_sw action sample rate 1024 group 2 &> /dev/null
check_fail $? "Managed to configure sampling rule with conflicting group"
log_test "tc sample (w/ flower) group conflict test"
tc filter del dev $rp1 ingress protocol ip pref 2 handle 102 flower
tc filter del dev $rp1 ingress protocol ip pref 1 handle 101 flower
}
__tc_sample_acl_rate_test()
{
local bind=$1; shift
local port=$1; shift
local pkts pct
RET=0
# Policy-based sampling is not supported on Spectrum-1.
[[ "$DEVLINK_VIDDID" == "15b3:cb84" ]] && return
tc filter add dev $port $bind protocol ip pref 1 handle 101 flower \
skip_sw dst_ip 198.51.100.1 action sample rate 32 group 1
check_err $? "Failed to configure sampling rule"
psample_capture_start
ip vrf exec v$h1 $MZ $h1 -c 3200 -d 1msec -p 64 -A 192.0.2.1 \
-B 198.51.100.1 -t udp dp=52768,sp=42768 -q
psample_capture_stop
pkts=$(grep -e "group 1 " $CAPTURE_FILE | wc -l)
pct=$((100 * (pkts - 100) / 100))
(( -25 <= pct && pct <= 25))
check_err $? "Expected 100 packets, got $pkts packets, which is $pct% off. Required accuracy is +-25%"
# Setup a filter that should not match any packet and make sure packets
# are not sampled.
tc filter del dev $port $bind protocol ip pref 1 handle 101 flower
tc filter add dev $port $bind protocol ip pref 1 handle 101 flower \
skip_sw dst_ip 198.51.100.10 action sample rate 32 group 1
check_err $? "Failed to configure sampling rule"
psample_capture_start
ip vrf exec v$h1 $MZ $h1 -c 3200 -d 1msec -p 64 -A 192.0.2.1 \
-B 198.51.100.1 -t udp dp=52768,sp=42768 -q
psample_capture_stop
grep -q -e "group 1 " $CAPTURE_FILE
check_fail $? "Sampled packets when should not"
log_test "tc sample (w/ flower) rate ($bind)"
tc filter del dev $port $bind protocol ip pref 1 handle 101 flower
}
tc_sample_acl_rate_test()
{
__tc_sample_acl_rate_test ingress $rp1
__tc_sample_acl_rate_test egress $rp2
}
tc_sample_acl_max_rate_test()
{
RET=0
# Policy-based sampling is not supported on Spectrum-1.
[[ "$DEVLINK_VIDDID" == "15b3:cb84" ]] && return
tc filter add dev $rp1 ingress protocol ip pref 1 handle 101 flower \
skip_sw action sample rate $((2 ** 24 - 1)) group 1
check_err $? "Failed to configure sampling rule with max rate"
tc filter del dev $rp1 ingress protocol ip pref 1 handle 101 flower
tc filter add dev $rp1 ingress protocol ip pref 1 handle 101 flower \
skip_sw action sample rate $((2 ** 24)) \
group 1 &> /dev/null
check_fail $? "Managed to configure sampling rate above maximum"
log_test "tc sample (w/ flower) maximum rate"
}
trap cleanup EXIT
setup_prepare
......
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