Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
259 changes: 259 additions & 0 deletions vppbld/patches/0012-sflow-per-port-sample-rate.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,259 @@
diff --git a/src/plugins/sflow/node.c b/src/plugins/sflow/node.c
index 3d5363166..3f45c97e2 100644
--- a/src/plugins/sflow/node.c
+++ b/src/plugins/sflow/node.c
@@ -73,26 +73,35 @@ sflow_node_ingress_egress (vlib_main_t *vm, vlib_node_runtime_t *node,
from = vlib_frame_vector_args (frame);
n_left_from = frame->n_vectors;

+ u32 dir = (sample_type == SFLOW_SAMPLETYPE_EGRESS) ? VLIB_TX : VLIB_RX;
+ u32 sw_if = vnet_buffer (vlib_get_buffer (vm, from[0]))->sw_if_index[dir];
+ vnet_hw_interface_t *hw = vnet_get_sup_hw_interface(smp->vnet_main, sw_if);
+ sflow_per_interface_data_t *sfif = vec_elt_at_index(smp->per_interface_data, hw->hw_if_index);
+
uword thread_index = os_get_thread_index ();
sflow_per_thread_data_t *sfwk =
vec_elt_at_index (smp->per_thread_data, thread_index);

- /* note that sfwk->skip==1 means "take the next packet",
- so we never see sfwk->skip==0. */
+ /* per-thread, per-interface sampling state: each thread keeps its own
+ skip/pool/seed so concurrent threads on the same interface don't race. */
+ sflow_per_if_sampler_t *smpif =
+ vec_elt_at_index (sfwk->per_interface, hw->hw_if_index);

+ /* note that smpif->skip==1 means "take the next packet",
+ so we never see smpif->skip==0. */
u32 pkts = n_left_from;
- if (PREDICT_TRUE (sfwk->skip > pkts))
+ if (PREDICT_TRUE (smpif->skip > pkts))
{
/* skip the whole frame-vector */
- sfwk->skip -= pkts;
- sfwk->pool += pkts;
+ smpif->skip -= pkts;
+ smpif->pool += pkts;
}
else
{
- while (pkts >= sfwk->skip)
+ while (pkts >= smpif->skip)
{
/* reach in to get the one we want. */
- vlib_buffer_t *bN = vlib_get_buffer (vm, from[sfwk->skip - 1]);
+ vlib_buffer_t *bN = vlib_get_buffer (vm, from[smpif->skip - 1]);

/* Sample this packet header. */
u32 hdr = bN->current_length;
@@ -123,7 +132,7 @@ sflow_node_ingress_egress (vlib_main_t *vm, vlib_node_runtime_t *node,

sflow_sample_t sample = {
.sample_type = sample_type,
- .samplingN = sfwk->smpN,
+ .samplingN = sfif->samplingN ? sfif->samplingN : smp->samplingN,
.input_if_index = if_index,
.output_if_index = if_index_out,
.sampled_packet_size =
@@ -151,14 +160,14 @@ sflow_node_ingress_egress (vlib_main_t *vm, vlib_node_runtime_t *node,
if (PREDICT_FALSE (!sflow_fifo_enqueue (&sfwk->fifo, &sample)))
sfwk->drop++;

- pkts -= sfwk->skip;
- sfwk->pool += sfwk->skip;
- sfwk->skip = sflow_next_random_skip (sfwk);
+ pkts -= smpif->skip;
+ smpif->pool += smpif->skip;
+ smpif->skip = sflow_next_random_skip_if (smp, sfif, smpif);
}
/* We took a sample (or several) from this frame-vector, but now we are
skipping the rest. */
- sfwk->skip -= pkts;
- sfwk->pool += pkts;
+ smpif->skip -= pkts;
+ smpif->pool += pkts;
}

/* the rest of this is boilerplate code just to make sure
diff --git a/src/plugins/sflow/sflow.c b/src/plugins/sflow/sflow.c
index 89529d1ca..908fbc5e1 100644
--- a/src/plugins/sflow/sflow.c
+++ b/src/plugins/sflow/sflow.c
@@ -515,11 +515,13 @@ read_node_counters (sflow_main_t *smp, sflow_err_ctrs_t *ctrs)
{
sflow_per_thread_data_t *sfwk =
vec_elt_at_index (smp->per_thread_data, thread_index);
- ctrs->counters[SFLOW_ERROR_PROCESSED] += sfwk->pool;
ctrs->counters[SFLOW_ERROR_SAMPLED] += sfwk->smpl;
ctrs->counters[SFLOW_ERROR_DROPPED] += sfwk->drop;
ctrs->counters[SFLOW_ERROR_DIPROCESSED] += sfwk->dsmp;
ctrs->counters[SFLOW_ERROR_DIDROPPED] += sfwk->ddrp;
+ /* pool is now tracked per-thread, per-interface */
+ for (int i = 0; i < vec_len (sfwk->per_interface); i++)
+ ctrs->counters[SFLOW_ERROR_PROCESSED] += sfwk->per_interface[i].pool;
}
}

@@ -641,6 +643,42 @@ sflow_set_worker_sampling_state (sflow_main_t *smp)
}
}

