50448dd3ab
Datapath queue is either Rx or Tx, so just one counter is sufficient for doorbells. It can count Tx doorbells in the case of Tx queue and Rx doorbells in the case of Rx queue. Signed-off-by: Andrew Rybchenko <andrew.rybchenko@oktetlabs.ru>
881 lines
22 KiB
C
881 lines
22 KiB
C
/* SPDX-License-Identifier: BSD-3-Clause
|
|
*
|
|
* Copyright(c) 2021 Xilinx, Inc.
|
|
*/
|
|
#include <rte_dev.h>
|
|
#include <rte_bitmap.h>
|
|
|
|
#include "sfc.h"
|
|
#include "sfc_rx.h"
|
|
#include "sfc_tx.h"
|
|
#include "sfc_sw_stats.h"
|
|
|
|
#define SFC_SW_STAT_INVALID UINT64_MAX
|
|
|
|
#define SFC_SW_STATS_GROUP_SIZE_MAX 2U
|
|
#define SFC_SW_STAT_GOOD_PACKETS "packets"
|
|
#define SFC_SW_STAT_GOOD_BYTES "bytes"
|
|
|
|
enum sfc_sw_stats_type {
|
|
SFC_SW_STATS_RX,
|
|
SFC_SW_STATS_TX,
|
|
};
|
|
|
|
enum sfc_sw_stats_group_basic {
|
|
SFC_SW_STATS_GROUP_BASIC_PKTS = 0,
|
|
SFC_SW_STATS_GROUP_BASIC_BYTES,
|
|
SFX_SW_STATS_GROUP_BASIC_MAX
|
|
};
|
|
|
|
typedef void sfc_get_sw_stat_val_t(struct sfc_adapter *sa, uint16_t qid,
|
|
uint64_t *values, unsigned int values_count);
|
|
|
|
struct sfc_sw_stat_descr {
|
|
const char *name;
|
|
enum sfc_sw_stats_type type;
|
|
sfc_get_sw_stat_val_t *get_val;
|
|
bool provide_total;
|
|
};
|
|
|
|
static sfc_get_sw_stat_val_t sfc_sw_stat_get_rx_good_pkts_bytes;
|
|
static void
|
|
sfc_sw_stat_get_rx_good_pkts_bytes(struct sfc_adapter *sa, uint16_t qid,
|
|
uint64_t *values,
|
|
unsigned int values_count)
|
|
{
|
|
struct sfc_adapter_shared *sas = sfc_sa2shared(sa);
|
|
struct sfc_rxq_info *rxq_info;
|
|
union sfc_pkts_bytes qstats;
|
|
|
|
RTE_SET_USED(values_count);
|
|
SFC_ASSERT(values_count == SFX_SW_STATS_GROUP_BASIC_MAX);
|
|
rxq_info = sfc_rxq_info_by_ethdev_qid(sas, qid);
|
|
if (rxq_info->state & SFC_RXQ_INITIALIZED) {
|
|
sfc_pkts_bytes_get(&rxq_info->dp->dpq.stats, &qstats);
|
|
values[SFC_SW_STATS_GROUP_BASIC_PKTS] = qstats.pkts;
|
|
values[SFC_SW_STATS_GROUP_BASIC_BYTES] = qstats.bytes;
|
|
} else {
|
|
values[SFC_SW_STATS_GROUP_BASIC_PKTS] = 0;
|
|
values[SFC_SW_STATS_GROUP_BASIC_BYTES] = 0;
|
|
}
|
|
}
|
|
|
|
static sfc_get_sw_stat_val_t sfc_sw_stat_get_tx_good_pkts_bytes;
|
|
static void
|
|
sfc_sw_stat_get_tx_good_pkts_bytes(struct sfc_adapter *sa, uint16_t qid,
|
|
uint64_t *values,
|
|
unsigned int values_count)
|
|
{
|
|
struct sfc_adapter_shared *sas = sfc_sa2shared(sa);
|
|
struct sfc_txq_info *txq_info;
|
|
union sfc_pkts_bytes qstats;
|
|
|
|
RTE_SET_USED(values_count);
|
|
SFC_ASSERT(values_count == SFX_SW_STATS_GROUP_BASIC_MAX);
|
|
txq_info = sfc_txq_info_by_ethdev_qid(sas, qid);
|
|
if (txq_info->state & SFC_TXQ_INITIALIZED) {
|
|
sfc_pkts_bytes_get(&txq_info->dp->dpq.stats, &qstats);
|
|
values[SFC_SW_STATS_GROUP_BASIC_PKTS] = qstats.pkts;
|
|
values[SFC_SW_STATS_GROUP_BASIC_BYTES] = qstats.bytes;
|
|
} else {
|
|
values[SFC_SW_STATS_GROUP_BASIC_PKTS] = 0;
|
|
values[SFC_SW_STATS_GROUP_BASIC_BYTES] = 0;
|
|
}
|
|
}
|
|
|
|
static sfc_get_sw_stat_val_t sfc_get_sw_stat_val_rx_dbells;
|
|
static void
|
|
sfc_get_sw_stat_val_rx_dbells(struct sfc_adapter *sa, uint16_t qid,
|
|
uint64_t *values, unsigned int values_count)
|
|
{
|
|
struct sfc_adapter_shared *sas = sfc_sa2shared(sa);
|
|
struct sfc_rxq_info *rxq_info;
|
|
|
|
RTE_SET_USED(values_count);
|
|
SFC_ASSERT(values_count == 1);
|
|
rxq_info = sfc_rxq_info_by_ethdev_qid(sas, qid);
|
|
values[0] = rxq_info->state & SFC_RXQ_INITIALIZED ?
|
|
rxq_info->dp->dpq.dbells : 0;
|
|
}
|
|
|
|
static sfc_get_sw_stat_val_t sfc_get_sw_stat_val_tx_dbells;
|
|
static void
|
|
sfc_get_sw_stat_val_tx_dbells(struct sfc_adapter *sa, uint16_t qid,
|
|
uint64_t *values, unsigned int values_count)
|
|
{
|
|
struct sfc_adapter_shared *sas = sfc_sa2shared(sa);
|
|
struct sfc_txq_info *txq_info;
|
|
|
|
RTE_SET_USED(values_count);
|
|
SFC_ASSERT(values_count == 1);
|
|
txq_info = sfc_txq_info_by_ethdev_qid(sas, qid);
|
|
values[0] = txq_info->state & SFC_TXQ_INITIALIZED ?
|
|
txq_info->dp->dpq.dbells : 0;
|
|
}
|
|
|
|
/*
|
|
* SW stats can be grouped together. When stats are grouped the corresponding
|
|
* stats values for each queue are obtained during calling one get value
|
|
* callback. Stats of the same group are contiguous in the structure below.
|
|
* The start of the group is denoted by stat implementing get value callback.
|
|
*/
|
|
const struct sfc_sw_stat_descr sfc_sw_stats_descr[] = {
|
|
/* Group of Rx packets/bytes stats */
|
|
{
|
|
.name = SFC_SW_STAT_GOOD_PACKETS,
|
|
.type = SFC_SW_STATS_RX,
|
|
.get_val = sfc_sw_stat_get_rx_good_pkts_bytes,
|
|
.provide_total = false,
|
|
},
|
|
{
|
|
.name = SFC_SW_STAT_GOOD_BYTES,
|
|
.type = SFC_SW_STATS_RX,
|
|
.get_val = NULL,
|
|
.provide_total = false,
|
|
},
|
|
/* Group of Tx packets/bytes stats */
|
|
{
|
|
.name = SFC_SW_STAT_GOOD_PACKETS,
|
|
.type = SFC_SW_STATS_TX,
|
|
.get_val = sfc_sw_stat_get_tx_good_pkts_bytes,
|
|
.provide_total = false,
|
|
},
|
|
{
|
|
.name = SFC_SW_STAT_GOOD_BYTES,
|
|
.type = SFC_SW_STATS_TX,
|
|
.get_val = NULL,
|
|
.provide_total = false,
|
|
},
|
|
/* End of basic stats */
|
|
{
|
|
.name = "dbells",
|
|
.type = SFC_SW_STATS_RX,
|
|
.get_val = sfc_get_sw_stat_val_rx_dbells,
|
|
.provide_total = true,
|
|
},
|
|
{
|
|
.name = "dbells",
|
|
.type = SFC_SW_STATS_TX,
|
|
.get_val = sfc_get_sw_stat_val_tx_dbells,
|
|
.provide_total = true,
|
|
}
|
|
};
|
|
|
|
static int
|
|
sfc_sw_stat_get_name(struct sfc_adapter *sa,
|
|
const struct sfc_sw_stat_descr *sw_stat, char *name,
|
|
size_t name_size, unsigned int id_off)
|
|
{
|
|
const char *prefix;
|
|
int ret;
|
|
|
|
switch (sw_stat->type) {
|
|
case SFC_SW_STATS_RX:
|
|
prefix = "rx";
|
|
break;
|
|
case SFC_SW_STATS_TX:
|
|
prefix = "tx";
|
|
break;
|
|
default:
|
|
sfc_err(sa, "%s: unknown software statistics type %d",
|
|
__func__, sw_stat->type);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (sw_stat->provide_total && id_off == 0) {
|
|
ret = snprintf(name, name_size, "%s_%s", prefix,
|
|
sw_stat->name);
|
|
if (ret < 0 || ret >= (int)name_size) {
|
|
sfc_err(sa, "%s: failed to fill xstat name %s_%s, err %d",
|
|
__func__, prefix, sw_stat->name, ret);
|
|
return ret > 0 ? -EINVAL : ret;
|
|
}
|
|
} else {
|
|
uint16_t qid = id_off - sw_stat->provide_total;
|
|
ret = snprintf(name, name_size, "%s_q%u_%s", prefix, qid,
|
|
sw_stat->name);
|
|
if (ret < 0 || ret >= (int)name_size) {
|
|
sfc_err(sa, "%s: failed to fill xstat name %s_q%u_%s, err %d",
|
|
__func__, prefix, qid, sw_stat->name, ret);
|
|
return ret > 0 ? -EINVAL : ret;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static unsigned int
|
|
sfc_sw_stat_get_queue_count(struct sfc_adapter *sa,
|
|
const struct sfc_sw_stat_descr *sw_stat)
|
|
{
|
|
struct sfc_adapter_shared *sas = sfc_sa2shared(sa);
|
|
|
|
switch (sw_stat->type) {
|
|
case SFC_SW_STATS_RX:
|
|
return sas->ethdev_rxq_count;
|
|
case SFC_SW_STATS_TX:
|
|
return sas->ethdev_txq_count;
|
|
default:
|
|
sfc_err(sa, "%s: unknown software statistics type %d",
|
|
__func__, sw_stat->type);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static unsigned int
|
|
sfc_sw_xstat_per_queue_get_count(const struct sfc_sw_stat_descr *sw_stat,
|
|
unsigned int nb_queues)
|
|
{
|
|
/* Take into account the total xstat of all queues */
|
|
return nb_queues > 0 ? sw_stat->provide_total + nb_queues : 0;
|
|
}
|
|
|
|
static unsigned int
|
|
sfc_sw_xstat_get_nb_supported(struct sfc_adapter *sa,
|
|
const struct sfc_sw_stat_descr *sw_stat)
|
|
{
|
|
unsigned int nb_queues;
|
|
|
|
nb_queues = sfc_sw_stat_get_queue_count(sa, sw_stat);
|
|
return sfc_sw_xstat_per_queue_get_count(sw_stat, nb_queues);
|
|
}
|
|
|
|
static int
|
|
sfc_sw_stat_get_names(struct sfc_adapter *sa,
|
|
const struct sfc_sw_stat_descr *sw_stat,
|
|
struct rte_eth_xstat_name *xstats_names,
|
|
unsigned int xstats_names_sz,
|
|
unsigned int *nb_written,
|
|
unsigned int *nb_supported)
|
|
{
|
|
const size_t name_size = sizeof(xstats_names[0].name);
|
|
unsigned int id_base = *nb_supported;
|
|
unsigned int nb_queues;
|
|
unsigned int qid;
|
|
int rc;
|
|
|
|
nb_queues = sfc_sw_stat_get_queue_count(sa, sw_stat);
|
|
if (nb_queues == 0)
|
|
return 0;
|
|
*nb_supported += sfc_sw_xstat_per_queue_get_count(sw_stat, nb_queues);
|
|
|
|
/*
|
|
* The order of each software xstat type is the total xstat
|
|
* followed by per-queue xstats.
|
|
*/
|
|
if (*nb_written < xstats_names_sz && sw_stat->provide_total) {
|
|
rc = sfc_sw_stat_get_name(sa, sw_stat,
|
|
xstats_names[*nb_written].name,
|
|
name_size, *nb_written - id_base);
|
|
if (rc != 0)
|
|
return rc;
|
|
(*nb_written)++;
|
|
}
|
|
|
|
for (qid = 0; qid < nb_queues; ++qid) {
|
|
if (*nb_written < xstats_names_sz) {
|
|
rc = sfc_sw_stat_get_name(sa, sw_stat,
|
|
xstats_names[*nb_written].name,
|
|
name_size, *nb_written - id_base);
|
|
if (rc != 0)
|
|
return rc;
|
|
(*nb_written)++;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
sfc_sw_xstat_get_names_by_id(struct sfc_adapter *sa,
|
|
const struct sfc_sw_stat_descr *sw_stat,
|
|
const uint64_t *ids,
|
|
struct rte_eth_xstat_name *xstats_names,
|
|
unsigned int size,
|
|
unsigned int *nb_supported)
|
|
{
|
|
const size_t name_size = sizeof(xstats_names[0].name);
|
|
unsigned int id_base = *nb_supported;
|
|
unsigned int id_end;
|
|
unsigned int nb_queues;
|
|
unsigned int i;
|
|
int rc;
|
|
|
|
nb_queues = sfc_sw_stat_get_queue_count(sa, sw_stat);
|
|
if (nb_queues == 0)
|
|
return 0;
|
|
*nb_supported += sfc_sw_xstat_per_queue_get_count(sw_stat, nb_queues);
|
|
|
|
/*
|
|
* The order of each software xstat type is the total xstat
|
|
* followed by per-queue xstats.
|
|
*/
|
|
id_end = id_base + sw_stat->provide_total + nb_queues;
|
|
for (i = 0; i < size; i++) {
|
|
if (id_base <= ids[i] && ids[i] < id_end) {
|
|
rc = sfc_sw_stat_get_name(sa, sw_stat,
|
|
xstats_names[i].name,
|
|
name_size, ids[i] - id_base);
|
|
if (rc != 0)
|
|
return rc;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static uint64_t
|
|
sfc_sw_stat_get_val(struct sfc_adapter *sa,
|
|
unsigned int sw_stat_idx, uint16_t qid)
|
|
{
|
|
struct sfc_sw_stats *sw_stats = &sa->sw_stats;
|
|
uint64_t *res = &sw_stats->supp[sw_stat_idx].cache[qid];
|
|
uint64_t values[SFC_SW_STATS_GROUP_SIZE_MAX];
|
|
unsigned int group_start_idx;
|
|
unsigned int group_size;
|
|
unsigned int i;
|
|
|
|
if (*res != SFC_SW_STAT_INVALID)
|
|
return *res;
|
|
|
|
/*
|
|
* Search for the group start, i.e. the stat that implements
|
|
* get value callback.
|
|
*/
|
|
group_start_idx = sw_stat_idx;
|
|
while (sw_stats->supp[group_start_idx].descr->get_val == NULL)
|
|
group_start_idx--;
|
|
|
|
/*
|
|
* Calculate number of elements in the group with loop till the next
|
|
* group start or the list end.
|
|
*/
|
|
group_size = 1;
|
|
for (i = sw_stat_idx + 1; i < sw_stats->supp_count; i++) {
|
|
if (sw_stats->supp[i].descr->get_val != NULL)
|
|
break;
|
|
group_size++;
|
|
}
|
|
group_size += sw_stat_idx - group_start_idx;
|
|
|
|
SFC_ASSERT(group_size <= SFC_SW_STATS_GROUP_SIZE_MAX);
|
|
sw_stats->supp[group_start_idx].descr->get_val(sa, qid, values,
|
|
group_size);
|
|
for (i = group_start_idx; i < (group_start_idx + group_size); i++)
|
|
sw_stats->supp[i].cache[qid] = values[i - group_start_idx];
|
|
|
|
return *res;
|
|
}
|
|
|
|
static void
|
|
sfc_sw_xstat_get_values(struct sfc_adapter *sa,
|
|
const struct sfc_sw_stat_descr *sw_stat,
|
|
unsigned int sw_stat_idx,
|
|
struct rte_eth_xstat *xstats,
|
|
unsigned int xstats_size,
|
|
unsigned int *nb_written,
|
|
unsigned int *nb_supported)
|
|
{
|
|
unsigned int qid;
|
|
uint64_t value;
|
|
struct rte_eth_xstat *total_xstat;
|
|
bool count_total_value = false;
|
|
unsigned int nb_queues;
|
|
|
|
nb_queues = sfc_sw_stat_get_queue_count(sa, sw_stat);
|
|
if (nb_queues == 0)
|
|
return;
|
|
*nb_supported += sfc_sw_xstat_per_queue_get_count(sw_stat, nb_queues);
|
|
|
|
/*
|
|
* The order of each software xstat type is the total xstat
|
|
* followed by per-queue xstats.
|
|
*/
|
|
if (*nb_written < xstats_size && sw_stat->provide_total) {
|
|
count_total_value = true;
|
|
total_xstat = &xstats[*nb_written];
|
|
xstats[*nb_written].id = *nb_written;
|
|
xstats[*nb_written].value = 0;
|
|
(*nb_written)++;
|
|
}
|
|
|
|
for (qid = 0; qid < nb_queues; ++qid) {
|
|
value = sfc_sw_stat_get_val(sa, sw_stat_idx, qid);
|
|
|
|
if (*nb_written < xstats_size) {
|
|
xstats[*nb_written].id = *nb_written;
|
|
xstats[*nb_written].value = value;
|
|
(*nb_written)++;
|
|
}
|
|
|
|
if (count_total_value)
|
|
total_xstat->value += value;
|
|
}
|
|
}
|
|
|
|
static void
|
|
sfc_sw_xstat_get_values_by_id(struct sfc_adapter *sa,
|
|
const struct sfc_sw_stat_descr *sw_stat,
|
|
unsigned int sw_stat_idx,
|
|
const uint64_t *ids,
|
|
uint64_t *values,
|
|
unsigned int ids_size,
|
|
unsigned int *nb_supported)
|
|
{
|
|
rte_spinlock_t *bmp_lock = &sa->sw_stats.queues_bitmap_lock;
|
|
struct rte_bitmap *bmp = sa->sw_stats.queues_bitmap;
|
|
unsigned int id_base = *nb_supported;
|
|
unsigned int id_base_q;
|
|
unsigned int id_end;
|
|
bool count_total_value = false;
|
|
unsigned int total_value_idx;
|
|
uint64_t total_value = 0;
|
|
unsigned int i, qid;
|
|
unsigned int nb_queues;
|
|
|
|
|
|
rte_spinlock_lock(bmp_lock);
|
|
rte_bitmap_reset(bmp);
|
|
|
|
nb_queues = sfc_sw_stat_get_queue_count(sa, sw_stat);
|
|
if (nb_queues == 0)
|
|
goto unlock;
|
|
*nb_supported += sfc_sw_xstat_per_queue_get_count(sw_stat, nb_queues);
|
|
|
|
/*
|
|
* The order of each software xstat type is the total xstat
|
|
* followed by per-queue xstats.
|
|
*/
|
|
id_end = id_base + sw_stat->provide_total + nb_queues;
|
|
for (i = 0; i < ids_size; i++) {
|
|
if (id_base <= ids[i] && ids[i] < id_end) {
|
|
if (sw_stat->provide_total && ids[i] == id_base) {
|
|
/* Accumulative value */
|
|
count_total_value = true;
|
|
total_value_idx = i;
|
|
continue;
|
|
}
|
|
id_base_q = id_base + sw_stat->provide_total;
|
|
qid = ids[i] - id_base_q;
|
|
values[i] = sfc_sw_stat_get_val(sa, sw_stat_idx, qid);
|
|
total_value += values[i];
|
|
|
|
rte_bitmap_set(bmp, qid);
|
|
}
|
|
}
|
|
|
|
if (count_total_value) {
|
|
values[total_value_idx] = 0;
|
|
for (qid = 0; qid < nb_queues; ++qid) {
|
|
if (rte_bitmap_get(bmp, qid) != 0)
|
|
continue;
|
|
values[total_value_idx] += sfc_sw_stat_get_val(sa,
|
|
sw_stat_idx,
|
|
qid);
|
|
}
|
|
values[total_value_idx] += total_value;
|
|
}
|
|
|
|
unlock:
|
|
rte_spinlock_unlock(bmp_lock);
|
|
}
|
|
|
|
unsigned int
|
|
sfc_sw_xstats_get_nb_supported(struct sfc_adapter *sa)
|
|
{
|
|
SFC_ASSERT(sfc_adapter_is_locked(sa));
|
|
return sa->sw_stats.xstats_count;
|
|
}
|
|
|
|
static void
|
|
sfc_sw_stats_clear_cache(struct sfc_adapter *sa)
|
|
{
|
|
unsigned int cache_count = sa->sw_stats.cache_count;
|
|
uint64_t *cache = sa->sw_stats.cache;
|
|
|
|
RTE_BUILD_BUG_ON(UINT64_C(0xffffffffffffffff) != SFC_SW_STAT_INVALID);
|
|
memset(cache, 0xff, cache_count * sizeof(*cache));
|
|
}
|
|
|
|
void
|
|
sfc_sw_xstats_get_vals(struct sfc_adapter *sa,
|
|
struct rte_eth_xstat *xstats,
|
|
unsigned int xstats_count,
|
|
unsigned int *nb_written,
|
|
unsigned int *nb_supported)
|
|
{
|
|
uint64_t *reset_vals = sa->sw_stats.reset_vals;
|
|
struct sfc_sw_stats *sw_stats = &sa->sw_stats;
|
|
unsigned int sw_xstats_offset;
|
|
unsigned int i;
|
|
|
|
sfc_adapter_lock(sa);
|
|
|
|
sfc_sw_stats_clear_cache(sa);
|
|
|
|
sw_xstats_offset = *nb_supported;
|
|
|
|
for (i = 0; i < sw_stats->supp_count; i++) {
|
|
sfc_sw_xstat_get_values(sa, sw_stats->supp[i].descr, i,
|
|
xstats, xstats_count, nb_written, nb_supported);
|
|
}
|
|
|
|
for (i = sw_xstats_offset; i < *nb_written; i++)
|
|
xstats[i].value -= reset_vals[i - sw_xstats_offset];
|
|
|
|
sfc_adapter_unlock(sa);
|
|
}
|
|
|
|
int
|
|
sfc_sw_xstats_get_names(struct sfc_adapter *sa,
|
|
struct rte_eth_xstat_name *xstats_names,
|
|
unsigned int xstats_count,
|
|
unsigned int *nb_written,
|
|
unsigned int *nb_supported)
|
|
{
|
|
struct sfc_sw_stats *sw_stats = &sa->sw_stats;
|
|
unsigned int i;
|
|
int ret;
|
|
|
|
sfc_adapter_lock(sa);
|
|
|
|
for (i = 0; i < sw_stats->supp_count; i++) {
|
|
ret = sfc_sw_stat_get_names(sa, sw_stats->supp[i].descr,
|
|
xstats_names, xstats_count,
|
|
nb_written, nb_supported);
|
|
if (ret != 0) {
|
|
sfc_adapter_unlock(sa);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
sfc_adapter_unlock(sa);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
sfc_sw_xstats_get_vals_by_id(struct sfc_adapter *sa,
|
|
const uint64_t *ids,
|
|
uint64_t *values,
|
|
unsigned int n,
|
|
unsigned int *nb_supported)
|
|
{
|
|
uint64_t *reset_vals = sa->sw_stats.reset_vals;
|
|
struct sfc_sw_stats *sw_stats = &sa->sw_stats;
|
|
unsigned int sw_xstats_offset;
|
|
unsigned int i;
|
|
|
|
sfc_adapter_lock(sa);
|
|
|
|
sfc_sw_stats_clear_cache(sa);
|
|
|
|
sw_xstats_offset = *nb_supported;
|
|
|
|
for (i = 0; i < sw_stats->supp_count; i++) {
|
|
sfc_sw_xstat_get_values_by_id(sa, sw_stats->supp[i].descr, i,
|
|
ids, values, n, nb_supported);
|
|
}
|
|
|
|
for (i = 0; i < n; i++) {
|
|
if (sw_xstats_offset <= ids[i] && ids[i] < *nb_supported)
|
|
values[i] -= reset_vals[ids[i] - sw_xstats_offset];
|
|
}
|
|
|
|
sfc_adapter_unlock(sa);
|
|
}
|
|
|
|
int
|
|
sfc_sw_xstats_get_names_by_id(struct sfc_adapter *sa,
|
|
const uint64_t *ids,
|
|
struct rte_eth_xstat_name *xstats_names,
|
|
unsigned int size,
|
|
unsigned int *nb_supported)
|
|
{
|
|
struct sfc_sw_stats *sw_stats = &sa->sw_stats;
|
|
unsigned int i;
|
|
int ret;
|
|
|
|
sfc_adapter_lock(sa);
|
|
|
|
for (i = 0; i < sw_stats->supp_count; i++) {
|
|
ret = sfc_sw_xstat_get_names_by_id(sa, sw_stats->supp[i].descr,
|
|
ids, xstats_names, size,
|
|
nb_supported);
|
|
if (ret != 0) {
|
|
sfc_adapter_unlock(sa);
|
|
SFC_ASSERT(ret < 0);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
sfc_adapter_unlock(sa);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
sfc_sw_xstat_reset(struct sfc_adapter *sa,
|
|
const struct sfc_sw_stat_descr *sw_stat,
|
|
unsigned int sw_stat_idx,
|
|
uint64_t *reset_vals)
|
|
{
|
|
unsigned int nb_queues;
|
|
unsigned int qid;
|
|
uint64_t *total_xstat_reset = NULL;
|
|
|
|
SFC_ASSERT(sfc_adapter_is_locked(sa));
|
|
|
|
nb_queues = sfc_sw_stat_get_queue_count(sa, sw_stat);
|
|
if (nb_queues == 0)
|
|
return;
|
|
|
|
/*
|
|
* The order of each software xstat type is the total xstat
|
|
* followed by per-queue xstats.
|
|
*/
|
|
if (sw_stat->provide_total) {
|
|
total_xstat_reset = reset_vals;
|
|
*total_xstat_reset = 0;
|
|
reset_vals++;
|
|
}
|
|
|
|
for (qid = 0; qid < nb_queues; ++qid) {
|
|
reset_vals[qid] = sfc_sw_stat_get_val(sa, sw_stat_idx, qid);
|
|
if (sw_stat->provide_total)
|
|
*total_xstat_reset += reset_vals[qid];
|
|
}
|
|
}
|
|
|
|
void
|
|
sfc_sw_xstats_reset(struct sfc_adapter *sa)
|
|
{
|
|
uint64_t *reset_vals = sa->sw_stats.reset_vals;
|
|
struct sfc_sw_stats *sw_stats = &sa->sw_stats;
|
|
unsigned int i;
|
|
|
|
SFC_ASSERT(sfc_adapter_is_locked(sa));
|
|
|
|
sfc_sw_stats_clear_cache(sa);
|
|
|
|
for (i = 0; i < sw_stats->supp_count; i++) {
|
|
sfc_sw_xstat_reset(sa, sw_stats->supp[i].descr, i, reset_vals);
|
|
reset_vals += sfc_sw_xstat_get_nb_supported(sa,
|
|
sw_stats->supp[i].descr);
|
|
}
|
|
}
|
|
|
|
static bool
|
|
sfc_sw_stats_is_packets_or_bytes(const char *xstat_name)
|
|
{
|
|
return strcmp(xstat_name, SFC_SW_STAT_GOOD_PACKETS) == 0 ||
|
|
strcmp(xstat_name, SFC_SW_STAT_GOOD_BYTES) == 0;
|
|
}
|
|
|
|
static void
|
|
sfc_sw_stats_fill_available_descr(struct sfc_adapter *sa)
|
|
{
|
|
const struct sfc_adapter_priv *sap = &sa->priv;
|
|
bool have_dp_rx_stats = sap->dp_rx->features & SFC_DP_RX_FEAT_STATS;
|
|
bool have_dp_tx_stats = sap->dp_tx->features & SFC_DP_TX_FEAT_STATS;
|
|
struct sfc_sw_stats *sw_stats = &sa->sw_stats;
|
|
const struct sfc_sw_stat_descr *sw_stat_descr;
|
|
unsigned int i;
|
|
|
|
sw_stats->supp_count = 0;
|
|
for (i = 0; i < RTE_DIM(sfc_sw_stats_descr); i++) {
|
|
sw_stat_descr = &sfc_sw_stats_descr[i];
|
|
if (!have_dp_rx_stats &&
|
|
sw_stat_descr->type == SFC_SW_STATS_RX &&
|
|
sfc_sw_stats_is_packets_or_bytes(sw_stat_descr->name))
|
|
continue;
|
|
if (!have_dp_tx_stats &&
|
|
sw_stat_descr->type == SFC_SW_STATS_TX &&
|
|
sfc_sw_stats_is_packets_or_bytes(sw_stat_descr->name))
|
|
continue;
|
|
sw_stats->supp[sw_stats->supp_count].descr = sw_stat_descr;
|
|
sw_stats->supp_count++;
|
|
}
|
|
}
|
|
|
|
static int
|
|
sfc_sw_stats_set_reset_basic_stats(struct sfc_adapter *sa)
|
|
{
|
|
uint64_t *reset_vals = sa->sw_stats.reset_vals;
|
|
struct sfc_sw_stats *sw_stats = &sa->sw_stats;
|
|
const struct sfc_sw_stat_descr *sw_stat;
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < sw_stats->supp_count; i++) {
|
|
sw_stat = sw_stats->supp[i].descr;
|
|
|
|
switch (sw_stat->type) {
|
|
case SFC_SW_STATS_RX:
|
|
if (strcmp(sw_stat->name,
|
|
SFC_SW_STAT_GOOD_PACKETS) == 0)
|
|
sa->sw_stats.reset_rx_pkts = reset_vals;
|
|
else if (strcmp(sw_stat->name,
|
|
SFC_SW_STAT_GOOD_BYTES) == 0)
|
|
sa->sw_stats.reset_rx_bytes = reset_vals;
|
|
break;
|
|
case SFC_SW_STATS_TX:
|
|
if (strcmp(sw_stat->name,
|
|
SFC_SW_STAT_GOOD_PACKETS) == 0)
|
|
sa->sw_stats.reset_tx_pkts = reset_vals;
|
|
else if (strcmp(sw_stat->name,
|
|
SFC_SW_STAT_GOOD_BYTES) == 0)
|
|
sa->sw_stats.reset_tx_bytes = reset_vals;
|
|
break;
|
|
default:
|
|
SFC_GENERIC_LOG(ERR, "Unknown SW stat type");
|
|
return -EINVAL;
|
|
}
|
|
|
|
reset_vals += sfc_sw_xstat_get_nb_supported(sa, sw_stat);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
sfc_sw_xstats_configure(struct sfc_adapter *sa)
|
|
{
|
|
uint64_t **reset_vals = &sa->sw_stats.reset_vals;
|
|
struct sfc_sw_stats *sw_stats = &sa->sw_stats;
|
|
unsigned int cache_count = 0;
|
|
uint64_t **cache = &sa->sw_stats.cache;
|
|
uint64_t *stat_cache;
|
|
size_t nb_supported = 0;
|
|
unsigned int i;
|
|
int rc;
|
|
|
|
sw_stats->supp_count = RTE_DIM(sfc_sw_stats_descr);
|
|
if (sw_stats->supp == NULL) {
|
|
sw_stats->supp = rte_malloc(NULL, sw_stats->supp_count *
|
|
sizeof(*sw_stats->supp), 0);
|
|
if (sw_stats->supp == NULL)
|
|
return -ENOMEM;
|
|
}
|
|
for (i = 0; i < sw_stats->supp_count; i++)
|
|
sw_stats->supp[i].descr = &sfc_sw_stats_descr[i];
|
|
sfc_sw_stats_fill_available_descr(sa);
|
|
|
|
for (i = 0; i < sw_stats->supp_count; i++) {
|
|
nb_supported += sfc_sw_xstat_get_nb_supported(sa,
|
|
sw_stats->supp[i].descr);
|
|
cache_count += sfc_sw_stat_get_queue_count(sa,
|
|
sw_stats->supp[i].descr);
|
|
}
|
|
sa->sw_stats.xstats_count = nb_supported;
|
|
|
|
*reset_vals = rte_realloc(*reset_vals,
|
|
nb_supported * sizeof(**reset_vals), 0);
|
|
if (*reset_vals == NULL) {
|
|
rc = -ENOMEM;
|
|
goto fail_reset_vals;
|
|
}
|
|
|
|
memset(*reset_vals, 0, nb_supported * sizeof(**reset_vals));
|
|
|
|
*cache = rte_realloc(*cache, cache_count * sizeof(*cache), 0);
|
|
if (*cache == NULL) {
|
|
rc = ENOMEM;
|
|
goto fail_cache;
|
|
}
|
|
sa->sw_stats.cache_count = cache_count;
|
|
stat_cache = *cache;
|
|
rc = sfc_sw_stats_set_reset_basic_stats(sa);
|
|
if (rc != 0)
|
|
goto fail_reset_basic_stats;
|
|
|
|
for (i = 0; i < sw_stats->supp_count; i++) {
|
|
sw_stats->supp[i].cache = stat_cache;
|
|
stat_cache += sfc_sw_stat_get_queue_count(sa,
|
|
sw_stats->supp[i].descr);
|
|
}
|
|
|
|
return 0;
|
|
|
|
fail_reset_basic_stats:
|
|
rte_free(*cache);
|
|
*cache = NULL;
|
|
sa->sw_stats.cache_count = 0;
|
|
fail_cache:
|
|
rte_free(*reset_vals);
|
|
*reset_vals = NULL;
|
|
fail_reset_vals:
|
|
sa->sw_stats.xstats_count = 0;
|
|
rte_free(sw_stats->supp);
|
|
sw_stats->supp = NULL;
|
|
sw_stats->supp_count = 0;
|
|
|
|
return rc;
|
|
}
|
|
|
|
static void
|
|
sfc_sw_xstats_free_queues_bitmap(struct sfc_adapter *sa)
|
|
{
|
|
rte_bitmap_free(sa->sw_stats.queues_bitmap);
|
|
rte_free(sa->sw_stats.queues_bitmap_mem);
|
|
}
|
|
|
|
static int
|
|
sfc_sw_xstats_alloc_queues_bitmap(struct sfc_adapter *sa)
|
|
{
|
|
struct rte_bitmap **queues_bitmap = &sa->sw_stats.queues_bitmap;
|
|
void **queues_bitmap_mem = &sa->sw_stats.queues_bitmap_mem;
|
|
uint32_t bmp_size;
|
|
int rc;
|
|
|
|
bmp_size = rte_bitmap_get_memory_footprint(RTE_MAX_QUEUES_PER_PORT);
|
|
*queues_bitmap_mem = NULL;
|
|
*queues_bitmap = NULL;
|
|
|
|
*queues_bitmap_mem = rte_calloc_socket("bitmap_mem", bmp_size, 1, 0,
|
|
sa->socket_id);
|
|
if (*queues_bitmap_mem == NULL)
|
|
return ENOMEM;
|
|
|
|
*queues_bitmap = rte_bitmap_init(RTE_MAX_QUEUES_PER_PORT,
|
|
*queues_bitmap_mem, bmp_size);
|
|
if (*queues_bitmap == NULL) {
|
|
rc = EINVAL;
|
|
goto fail;
|
|
}
|
|
|
|
rte_spinlock_init(&sa->sw_stats.queues_bitmap_lock);
|
|
return 0;
|
|
|
|
fail:
|
|
sfc_sw_xstats_free_queues_bitmap(sa);
|
|
return rc;
|
|
}
|
|
|
|
int
|
|
sfc_sw_xstats_init(struct sfc_adapter *sa)
|
|
{
|
|
sa->sw_stats.xstats_count = 0;
|
|
sa->sw_stats.supp = NULL;
|
|
sa->sw_stats.supp_count = 0;
|
|
sa->sw_stats.cache = NULL;
|
|
sa->sw_stats.cache_count = 0;
|
|
sa->sw_stats.reset_vals = NULL;
|
|
|
|
return sfc_sw_xstats_alloc_queues_bitmap(sa);
|
|
}
|
|
|
|
void
|
|
sfc_sw_xstats_close(struct sfc_adapter *sa)
|
|
{
|
|
sfc_sw_xstats_free_queues_bitmap(sa);
|
|
sa->sw_stats.reset_vals = NULL;
|
|
rte_free(sa->sw_stats.cache);
|
|
sa->sw_stats.cache = NULL;
|
|
sa->sw_stats.cache_count = 0;
|
|
rte_free(sa->sw_stats.reset_vals);
|
|
rte_free(sa->sw_stats.supp);
|
|
sa->sw_stats.supp = NULL;
|
|
sa->sw_stats.supp_count = 0;
|
|
sa->sw_stats.xstats_count = 0;
|
|
}
|