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, ...@@ -2007,3 +2007,134 @@ int mlxsw_afa_block_append_l4port(struct mlxsw_afa_block *block, bool is_dport,
return 0; return 0;
} }
EXPORT_SYMBOL(mlxsw_afa_block_append_l4port); 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 { ...@@ -30,6 +30,12 @@ struct mlxsw_afa_ops {
u16 *p_policer_index, u16 *p_policer_index,
struct netlink_ext_ack *extack); struct netlink_ext_ack *extack);
void (*policer_del)(void *priv, u16 policer_index); 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; bool dummy_first_set;
}; };
...@@ -92,5 +98,10 @@ int mlxsw_afa_block_append_police(struct mlxsw_afa_block *block, ...@@ -92,5 +98,10 @@ int mlxsw_afa_block_append_police(struct mlxsw_afa_block *block,
u32 fa_index, u64 rate_bytes_ps, u32 burst, u32 fa_index, u64 rate_bytes_ps, u32 burst,
u16 *p_policer_index, u16 *p_policer_index,
struct netlink_ext_ack *extack); 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 #endif
...@@ -23,6 +23,8 @@ ...@@ -23,6 +23,8 @@
#include <linux/netlink.h> #include <linux/netlink.h>
#include <linux/jhash.h> #include <linux/jhash.h>
#include <linux/log2.h> #include <linux/log2.h>
#include <linux/refcount.h>
#include <linux/rhashtable.h>
#include <net/switchdev.h> #include <net/switchdev.h>
#include <net/pkt_cls.h> #include <net/pkt_cls.h>
#include <net/netevent.h> #include <net/netevent.h>
...@@ -2550,6 +2552,142 @@ static const struct mlxsw_sp_ptp_ops mlxsw_sp2_ptp_ops = { ...@@ -2550,6 +2552,142 @@ static const struct mlxsw_sp_ptp_ops mlxsw_sp2_ptp_ops = {
.get_stats = mlxsw_sp2_get_stats, .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, static int mlxsw_sp_netdevice_event(struct notifier_block *unused,
unsigned long event, void *ptr); unsigned long event, void *ptr);
...@@ -2704,6 +2842,13 @@ static int mlxsw_sp_init(struct mlxsw_core *mlxsw_core, ...@@ -2704,6 +2842,13 @@ static int mlxsw_sp_init(struct mlxsw_core *mlxsw_core,
goto err_port_module_info_init; 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); err = mlxsw_sp_ports_create(mlxsw_sp);
if (err) { if (err) {
dev_err(mlxsw_sp->bus_info->dev, "Failed to create ports\n"); 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, ...@@ -2713,6 +2858,8 @@ static int mlxsw_sp_init(struct mlxsw_core *mlxsw_core,
return 0; return 0;
err_ports_create: err_ports_create:
rhashtable_destroy(&mlxsw_sp->sample_trigger_ht);
err_sample_trigger_init:
mlxsw_sp_port_module_info_fini(mlxsw_sp); mlxsw_sp_port_module_info_fini(mlxsw_sp);
err_port_module_info_init: err_port_module_info_init:
mlxsw_sp_dpipe_fini(mlxsw_sp); mlxsw_sp_dpipe_fini(mlxsw_sp);
...@@ -2847,6 +2994,7 @@ static void mlxsw_sp_fini(struct mlxsw_core *mlxsw_core) ...@@ -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); struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core);
mlxsw_sp_ports_remove(mlxsw_sp); mlxsw_sp_ports_remove(mlxsw_sp);
rhashtable_destroy(&mlxsw_sp->sample_trigger_ht);
mlxsw_sp_port_module_info_fini(mlxsw_sp); mlxsw_sp_port_module_info_fini(mlxsw_sp);
mlxsw_sp_dpipe_fini(mlxsw_sp); mlxsw_sp_dpipe_fini(mlxsw_sp);
unregister_netdevice_notifier_net(mlxsw_sp_net(mlxsw_sp), unregister_netdevice_notifier_net(mlxsw_sp_net(mlxsw_sp),
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include <linux/in6.h> #include <linux/in6.h>
#include <linux/notifier.h> #include <linux/notifier.h>
#include <linux/net_namespace.h> #include <linux/net_namespace.h>
#include <linux/spinlock.h>
#include <net/psample.h> #include <net/psample.h>
#include <net/pkt_cls.h> #include <net/pkt_cls.h>
#include <net/red.h> #include <net/red.h>
...@@ -133,6 +134,7 @@ struct mlxsw_sp_ptp_state; ...@@ -133,6 +134,7 @@ struct mlxsw_sp_ptp_state;
struct mlxsw_sp_ptp_ops; struct mlxsw_sp_ptp_ops;
struct mlxsw_sp_span_ops; struct mlxsw_sp_span_ops;
struct mlxsw_sp_qdisc_state; struct mlxsw_sp_qdisc_state;
struct mlxsw_sp_mall_entry;
struct mlxsw_sp_port_mapping { struct mlxsw_sp_port_mapping {
u8 module; u8 module;
...@@ -148,6 +150,7 @@ struct mlxsw_sp { ...@@ -148,6 +150,7 @@ struct mlxsw_sp {
const unsigned char *mac_mask; const unsigned char *mac_mask;
struct mlxsw_sp_upper *lags; struct mlxsw_sp_upper *lags;
struct mlxsw_sp_port_mapping **port_mapping; struct mlxsw_sp_port_mapping **port_mapping;
struct rhashtable sample_trigger_ht;
struct mlxsw_sp_sb *sb; struct mlxsw_sp_sb *sb;
struct mlxsw_sp_bridge *bridge; struct mlxsw_sp_bridge *bridge;
struct mlxsw_sp_router *router; struct mlxsw_sp_router *router;
...@@ -233,12 +236,22 @@ struct mlxsw_sp_port_pcpu_stats { ...@@ -233,12 +236,22 @@ struct mlxsw_sp_port_pcpu_stats {
u32 tx_dropped; 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; struct psample_group *psample_group;
u32 trunc_size; u32 trunc_size;
u32 rate; u32 rate;
bool truncate; bool truncate;
int span_id; /* Relevant for Spectrum-2 onwards. */
}; };
struct mlxsw_sp_bridge_port; struct mlxsw_sp_bridge_port;
...@@ -304,7 +317,6 @@ struct mlxsw_sp_port { ...@@ -304,7 +317,6 @@ struct mlxsw_sp_port {
struct mlxsw_sp_port_xstats xstats; struct mlxsw_sp_port_xstats xstats;
struct delayed_work update_dw; struct delayed_work update_dw;
} periodic_hw_stats; } periodic_hw_stats;
struct mlxsw_sp_port_sample __rcu *sample;
struct list_head vlans_list; struct list_head vlans_list;
struct mlxsw_sp_port_vlan *default_vlan; struct mlxsw_sp_port_vlan *default_vlan;
struct mlxsw_sp_qdisc_state *qdisc; struct mlxsw_sp_qdisc_state *qdisc;
...@@ -533,6 +545,17 @@ void mlxsw_sp_hdroom_bufs_reset_sizes(struct mlxsw_sp_port *mlxsw_sp_port, ...@@ -533,6 +545,17 @@ void mlxsw_sp_hdroom_bufs_reset_sizes(struct mlxsw_sp_port *mlxsw_sp_port,
struct mlxsw_sp_hdroom *hdroom); struct mlxsw_sp_hdroom *hdroom);
int mlxsw_sp_hdroom_configure(struct mlxsw_sp_port *mlxsw_sp_port, int mlxsw_sp_hdroom_configure(struct mlxsw_sp_port *mlxsw_sp_port,
const struct mlxsw_sp_hdroom *hdroom); 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_sp1_sb_vals;
extern const struct mlxsw_sp_sb_vals mlxsw_sp2_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, ...@@ -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, 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;
...@@ -1035,9 +1064,12 @@ extern const struct mlxsw_afk_ops mlxsw_sp2_afk_ops; ...@@ -1035,9 +1064,12 @@ extern const struct mlxsw_afk_ops mlxsw_sp2_afk_ops;
/* spectrum_matchall.c */ /* spectrum_matchall.c */
struct mlxsw_sp_mall_ops { struct mlxsw_sp_mall_ops {
int (*sample_add)(struct mlxsw_sp *mlxsw_sp, 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, 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; extern const struct mlxsw_sp_mall_ops mlxsw_sp1_mall_ops;
...@@ -1058,6 +1090,11 @@ struct mlxsw_sp_mall_trap_entry { ...@@ -1058,6 +1090,11 @@ struct mlxsw_sp_mall_trap_entry {
int span_id; 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 mlxsw_sp_mall_entry {
struct list_head list; struct list_head list;
unsigned long cookie; unsigned long cookie;
...@@ -1067,7 +1104,7 @@ struct mlxsw_sp_mall_entry { ...@@ -1067,7 +1104,7 @@ struct mlxsw_sp_mall_entry {
union { union {
struct mlxsw_sp_mall_mirror_entry mirror; struct mlxsw_sp_mall_mirror_entry mirror;
struct mlxsw_sp_mall_trap_entry trap; struct mlxsw_sp_mall_trap_entry trap;
struct mlxsw_sp_port_sample sample; struct mlxsw_sp_mall_sample_entry sample;
}; };
struct rcu_head rcu; struct rcu_head rcu;
}; };
...@@ -1078,7 +1115,8 @@ int mlxsw_sp_mall_replace(struct mlxsw_sp *mlxsw_sp, ...@@ -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, void mlxsw_sp_mall_destroy(struct mlxsw_sp_flow_block *block,
struct tc_cls_matchall_offload *f); struct tc_cls_matchall_offload *f);
int mlxsw_sp_mall_port_bind(struct mlxsw_sp_flow_block *block, 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, void mlxsw_sp_mall_port_unbind(struct mlxsw_sp_flow_block *block,
struct mlxsw_sp_port *mlxsw_sp_port); struct mlxsw_sp_port *mlxsw_sp_port);
int mlxsw_sp_mall_prio_get(struct mlxsw_sp_flow_block *block, u32 chain_index, 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, ...@@ -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,
}; };
......
...@@ -71,7 +71,7 @@ static int mlxsw_sp_flow_block_bind(struct mlxsw_sp *mlxsw_sp, ...@@ -71,7 +71,7 @@ static int mlxsw_sp_flow_block_bind(struct mlxsw_sp *mlxsw_sp,
return -EOPNOTSUPP; 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) if (err)
return err; return err;
......
...@@ -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");
......
...@@ -24,7 +24,8 @@ mlxsw_sp_mall_entry_find(struct mlxsw_sp_flow_block *block, unsigned long cookie ...@@ -24,7 +24,8 @@ mlxsw_sp_mall_entry_find(struct mlxsw_sp_flow_block *block, unsigned long cookie
static int static int
mlxsw_sp_mall_port_mirror_add(struct mlxsw_sp_port *mlxsw_sp_port, mlxsw_sp_mall_port_mirror_add(struct mlxsw_sp_port *mlxsw_sp_port,
struct mlxsw_sp_mall_entry *mall_entry) struct mlxsw_sp_mall_entry *mall_entry,
struct netlink_ext_ack *extack)
{ {
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
struct mlxsw_sp_span_agent_parms agent_parms = {}; struct mlxsw_sp_span_agent_parms agent_parms = {};
...@@ -33,20 +34,24 @@ mlxsw_sp_mall_port_mirror_add(struct mlxsw_sp_port *mlxsw_sp_port, ...@@ -33,20 +34,24 @@ mlxsw_sp_mall_port_mirror_add(struct mlxsw_sp_port *mlxsw_sp_port,
int err; int err;
if (!mall_entry->mirror.to_dev) { if (!mall_entry->mirror.to_dev) {
netdev_err(mlxsw_sp_port->dev, "Could not find requested device\n"); NL_SET_ERR_MSG(extack, "Could not find requested device");
return -EINVAL; return -EINVAL;
} }
agent_parms.to_dev = mall_entry->mirror.to_dev; agent_parms.to_dev = mall_entry->mirror.to_dev;
err = mlxsw_sp_span_agent_get(mlxsw_sp, &mall_entry->mirror.span_id, err = mlxsw_sp_span_agent_get(mlxsw_sp, &mall_entry->mirror.span_id,
&agent_parms); &agent_parms);
if (err) if (err) {
NL_SET_ERR_MSG(extack, "Failed to get SPAN agent");
return err; return err;
}
err = mlxsw_sp_span_analyzed_port_get(mlxsw_sp_port, err = mlxsw_sp_span_analyzed_port_get(mlxsw_sp_port,
mall_entry->ingress); mall_entry->ingress);
if (err) if (err) {
NL_SET_ERR_MSG(extack, "Failed to get analyzed port");
goto err_analyzed_port_get; goto err_analyzed_port_get;
}
trigger = mall_entry->ingress ? MLXSW_SP_SPAN_TRIGGER_INGRESS : trigger = mall_entry->ingress ? MLXSW_SP_SPAN_TRIGGER_INGRESS :
MLXSW_SP_SPAN_TRIGGER_EGRESS; MLXSW_SP_SPAN_TRIGGER_EGRESS;
...@@ -54,8 +59,10 @@ mlxsw_sp_mall_port_mirror_add(struct mlxsw_sp_port *mlxsw_sp_port, ...@@ -54,8 +59,10 @@ mlxsw_sp_mall_port_mirror_add(struct mlxsw_sp_port *mlxsw_sp_port,
parms.probability_rate = 1; parms.probability_rate = 1;
err = mlxsw_sp_span_agent_bind(mlxsw_sp, trigger, mlxsw_sp_port, err = mlxsw_sp_span_agent_bind(mlxsw_sp, trigger, mlxsw_sp_port,
&parms); &parms);
if (err) if (err) {
NL_SET_ERR_MSG(extack, "Failed to bind SPAN agent");
goto err_agent_bind; goto err_agent_bind;
}
return 0; return 0;
...@@ -94,49 +101,64 @@ static int mlxsw_sp_mall_port_sample_set(struct mlxsw_sp_port *mlxsw_sp_port, ...@@ -94,49 +101,64 @@ static int mlxsw_sp_mall_port_sample_set(struct mlxsw_sp_port *mlxsw_sp_port,
static int static int
mlxsw_sp_mall_port_sample_add(struct mlxsw_sp_port *mlxsw_sp_port, mlxsw_sp_mall_port_sample_add(struct mlxsw_sp_port *mlxsw_sp_port,
struct mlxsw_sp_mall_entry *mall_entry) struct mlxsw_sp_mall_entry *mall_entry,
struct netlink_ext_ack *extack)
{ {
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
struct mlxsw_sp_sample_trigger trigger;
int err; int err;
if (rtnl_dereference(mlxsw_sp_port->sample)) { if (mall_entry->ingress)
netdev_err(mlxsw_sp_port->dev, "sample already active\n"); trigger.type = MLXSW_SP_SAMPLE_TRIGGER_TYPE_INGRESS;
return -EEXIST; else
} trigger.type = MLXSW_SP_SAMPLE_TRIGGER_TYPE_EGRESS;
rcu_assign_pointer(mlxsw_sp_port->sample, &mall_entry->sample); trigger.local_port = mlxsw_sp_port->local_port;
err = mlxsw_sp_sample_trigger_params_set(mlxsw_sp, &trigger,
&mall_entry->sample.params,
extack);
if (err)
return err;
err = mlxsw_sp->mall_ops->sample_add(mlxsw_sp, mlxsw_sp_port, err = mlxsw_sp->mall_ops->sample_add(mlxsw_sp, mlxsw_sp_port,
mall_entry->sample.rate); mall_entry, extack);
if (err) if (err)
goto err_port_sample_set; goto err_port_sample_set;
return 0; return 0;
err_port_sample_set: err_port_sample_set:
RCU_INIT_POINTER(mlxsw_sp_port->sample, NULL); mlxsw_sp_sample_trigger_params_unset(mlxsw_sp, &trigger);
return err; return err;
} }
static void static void
mlxsw_sp_mall_port_sample_del(struct mlxsw_sp_port *mlxsw_sp_port) mlxsw_sp_mall_port_sample_del(struct mlxsw_sp_port *mlxsw_sp_port,
struct mlxsw_sp_mall_entry *mall_entry)
{ {
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
struct mlxsw_sp_sample_trigger trigger;
if (!mlxsw_sp_port->sample) if (mall_entry->ingress)
return; trigger.type = MLXSW_SP_SAMPLE_TRIGGER_TYPE_INGRESS;
else
trigger.type = MLXSW_SP_SAMPLE_TRIGGER_TYPE_EGRESS;
trigger.local_port = mlxsw_sp_port->local_port;
mlxsw_sp->mall_ops->sample_del(mlxsw_sp, mlxsw_sp_port); mlxsw_sp->mall_ops->sample_del(mlxsw_sp, mlxsw_sp_port, mall_entry);
RCU_INIT_POINTER(mlxsw_sp_port->sample, NULL); mlxsw_sp_sample_trigger_params_unset(mlxsw_sp, &trigger);
} }
static int static int
mlxsw_sp_mall_port_rule_add(struct mlxsw_sp_port *mlxsw_sp_port, mlxsw_sp_mall_port_rule_add(struct mlxsw_sp_port *mlxsw_sp_port,
struct mlxsw_sp_mall_entry *mall_entry) struct mlxsw_sp_mall_entry *mall_entry,
struct netlink_ext_ack *extack)
{ {
switch (mall_entry->type) { switch (mall_entry->type) {
case MLXSW_SP_MALL_ACTION_TYPE_MIRROR: case MLXSW_SP_MALL_ACTION_TYPE_MIRROR:
return mlxsw_sp_mall_port_mirror_add(mlxsw_sp_port, mall_entry); return mlxsw_sp_mall_port_mirror_add(mlxsw_sp_port, mall_entry,
extack);
case MLXSW_SP_MALL_ACTION_TYPE_SAMPLE: case MLXSW_SP_MALL_ACTION_TYPE_SAMPLE:
return mlxsw_sp_mall_port_sample_add(mlxsw_sp_port, mall_entry); return mlxsw_sp_mall_port_sample_add(mlxsw_sp_port, mall_entry,
extack);
default: default:
WARN_ON(1); WARN_ON(1);
return -EINVAL; return -EINVAL;
...@@ -152,7 +174,7 @@ mlxsw_sp_mall_port_rule_del(struct mlxsw_sp_port *mlxsw_sp_port, ...@@ -152,7 +174,7 @@ mlxsw_sp_mall_port_rule_del(struct mlxsw_sp_port *mlxsw_sp_port,
mlxsw_sp_mall_port_mirror_del(mlxsw_sp_port, mall_entry); mlxsw_sp_mall_port_mirror_del(mlxsw_sp_port, mall_entry);
break; break;
case MLXSW_SP_MALL_ACTION_TYPE_SAMPLE: case MLXSW_SP_MALL_ACTION_TYPE_SAMPLE:
mlxsw_sp_mall_port_sample_del(mlxsw_sp_port); mlxsw_sp_mall_port_sample_del(mlxsw_sp_port, mall_entry);
break; break;
default: default:
WARN_ON(1); WARN_ON(1);
...@@ -242,27 +264,17 @@ int mlxsw_sp_mall_replace(struct mlxsw_sp *mlxsw_sp, ...@@ -242,27 +264,17 @@ int mlxsw_sp_mall_replace(struct mlxsw_sp *mlxsw_sp,
mall_entry->mirror.to_dev = act->dev; mall_entry->mirror.to_dev = act->dev;
} else if (act->id == FLOW_ACTION_SAMPLE && } else if (act->id == FLOW_ACTION_SAMPLE &&
protocol == htons(ETH_P_ALL)) { protocol == htons(ETH_P_ALL)) {
if (!mall_entry->ingress) {
NL_SET_ERR_MSG(f->common.extack, "Sample is not supported on egress");
err = -EOPNOTSUPP;
goto errout;
}
if (flower_prio_valid && if (flower_prio_valid &&
mall_entry->priority >= flower_min_prio) { mall_entry->priority >= flower_min_prio) {
NL_SET_ERR_MSG(f->common.extack, "Failed to add behind existing flower rules"); NL_SET_ERR_MSG(f->common.extack, "Failed to add behind existing flower rules");
err = -EOPNOTSUPP; err = -EOPNOTSUPP;
goto errout; goto errout;
} }
if (act->sample.rate > MLXSW_REG_MPSC_RATE_MAX) {
NL_SET_ERR_MSG(f->common.extack, "Sample rate not supported");
err = -EOPNOTSUPP;
goto errout;
}
mall_entry->type = MLXSW_SP_MALL_ACTION_TYPE_SAMPLE; mall_entry->type = MLXSW_SP_MALL_ACTION_TYPE_SAMPLE;
mall_entry->sample.psample_group = act->sample.psample_group; mall_entry->sample.params.psample_group = act->sample.psample_group;
mall_entry->sample.truncate = act->sample.truncate; mall_entry->sample.params.truncate = act->sample.truncate;
mall_entry->sample.trunc_size = act->sample.trunc_size; mall_entry->sample.params.trunc_size = act->sample.trunc_size;
mall_entry->sample.rate = act->sample.rate; mall_entry->sample.params.rate = act->sample.rate;
} else { } else {
err = -EOPNOTSUPP; err = -EOPNOTSUPP;
goto errout; goto errout;
...@@ -270,7 +282,7 @@ int mlxsw_sp_mall_replace(struct mlxsw_sp *mlxsw_sp, ...@@ -270,7 +282,7 @@ int mlxsw_sp_mall_replace(struct mlxsw_sp *mlxsw_sp,
list_for_each_entry(binding, &block->binding_list, list) { list_for_each_entry(binding, &block->binding_list, list) {
err = mlxsw_sp_mall_port_rule_add(binding->mlxsw_sp_port, err = mlxsw_sp_mall_port_rule_add(binding->mlxsw_sp_port,
mall_entry); mall_entry, f->common.extack);
if (err) if (err)
goto rollback; goto rollback;
} }
...@@ -318,13 +330,15 @@ void mlxsw_sp_mall_destroy(struct mlxsw_sp_flow_block *block, ...@@ -318,13 +330,15 @@ void mlxsw_sp_mall_destroy(struct mlxsw_sp_flow_block *block,
} }
int mlxsw_sp_mall_port_bind(struct mlxsw_sp_flow_block *block, 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)
{ {
struct mlxsw_sp_mall_entry *mall_entry; struct mlxsw_sp_mall_entry *mall_entry;
int err; int err;
list_for_each_entry(mall_entry, &block->mall.list, list) { list_for_each_entry(mall_entry, &block->mall.list, list) {
err = mlxsw_sp_mall_port_rule_add(mlxsw_sp_port, mall_entry); err = mlxsw_sp_mall_port_rule_add(mlxsw_sp_port, mall_entry,
extack);
if (err) if (err)
goto rollback; goto rollback;
} }
...@@ -362,13 +376,27 @@ int mlxsw_sp_mall_prio_get(struct mlxsw_sp_flow_block *block, u32 chain_index, ...@@ -362,13 +376,27 @@ int mlxsw_sp_mall_prio_get(struct mlxsw_sp_flow_block *block, u32 chain_index,
static int mlxsw_sp1_mall_sample_add(struct mlxsw_sp *mlxsw_sp, static int mlxsw_sp1_mall_sample_add(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_port *mlxsw_sp_port, struct mlxsw_sp_port *mlxsw_sp_port,
u32 rate) struct mlxsw_sp_mall_entry *mall_entry,
struct netlink_ext_ack *extack)
{ {
u32 rate = mall_entry->sample.params.rate;
if (!mall_entry->ingress) {
NL_SET_ERR_MSG(extack, "Sampling is not supported on egress");
return -EOPNOTSUPP;
}
if (rate > MLXSW_REG_MPSC_RATE_MAX) {
NL_SET_ERR_MSG(extack, "Unsupported sampling rate");
return -EOPNOTSUPP;
}
return mlxsw_sp_mall_port_sample_set(mlxsw_sp_port, true, rate); return mlxsw_sp_mall_port_sample_set(mlxsw_sp_port, true, rate);
} }
static void mlxsw_sp1_mall_sample_del(struct mlxsw_sp *mlxsw_sp, static void mlxsw_sp1_mall_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)
{ {
mlxsw_sp_mall_port_sample_set(mlxsw_sp_port, false, 1); mlxsw_sp_mall_port_sample_set(mlxsw_sp_port, false, 1);
} }
...@@ -380,55 +408,66 @@ const struct mlxsw_sp_mall_ops mlxsw_sp1_mall_ops = { ...@@ -380,55 +408,66 @@ const struct mlxsw_sp_mall_ops mlxsw_sp1_mall_ops = {
static int mlxsw_sp2_mall_sample_add(struct mlxsw_sp *mlxsw_sp, static int mlxsw_sp2_mall_sample_add(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_port *mlxsw_sp_port, struct mlxsw_sp_port *mlxsw_sp_port,
u32 rate) struct mlxsw_sp_mall_entry *mall_entry,
struct netlink_ext_ack *extack)
{ {
struct mlxsw_sp_span_trigger_parms trigger_parms = {}; struct mlxsw_sp_span_trigger_parms trigger_parms = {};
struct mlxsw_sp_span_agent_parms agent_parms = { struct mlxsw_sp_span_agent_parms agent_parms = {
.to_dev = NULL, /* Mirror to CPU. */ .to_dev = NULL, /* Mirror to CPU. */
.session_id = MLXSW_SP_SPAN_SESSION_ID_SAMPLING, .session_id = MLXSW_SP_SPAN_SESSION_ID_SAMPLING,
}; };
struct mlxsw_sp_port_sample *sample; u32 rate = mall_entry->sample.params.rate;
enum mlxsw_sp_span_trigger span_trigger;
int err; int err;
sample = rtnl_dereference(mlxsw_sp_port->sample); err = mlxsw_sp_span_agent_get(mlxsw_sp, &mall_entry->sample.span_id,
&agent_parms);
err = mlxsw_sp_span_agent_get(mlxsw_sp, &sample->span_id, &agent_parms); if (err) {
if (err) NL_SET_ERR_MSG(extack, "Failed to get SPAN agent");
return err; return err;
}
err = mlxsw_sp_span_analyzed_port_get(mlxsw_sp_port, true); err = mlxsw_sp_span_analyzed_port_get(mlxsw_sp_port,
if (err) mall_entry->ingress);
if (err) {
NL_SET_ERR_MSG(extack, "Failed to get analyzed port");
goto err_analyzed_port_get; goto err_analyzed_port_get;
}
trigger_parms.span_id = sample->span_id; span_trigger = mall_entry->ingress ? MLXSW_SP_SPAN_TRIGGER_INGRESS :
MLXSW_SP_SPAN_TRIGGER_EGRESS;
trigger_parms.span_id = mall_entry->sample.span_id;
trigger_parms.probability_rate = rate; trigger_parms.probability_rate = rate;
err = mlxsw_sp_span_agent_bind(mlxsw_sp, MLXSW_SP_SPAN_TRIGGER_INGRESS, err = mlxsw_sp_span_agent_bind(mlxsw_sp, span_trigger, mlxsw_sp_port,
mlxsw_sp_port, &trigger_parms); &trigger_parms);
if (err) if (err) {
NL_SET_ERR_MSG(extack, "Failed to bind SPAN agent");
goto err_agent_bind; goto err_agent_bind;
}
return 0; return 0;
err_agent_bind: err_agent_bind:
mlxsw_sp_span_analyzed_port_put(mlxsw_sp_port, true); mlxsw_sp_span_analyzed_port_put(mlxsw_sp_port, mall_entry->ingress);
err_analyzed_port_get: err_analyzed_port_get:
mlxsw_sp_span_agent_put(mlxsw_sp, sample->span_id); mlxsw_sp_span_agent_put(mlxsw_sp, mall_entry->sample.span_id);
return err; return err;
} }
static void mlxsw_sp2_mall_sample_del(struct mlxsw_sp *mlxsw_sp, static void mlxsw_sp2_mall_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)
{ {
struct mlxsw_sp_span_trigger_parms trigger_parms = {}; struct mlxsw_sp_span_trigger_parms trigger_parms = {};
struct mlxsw_sp_port_sample *sample; enum mlxsw_sp_span_trigger span_trigger;
sample = rtnl_dereference(mlxsw_sp_port->sample);
trigger_parms.span_id = sample->span_id; span_trigger = mall_entry->ingress ? MLXSW_SP_SPAN_TRIGGER_INGRESS :
mlxsw_sp_span_agent_unbind(mlxsw_sp, MLXSW_SP_SPAN_TRIGGER_INGRESS, MLXSW_SP_SPAN_TRIGGER_EGRESS;
mlxsw_sp_port, &trigger_parms); trigger_parms.span_id = mall_entry->sample.span_id;
mlxsw_sp_span_analyzed_port_put(mlxsw_sp_port, true); mlxsw_sp_span_agent_unbind(mlxsw_sp, span_trigger, mlxsw_sp_port,
mlxsw_sp_span_agent_put(mlxsw_sp, sample->span_id); &trigger_parms);
mlxsw_sp_span_analyzed_port_put(mlxsw_sp_port, mall_entry->ingress);
mlxsw_sp_span_agent_put(mlxsw_sp, mall_entry->sample.span_id);
} }
const struct mlxsw_sp_mall_ops mlxsw_sp2_mall_ops = { const struct mlxsw_sp_mall_ops mlxsw_sp2_mall_ops = {
......
...@@ -51,8 +51,12 @@ enum { ...@@ -51,8 +51,12 @@ 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. */
MLXSW_SP_MIRROR_REASON_EGRESS = 14,
}; };
static int mlxsw_sp_rx_listener(struct mlxsw_sp *mlxsw_sp, struct sk_buff *skb, 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, ...@@ -257,8 +261,9 @@ static void mlxsw_sp_rx_sample_listener(struct sk_buff *skb, u8 local_port,
void *trap_ctx) void *trap_ctx)
{ {
struct mlxsw_sp *mlxsw_sp = devlink_trap_ctx_priv(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 *mlxsw_sp_port;
struct mlxsw_sp_port_sample *sample;
struct psample_metadata md = {}; struct psample_metadata md = {};
int err; int err;
...@@ -270,8 +275,10 @@ static void mlxsw_sp_rx_sample_listener(struct sk_buff *skb, u8 local_port, ...@@ -270,8 +275,10 @@ static void mlxsw_sp_rx_sample_listener(struct sk_buff *skb, u8 local_port,
if (!mlxsw_sp_port) if (!mlxsw_sp_port)
goto out; goto out;
sample = rcu_dereference(mlxsw_sp_port->sample); trigger.type = MLXSW_SP_SAMPLE_TRIGGER_TYPE_INGRESS;
if (!sample) trigger.local_port = local_port;
params = mlxsw_sp_sample_trigger_params_lookup(mlxsw_sp, &trigger);
if (!params)
goto out; goto out;
/* The psample module expects skb->data to point to the start of the /* 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, ...@@ -279,9 +286,95 @@ static void mlxsw_sp_rx_sample_listener(struct sk_buff *skb, u8 local_port,
*/ */
skb_push(skb, ETH_HLEN); skb_push(skb, ETH_HLEN);
mlxsw_sp_psample_md_init(mlxsw_sp, &md, skb, mlxsw_sp_psample_md_init(mlxsw_sp, &md, skb,
mlxsw_sp_port->dev->ifindex, sample->truncate, mlxsw_sp_port->dev->ifindex, params->truncate,
sample->trunc_size); params->trunc_size);
psample_sample_packet(sample->psample_group, skb, sample->rate, &md); 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: out:
consume_skb(skb); consume_skb(skb);
} }
...@@ -1840,6 +1933,12 @@ mlxsw_sp2_trap_items_arr[] = { ...@@ -1840,6 +1933,12 @@ mlxsw_sp2_trap_items_arr[] = {
MLXSW_RXL_MIRROR(mlxsw_sp_rx_sample_listener, 1, MLXSW_RXL_MIRROR(mlxsw_sp_rx_sample_listener, 1,
SP_PKT_SAMPLE, SP_PKT_SAMPLE,
MLXSW_SP_MIRROR_REASON_INGRESS), 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 ...@@ -18,6 +18,7 @@ NUM_NETIFS=2
source $lib_dir/tc_common.sh source $lib_dir/tc_common.sh
source $lib_dir/lib.sh source $lib_dir/lib.sh
source $lib_dir/devlink_lib.sh
switch_create() switch_create()
{ {
...@@ -166,7 +167,8 @@ matchall_sample_egress_test() ...@@ -166,7 +167,8 @@ matchall_sample_egress_test()
RET=0 RET=0
# It is forbidden in mlxsw driver to have matchall with sample action # 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 tc qdisc add dev $swp1 clsact
......
...@@ -41,6 +41,10 @@ ALL_TESTS=" ...@@ -41,6 +41,10 @@ ALL_TESTS="
tc_sample_md_lag_oif_test tc_sample_md_lag_oif_test
tc_sample_md_out_tc_test tc_sample_md_out_tc_test
tc_sample_md_out_tc_occ_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 NUM_NETIFS=8
CAPTURE_FILE=$(mktemp) CAPTURE_FILE=$(mktemp)
...@@ -482,6 +486,137 @@ tc_sample_md_out_tc_occ_test() ...@@ -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 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 trap cleanup EXIT
setup_prepare 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