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

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
Loading
Loading
Loading
Loading
+131 −0
Original line number Diff line number Diff line
@@ -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 −0
Original line number Diff line number Diff line
@@ -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
+148 −0
Original line number Diff line number Diff line
@@ -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),
+45 −7
Original line number Diff line number Diff line
@@ -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,
+25 −0
Original line number Diff line number Diff line
@@ -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,
Loading