From: Pravin Pathak <[email protected]> Implement the eventdev extended statistics (xstats) interface and enhance PMD diagnostics in the dump callback.
Device and per-port statistics are exposed through xstats callbacks, including enqueue/dequeue counts, credit loan counts, and migration tallies. The slb_dump callback is extended to display detailed per-port and per-queue internal state, atomic migration status, and queue depth. Signed-off-by: Pravin Pathak <[email protected]> --- drivers/event/slb/meson.build | 5 + drivers/event/slb/slb_evdev.c | 94 ++++++++- drivers/event/slb/slb_evdev.h | 11 ++ drivers/event/slb/slb_xstats.c | 347 +++++++++++++++++++++++++++++++++ 4 files changed, 450 insertions(+), 7 deletions(-) create mode 100644 drivers/event/slb/slb_xstats.c diff --git a/drivers/event/slb/meson.build b/drivers/event/slb/meson.build index 9a1dff524f..d698a58f16 100644 --- a/drivers/event/slb/meson.build +++ b/drivers/event/slb/meson.build @@ -10,6 +10,11 @@ endif sources = files( 'slb_evdev.c', 'slb_event.c', + 'slb_xstats.c', ) deps += ['bus_vdev', 'ring'] + +if cc.has_argument('-Wno-format-nonliteral') + cflags += '-Wno-format-nonliteral' +endif diff --git a/drivers/event/slb/slb_evdev.c b/drivers/event/slb/slb_evdev.c index 1916b7faa4..04a2aba165 100644 --- a/drivers/event/slb/slb_evdev.c +++ b/drivers/event/slb/slb_evdev.c @@ -606,6 +606,8 @@ slb_dump(struct rte_eventdev *dev, FILE *f) { struct slb_evdev *slb = slb_pmd_priv(dev); int32_t pool_on_loan, pool_avail; + uint64_t total_mig_in = 0, total_mig_out = 0; + int i; if (dev == NULL || f == NULL) return; @@ -617,20 +619,41 @@ slb_dump(struct rte_eventdev *dev, FILE *f) fprintf(f, "\n[SLB EVENTDEV DEBUG STATS]\n"); fprintf(f, " Device ID: %u, Driver: %s\n", dev->data->dev_id, SLB_PMD_NAME); - fprintf(f, " Credit Pool: %d on_loan / %d max_inflight (%d avail)\n", + fprintf(f, " Credit Pool: %d on_loan / %d max_inflight (%d available)\n", pool_on_loan, slb->max_inflight, pool_avail); + fprintf(f, "\n PORT FLOW MIGRATION COUNTERS:\n"); + for (i = 0; i < slb->num_ports; i++) { + struct slb_port *p = &slb->ports[i]; + + total_mig_in += p->migrations_in; + total_mig_out += p->migrations_out; + fprintf(f, + " Port %2u: Migrated-In: %6" PRIu64 + " | Migrated-Out: %6" PRIu64 " | Net: %+5" PRId64 "\n", + i, p->migrations_in, p->migrations_out, + (int64_t)p->migrations_in - (int64_t)p->migrations_out); + } + fprintf(f, " Total Flow Migrations: IN=%" PRIu64 ", OUT=%" PRIu64 "\n", + total_mig_in, total_mig_out); + fprintf(f, "\n PORT STATISTICS & QUEUE LINKAGES:\n"); - for (int i = 0; i < slb->num_ports; i++) { + for (i = 0; i < slb->num_ports; i++) { struct slb_port *p = &slb->ports[i]; uint64_t aq = rte_atomic_load_explicit(&p->active_queues, rte_memory_order_acquire); - fprintf(f, " Port %2u (credits=%3d, active=0x%08" PRIx64 "): ", - i, p->inflight_credits, aq); int linked_count = 0; + + fprintf(f, + " Port %2u (credits=%3d, active=0x%08" PRIx64 + ", loaded=%d, mig_in=%" PRIu64 ", mig_out=%" PRIu64 "): ", + i, p->inflight_credits, aq, p->is_loaded, + p->migrations_in, p->migrations_out); + if (p->is_single_linked) { if (p->single_linked_q) { int qid = p->single_linked_q - slb->queues; + fprintf(f, "Q%d (single-link)", qid); linked_count++; } @@ -638,6 +661,7 @@ slb_dump(struct rte_eventdev *dev, FILE *f) for (int pr = 0; pr < SLB_MAX_PRIORITIES; pr++) { for (int s = 0; s < p->num_q[pr]; s++) { int qid = p->q_entries[pr][s].queue_id; + if (linked_count > 0) fprintf(f, ", "); fprintf(f, "Q%d(p%d)", qid, pr); @@ -648,16 +672,69 @@ slb_dump(struct rte_eventdev *dev, FILE *f) if (linked_count == 0) fprintf(f, "(no queues linked)"); fprintf(f, "\n"); + fprintf(f, + " stats: enq_calls=%" PRIu64 + ", new_enq=%" PRIu64 ", fwd_enq=%" PRIu64 + ", rel_enq=%" PRIu64 ", deq_calls=%" PRIu64 + ", deq=%" PRIu64 "\n", + p->enqueue_calls, p->new_enqueued, + p->forward_enqueued, p->release_enqueued, + p->dequeue_calls, p->dequeued); } fprintf(f, "\n ALL QUEUES STATUS:\n"); - for (int i = 0; i < slb->num_queues; i++) { + for (i = 0; i < slb->num_queues; i++) { struct slb_queue *q = &slb->queues[i]; uint32_t count = rte_event_ring_count(q->q_ring); uint32_t ring_size = rte_event_ring_get_capacity(q->q_ring); - fprintf(f, " Queue %2u: %6u/%6u events, serving_ports=0x%016" PRIx64 ", sched=%u, single=%d\n", + + fprintf(f, + " Queue %2u: %6u/%6u events, serving_ports=0x%016" PRIx64 + " (count=%u), sched_type=%u, single_link=%d\n", i, count, ring_size, q->serving_ports_mask, - q->schedule_type, q->is_single_link); + q->num_serving_ports, q->schedule_type, + q->is_single_link); + if (q->schedule_type == RTE_SCHED_TYPE_ATOMIC) { + if (q->atomic_state) { + fprintf(f, + " Atomic State: total_migrations=%" PRIu64 + ", hold_count=%u\n", + q->atomic_state->migrations_count, + q->atomic_state->hold_count); + if (q->atomic_state->migration.in_progress) { + fprintf(f, + " MIGRATION IN PROGRESS: from port %u, flow_idx=%u\n", + q->atomic_state->migration.port_id, + q->atomic_state->migration.flow_idx); + } + for (int j = 0; j < slb->num_ports; j++) { + if (q->per_port_rings[j]) { + struct slb_atomic_port_stats *ps = + &q->atomic_state->port_stats[j]; + + fprintf(f, + " Per-port ring %d: %u events" + " (load_counter=%u, flows=%u," + " enq=%" PRIu64 ", ret=%" PRIu64 ")\n", + j, + rte_event_ring_count(q->per_port_rings[j]), + ps->load_counter, + ps->flow_count, + ps->q_enqueued, + ps->q_returned); + } + } + } + } else if (q->schedule_type == RTE_SCHED_TYPE_ORDERED) { + fprintf(f, " Ordered reorder_drain_idx=%u\n", + q->reorder_drain_idx); + } + if (count > 0 && q->serving_ports_mask == 0) + fprintf(f, + " ^^^ PROBLEM: Queue has %u events but serving_ports_mask=0!\n", + count); + if (count == 0 && q->serving_ports_mask == 0) + fprintf(f, " (unused queue)\n"); } fprintf(f, "[END DEBUG]\n\n"); fflush(f); @@ -678,6 +755,9 @@ static struct eventdev_ops slb_evdev_ops = { .dev_stop = slb_stop, .dev_close = slb_close, .dump = slb_dump, + .xstats_get = slb_xstats_get, + .xstats_get_names = slb_xstats_get_names, + .xstats_get_by_name = slb_xstats_get_by_name, }; static int diff --git a/drivers/event/slb/slb_evdev.h b/drivers/event/slb/slb_evdev.h index b1a6de8606..17f3154efa 100644 --- a/drivers/event/slb/slb_evdev.h +++ b/drivers/event/slb/slb_evdev.h @@ -231,6 +231,17 @@ void slb_reorder_drain(struct slb_evdev *slb, struct slb_queue *queue); void slb_dump(struct rte_eventdev *dev, FILE *f); +int slb_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, + uint64_t *ids, unsigned int capacity); +int slb_xstats_get(const struct rte_eventdev *dev, + enum rte_event_dev_xstats_mode mode, uint8_t queue_port_id, + const uint64_t ids[], uint64_t values[], unsigned int n); +uint64_t slb_xstats_get_by_name(const struct rte_eventdev *dev, + const char *name, uint64_t *id); + static inline struct slb_evdev * slb_pmd_priv(const struct rte_eventdev *eventdev) { diff --git a/drivers/event/slb/slb_xstats.c b/drivers/event/slb/slb_xstats.c new file mode 100644 index 0000000000..fdb7dd6cc5 --- /dev/null +++ b/drivers/event/slb/slb_xstats.c @@ -0,0 +1,347 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2026 Pravin Pathak <[email protected]> + */ + +#include "slb_evdev.h" + +#include <limits.h> +#include <stdbool.h> +#include <string.h> + +#include <rte_debug.h> +#include <rte_string_fns.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 SLB_XSTATS_ID_PARAM_BITS (8) +#define SLB_XSTATS_ID_STAT_BITS \ + (sizeof(uint64_t) * CHAR_BIT - SLB_XSTATS_ID_PARAM_BITS) +#define SLB_XSTATS_ID_STAT_MASK ((UINT64_C(1) << SLB_XSTATS_ID_STAT_BITS) - 1) + +#define SLB_XSTATS_ID_GET_PARAM(id) \ + ((id) >> SLB_XSTATS_ID_STAT_BITS) + +#define SLB_XSTATS_ID_GET_STAT(id) \ + ((id) & SLB_XSTATS_ID_STAT_MASK) + +#define SLB_XSTATS_ID_CREATE(id, param_value) \ + ((((uint64_t)(param_value)) << SLB_XSTATS_ID_STAT_BITS) | (id)) + +typedef uint64_t (*slb_xstats_dev_get_value_fn)(struct slb_evdev *slb); + +struct slb_xstat_dev { + const char *name; + slb_xstats_dev_get_value_fn get_value_fn; +}; + +typedef uint64_t (*slb_xstats_port_get_value_fn)(struct slb_evdev *slb, + uint8_t port_id, + uint8_t queue_id); + +struct slb_xstats_port { + const char *name_fmt; + slb_xstats_port_get_value_fn get_value_fn; + bool per_queue; +}; + +static uint64_t +slb_xstats_dev_credits_on_loan(struct slb_evdev *slb) +{ + return rte_atomic_load_explicit(&slb->credits_on_loan, + rte_memory_order_relaxed); +} + +static struct slb_xstat_dev slb_dev_xstats[] = { + { "dev_credits_on_loan", slb_xstats_dev_credits_on_loan } +}; + +#define SLB_GEN_PORT_ACCESS_FN(_variable) \ + static uint64_t \ + slb_xstats_port_get_ ## _variable(struct slb_evdev *slb, \ + uint8_t port_id, \ + uint8_t queue_id __rte_unused) \ + { \ + return slb->ports[port_id]._variable; \ + } + +SLB_GEN_PORT_ACCESS_FN(new_enqueued) +SLB_GEN_PORT_ACCESS_FN(forward_enqueued) +SLB_GEN_PORT_ACCESS_FN(release_enqueued) + +static uint64_t +slb_xstats_port_get_queue_enqueued(struct slb_evdev *slb, uint8_t port_id, + uint8_t queue_id) +{ + return slb->ports[port_id].queue_enqueued[queue_id]; +} + +SLB_GEN_PORT_ACCESS_FN(dequeued) + +static uint64_t +slb_xstats_port_get_queue_dequeued(struct slb_evdev *slb, uint8_t port_id, + uint8_t queue_id) +{ + return slb->ports[port_id].queue_dequeued[queue_id]; +} + +SLB_GEN_PORT_ACCESS_FN(inflight_credits) +SLB_GEN_PORT_ACCESS_FN(pending_releases) +SLB_GEN_PORT_ACCESS_FN(migrations_in) +SLB_GEN_PORT_ACCESS_FN(migrations_out) + +static struct slb_xstats_port slb_port_xstats[] = { + { "port_%u_new_enqueued", slb_xstats_port_get_new_enqueued, + false }, + { "port_%u_forward_enqueued", slb_xstats_port_get_forward_enqueued, + false }, + { "port_%u_release_enqueued", slb_xstats_port_get_release_enqueued, + false }, + { "port_%u_queue_%u_enqueued", slb_xstats_port_get_queue_enqueued, + true }, + { "port_%u_dequeued", slb_xstats_port_get_dequeued, + false }, + { "port_%u_queue_%u_dequeued", slb_xstats_port_get_queue_dequeued, + true }, + { "port_%u_inflight_credits", slb_xstats_port_get_inflight_credits, + false }, + { "port_%u_pending_releases", slb_xstats_port_get_pending_releases, + false }, + { "port_%u_migrations_in", slb_xstats_port_get_migrations_in, + false }, + { "port_%u_migrations_out", slb_xstats_port_get_migrations_out, + false }, +}; + +typedef void (*slb_xstats_foreach_fn)(const char *xstats_name, + enum rte_event_dev_xstats_mode mode, + uint8_t queue_port_id, + uint64_t xstats_id, + void *data); + +static void +slb_xstats_dev_foreach(slb_xstats_foreach_fn fn, void *fn_data) +{ + unsigned int i; + + for (i = 0; i < RTE_DIM(slb_dev_xstats); i++) + fn(slb_dev_xstats[i].name, RTE_EVENT_DEV_XSTATS_DEVICE, 0, + i, fn_data); +} + +static void +slb_xstats_port_foreach(struct slb_evdev *slb, uint8_t port_id, + slb_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(slb_port_xstats);) { + struct slb_xstats_port *xstat = &slb_port_xstats[stat_idx]; + char xstats_name[RTE_EVENT_DEV_XSTATS_NAME_SIZE]; + uint64_t xstats_id; + + if (xstat->per_queue) { + xstats_id = SLB_XSTATS_ID_CREATE(stat_idx, queue_id); + snprintf(xstats_name, sizeof(xstats_name), + slb_port_xstats[stat_idx].name_fmt, port_id, + queue_id); + queue_id++; + } else { + xstats_id = stat_idx; + snprintf(xstats_name, sizeof(xstats_name), + slb_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 < slb->num_queues)) { + stat_idx++; + queue_id = 0; + } + } +} + +struct store_ctx { + struct rte_event_dev_xstats_name *names; + uint64_t *ids; + unsigned int count; + unsigned int capacity; +}; + +static void +slb_xstats_store_stat(const char *xstats_name, + enum rte_event_dev_xstats_mode mode, + uint8_t queue_port_id, uint64_t xstats_id, + void *data) +{ + struct store_ctx *ctx = data; + + RTE_SET_USED(mode); + RTE_SET_USED(queue_port_id); + + if (ctx->count < ctx->capacity) { + if (ctx->names != NULL) + strlcpy(ctx->names[ctx->count].name, xstats_name, + RTE_EVENT_DEV_XSTATS_NAME_SIZE); + if (ctx->ids != NULL) + ctx->ids[ctx->count] = xstats_id; + } + + ctx->count++; +} + +int +slb_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, + uint64_t *ids, unsigned int capacity) +{ + struct slb_evdev *slb = slb_pmd_priv(dev); + struct store_ctx ctx = { + .names = xstats_names, + .ids = ids, + .capacity = capacity + }; + + switch (mode) { + case RTE_EVENT_DEV_XSTATS_DEVICE: + slb_xstats_dev_foreach(slb_xstats_store_stat, &ctx); + return ctx.count; + case RTE_EVENT_DEV_XSTATS_PORT: + if (queue_port_id >= slb->num_ports) + return -EINVAL; + slb_xstats_port_foreach(slb, queue_port_id, + slb_xstats_store_stat, &ctx); + return ctx.count; + case RTE_EVENT_DEV_XSTATS_QUEUE: + return 0; + default: + return -EINVAL; + } +} + +static int +slb_xstats_dev_get(const struct rte_eventdev *dev, + const uint64_t ids[], uint64_t values[], unsigned int n) +{ + struct slb_evdev *slb = slb_pmd_priv(dev); + unsigned int i; + + for (i = 0; i < n; i++) { + uint64_t id = ids[i]; + struct slb_xstat_dev *xstat; + + if (id >= RTE_DIM(slb_dev_xstats)) + return -EINVAL; + + xstat = &slb_dev_xstats[id]; + values[i] = xstat->get_value_fn(slb); + } + return n; +} + +static int +slb_xstats_port_get(const struct rte_eventdev *dev, uint8_t port_id, + const uint64_t ids[], uint64_t values[], unsigned int n) +{ + struct slb_evdev *slb = slb_pmd_priv(dev); + unsigned int i; + + for (i = 0; i < n; i++) { + uint64_t id = ids[i]; + unsigned int stat_idx = SLB_XSTATS_ID_GET_STAT(id); + struct slb_xstats_port *xstat; + uint8_t queue_id = 0; + + if (stat_idx >= RTE_DIM(slb_port_xstats)) + return -EINVAL; + + xstat = &slb_port_xstats[stat_idx]; + + if (xstat->per_queue) { + queue_id = SLB_XSTATS_ID_GET_PARAM(id); + if (queue_id >= slb->num_queues) + return -EINVAL; + } + + values[i] = xstat->get_value_fn(slb, port_id, queue_id); + } + return n; +} + +int +slb_xstats_get(const struct rte_eventdev *dev, + enum rte_event_dev_xstats_mode mode, uint8_t queue_port_id, + const uint64_t ids[], uint64_t values[], unsigned int n) +{ + struct slb_evdev *slb = slb_pmd_priv(dev); + + switch (mode) { + case RTE_EVENT_DEV_XSTATS_DEVICE: + return slb_xstats_dev_get(dev, ids, values, n); + case RTE_EVENT_DEV_XSTATS_PORT: + if (queue_port_id >= slb->num_ports) + return -EINVAL; + return slb_xstats_port_get(dev, queue_port_id, ids, values, n); + case RTE_EVENT_DEV_XSTATS_QUEUE: + return 0; + default: + return -EINVAL; + } +} + +struct find_ctx { + const struct rte_eventdev *dev; + const char *name; + uint64_t *id; + uint64_t value; +}; + +static void +slb_xstats_find_stat(const char *xstats_name, + enum rte_event_dev_xstats_mode mode, + uint8_t queue_port_id, uint64_t xstats_id, + void *data) +{ + struct find_ctx *ctx = data; + + if (strcmp(ctx->name, xstats_name) == 0) { + if (ctx->id != NULL) + *ctx->id = xstats_id; + slb_xstats_get(ctx->dev, mode, queue_port_id, &xstats_id, + &ctx->value, 1); + } +} + +uint64_t +slb_xstats_get_by_name(const struct rte_eventdev *dev, const char *name, + uint64_t *id) +{ + struct slb_evdev *slb = slb_pmd_priv(dev); + struct find_ctx ctx = { + .dev = dev, + .name = name, + .id = id, + .value = -EINVAL + }; + uint16_t port_id; + + if (id != NULL) + *id = (uint64_t)-1; + + if (name == NULL) + return -EINVAL; + + slb_xstats_dev_foreach(slb_xstats_find_stat, &ctx); + + for (port_id = 0; port_id < slb->num_ports; port_id++) + slb_xstats_port_foreach(slb, port_id, slb_xstats_find_stat, + &ctx); + + return ctx.value; +} -- 2.53.0

