73bf9235e9
mlx5 statistics are calculated by several methods: 1. In software when packets go through datapath. 2. Calling ioctl with ETHTOOL command (Linux specific). 3. Reading counters from SYSFS device path (Linux specific). The Linux related functions are moved to file linux/mlx5_os.c. Signed-off-by: Ophir Munk <ophirmu@mellanox.com> Acked-by: Matan Azrad <matan@mellanox.com>
276 lines
6.7 KiB
C
276 lines
6.7 KiB
C
/* SPDX-License-Identifier: BSD-3-Clause
|
|
* Copyright 2015 6WIND S.A.
|
|
* Copyright 2015 Mellanox Technologies, Ltd
|
|
*/
|
|
|
|
#include <inttypes.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
|
|
#include <rte_ethdev_driver.h>
|
|
#include <rte_common.h>
|
|
#include <rte_malloc.h>
|
|
|
|
#include <mlx5_common.h>
|
|
|
|
#include "mlx5_defs.h"
|
|
#include "mlx5.h"
|
|
#include "mlx5_rxtx.h"
|
|
|
|
/**
|
|
* DPDK callback to get extended device statistics.
|
|
*
|
|
* @param dev
|
|
* Pointer to Ethernet device.
|
|
* @param[out] stats
|
|
* Pointer to rte extended stats table.
|
|
* @param n
|
|
* The size of the stats table.
|
|
*
|
|
* @return
|
|
* Number of extended stats on success and stats is filled,
|
|
* negative on error and rte_errno is set.
|
|
*/
|
|
int
|
|
mlx5_xstats_get(struct rte_eth_dev *dev, struct rte_eth_xstat *stats,
|
|
unsigned int n)
|
|
{
|
|
struct mlx5_priv *priv = dev->data->dev_private;
|
|
unsigned int i;
|
|
uint64_t counters[n];
|
|
struct mlx5_xstats_ctrl *xstats_ctrl = &priv->xstats_ctrl;
|
|
uint16_t mlx5_stats_n = xstats_ctrl->mlx5_stats_n;
|
|
|
|
if (n >= mlx5_stats_n && stats) {
|
|
int stats_n;
|
|
int ret;
|
|
|
|
stats_n = mlx5_os_get_stats_n(dev);
|
|
if (stats_n < 0)
|
|
return stats_n;
|
|
if (xstats_ctrl->stats_n != stats_n)
|
|
mlx5_os_stats_init(dev);
|
|
ret = mlx5_os_read_dev_counters(dev, counters);
|
|
if (ret)
|
|
return ret;
|
|
for (i = 0; i != mlx5_stats_n; ++i) {
|
|
stats[i].id = i;
|
|
if (xstats_ctrl->info[i].dev) {
|
|
uint64_t wrap_n;
|
|
uint64_t hw_stat = xstats_ctrl->hw_stats[i];
|
|
|
|
stats[i].value = (counters[i] -
|
|
xstats_ctrl->base[i]) &
|
|
(uint64_t)UINT32_MAX;
|
|
wrap_n = hw_stat >> 32;
|
|
if (stats[i].value <
|
|
(hw_stat & (uint64_t)UINT32_MAX))
|
|
wrap_n++;
|
|
stats[i].value |= (wrap_n) << 32;
|
|
xstats_ctrl->hw_stats[i] = stats[i].value;
|
|
} else {
|
|
stats[i].value =
|
|
(counters[i] - xstats_ctrl->base[i]);
|
|
}
|
|
}
|
|
}
|
|
return mlx5_stats_n;
|
|
}
|
|
|
|
/**
|
|
* DPDK callback to get device statistics.
|
|
*
|
|
* @param dev
|
|
* Pointer to Ethernet device structure.
|
|
* @param[out] stats
|
|
* Stats structure output buffer.
|
|
*
|
|
* @return
|
|
* 0 on success and stats is filled, negative errno value otherwise and
|
|
* rte_errno is set.
|
|
*/
|
|
int
|
|
mlx5_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats)
|
|
{
|
|
struct mlx5_priv *priv = dev->data->dev_private;
|
|
struct mlx5_stats_ctrl *stats_ctrl = &priv->stats_ctrl;
|
|
struct rte_eth_stats tmp;
|
|
unsigned int i;
|
|
unsigned int idx;
|
|
uint64_t wrap_n;
|
|
int ret;
|
|
|
|
memset(&tmp, 0, sizeof(tmp));
|
|
/* Add software counters. */
|
|
for (i = 0; (i != priv->rxqs_n); ++i) {
|
|
struct mlx5_rxq_data *rxq = (*priv->rxqs)[i];
|
|
|
|
if (rxq == NULL)
|
|
continue;
|
|
idx = rxq->idx;
|
|
if (idx < RTE_ETHDEV_QUEUE_STAT_CNTRS) {
|
|
#ifdef MLX5_PMD_SOFT_COUNTERS
|
|
tmp.q_ipackets[idx] += rxq->stats.ipackets;
|
|
tmp.q_ibytes[idx] += rxq->stats.ibytes;
|
|
#endif
|
|
tmp.q_errors[idx] += (rxq->stats.idropped +
|
|
rxq->stats.rx_nombuf);
|
|
}
|
|
#ifdef MLX5_PMD_SOFT_COUNTERS
|
|
tmp.ipackets += rxq->stats.ipackets;
|
|
tmp.ibytes += rxq->stats.ibytes;
|
|
#endif
|
|
tmp.ierrors += rxq->stats.idropped;
|
|
tmp.rx_nombuf += rxq->stats.rx_nombuf;
|
|
}
|
|
for (i = 0; (i != priv->txqs_n); ++i) {
|
|
struct mlx5_txq_data *txq = (*priv->txqs)[i];
|
|
|
|
if (txq == NULL)
|
|
continue;
|
|
idx = txq->idx;
|
|
if (idx < RTE_ETHDEV_QUEUE_STAT_CNTRS) {
|
|
#ifdef MLX5_PMD_SOFT_COUNTERS
|
|
tmp.q_opackets[idx] += txq->stats.opackets;
|
|
tmp.q_obytes[idx] += txq->stats.obytes;
|
|
#endif
|
|
}
|
|
#ifdef MLX5_PMD_SOFT_COUNTERS
|
|
tmp.opackets += txq->stats.opackets;
|
|
tmp.obytes += txq->stats.obytes;
|
|
#endif
|
|
tmp.oerrors += txq->stats.oerrors;
|
|
}
|
|
ret = mlx5_os_read_dev_stat(priv, "out_of_buffer", &tmp.imissed);
|
|
if (ret == 0) {
|
|
tmp.imissed = (tmp.imissed - stats_ctrl->imissed_base) &
|
|
(uint64_t)UINT32_MAX;
|
|
wrap_n = stats_ctrl->imissed >> 32;
|
|
if (tmp.imissed < (stats_ctrl->imissed & (uint64_t)UINT32_MAX))
|
|
wrap_n++;
|
|
tmp.imissed |= (wrap_n) << 32;
|
|
stats_ctrl->imissed = tmp.imissed;
|
|
} else {
|
|
tmp.imissed = stats_ctrl->imissed;
|
|
}
|
|
#ifndef MLX5_PMD_SOFT_COUNTERS
|
|
/* FIXME: retrieve and add hardware counters. */
|
|
#endif
|
|
*stats = tmp;
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* DPDK callback to clear device statistics.
|
|
*
|
|
* @param dev
|
|
* Pointer to Ethernet device structure.
|
|
*
|
|
* @return
|
|
* always 0 on success and stats is reset
|
|
*/
|
|
int
|
|
mlx5_stats_reset(struct rte_eth_dev *dev)
|
|
{
|
|
struct mlx5_priv *priv = dev->data->dev_private;
|
|
struct mlx5_stats_ctrl *stats_ctrl = &priv->stats_ctrl;
|
|
unsigned int i;
|
|
|
|
for (i = 0; (i != priv->rxqs_n); ++i) {
|
|
if ((*priv->rxqs)[i] == NULL)
|
|
continue;
|
|
memset(&(*priv->rxqs)[i]->stats, 0,
|
|
sizeof(struct mlx5_rxq_stats));
|
|
}
|
|
for (i = 0; (i != priv->txqs_n); ++i) {
|
|
if ((*priv->txqs)[i] == NULL)
|
|
continue;
|
|
memset(&(*priv->txqs)[i]->stats, 0,
|
|
sizeof(struct mlx5_txq_stats));
|
|
}
|
|
mlx5_os_read_dev_stat(priv, "out_of_buffer", &stats_ctrl->imissed_base);
|
|
stats_ctrl->imissed = 0;
|
|
#ifndef MLX5_PMD_SOFT_COUNTERS
|
|
/* FIXME: reset hardware counters. */
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* DPDK callback to clear device extended statistics.
|
|
*
|
|
* @param dev
|
|
* Pointer to Ethernet device structure.
|
|
*
|
|
* @return
|
|
* 0 on success and stats is reset, negative errno value otherwise and
|
|
* rte_errno is set.
|
|
*/
|
|
int
|
|
mlx5_xstats_reset(struct rte_eth_dev *dev)
|
|
{
|
|
struct mlx5_priv *priv = dev->data->dev_private;
|
|
struct mlx5_xstats_ctrl *xstats_ctrl = &priv->xstats_ctrl;
|
|
int stats_n;
|
|
unsigned int i;
|
|
unsigned int n = xstats_ctrl->mlx5_stats_n;
|
|
uint64_t counters[n];
|
|
int ret;
|
|
|
|
stats_n = mlx5_os_get_stats_n(dev);
|
|
if (stats_n < 0) {
|
|
DRV_LOG(ERR, "port %u cannot get stats: %s", dev->data->port_id,
|
|
strerror(-stats_n));
|
|
return stats_n;
|
|
}
|
|
if (xstats_ctrl->stats_n != stats_n)
|
|
mlx5_os_stats_init(dev);
|
|
ret = mlx5_os_read_dev_counters(dev, counters);
|
|
if (ret) {
|
|
DRV_LOG(ERR, "port %u cannot read device counters: %s",
|
|
dev->data->port_id, strerror(rte_errno));
|
|
return ret;
|
|
}
|
|
for (i = 0; i != n; ++i) {
|
|
xstats_ctrl->base[i] = counters[i];
|
|
xstats_ctrl->hw_stats[i] = 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* DPDK callback to retrieve names of extended device statistics
|
|
*
|
|
* @param dev
|
|
* Pointer to Ethernet device structure.
|
|
* @param[out] xstats_names
|
|
* Buffer to insert names into.
|
|
* @param n
|
|
* Number of names.
|
|
*
|
|
* @return
|
|
* Number of xstats names.
|
|
*/
|
|
int
|
|
mlx5_xstats_get_names(struct rte_eth_dev *dev __rte_unused,
|
|
struct rte_eth_xstat_name *xstats_names, unsigned int n)
|
|
{
|
|
unsigned int i;
|
|
struct mlx5_priv *priv = dev->data->dev_private;
|
|
struct mlx5_xstats_ctrl *xstats_ctrl = &priv->xstats_ctrl;
|
|
unsigned int mlx5_xstats_n = xstats_ctrl->mlx5_stats_n;
|
|
|
|
if (n >= mlx5_xstats_n && xstats_names) {
|
|
for (i = 0; i != mlx5_xstats_n; ++i) {
|
|
strncpy(xstats_names[i].name,
|
|
xstats_ctrl->info[i].dpdk_name,
|
|
RTE_ETH_XSTATS_NAME_SIZE);
|
|
xstats_names[i].name[RTE_ETH_XSTATS_NAME_SIZE - 1] = 0;
|
|
}
|
|
}
|
|
return mlx5_xstats_n;
|
|
}
|