mlxsw: spectrum: Track sampling triggers in a hash table

Currently, mlxsw supports a single sampling trigger type (i.e., received
packet). When sampling is configured on an ingress port, the sampling
parameters (e.g., pointer to the psample group) are stored as an
attribute of the port, so that they could be passed to
psample_sample_packet() when a sampled packet is trapped to the CPU.

Subsequent patches are going to add more types of sampling triggers,
making it difficult to maintain the current scheme.

Instead, store all the active sampling triggers with their associated
parameters in a hash table. That way, more trigger types can be easily
added.

The next patch will flip mlxsw to use the hash table instead of the
current scheme.

Signed-off-by: Ido Schimmel <idosch@nvidia.com>
Reviewed-by: Jiri Pirko <jiri@nvidia.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Ido Schimmel 2021-03-16 17:02:57 +02:00 committed by David S. Miller
parent e09a59555a
commit 1b9fc42e46
2 changed files with 178 additions and 0 deletions

View File

@ -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 = {
.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,
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,
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)
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),

View File

@ -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>
@ -149,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;
@ -234,6 +236,23 @@ struct mlxsw_sp_port_pcpu_stats {
u32 tx_dropped; u32 tx_dropped;
}; };
enum mlxsw_sp_sample_trigger_type {
MLXSW_SP_SAMPLE_TRIGGER_TYPE_INGRESS,
MLXSW_SP_SAMPLE_TRIGGER_TYPE_EGRESS,
};
struct mlxsw_sp_sample_trigger {
enum mlxsw_sp_sample_trigger_type type;
u8 local_port;
};
struct mlxsw_sp_sample_params {
struct psample_group *psample_group;
u32 trunc_size;
u32 rate;
bool truncate;
};
struct mlxsw_sp_port_sample { struct mlxsw_sp_port_sample {
struct psample_group *psample_group; struct psample_group *psample_group;
u32 trunc_size; u32 trunc_size;
@ -534,6 +553,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;