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: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
46bb5a9c8b
@ -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, ¶ms,
|
||||
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");
|
||||
|
@ -24,7 +24,8 @@ mlxsw_sp_mall_entry_find(struct mlxsw_sp_flow_block *block, unsigned long cookie
|
||||
|
||||
static int
|
||||
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_span_agent_parms agent_parms = {};
|
||||
@ -33,20 +34,24 @@ mlxsw_sp_mall_port_mirror_add(struct mlxsw_sp_port *mlxsw_sp_port,
|
||||
int err;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
agent_parms.to_dev = mall_entry->mirror.to_dev;
|
||||
err = mlxsw_sp_span_agent_get(mlxsw_sp, &mall_entry->mirror.span_id,
|
||||
&agent_parms);
|
||||
if (err)
|
||||
if (err) {
|
||||
NL_SET_ERR_MSG(extack, "Failed to get SPAN agent");
|
||||
return err;
|
||||
}
|
||||
|
||||
err = mlxsw_sp_span_analyzed_port_get(mlxsw_sp_port,
|
||||
mall_entry->ingress);
|
||||
if (err)
|
||||
if (err) {
|
||||
NL_SET_ERR_MSG(extack, "Failed to get analyzed port");
|
||||
goto err_analyzed_port_get;
|
||||
}
|
||||
|
||||
trigger = mall_entry->ingress ? MLXSW_SP_SPAN_TRIGGER_INGRESS :
|
||||
MLXSW_SP_SPAN_TRIGGER_EGRESS;
|
||||
@ -54,8 +59,10 @@ mlxsw_sp_mall_port_mirror_add(struct mlxsw_sp_port *mlxsw_sp_port,
|
||||
parms.probability_rate = 1;
|
||||
err = mlxsw_sp_span_agent_bind(mlxsw_sp, trigger, mlxsw_sp_port,
|
||||
&parms);
|
||||
if (err)
|
||||
if (err) {
|
||||
NL_SET_ERR_MSG(extack, "Failed to bind SPAN agent");
|
||||
goto err_agent_bind;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
@ -94,49 +101,64 @@ static int mlxsw_sp_mall_port_sample_set(struct mlxsw_sp_port *mlxsw_sp_port,
|
||||
|
||||
static int
|
||||
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_sample_trigger trigger;
|
||||
int err;
|
||||
|
||||
if (rtnl_dereference(mlxsw_sp_port->sample)) {
|
||||
netdev_err(mlxsw_sp_port->dev, "sample already active\n");
|
||||
return -EEXIST;
|
||||
}
|
||||
rcu_assign_pointer(mlxsw_sp_port->sample, &mall_entry->sample);
|
||||
if (mall_entry->ingress)
|
||||
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;
|
||||
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,
|
||||
mall_entry->sample.rate);
|
||||
mall_entry, extack);
|
||||
if (err)
|
||||
goto err_port_sample_set;
|
||||
return 0;
|
||||
|
||||
err_port_sample_set:
|
||||
RCU_INIT_POINTER(mlxsw_sp_port->sample, NULL);
|
||||
mlxsw_sp_sample_trigger_params_unset(mlxsw_sp, &trigger);
|
||||
return err;
|
||||
}
|
||||
|
||||
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_sample_trigger trigger;
|
||||
|
||||
if (!mlxsw_sp_port->sample)
|
||||
return;
|
||||
if (mall_entry->ingress)
|
||||
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);
|
||||
RCU_INIT_POINTER(mlxsw_sp_port->sample, NULL);
|
||||
mlxsw_sp->mall_ops->sample_del(mlxsw_sp, mlxsw_sp_port, mall_entry);
|
||||
mlxsw_sp_sample_trigger_params_unset(mlxsw_sp, &trigger);
|
||||
}
|
||||
|
||||
static int
|
||||
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) {
|
||||
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:
|
||||
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:
|
||||
WARN_ON(1);
|
||||
return -EINVAL;
|
||||
@ -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);
|
||||
break;
|
||||
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;
|
||||
default:
|
||||
WARN_ON(1);
|
||||
@ -242,27 +264,17 @@ int mlxsw_sp_mall_replace(struct mlxsw_sp *mlxsw_sp,
|
||||
mall_entry->mirror.to_dev = act->dev;
|
||||
} else if (act->id == FLOW_ACTION_SAMPLE &&
|
||||
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 &&
|
||||
mall_entry->priority >= flower_min_prio) {
|
||||
NL_SET_ERR_MSG(f->common.extack, "Failed to add behind existing flower rules");
|
||||
err = -EOPNOTSUPP;
|
||||
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->sample.psample_group = act->sample.psample_group;
|
||||
mall_entry->sample.truncate = act->sample.truncate;
|
||||
mall_entry->sample.trunc_size = act->sample.trunc_size;
|
||||
mall_entry->sample.rate = act->sample.rate;
|
||||
mall_entry->sample.params.psample_group = act->sample.psample_group;
|
||||
mall_entry->sample.params.truncate = act->sample.truncate;
|
||||
mall_entry->sample.params.trunc_size = act->sample.trunc_size;
|
||||
mall_entry->sample.params.rate = act->sample.rate;
|
||||
} else {
|
||||
err = -EOPNOTSUPP;
|
||||
goto errout;
|
||||
@ -270,7 +282,7 @@ int mlxsw_sp_mall_replace(struct mlxsw_sp *mlxsw_sp,
|
||||
|
||||
list_for_each_entry(binding, &block->binding_list, list) {
|
||||
err = mlxsw_sp_mall_port_rule_add(binding->mlxsw_sp_port,
|
||||
mall_entry);
|
||||
mall_entry, f->common.extack);
|
||||
if (err)
|
||||
goto rollback;
|
||||
}
|
||||
@ -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,
|
||||
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;
|
||||
int err;
|
||||
|
||||
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)
|
||||
goto rollback;
|
||||
}
|
||||
@ -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,
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
@ -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,
|
||||
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_agent_parms agent_parms = {
|
||||
.to_dev = NULL, /* Mirror to CPU. */
|
||||
.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;
|
||||
|
||||
sample = rtnl_dereference(mlxsw_sp_port->sample);
|
||||
|
||||
err = mlxsw_sp_span_agent_get(mlxsw_sp, &sample->span_id, &agent_parms);
|
||||
if (err)
|
||||
err = mlxsw_sp_span_agent_get(mlxsw_sp, &mall_entry->sample.span_id,
|
||||
&agent_parms);
|
||||
if (err) {
|
||||
NL_SET_ERR_MSG(extack, "Failed to get SPAN agent");
|
||||
return err;
|
||||
}
|
||||
|
||||
err = mlxsw_sp_span_analyzed_port_get(mlxsw_sp_port, true);
|
||||
if (err)
|
||||
err = mlxsw_sp_span_analyzed_port_get(mlxsw_sp_port,
|
||||
mall_entry->ingress);
|
||||
if (err) {
|
||||
NL_SET_ERR_MSG(extack, "Failed to get analyzed port");
|
||||
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;
|
||||
err = mlxsw_sp_span_agent_bind(mlxsw_sp, MLXSW_SP_SPAN_TRIGGER_INGRESS,
|
||||
mlxsw_sp_port, &trigger_parms);
|
||||
if (err)
|
||||
err = mlxsw_sp_span_agent_bind(mlxsw_sp, span_trigger, mlxsw_sp_port,
|
||||
&trigger_parms);
|
||||
if (err) {
|
||||
NL_SET_ERR_MSG(extack, "Failed to bind SPAN agent");
|
||||
goto err_agent_bind;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
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:
|
||||
mlxsw_sp_span_agent_put(mlxsw_sp, sample->span_id);
|
||||
mlxsw_sp_span_agent_put(mlxsw_sp, mall_entry->sample.span_id);
|
||||
return err;
|
||||
}
|
||||
|
||||
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_port_sample *sample;
|
||||
enum mlxsw_sp_span_trigger span_trigger;
|
||||
|
||||
sample = rtnl_dereference(mlxsw_sp_port->sample);
|
||||
|
||||
trigger_parms.span_id = sample->span_id;
|
||||
mlxsw_sp_span_agent_unbind(mlxsw_sp, MLXSW_SP_SPAN_TRIGGER_INGRESS,
|
||||
mlxsw_sp_port, &trigger_parms);
|
||||
mlxsw_sp_span_analyzed_port_put(mlxsw_sp_port, true);
|
||||
mlxsw_sp_span_agent_put(mlxsw_sp, 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;
|
||||
mlxsw_sp_span_agent_unbind(mlxsw_sp, span_trigger, mlxsw_sp_port,
|
||||
&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 = {
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user