b97d3a9cbc
DSW keeps an internal port load estimate, used by the load balancing mechanism. As a side effect, it keeps track of the total number of busy cycles since startup. This metric is indirectly exposed in the form of DSW xstats' "port_<n>_event_proc_latency", which is the total number of busy cycles divided by the total number of events processed on a particular port. An external application can take (event_latency * dequeued) to go back to busy_cycles. One reason for doing this is to measure the port's load during a longer time period, without resorting to sampling "port_<n>_load". However, as the number dequeued events grows, a rounding error in event_latency renders the application-calculated busy_cycles inaccurate. Thus, it makes sense to directly expose the number of busy cycles as a DSW xstats, even though it might seem redundant. Signed-off-by: Mattias Rönnblom <mattias.ronnblom@ericsson.com>
377 lines
9.4 KiB
C
377 lines
9.4 KiB
C
/* SPDX-License-Identifier: BSD-3-Clause
|
|
* Copyright(c) 2018-2019 Ericsson AB
|
|
*/
|
|
|
|
#include "dsw_evdev.h"
|
|
|
|
#include <stdbool.h>
|
|
#include <string.h>
|
|
|
|
#include <rte_debug.h>
|
|
|
|
/* The high bits in the xstats id is used to store an additional
|
|
* parameter (beyond the queue or port id already in the xstats
|
|
* interface).
|
|
*/
|
|
#define DSW_XSTATS_ID_PARAM_BITS (8)
|
|
#define DSW_XSTATS_ID_STAT_BITS \
|
|
(sizeof(unsigned int)*CHAR_BIT - DSW_XSTATS_ID_PARAM_BITS)
|
|
#define DSW_XSTATS_ID_STAT_MASK ((1 << DSW_XSTATS_ID_STAT_BITS) - 1)
|
|
|
|
#define DSW_XSTATS_ID_GET_PARAM(id) \
|
|
((id)>>DSW_XSTATS_ID_STAT_BITS)
|
|
|
|
#define DSW_XSTATS_ID_GET_STAT(id) \
|
|
((id) & DSW_XSTATS_ID_STAT_MASK)
|
|
|
|
#define DSW_XSTATS_ID_CREATE(id, param_value) \
|
|
(((param_value) << DSW_XSTATS_ID_STAT_BITS) | id)
|
|
|
|
typedef
|
|
uint64_t (*dsw_xstats_dev_get_value_fn)(struct dsw_evdev *dsw);
|
|
|
|
struct dsw_xstat_dev {
|
|
const char *name;
|
|
dsw_xstats_dev_get_value_fn get_value_fn;
|
|
};
|
|
|
|
typedef
|
|
uint64_t (*dsw_xstats_port_get_value_fn)(struct dsw_evdev *dsw,
|
|
uint8_t port_id, uint8_t queue_id);
|
|
|
|
struct dsw_xstats_port {
|
|
const char *name_fmt;
|
|
dsw_xstats_port_get_value_fn get_value_fn;
|
|
bool per_queue;
|
|
};
|
|
|
|
static uint64_t
|
|
dsw_xstats_dev_credits_on_loan(struct dsw_evdev *dsw)
|
|
{
|
|
return rte_atomic32_read(&dsw->credits_on_loan);
|
|
}
|
|
|
|
static struct dsw_xstat_dev dsw_dev_xstats[] = {
|
|
{ "dev_credits_on_loan", dsw_xstats_dev_credits_on_loan }
|
|
};
|
|
|
|
#define DSW_GEN_PORT_ACCESS_FN(_variable) \
|
|
static uint64_t \
|
|
dsw_xstats_port_get_ ## _variable(struct dsw_evdev *dsw, \
|
|
uint8_t port_id, \
|
|
uint8_t queue_id __rte_unused) \
|
|
{ \
|
|
return dsw->ports[port_id]._variable; \
|
|
}
|
|
|
|
DSW_GEN_PORT_ACCESS_FN(new_enqueued)
|
|
DSW_GEN_PORT_ACCESS_FN(forward_enqueued)
|
|
DSW_GEN_PORT_ACCESS_FN(release_enqueued)
|
|
|
|
static uint64_t
|
|
dsw_xstats_port_get_queue_enqueued(struct dsw_evdev *dsw, uint8_t port_id,
|
|
uint8_t queue_id)
|
|
{
|
|
return dsw->ports[port_id].queue_enqueued[queue_id];
|
|
}
|
|
|
|
DSW_GEN_PORT_ACCESS_FN(dequeued)
|
|
|
|
static uint64_t
|
|
dsw_xstats_port_get_queue_dequeued(struct dsw_evdev *dsw, uint8_t port_id,
|
|
uint8_t queue_id)
|
|
{
|
|
return dsw->ports[port_id].queue_dequeued[queue_id];
|
|
}
|
|
|
|
DSW_GEN_PORT_ACCESS_FN(emigrations)
|
|
DSW_GEN_PORT_ACCESS_FN(immigrations)
|
|
|
|
static uint64_t
|
|
dsw_xstats_port_get_migration_latency(struct dsw_evdev *dsw, uint8_t port_id,
|
|
uint8_t queue_id __rte_unused)
|
|
{
|
|
uint64_t total_latency = dsw->ports[port_id].emigration_latency;
|
|
uint64_t num_emigrations = dsw->ports[port_id].emigrations;
|
|
|
|
return num_emigrations > 0 ? total_latency / num_emigrations : 0;
|
|
}
|
|
|
|
static uint64_t
|
|
dsw_xstats_port_get_event_proc_latency(struct dsw_evdev *dsw, uint8_t port_id,
|
|
uint8_t queue_id __rte_unused)
|
|
{
|
|
uint64_t total_busy_cycles =
|
|
dsw->ports[port_id].total_busy_cycles;
|
|
uint64_t dequeued =
|
|
dsw->ports[port_id].dequeued;
|
|
|
|
return dequeued > 0 ? total_busy_cycles / dequeued : 0;
|
|
}
|
|
|
|
static uint64_t
|
|
dsw_xstats_port_get_busy_cycles(struct dsw_evdev *dsw, uint8_t port_id,
|
|
uint8_t queue_id __rte_unused)
|
|
{
|
|
return dsw->ports[port_id].total_busy_cycles;
|
|
}
|
|
|
|
DSW_GEN_PORT_ACCESS_FN(inflight_credits)
|
|
|
|
DSW_GEN_PORT_ACCESS_FN(pending_releases)
|
|
|
|
static uint64_t
|
|
dsw_xstats_port_get_load(struct dsw_evdev *dsw, uint8_t port_id,
|
|
uint8_t queue_id __rte_unused)
|
|
{
|
|
int16_t load;
|
|
|
|
load = rte_atomic16_read(&dsw->ports[port_id].load);
|
|
|
|
return DSW_LOAD_TO_PERCENT(load);
|
|
}
|
|
|
|
DSW_GEN_PORT_ACCESS_FN(last_bg)
|
|
|
|
static struct dsw_xstats_port dsw_port_xstats[] = {
|
|
{ "port_%u_new_enqueued", dsw_xstats_port_get_new_enqueued,
|
|
false },
|
|
{ "port_%u_forward_enqueued", dsw_xstats_port_get_forward_enqueued,
|
|
false },
|
|
{ "port_%u_release_enqueued", dsw_xstats_port_get_release_enqueued,
|
|
false },
|
|
{ "port_%u_queue_%u_enqueued", dsw_xstats_port_get_queue_enqueued,
|
|
true },
|
|
{ "port_%u_dequeued", dsw_xstats_port_get_dequeued,
|
|
false },
|
|
{ "port_%u_queue_%u_dequeued", dsw_xstats_port_get_queue_dequeued,
|
|
true },
|
|
{ "port_%u_emigrations", dsw_xstats_port_get_emigrations,
|
|
false },
|
|
{ "port_%u_migration_latency", dsw_xstats_port_get_migration_latency,
|
|
false },
|
|
{ "port_%u_immigrations", dsw_xstats_port_get_immigrations,
|
|
false },
|
|
{ "port_%u_event_proc_latency", dsw_xstats_port_get_event_proc_latency,
|
|
false },
|
|
{ "port_%u_busy_cycles", dsw_xstats_port_get_busy_cycles,
|
|
false },
|
|
{ "port_%u_inflight_credits", dsw_xstats_port_get_inflight_credits,
|
|
false },
|
|
{ "port_%u_pending_releases", dsw_xstats_port_get_pending_releases,
|
|
false },
|
|
{ "port_%u_load", dsw_xstats_port_get_load,
|
|
false },
|
|
{ "port_%u_last_bg", dsw_xstats_port_get_last_bg,
|
|
false }
|
|
};
|
|
|
|
typedef
|
|
void (*dsw_xstats_foreach_fn)(const char *xstats_name,
|
|
enum rte_event_dev_xstats_mode mode,
|
|
uint8_t queue_port_id, unsigned int xstats_id,
|
|
void *data);
|
|
|
|
static void
|
|
dsw_xstats_dev_foreach(dsw_xstats_foreach_fn fn, void *fn_data)
|
|
{
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < RTE_DIM(dsw_dev_xstats); i++)
|
|
fn(dsw_dev_xstats[i].name, RTE_EVENT_DEV_XSTATS_DEVICE, 0,
|
|
i, fn_data);
|
|
}
|
|
|
|
static void
|
|
dsw_xstats_port_foreach(struct dsw_evdev *dsw, uint8_t port_id,
|
|
dsw_xstats_foreach_fn fn, void *fn_data)
|
|
{
|
|
uint8_t queue_id;
|
|
unsigned int stat_idx;
|
|
|
|
for (stat_idx = 0, queue_id = 0;
|
|
stat_idx < RTE_DIM(dsw_port_xstats);) {
|
|
struct dsw_xstats_port *xstat = &dsw_port_xstats[stat_idx];
|
|
char xstats_name[RTE_EVENT_DEV_XSTATS_NAME_SIZE];
|
|
unsigned int xstats_id;
|
|
|
|
if (xstat->per_queue) {
|
|
xstats_id = DSW_XSTATS_ID_CREATE(stat_idx, queue_id);
|
|
snprintf(xstats_name, sizeof(xstats_name),
|
|
dsw_port_xstats[stat_idx].name_fmt, port_id,
|
|
queue_id);
|
|
queue_id++;
|
|
} else {
|
|
xstats_id = stat_idx;
|
|
snprintf(xstats_name, sizeof(xstats_name),
|
|
dsw_port_xstats[stat_idx].name_fmt, port_id);
|
|
}
|
|
|
|
fn(xstats_name, RTE_EVENT_DEV_XSTATS_PORT, port_id,
|
|
xstats_id, fn_data);
|
|
|
|
if (!(xstat->per_queue && queue_id < dsw->num_queues)) {
|
|
stat_idx++;
|
|
queue_id = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
struct store_ctx {
|
|
struct rte_event_dev_xstats_name *names;
|
|
unsigned int *ids;
|
|
unsigned int count;
|
|
unsigned int capacity;
|
|
};
|
|
|
|
static void
|
|
dsw_xstats_store_stat(const char *xstats_name,
|
|
enum rte_event_dev_xstats_mode mode,
|
|
uint8_t queue_port_id, unsigned int xstats_id,
|
|
void *data)
|
|
{
|
|
struct store_ctx *ctx = data;
|
|
|
|
RTE_SET_USED(mode);
|
|
RTE_SET_USED(queue_port_id);
|
|
|
|
if (ctx->count < ctx->capacity) {
|
|
strcpy(ctx->names[ctx->count].name, xstats_name);
|
|
ctx->ids[ctx->count] = xstats_id;
|
|
}
|
|
|
|
ctx->count++;
|
|
}
|
|
|
|
int
|
|
dsw_xstats_get_names(const struct rte_eventdev *dev,
|
|
enum rte_event_dev_xstats_mode mode,
|
|
uint8_t queue_port_id,
|
|
struct rte_event_dev_xstats_name *xstats_names,
|
|
unsigned int *ids, unsigned int capacity)
|
|
{
|
|
struct dsw_evdev *dsw = dsw_pmd_priv(dev);
|
|
|
|
struct store_ctx ctx = {
|
|
.names = xstats_names,
|
|
.ids = ids,
|
|
.capacity = capacity
|
|
};
|
|
|
|
switch (mode) {
|
|
case RTE_EVENT_DEV_XSTATS_DEVICE:
|
|
dsw_xstats_dev_foreach(dsw_xstats_store_stat, &ctx);
|
|
return ctx.count;
|
|
case RTE_EVENT_DEV_XSTATS_PORT:
|
|
dsw_xstats_port_foreach(dsw, queue_port_id,
|
|
dsw_xstats_store_stat, &ctx);
|
|
return ctx.count;
|
|
case RTE_EVENT_DEV_XSTATS_QUEUE:
|
|
return 0;
|
|
default:
|
|
RTE_ASSERT(false);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
static int
|
|
dsw_xstats_dev_get(const struct rte_eventdev *dev,
|
|
const unsigned int ids[], uint64_t values[], unsigned int n)
|
|
{
|
|
struct dsw_evdev *dsw = dsw_pmd_priv(dev);
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < n; i++) {
|
|
unsigned int id = ids[i];
|
|
struct dsw_xstat_dev *xstat = &dsw_dev_xstats[id];
|
|
values[i] = xstat->get_value_fn(dsw);
|
|
}
|
|
return n;
|
|
}
|
|
|
|
static int
|
|
dsw_xstats_port_get(const struct rte_eventdev *dev, uint8_t port_id,
|
|
const unsigned int ids[], uint64_t values[], unsigned int n)
|
|
{
|
|
struct dsw_evdev *dsw = dsw_pmd_priv(dev);
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < n; i++) {
|
|
unsigned int id = ids[i];
|
|
unsigned int stat_idx = DSW_XSTATS_ID_GET_STAT(id);
|
|
struct dsw_xstats_port *xstat = &dsw_port_xstats[stat_idx];
|
|
uint8_t queue_id = 0;
|
|
|
|
if (xstat->per_queue)
|
|
queue_id = DSW_XSTATS_ID_GET_PARAM(id);
|
|
|
|
values[i] = xstat->get_value_fn(dsw, port_id, queue_id);
|
|
}
|
|
return n;
|
|
}
|
|
|
|
int
|
|
dsw_xstats_get(const struct rte_eventdev *dev,
|
|
enum rte_event_dev_xstats_mode mode, uint8_t queue_port_id,
|
|
const unsigned int ids[], uint64_t values[], unsigned int n)
|
|
{
|
|
switch (mode) {
|
|
case RTE_EVENT_DEV_XSTATS_DEVICE:
|
|
return dsw_xstats_dev_get(dev, ids, values, n);
|
|
case RTE_EVENT_DEV_XSTATS_PORT:
|
|
return dsw_xstats_port_get(dev, queue_port_id, ids, values, n);
|
|
case RTE_EVENT_DEV_XSTATS_QUEUE:
|
|
return 0;
|
|
default:
|
|
RTE_ASSERT(false);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
struct find_ctx {
|
|
const struct rte_eventdev *dev;
|
|
const char *name;
|
|
unsigned int *id;
|
|
uint64_t value;
|
|
};
|
|
|
|
static void
|
|
dsw_xstats_find_stat(const char *xstats_name,
|
|
enum rte_event_dev_xstats_mode mode,
|
|
uint8_t queue_port_id, unsigned int xstats_id,
|
|
void *data)
|
|
{
|
|
struct find_ctx *ctx = data;
|
|
|
|
if (strcmp(ctx->name, xstats_name) == 0) {
|
|
if (ctx->id != NULL)
|
|
*ctx->id = xstats_id;
|
|
dsw_xstats_get(ctx->dev, mode, queue_port_id, &xstats_id,
|
|
&ctx->value, 1);
|
|
}
|
|
}
|
|
|
|
uint64_t
|
|
dsw_xstats_get_by_name(const struct rte_eventdev *dev, const char *name,
|
|
unsigned int *id)
|
|
{
|
|
struct dsw_evdev *dsw = dsw_pmd_priv(dev);
|
|
uint16_t port_id;
|
|
|
|
struct find_ctx ctx = {
|
|
.dev = dev,
|
|
.name = name,
|
|
.id = id,
|
|
.value = -EINVAL
|
|
};
|
|
|
|
dsw_xstats_dev_foreach(dsw_xstats_find_stat, &ctx);
|
|
|
|
for (port_id = 0; port_id < dsw->num_ports; port_id++)
|
|
dsw_xstats_port_foreach(dsw, port_id, dsw_xstats_find_stat,
|
|
&ctx);
|
|
|
|
return ctx.value;
|
|
}
|