Commit 1520929e authored by David S. Miller's avatar David S. Miller

Merge branch 'mlxsw-Implement-sampling-using-mirroring'

Ido Schimmel says:

====================
mlxsw: Implement sampling using mirroring

So far, sampling was implemented using a dedicated sampling mechanism
that is available on all Spectrum ASICs. Spectrum-2 and later ASICs
support sampling by mirroring packets to the CPU port with probability.
This method has a couple of advantages compared to the legacy method:

* Extra metadata per-packet: Egress port, egress traffic class, traffic
  class occupancy and end-to-end latency
* Ability to sample packets on egress / per-flow as opposed to only
  ingress

This series should not result in any user-visible changes and its aim is
to convert Spectrum-2 and later ASICs to perform sampling by mirroring
to the CPU port with probability. Future submissions will expose the
additional metadata and enable sampling using more triggers (e.g.,
egress).

Series overview:

Patches #1-#3 extend the SPAN (mirroring) module to accept new
parameters required for sampling. See individual commit messages for
detailed explanation.

Patch #4-#5 split sampling support between Spectrum-1 and later ASIC while
still using the legacy method for all ASIC generations.

Patch #6 converts Spectrum-2 and later ASICs to perform sampling by
mirroring to the CPU port with probability.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 1bc61c9d cf31190a
......@@ -9925,15 +9925,28 @@ MLXSW_ITEM32(reg, mpar, enable, 0x04, 31, 1);
*/
MLXSW_ITEM32(reg, mpar, pa_id, 0x04, 0, 4);
#define MLXSW_REG_MPAR_RATE_MAX 3500000000UL
/* reg_mpar_probability_rate
* Sampling rate.
* Valid values are: 1 to 3.5*10^9
* Value of 1 means "sample all". Default is 1.
* Reserved when Spectrum-1.
* Access: RW
*/
MLXSW_ITEM32(reg, mpar, probability_rate, 0x08, 0, 32);
static inline void mlxsw_reg_mpar_pack(char *payload, u8 local_port,
enum mlxsw_reg_mpar_i_e i_e,
bool enable, u8 pa_id)
bool enable, u8 pa_id,
u32 probability_rate)
{
MLXSW_REG_ZERO(mpar, payload);
mlxsw_reg_mpar_local_port_set(payload, local_port);
mlxsw_reg_mpar_enable_set(payload, enable);
mlxsw_reg_mpar_i_e_set(payload, i_e);
mlxsw_reg_mpar_pa_id_set(payload, pa_id);
mlxsw_reg_mpar_probability_rate_set(payload, probability_rate);
}
/* MGIR - Management General Information Register
......@@ -10577,6 +10590,8 @@ MLXSW_ITEM32(reg, mpagr, trigger, 0x00, 0, 4);
*/
MLXSW_ITEM32(reg, mpagr, pa_id, 0x04, 0, 4);
#define MLXSW_REG_MPAGR_RATE_MAX 3500000000UL
/* reg_mpagr_probability_rate
* Sampling rate.
* Valid values are: 1 to 3.5*10^9
......
......@@ -2804,6 +2804,7 @@ static int mlxsw_sp1_init(struct mlxsw_core *mlxsw_core,
mlxsw_sp->span_ops = &mlxsw_sp1_span_ops;
mlxsw_sp->policer_core_ops = &mlxsw_sp1_policer_core_ops;
mlxsw_sp->trap_ops = &mlxsw_sp1_trap_ops;
mlxsw_sp->mall_ops = &mlxsw_sp1_mall_ops;
mlxsw_sp->listeners = mlxsw_sp1_listener;
mlxsw_sp->listeners_count = ARRAY_SIZE(mlxsw_sp1_listener);
mlxsw_sp->lowest_shaper_bs = MLXSW_REG_QEEC_LOWEST_SHAPER_BS_SP1;
......@@ -2833,6 +2834,7 @@ static int mlxsw_sp2_init(struct mlxsw_core *mlxsw_core,
mlxsw_sp->span_ops = &mlxsw_sp2_span_ops;
mlxsw_sp->policer_core_ops = &mlxsw_sp2_policer_core_ops;
mlxsw_sp->trap_ops = &mlxsw_sp2_trap_ops;
mlxsw_sp->mall_ops = &mlxsw_sp2_mall_ops;
mlxsw_sp->lowest_shaper_bs = MLXSW_REG_QEEC_LOWEST_SHAPER_BS_SP2;
return mlxsw_sp_init(mlxsw_core, mlxsw_bus_info, extack);
......@@ -2860,6 +2862,7 @@ static int mlxsw_sp3_init(struct mlxsw_core *mlxsw_core,
mlxsw_sp->span_ops = &mlxsw_sp3_span_ops;
mlxsw_sp->policer_core_ops = &mlxsw_sp2_policer_core_ops;
mlxsw_sp->trap_ops = &mlxsw_sp2_trap_ops;
mlxsw_sp->mall_ops = &mlxsw_sp2_mall_ops;
mlxsw_sp->lowest_shaper_bs = MLXSW_REG_QEEC_LOWEST_SHAPER_BS_SP3;
return mlxsw_sp_init(mlxsw_core, mlxsw_bus_info, extack);
......
......@@ -179,6 +179,7 @@ struct mlxsw_sp {
const struct mlxsw_sp_span_ops *span_ops;
const struct mlxsw_sp_policer_core_ops *policer_core_ops;
const struct mlxsw_sp_trap_ops *trap_ops;
const struct mlxsw_sp_mall_ops *mall_ops;
const struct mlxsw_listener *listeners;
size_t listeners_count;
u32 lowest_shaper_bs;
......@@ -237,6 +238,7 @@ struct mlxsw_sp_port_sample {
u32 trunc_size;
u32 rate;
bool truncate;
int span_id; /* Relevant for Spectrum-2 onwards. */
};
struct mlxsw_sp_bridge_port;
......@@ -1033,6 +1035,16 @@ extern const struct mlxsw_afk_ops mlxsw_sp1_afk_ops;
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);
void (*sample_del)(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_port *mlxsw_sp_port);
};
extern const struct mlxsw_sp_mall_ops mlxsw_sp1_mall_ops;
extern const struct mlxsw_sp_mall_ops mlxsw_sp2_mall_ops;
enum mlxsw_sp_mall_action_type {
MLXSW_SP_MALL_ACTION_TYPE_MIRROR,
MLXSW_SP_MALL_ACTION_TYPE_SAMPLE,
......
......@@ -51,6 +51,7 @@ mlxsw_sp_mall_port_mirror_add(struct mlxsw_sp_port *mlxsw_sp_port,
trigger = mall_entry->ingress ? MLXSW_SP_SPAN_TRIGGER_INGRESS :
MLXSW_SP_SPAN_TRIGGER_EGRESS;
parms.span_id = mall_entry->mirror.span_id;
parms.probability_rate = 1;
err = mlxsw_sp_span_agent_bind(mlxsw_sp, trigger, mlxsw_sp_port,
&parms);
if (err)
......@@ -95,6 +96,7 @@ 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 *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
int err;
if (rtnl_dereference(mlxsw_sp_port->sample)) {
......@@ -103,8 +105,8 @@ mlxsw_sp_mall_port_sample_add(struct mlxsw_sp_port *mlxsw_sp_port,
}
rcu_assign_pointer(mlxsw_sp_port->sample, &mall_entry->sample);
err = mlxsw_sp_mall_port_sample_set(mlxsw_sp_port, true,
mall_entry->sample.rate);
err = mlxsw_sp->mall_ops->sample_add(mlxsw_sp, mlxsw_sp_port,
mall_entry->sample.rate);
if (err)
goto err_port_sample_set;
return 0;
......@@ -117,10 +119,12 @@ mlxsw_sp_mall_port_sample_add(struct mlxsw_sp_port *mlxsw_sp_port,
static void
mlxsw_sp_mall_port_sample_del(struct mlxsw_sp_port *mlxsw_sp_port)
{
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
if (!mlxsw_sp_port->sample)
return;
mlxsw_sp_mall_port_sample_set(mlxsw_sp_port, false, 1);
mlxsw_sp->mall_ops->sample_del(mlxsw_sp, mlxsw_sp_port);
RCU_INIT_POINTER(mlxsw_sp_port->sample, NULL);
}
......@@ -355,3 +359,79 @@ int mlxsw_sp_mall_prio_get(struct mlxsw_sp_flow_block *block, u32 chain_index,
*p_max_prio = block->mall.max_prio;
return 0;
}
static int mlxsw_sp1_mall_sample_add(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_port *mlxsw_sp_port,
u32 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,
struct mlxsw_sp_port *mlxsw_sp_port)
{
mlxsw_sp_mall_port_sample_set(mlxsw_sp_port, false, 1);
}
const struct mlxsw_sp_mall_ops mlxsw_sp1_mall_ops = {
.sample_add = mlxsw_sp1_mall_sample_add,
.sample_del = mlxsw_sp1_mall_sample_del,
};
static int mlxsw_sp2_mall_sample_add(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_port *mlxsw_sp_port,
u32 rate)
{
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;
int err;
sample = rtnl_dereference(mlxsw_sp_port->sample);
err = mlxsw_sp_span_agent_get(mlxsw_sp, &sample->span_id, &agent_parms);
if (err)
return err;
err = mlxsw_sp_span_analyzed_port_get(mlxsw_sp_port, true);
if (err)
goto err_analyzed_port_get;
trigger_parms.span_id = 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)
goto err_agent_bind;
return 0;
err_agent_bind:
mlxsw_sp_span_analyzed_port_put(mlxsw_sp_port, true);
err_analyzed_port_get:
mlxsw_sp_span_agent_put(mlxsw_sp, 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_span_trigger_parms trigger_parms = {};
struct mlxsw_sp_port_sample *sample;
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);
}
const struct mlxsw_sp_mall_ops mlxsw_sp2_mall_ops = {
.sample_add = mlxsw_sp2_mall_sample_add,
.sample_del = mlxsw_sp2_mall_sample_del,
};
......@@ -1341,6 +1341,7 @@ static int mlxsw_sp_qevent_span_configure(struct mlxsw_sp *mlxsw_sp,
goto err_analyzed_port_get;
trigger_parms.span_id = span_id;
trigger_parms.probability_rate = 1;
err = mlxsw_sp_span_agent_bind(mlxsw_sp, qevent_binding->span_trigger, mlxsw_sp_port,
&trigger_parms);
if (err)
......@@ -1404,7 +1405,9 @@ static int mlxsw_sp_qevent_trap_configure(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_mall_entry *mall_entry,
struct mlxsw_sp_qevent_binding *qevent_binding)
{
struct mlxsw_sp_span_agent_parms agent_parms = {};
struct mlxsw_sp_span_agent_parms agent_parms = {
.session_id = MLXSW_SP_SPAN_SESSION_ID_BUFFER,
};
int err;
err = mlxsw_sp_trap_group_policer_hw_id_get(mlxsw_sp,
......
......@@ -186,6 +186,7 @@ mlxsw_sp_span_entry_phys_configure(struct mlxsw_sp_span_entry *span_entry,
/* Create a new port analayzer entry for local_port. */
mlxsw_reg_mpat_pack(mpat_pl, pa_id, local_port, true,
MLXSW_REG_MPAT_SPAN_TYPE_LOCAL_ETH);
mlxsw_reg_mpat_session_id_set(mpat_pl, sparms.session_id);
mlxsw_reg_mpat_pide_set(mpat_pl, sparms.policer_enable);
mlxsw_reg_mpat_pid_set(mpat_pl, sparms.policer_id);
......@@ -203,6 +204,7 @@ mlxsw_sp_span_entry_deconfigure_common(struct mlxsw_sp_span_entry *span_entry,
int pa_id = span_entry->id;
mlxsw_reg_mpat_pack(mpat_pl, pa_id, local_port, false, span_type);
mlxsw_reg_mpat_session_id_set(mpat_pl, span_entry->parms.session_id);
mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mpat), mpat_pl);
}
......@@ -938,7 +940,8 @@ mlxsw_sp_span_entry_find_by_parms(struct mlxsw_sp *mlxsw_sp,
if (refcount_read(&curr->ref_count) && curr->to_dev == to_dev &&
curr->parms.policer_enable == sparms->policer_enable &&
curr->parms.policer_id == sparms->policer_id)
curr->parms.policer_id == sparms->policer_id &&
curr->parms.session_id == sparms->session_id)
return curr;
}
return NULL;
......@@ -1085,6 +1088,7 @@ int mlxsw_sp_span_agent_get(struct mlxsw_sp *mlxsw_sp, int *p_span_id,
sparms.policer_id = parms->policer_id;
sparms.policer_enable = parms->policer_enable;
sparms.session_id = parms->session_id;
span_entry = mlxsw_sp_span_entry_get(mlxsw_sp, to_dev, ops, sparms);
if (!span_entry)
return -ENOBUFS;
......@@ -1227,8 +1231,12 @@ __mlxsw_sp_span_trigger_port_bind(struct mlxsw_sp_span *span,
return -EINVAL;
}
if (trigger_entry->parms.probability_rate > MLXSW_REG_MPAR_RATE_MAX)
return -EINVAL;
mlxsw_reg_mpar_pack(mpar_pl, trigger_entry->local_port, i_e, enable,
trigger_entry->parms.span_id);
trigger_entry->parms.span_id,
trigger_entry->parms.probability_rate);
return mlxsw_reg_write(span->mlxsw_sp->core, MLXSW_REG(mpar), mpar_pl);
}
......@@ -1362,8 +1370,11 @@ mlxsw_sp2_span_trigger_global_bind(struct mlxsw_sp_span_trigger_entry *
return -EINVAL;
}
if (trigger_entry->parms.probability_rate > MLXSW_REG_MPAGR_RATE_MAX)
return -EINVAL;
mlxsw_reg_mpagr_pack(mpagr_pl, trigger, trigger_entry->parms.span_id,
1);
trigger_entry->parms.probability_rate);
return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mpagr), mpagr_pl);
}
......@@ -1561,7 +1572,9 @@ int mlxsw_sp_span_agent_bind(struct mlxsw_sp *mlxsw_sp,
trigger,
mlxsw_sp_port);
if (trigger_entry) {
if (trigger_entry->parms.span_id != parms->span_id)
if (trigger_entry->parms.span_id != parms->span_id ||
trigger_entry->parms.probability_rate !=
parms->probability_rate)
return -EINVAL;
refcount_inc(&trigger_entry->ref_count);
goto out;
......
......@@ -13,6 +13,19 @@
struct mlxsw_sp;
struct mlxsw_sp_port;
/* SPAN session identifiers that correspond to MLXSW_TRAP_ID_MIRROR_SESSION<i>
* trap identifiers. The session identifier is an attribute of the SPAN agent,
* which determines the trap identifier of packets that are mirrored to the
* CPU. Packets that are trapped to the CPU for the same logical reason (e.g.,
* buffer drops) should use the same session identifier.
*/
enum mlxsw_sp_span_session_id {
MLXSW_SP_SPAN_SESSION_ID_BUFFER,
MLXSW_SP_SPAN_SESSION_ID_SAMPLING,
__MLXSW_SP_SPAN_SESSION_ID_MAX = 8,
};
struct mlxsw_sp_span_parms {
struct mlxsw_sp_port *dest_port; /* NULL for unoffloaded SPAN. */
unsigned int ttl;
......@@ -23,6 +36,7 @@ struct mlxsw_sp_span_parms {
u16 vid;
u16 policer_id;
bool policer_enable;
enum mlxsw_sp_span_session_id session_id;
};
enum mlxsw_sp_span_trigger {
......@@ -35,12 +49,14 @@ enum mlxsw_sp_span_trigger {
struct mlxsw_sp_span_trigger_parms {
int span_id;
u32 probability_rate;
};
struct mlxsw_sp_span_agent_parms {
const struct net_device *to_dev;
u16 policer_id;
bool policer_enable;
enum mlxsw_sp_span_session_id session_id;
};
struct mlxsw_sp_span_entry_ops;
......
......@@ -49,6 +49,8 @@ enum {
#define MLXSW_SP_TRAP_METADATA DEVLINK_TRAP_METADATA_TYPE_F_IN_PORT
enum {
/* Packet was mirrored from ingress. */
MLXSW_SP_MIRROR_REASON_INGRESS = 1,
/* Packet was early dropped. */
MLXSW_SP_MIRROR_REASON_INGRESS_WRED = 9,
};
......@@ -463,11 +465,6 @@ static const struct mlxsw_sp_trap_group_item mlxsw_sp_trap_group_items_arr[] = {
.hw_group_id = MLXSW_REG_HTGT_TRAP_GROUP_SP_PTP1,
.priority = 2,
},
{
.group = DEVLINK_TRAP_GROUP_GENERIC(ACL_SAMPLE, 0),
.hw_group_id = MLXSW_REG_HTGT_TRAP_GROUP_SP_PKT_SAMPLE,
.priority = 0,
},
{
.group = DEVLINK_TRAP_GROUP_GENERIC(ACL_TRAP, 18),
.hw_group_id = MLXSW_REG_HTGT_TRAP_GROUP_SP_FLOW_LOGGING,
......@@ -992,14 +989,6 @@ static const struct mlxsw_sp_trap_item mlxsw_sp_trap_items_arr[] = {
MLXSW_SP_RXL_NO_MARK(PTP1, PTP1, TRAP_TO_CPU, false),
},
},
{
.trap = MLXSW_SP_TRAP_CONTROL(FLOW_ACTION_SAMPLE, ACL_SAMPLE,
MIRROR),
.listeners_arr = {
MLXSW_RXL(mlxsw_sp_rx_sample_listener, PKT_SAMPLE,
MIRROR_TO_CPU, false, SP_PKT_SAMPLE, DISCARD),
},
},
{
.trap = MLXSW_SP_TRAP_CONTROL(FLOW_ACTION_TRAP, ACL_TRAP, TRAP),
.listeners_arr = {
......@@ -1709,10 +1698,23 @@ int mlxsw_sp_trap_group_policer_hw_id_get(struct mlxsw_sp *mlxsw_sp, u16 id,
static const struct mlxsw_sp_trap_group_item
mlxsw_sp1_trap_group_items_arr[] = {
{
.group = DEVLINK_TRAP_GROUP_GENERIC(ACL_SAMPLE, 0),
.hw_group_id = MLXSW_REG_HTGT_TRAP_GROUP_SP_PKT_SAMPLE,
.priority = 0,
},
};
static const struct mlxsw_sp_trap_item
mlxsw_sp1_trap_items_arr[] = {
{
.trap = MLXSW_SP_TRAP_CONTROL(FLOW_ACTION_SAMPLE, ACL_SAMPLE,
MIRROR),
.listeners_arr = {
MLXSW_RXL(mlxsw_sp_rx_sample_listener, PKT_SAMPLE,
MIRROR_TO_CPU, false, SP_PKT_SAMPLE, DISCARD),
},
},
};
static int
......@@ -1749,6 +1751,12 @@ mlxsw_sp2_trap_group_items_arr[] = {
.priority = 0,
.fixed_policer = true,
},
{
.group = DEVLINK_TRAP_GROUP_GENERIC(ACL_SAMPLE, 0),
.hw_group_id = MLXSW_REG_HTGT_TRAP_GROUP_SP_PKT_SAMPLE,
.priority = 0,
.fixed_policer = true,
},
};
static const struct mlxsw_sp_trap_item
......@@ -1760,6 +1768,15 @@ mlxsw_sp2_trap_items_arr[] = {
},
.is_source = true,
},
{
.trap = MLXSW_SP_TRAP_CONTROL(FLOW_ACTION_SAMPLE, ACL_SAMPLE,
MIRROR),
.listeners_arr = {
MLXSW_RXL_MIRROR(mlxsw_sp_rx_sample_listener, 1,
SP_PKT_SAMPLE,
MLXSW_SP_MIRROR_REASON_INGRESS),
},
},
};
static int
......
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