+/* (Re)initialize the per-thread, per-interface sampling state for one
+ interface across all threads. Called from the control path only (interface
+ enable, per-port or global rate change) so the datapath never races on
+ skip/pool/seed. */
+static void
+sflow_reset_interface_samplers (sflow_main_t *smp, u32 hw_if_index)
+{
+ sflow_per_interface_data_t *sfif =
+ vec_elt_at_index (smp->per_interface_data, hw_if_index);
+ for (clib_thread_index_t thread_index = 0;
+ thread_index < smp->total_threads; thread_index++)
+ {
+ sflow_per_thread_data_t *sfwk =
+ vec_elt_at_index (smp->per_thread_data, thread_index);
+ vec_validate (sfwk->per_interface, hw_if_index);
+ sflow_per_if_sampler_t *smpif =
+ vec_elt_at_index (sfwk->per_interface, hw_if_index);
+ smpif->pool = 0;
+ /* distinct seed per (thread, interface) so threads don't correlate */
+ smpif->seed = hw_if_index ^ (thread_index + 1);
+ smpif->skip = sflow_next_random_skip_if (smp, sfif, smpif);
+ }
+}
+
+static void
+sflow_set_interface_sampling_rate (sflow_main_t *smp)
+{
+ for (int i = 0; i < vec_len (smp->per_interface_data); i++)
+ {
+ sflow_per_interface_data_t *sfif =
+ vec_elt_at_index (smp->per_interface_data, i);
+ if (sfif && sfif->sflow_enabled && !sfif->samplingN)
+ sflow_reset_interface_samplers (smp, i);
+ }
+}
+
static void
sflow_sampling_start (sflow_main_t *smp)
{
@@ -713,6 +751,7 @@ sflow_sampling_rate (sflow_main_t *smp, u32 samplingN)
// dynamic change of sampling rate
smp->samplingN = samplingN;
sflow_set_worker_sampling_state (smp);
+ sflow_set_interface_sampling_rate (smp);
}
else
{
@@ -854,14 +893,53 @@ sflow_enable_disable (sflow_main_t *smp, u32 sw_if_index, bool enable_disable)
sfif->hw_if_index = sw->hw_if_index;
sfif->polled = 0;
sfif->sflow_enabled = enable_disable;
+
+ if (enable_disable)
+ {
+ /* make sure per-thread structures exist, then seed this
+ interface's per-thread samplers before the feature arc starts
+ delivering packets to the workers. */
+ sflow_set_worker_sampling_state (smp);
+ sflow_reset_interface_samplers (smp, sw->hw_if_index);
+ }
+
sflow_enable_disable_interface (smp, sfif);
smp->interfacesEnabled += (enable_disable) ? 1 : -1;
}
-
sflow_sampling_start_stop (smp);
return 0;
}

