Skip to content

Commit

Permalink
Merge branch 'mlxsw-Add-support-for-egress-and-policy-based-sampling'
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
David S. Miller committed Mar 16, 2021
2 parents 5bdbdb8 + 0f967d9 commit 46bb5a9
Show file tree
Hide file tree
Showing 12 changed files with 808 additions and 79 deletions.
131 changes: 131 additions & 0 deletions drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
11 changes: 11 additions & 0 deletions drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};

Expand Down Expand Up @@ -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
148 changes: 148 additions & 0 deletions drivers/net/ethernet/mellanox/mlxsw/spectrum.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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");
Expand All @@ -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);
Expand Down Expand Up @@ -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),
Expand Down
Loading

0 comments on commit 46bb5a9

Please sign in to comment.