mirror of
https://github.com/openvswitch/ovs
synced 2025-10-13 14:07:02 +00:00
dpif-netdev: Detailed performance stats for PMDs
This patch instruments the dpif-netdev datapath to record detailed statistics of what is happening in every iteration of a PMD thread. The collection of detailed statistics can be controlled by a new Open_vSwitch configuration parameter "other_config:pmd-perf-metrics". By default it is disabled. The run-time overhead, when enabled, is in the order of 1%. The covered metrics per iteration are: - cycles - packets - (rx) batches - packets/batch - max. vhostuser qlen - upcalls - cycles spent in upcalls This raw recorded data is used threefold: 1. In histograms for each of the following metrics: - cycles/iteration (log.) - packets/iteration (log.) - cycles/packet - packets/batch - max. vhostuser qlen (log.) - upcalls - cycles/upcall (log) The histograms bins are divided linear or logarithmic. 2. A cyclic history of the above statistics for 999 iterations 3. A cyclic history of the cummulative/average values per millisecond wall clock for the last 1000 milliseconds: - number of iterations - avg. cycles/iteration - packets (Kpps) - avg. packets/batch - avg. max vhost qlen - upcalls - avg. cycles/upcall The gathered performance metrics can be printed at any time with the new CLI command ovs-appctl dpif-netdev/pmd-perf-show [-nh] [-it iter_len] [-ms ms_len] [-pmd core] [dp] The options are -nh: Suppress the histograms -it iter_len: Display the last iter_len iteration stats -ms ms_len: Display the last ms_len millisecond stats -pmd core: Display only the specified PMD The performance statistics are reset with the existing dpif-netdev/pmd-stats-clear command. The output always contains the following global PMD statistics, similar to the pmd-stats-show command: Time: 15:24:55.270 Measurement duration: 1.008 s pmd thread numa_id 0 core_id 1: Cycles: 2419034712 (2.40 GHz) Iterations: 572817 (1.76 us/it) - idle: 486808 (15.9 % cycles) - busy: 86009 (84.1 % cycles) Rx packets: 2399607 (2381 Kpps, 848 cycles/pkt) Datapath passes: 3599415 (1.50 passes/pkt) - EMC hits: 336472 ( 9.3 %) - Megaflow hits: 3262943 (90.7 %, 1.00 subtbl lookups/hit) - Upcalls: 0 ( 0.0 %, 0.0 us/upcall) - Lost upcalls: 0 ( 0.0 %) Tx packets: 2399607 (2381 Kpps) Tx batches: 171400 (14.00 pkts/batch) Signed-off-by: Jan Scheurich <jan.scheurich@ericsson.com> Acked-by: Billy O'Mahony <billy.o.mahony@intel.com> Signed-off-by: Ian Stokes <ian.stokes@intel.com>
This commit is contained in:
committed by
Ian Stokes
parent
8492adc270
commit
79f368756c
@@ -38,10 +38,18 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* This module encapsulates data structures and functions to maintain PMD
|
||||
* performance metrics such as packet counters, execution cycles. It
|
||||
* provides a clean API for dpif-netdev to initialize, update and read and
|
||||
/* This module encapsulates data structures and functions to maintain basic PMD
|
||||
* performance metrics such as packet counters, execution cycles as well as
|
||||
* histograms and time series recording for more detailed PMD metrics.
|
||||
*
|
||||
* It provides a clean API for dpif-netdev to initialize, update and read and
|
||||
* reset these metrics.
|
||||
*
|
||||
* The basic set of PMD counters is implemented as atomic_uint64_t variables
|
||||
* to guarantee correct read also in 32-bit systems.
|
||||
*
|
||||
* The detailed PMD performance metrics are only supported on 64-bit systems
|
||||
* with atomic 64-bit read and store semantics for plain uint64_t counters.
|
||||
*/
|
||||
|
||||
/* Set of counter types maintained in pmd_perf_stats. */
|
||||
@@ -66,6 +74,7 @@ enum pmd_stat_type {
|
||||
PMD_STAT_SENT_BATCHES, /* Number of batches sent. */
|
||||
PMD_CYCLES_ITER_IDLE, /* Cycles spent in idle iterations. */
|
||||
PMD_CYCLES_ITER_BUSY, /* Cycles spent in busy iterations. */
|
||||
PMD_CYCLES_UPCALL, /* Cycles spent processing upcalls. */
|
||||
PMD_N_STATS
|
||||
};
|
||||
|
||||
@@ -81,18 +90,91 @@ struct pmd_counters {
|
||||
uint64_t zero[PMD_N_STATS]; /* Value at last _clear(). */
|
||||
};
|
||||
|
||||
/* Container for all performance metrics of a PMD.
|
||||
* Part of the struct dp_netdev_pmd_thread. */
|
||||
/* Data structure to collect statistical distribution of an integer measurement
|
||||
* type in form of a histogram. The wall[] array contains the inclusive
|
||||
* upper boundaries of the bins, while the bin[] array contains the actual
|
||||
* counters per bin. The histogram walls are typically set automatically
|
||||
* using the functions provided below.*/
|
||||
|
||||
#define NUM_BINS 32 /* Number of histogram bins. */
|
||||
|
||||
struct histogram {
|
||||
uint32_t wall[NUM_BINS];
|
||||
uint64_t bin[NUM_BINS];
|
||||
};
|
||||
|
||||
/* Data structure to record details PMD execution metrics per iteration for
|
||||
* a history period of up to HISTORY_LEN iterations in circular buffer.
|
||||
* Also used to record up to HISTORY_LEN millisecond averages/totals of these
|
||||
* metrics.*/
|
||||
|
||||
struct iter_stats {
|
||||
uint64_t timestamp; /* Iteration no. or millisecond. */
|
||||
uint64_t cycles; /* Number of TSC cycles spent in it. or ms. */
|
||||
uint64_t busy_cycles; /* Cycles spent in busy iterations or ms. */
|
||||
uint32_t iterations; /* Iterations in ms. */
|
||||
uint32_t pkts; /* Packets processed in iteration or ms. */
|
||||
uint32_t upcalls; /* Number of upcalls in iteration or ms. */
|
||||
uint32_t upcall_cycles; /* Cycles spent in upcalls in it. or ms. */
|
||||
uint32_t batches; /* Number of rx batches in iteration or ms. */
|
||||
uint32_t max_vhost_qfill; /* Maximum fill level in iteration or ms. */
|
||||
};
|
||||
|
||||
#define HISTORY_LEN 1000 /* Length of recorded history
|
||||
(iterations and ms). */
|
||||
#define DEF_HIST_SHOW 20 /* Default number of history samples to
|
||||
display. */
|
||||
|
||||
struct history {
|
||||
size_t idx; /* Slot to which next call to history_store()
|
||||
will write. */
|
||||
struct iter_stats sample[HISTORY_LEN];
|
||||
};
|
||||
|
||||
/* Container for all performance metrics of a PMD within the struct
|
||||
* dp_netdev_pmd_thread. The metrics must be updated from within the PMD
|
||||
* thread but can be read from any thread. The basic PMD counters in
|
||||
* struct pmd_counters can be read without protection against concurrent
|
||||
* clearing. The other metrics may only be safely read with the clear_mutex
|
||||
* held to protect against concurrent clearing. */
|
||||
|
||||
struct pmd_perf_stats {
|
||||
/* Start of the current PMD iteration in TSC cycles.*/
|
||||
uint64_t start_it_tsc;
|
||||
/* Prevents interference between PMD polling and stats clearing. */
|
||||
struct ovs_mutex stats_mutex;
|
||||
/* Set by CLI thread to order clearing of PMD stats. */
|
||||
volatile bool clear;
|
||||
/* Prevents stats retrieval while clearing is in progress. */
|
||||
struct ovs_mutex clear_mutex;
|
||||
/* Start of the current performance measurement period. */
|
||||
uint64_t start_ms;
|
||||
/* Counter for PMD iterations. */
|
||||
uint64_t iteration_cnt;
|
||||
/* Start of the current iteration. */
|
||||
uint64_t start_tsc;
|
||||
/* Latest TSC time stamp taken in PMD. */
|
||||
uint64_t last_tsc;
|
||||
/* Used to space certain checks in time. */
|
||||
uint64_t next_check_tsc;
|
||||
/* If non-NULL, outermost cycle timer currently running in PMD. */
|
||||
struct cycle_timer *cur_timer;
|
||||
/* Set of PMD counters with their zero offsets. */
|
||||
struct pmd_counters counters;
|
||||
/* Statistics of the current iteration. */
|
||||
struct iter_stats current;
|
||||
/* Totals for the current millisecond. */
|
||||
struct iter_stats totals;
|
||||
/* Histograms for the PMD metrics. */
|
||||
struct histogram cycles;
|
||||
struct histogram pkts;
|
||||
struct histogram cycles_per_pkt;
|
||||
struct histogram upcalls;
|
||||
struct histogram cycles_per_upcall;
|
||||
struct histogram pkts_per_batch;
|
||||
struct histogram max_vhost_qfill;
|
||||
/* Iteration history buffer. */
|
||||
struct history iterations;
|
||||
/* Millisecond history buffer. */
|
||||
struct history milliseconds;
|
||||
};
|
||||
|
||||
/* Support for accurate timing of PMD execution on TSC clock cycle level.
|
||||
@@ -175,8 +257,14 @@ cycle_timer_stop(struct pmd_perf_stats *s,
|
||||
return now - timer->start;
|
||||
}
|
||||
|
||||
/* Functions to initialize and reset the PMD performance metrics. */
|
||||
|
||||
void pmd_perf_stats_init(struct pmd_perf_stats *s);
|
||||
void pmd_perf_stats_clear(struct pmd_perf_stats *s);
|
||||
void pmd_perf_stats_clear_lock(struct pmd_perf_stats *s);
|
||||
|
||||
/* Functions to read and update PMD counters. */
|
||||
|
||||
void pmd_perf_read_counters(struct pmd_perf_stats *s,
|
||||
uint64_t stats[PMD_N_STATS]);
|
||||
|
||||
@@ -199,32 +287,95 @@ pmd_perf_update_counter(struct pmd_perf_stats *s,
|
||||
atomic_store_relaxed(&s->counters.n[counter], tmp);
|
||||
}
|
||||
|
||||
static inline void
|
||||
pmd_perf_start_iteration(struct pmd_perf_stats *s)
|
||||
{
|
||||
if (OVS_LIKELY(s->last_tsc)) {
|
||||
/* We assume here that last_tsc was updated immediately prior at
|
||||
* the end of the previous iteration, or just before the first
|
||||
* iteration. */
|
||||
s->start_it_tsc = s->last_tsc;
|
||||
} else {
|
||||
/* In case last_tsc has never been set before. */
|
||||
s->start_it_tsc = cycles_counter_update(s);
|
||||
}
|
||||
}
|
||||
/* Functions to manipulate a sample history. */
|
||||
|
||||
static inline void
|
||||
pmd_perf_end_iteration(struct pmd_perf_stats *s, int rx_packets)
|
||||
histogram_add_sample(struct histogram *hist, uint32_t val)
|
||||
{
|
||||
uint64_t cycles = cycles_counter_update(s) - s->start_it_tsc;
|
||||
|
||||
if (rx_packets > 0) {
|
||||
pmd_perf_update_counter(s, PMD_CYCLES_ITER_BUSY, cycles);
|
||||
} else {
|
||||
pmd_perf_update_counter(s, PMD_CYCLES_ITER_IDLE, cycles);
|
||||
/* TODO: Can do better with binary search? */
|
||||
for (int i = 0; i < NUM_BINS-1; i++) {
|
||||
if (val <= hist->wall[i]) {
|
||||
hist->bin[i]++;
|
||||
return;
|
||||
}
|
||||
}
|
||||
hist->bin[NUM_BINS-1]++;
|
||||
}
|
||||
|
||||
uint64_t histogram_samples(const struct histogram *hist);
|
||||
|
||||
/* This function is used to advance the given history index by positive
|
||||
* offset in the circular history buffer. */
|
||||
static inline uint32_t
|
||||
history_add(uint32_t idx, uint32_t offset)
|
||||
{
|
||||
return (idx + offset) % HISTORY_LEN;
|
||||
}
|
||||
|
||||
/* This function computes the difference between two indices into the
|
||||
* circular history buffer. The result is always positive in the range
|
||||
* 0 .. HISTORY_LEN-1 and specifies the number of steps to reach idx1
|
||||
* starting from idx2. It can also be used to retreat the history index
|
||||
* idx1 by idx2 steps. */
|
||||
static inline uint32_t
|
||||
history_sub(uint32_t idx1, uint32_t idx2)
|
||||
{
|
||||
return (idx1 + HISTORY_LEN - idx2) % HISTORY_LEN;
|
||||
}
|
||||
|
||||
static inline struct iter_stats *
|
||||
history_current(struct history *h)
|
||||
{
|
||||
return &h->sample[h->idx];
|
||||
}
|
||||
|
||||
static inline struct iter_stats *
|
||||
history_next(struct history *h)
|
||||
{
|
||||
size_t next_idx = history_add(h->idx, 1);
|
||||
struct iter_stats *next = &h->sample[next_idx];
|
||||
|
||||
memset(next, 0, sizeof(*next));
|
||||
h->idx = next_idx;
|
||||
return next;
|
||||
}
|
||||
|
||||
static inline struct iter_stats *
|
||||
history_store(struct history *h, struct iter_stats *is)
|
||||
{
|
||||
if (is) {
|
||||
h->sample[h->idx] = *is;
|
||||
}
|
||||
/* Advance the history pointer */
|
||||
return history_next(h);
|
||||
}
|
||||
|
||||
/* Functions recording PMD metrics per iteration. */
|
||||
|
||||
void
|
||||
pmd_perf_start_iteration(struct pmd_perf_stats *s);
|
||||
void
|
||||
pmd_perf_end_iteration(struct pmd_perf_stats *s, int rx_packets,
|
||||
int tx_packets, bool full_metrics);
|
||||
|
||||
/* Formatting the output of commands. */
|
||||
|
||||
struct pmd_perf_params {
|
||||
int command_type;
|
||||
bool histograms;
|
||||
size_t iter_hist_len;
|
||||
size_t ms_hist_len;
|
||||
};
|
||||
|
||||
void pmd_perf_format_overall_stats(struct ds *str, struct pmd_perf_stats *s,
|
||||
double duration);
|
||||
void pmd_perf_format_histograms(struct ds *str, struct pmd_perf_stats *s);
|
||||
void pmd_perf_format_iteration_history(struct ds *str,
|
||||
struct pmd_perf_stats *s,
|
||||
int n_iter);
|
||||
void pmd_perf_format_ms_history(struct ds *str, struct pmd_perf_stats *s,
|
||||
int n_ms);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
Reference in New Issue
Block a user