+int
+sflow_set_sampling_rate_set (sflow_main_t *smp, u32 sw_if_index, u32 samplingN)
+{
+ vnet_sw_interface_t *sw;
+
+ /* Utterly wrong? */
+ if (pool_is_free_index (smp->vnet_main->interface_main.sw_interfaces,
+ sw_if_index))
+ return VNET_API_ERROR_INVALID_SW_IF_INDEX;
+
+ /* Not a physical port? */
+ sw = vnet_get_sw_interface (smp->vnet_main, sw_if_index);
+ if (sw->type != VNET_SW_INTERFACE_TYPE_HARDWARE)
+ return VNET_API_ERROR_INVALID_SW_IF_INDEX;
+
+ SFLOW_DBG ("sw_if_index=%u, sup_sw_if_index=%u, hw_if_index=%u\n", sw->sw_if_index, sw->sup_sw_if_index, sw->hw_if_index);
+
+ vec_validate (smp->per_interface_data, sw->hw_if_index);
+ sflow_per_interface_data_t *sfif = vec_elt_at_index (smp->per_interface_data, sw->hw_if_index);
+
+ sfif->samplingN = samplingN;
+ /* re-seed the per-thread samplers for this interface with the new rate.
+ If sampling hasn't started yet the samplers are seeded on enable. */
+ if (smp->total_threads)
+ sflow_reset_interface_samplers (smp, sw->hw_if_index);
+
+ return 0;
+
+}
+
static clib_error_t *
sflow_sampling_rate_command_fn (vlib_main_t *vm, unformat_input_t *input,
vlib_cli_command_t *cmd)
diff --git a/src/plugins/sflow/sflow.h b/src/plugins/sflow/sflow.h
index 7e8c0469e..cf90d9cea 100644
--- a/src/plugins/sflow/sflow.h
+++ b/src/plugins/sflow/sflow.h
@@ -162,6 +162,16 @@ sflow_drop_fifo_dequeue (sflow_drop_fifo_t *fifo, sflow_sample_t *sample)
return true;
}

+/* per-thread, per-interface sampling counters. Kept per-thread so that
+ multiple worker threads sampling the same interface never share (and race
+ on) skip/pool/seed. Indexed by hw_if_index. */
+typedef struct
+{
+ u32 skip;
+ u32 pool;
+ u32 seed;
+} sflow_per_if_sampler_t;
+
/* private to worker */
typedef struct
{
@@ -173,6 +183,8 @@ typedef struct
u32 drop;
u32 dsmp;
u32 ddrp;
+ /* per-interface sampling state, indexed by hw_if_index */
+ sflow_per_if_sampler_t *per_interface;
CLIB_CACHE_LINE_ALIGN_MARK (_fifo);
sflow_fifo_t fifo;
CLIB_CACHE_LINE_ALIGN_MARK (_drop_fifo);
@@ -246,4 +258,15 @@ sflow_next_random_skip (sflow_per_thread_data_t *sfwk)
return (random_u32 (&sfwk->seed) % lim) + 1;
}

+static inline u32
+sflow_next_random_skip_if (sflow_main_t *smp, sflow_per_interface_data_t *sfif,
+ sflow_per_if_sampler_t *smpif)
+{
+ u32 effectiveN = sfif->samplingN ? sfif->samplingN : smp->samplingN;
+ if (effectiveN <= 1)
+ return 1;
+ u32 lim = (2 * effectiveN) - 1;
+ return (random_u32 (&smpif->seed) % lim) + 1;
+}
+
#endif /* __included_sflow_h__ */
diff --git a/src/plugins/sflow/sflow_common.h b/src/plugins/sflow/sflow_common.h
index 3e1021e4a..cd73302a8 100644
--- a/src/plugins/sflow/sflow_common.h
+++ b/src/plugins/sflow/sflow_common.h
@@ -19,6 +19,7 @@ typedef struct
u32 linux_if_index;
u32 polled;
int sflow_enabled;
+ u32 samplingN;
} sflow_per_interface_data_t;

/* mirror sflow_direction enum in sflow.api */
88 changes: 88 additions & 0 deletions vppbld/patches/0013-sflow-per-port-direction.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
diff --git a/src/plugins/sflow/sflow.c b/src/plugins/sflow/sflow.c
index 5b7237c9c..f4283f608 100644
--- a/src/plugins/sflow/sflow.c
+++ b/src/plugins/sflow/sflow.c
@@ -792,12 +792,14 @@ void
sflow_enable_disable_interface (sflow_main_t *smp,
sflow_per_interface_data_t *sfif)
{
+ u32 direction = sflow_direction_resolve (smp, sfif);
+
bool ingress_on =
- sfif->sflow_enabled && (smp->samplingD == SFLOW_DIRN_INGRESS ||
- smp->samplingD == SFLOW_DIRN_BOTH);
+ sfif->sflow_enabled && (direction == SFLOW_DIRN_INGRESS ||
+ direction == SFLOW_DIRN_BOTH);
bool egress_on =
sfif->sflow_enabled &&
- (smp->samplingD == SFLOW_DIRN_EGRESS || smp->samplingD == SFLOW_DIRN_BOTH);
+ (direction == SFLOW_DIRN_EGRESS || direction == SFLOW_DIRN_BOTH);
bool drop_on = sfif->sflow_enabled && smp->dropM;
bool ingress_enabled = (vnet_feature_is_enabled ("device-input", "sflow",
sfif->sw_if_index) == 1);
@@ -940,6 +942,37 @@ sflow_set_sampling_rate_set (sflow_main_t *smp, u32 sw_if_index, u32 samplingN)

}

+int
+sflow_set_direction_set (sflow_main_t *smp, u32 sw_if_index, u32 direction)
+{
+ vnet_sw_interface_t *sw;
+
+ /* Utterly wrong? */
+ if (pool_is_free_index (smp->vnet_main->interface_main.sw_interfaces,
+ sw_if_index))
+ return VNET_API_ERROR_INVALID_SW_IF_INDEX;
+
+ /* Not a physical port? */
+ sw = vnet_get_sw_interface (smp->vnet_main, sw_if_index);
+ if (sw->type != VNET_SW_INTERFACE_TYPE_HARDWARE)
+ return VNET_API_ERROR_INVALID_SW_IF_INDEX;
+
+ if (direction > SFLOW_DIRN_BOTH)
+ return VNET_API_ERROR_INVALID_VALUE;
+
+ SFLOW_DBG ("sw_if_index=%u, sup_sw_if_index=%u, hw_if_index=%u\n",
+ sw->sw_if_index, sw->sup_sw_if_index, sw->hw_if_index);
+
+ vec_validate (smp->per_interface_data, sw->hw_if_index);
+ sflow_per_interface_data_t *sfif =
+ vec_elt_at_index (smp->per_interface_data, sw->hw_if_index);
+
+ sfif->direction = direction;
+ sflow_enable_disable_interface (smp, sfif);
+
+ return 0;
+}
+
static clib_error_t *
sflow_sampling_rate_command_fn (vlib_main_t *vm, unformat_input_t *input,
vlib_cli_command_t *cmd)
diff --git a/src/plugins/sflow/sflow.h b/src/plugins/sflow/sflow.h
index cf90d9cea..e26896b1a 100644
--- a/src/plugins/sflow/sflow.h
+++ b/src/plugins/sflow/sflow.h
@@ -269,4 +269,11 @@ sflow_next_random_skip_if (sflow_main_t *smp, sflow_per_interface_data_t *sfif,
return (random_u32 (&smpif->seed) % lim) + 1;
}

+static inline u32
+sflow_direction_resolve (sflow_main_t *smp, sflow_per_interface_data_t *sfif)
+{
+ u32 effectiveD = sfif->direction ? sfif->direction : smp->samplingD;
+ return effectiveD;
+}
+
#endif /* __included_sflow_h__ */
diff --git a/src/plugins/sflow/sflow_common.h b/src/plugins/sflow/sflow_common.h
index cd73302a8..6055d04f1 100644
--- a/src/plugins/sflow/sflow_common.h
+++ b/src/plugins/sflow/sflow_common.h
@@ -20,6 +20,7 @@ typedef struct
u32 polled;
int sflow_enabled;
u32 samplingN;
+ u32 direction;
} sflow_per_interface_data_t;

/* mirror sflow_direction enum in sflow.api */
Loading