Skip to content

Commit

Permalink
Merge branch 'ethtool-add-pause-frame-stats'
Browse files Browse the repository at this point in the history
Jakub Kicinski says:

====================
ethtool: add pause frame stats

This is the first (small) series which exposes some stats via
the corresponding ethtool interface. Here (thanks to the
excitability of netlink) we expose pause frame stats via
the same interfaces as ethtool -a / -A.

In particular the following stats from the standard:
 - 30.3.4.2 aPAUSEMACCtrlFramesTransmitted
 - 30.3.4.3 aPAUSEMACCtrlFramesReceived

4 real drivers are converted, I believe we got confirmation
from maintainers that all exposed stats match the standard.

v3:
 - fix mlx5 build
 - adjust the init logic in patch 1
v2:
 - netdevsim: add missing static
 - bnxt: fix sparse warning
 - mlx5: address Saeed's comments
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Sep 15, 2020
2 parents 0f9ad4e + 12d342f commit 945c570
Show file tree
Hide file tree
Showing 18 changed files with 462 additions and 8 deletions.
11 changes: 11 additions & 0 deletions Documentation/networking/ethtool-netlink.rst
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ the flags may not apply to requests. Recognized flags are:
================================= ===================================
``ETHTOOL_FLAG_COMPACT_BITSETS`` use compact format bitsets in reply
``ETHTOOL_FLAG_OMIT_REPLY`` omit optional reply (_SET and _ACT)
``ETHTOOL_FLAG_STATS`` include optional device statistics
================================= ===================================

New request flags should follow the general idea that if the flag is not set,
Expand Down Expand Up @@ -989,8 +990,18 @@ Kernel response contents:
``ETHTOOL_A_PAUSE_AUTONEG`` bool pause autonegotiation
``ETHTOOL_A_PAUSE_RX`` bool receive pause frames
``ETHTOOL_A_PAUSE_TX`` bool transmit pause frames
``ETHTOOL_A_PAUSE_STATS`` nested pause statistics
===================================== ====== ==========================

``ETHTOOL_A_PAUSE_STATS`` are reported if ``ETHTOOL_FLAG_STATS`` was set
in ``ETHTOOL_A_HEADER_FLAGS``.
It will be empty if driver did not report any statistics. Drivers fill in
the statistics in the following structure:

.. kernel-doc:: include/linux/ethtool.h
:identifiers: ethtool_pause_stats

Each member has a corresponding attribute defined.

PAUSE_SET
============
Expand Down
57 changes: 52 additions & 5 deletions Documentation/networking/statistics.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,23 @@
Interface statistics
====================

Overview
========

This document is a guide to Linux network interface statistics.

There are two main sources of interface statistics in Linux:
There are three main sources of interface statistics in Linux:

- standard interface statistics based on
:c:type:`struct rtnl_link_stats64 <rtnl_link_stats64>`; and
:c:type:`struct rtnl_link_stats64 <rtnl_link_stats64>`;
- protocol-specific statistics; and
- driver-defined statistics available via ethtool.

There are multiple interfaces to reach the former. Most commonly used
is the `ip` command from `iproute2`::
Standard interface statistics
-----------------------------

There are multiple interfaces to reach the standard statistics.
Most commonly used is the `ip` command from `iproute2`::

$ ip -s -s link show dev ens4u1u1
6: ens4u1u1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
Expand All @@ -34,7 +41,26 @@ If `-s` is specified once the detailed errors won't be shown.

`ip` supports JSON formatting via the `-j` option.

Ethtool statistics can be dumped using `ethtool -S $ifc`, e.g.::
Protocol-specific statistics
----------------------------

Some of the interfaces used for configuring devices are also able
to report related statistics. For example ethtool interface used
to configure pause frames can report corresponding hardware counters::

$ ethtool --include-statistics -a eth0
Pause parameters for eth0:
Autonegotiate: on
RX: on
TX: on
Statistics:
tx_pause_frames: 1
rx_pause_frames: 1

Driver-defined statistics
-------------------------

Driver-defined ethtool statistics can be dumped using `ethtool -S $ifc`, e.g.::

$ ethtool -S ens4u1u1
NIC statistics:
Expand Down Expand Up @@ -94,6 +120,17 @@ Identifiers via `ETHTOOL_GSTRINGS` with `string_set` set to `ETH_SS_STATS`,
and values via `ETHTOOL_GSTATS`. User space should use `ETHTOOL_GDRVINFO`
to retrieve the number of statistics (`.n_stats`).

ethtool-netlink
---------------

Ethtool netlink is a replacement for the older IOCTL interface.

Protocol-related statistics can be requested in get commands by setting
the `ETHTOOL_FLAG_STATS` flag in `ETHTOOL_A_HEADER_FLAGS`. Currently
statistics are supported in the following commands:

- `ETHTOOL_MSG_PAUSE_GET`

debugfs
-------

Expand Down Expand Up @@ -130,3 +167,13 @@ user space trying to read them.

Statistics must persist across routine operations like bringing the interface
down and up.

Kernel-internal data structures
-------------------------------

The following structures are internal to the kernel, their members are
translated to netlink attributes when dumped. Drivers must not overwrite
the statistics they don't report with 0.

.. kernel-doc:: include/linux/ethtool.h
:identifiers: ethtool_pause_stats
17 changes: 17 additions & 0 deletions drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c
Original file line number Diff line number Diff line change
Expand Up @@ -1778,6 +1778,22 @@ static void bnxt_get_pauseparam(struct net_device *dev,
epause->tx_pause = !!(link_info->req_flow_ctrl & BNXT_LINK_PAUSE_TX);
}

static void bnxt_get_pause_stats(struct net_device *dev,
struct ethtool_pause_stats *epstat)
{
struct bnxt *bp = netdev_priv(dev);
u64 *rx, *tx;

if (BNXT_VF(bp) || !(bp->flags & BNXT_FLAG_PORT_STATS))
return;

rx = bp->port_stats.sw_stats;
tx = bp->port_stats.sw_stats + BNXT_TX_PORT_STATS_BYTE_OFFSET / 8;

epstat->rx_pause_frames = BNXT_GET_RX_PORT_STATS64(rx, rx_pause_frames);
epstat->tx_pause_frames = BNXT_GET_TX_PORT_STATS64(tx, tx_pause_frames);
}

static int bnxt_set_pauseparam(struct net_device *dev,
struct ethtool_pauseparam *epause)
{
Expand Down Expand Up @@ -3645,6 +3661,7 @@ const struct ethtool_ops bnxt_ethtool_ops = {
ETHTOOL_COALESCE_USE_ADAPTIVE_RX,
.get_link_ksettings = bnxt_get_link_ksettings,
.set_link_ksettings = bnxt_set_link_ksettings,
.get_pause_stats = bnxt_get_pause_stats,
.get_pauseparam = bnxt_get_pauseparam,
.set_pauseparam = bnxt_set_pauseparam,
.get_drvinfo = bnxt_get_drvinfo,
Expand Down
11 changes: 11 additions & 0 deletions drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c
Original file line number Diff line number Diff line change
Expand Up @@ -531,6 +531,16 @@ static int ixgbe_set_link_ksettings(struct net_device *netdev,
return err;
}

static void ixgbe_get_pause_stats(struct net_device *netdev,
struct ethtool_pause_stats *stats)
{
struct ixgbe_adapter *adapter = netdev_priv(netdev);
struct ixgbe_hw_stats *hwstats = &adapter->stats;

stats->tx_pause_frames = hwstats->lxontxc + hwstats->lxofftxc;
stats->rx_pause_frames = hwstats->lxonrxc + hwstats->lxoffrxc;
}

static void ixgbe_get_pauseparam(struct net_device *netdev,
struct ethtool_pauseparam *pause)
{
Expand Down Expand Up @@ -3546,6 +3556,7 @@ static const struct ethtool_ops ixgbe_ethtool_ops = {
.set_eeprom = ixgbe_set_eeprom,
.get_ringparam = ixgbe_get_ringparam,
.set_ringparam = ixgbe_set_ringparam,
.get_pause_stats = ixgbe_get_pause_stats,
.get_pauseparam = ixgbe_get_pauseparam,
.set_pauseparam = ixgbe_set_pauseparam,
.get_msglevel = ixgbe_get_msglevel,
Expand Down
19 changes: 19 additions & 0 deletions drivers/net/ethernet/mellanox/mlx4/en_ethtool.c
Original file line number Diff line number Diff line change
Expand Up @@ -1106,6 +1106,24 @@ static int mlx4_en_set_pauseparam(struct net_device *dev,
return err;
}

static void mlx4_en_get_pause_stats(struct net_device *dev,
struct ethtool_pause_stats *stats)
{
struct mlx4_en_priv *priv = netdev_priv(dev);
struct bitmap_iterator it;

bitmap_iterator_init(&it, priv->stats_bitmap.bitmap, NUM_ALL_STATS);

spin_lock_bh(&priv->stats_lock);
if (test_bit(FLOW_PRIORITY_STATS_IDX_TX_FRAMES,
priv->stats_bitmap.bitmap))
stats->tx_pause_frames = priv->tx_flowstats.tx_pause;
if (test_bit(FLOW_PRIORITY_STATS_IDX_RX_FRAMES,
priv->stats_bitmap.bitmap))
stats->rx_pause_frames = priv->rx_flowstats.rx_pause;
spin_unlock_bh(&priv->stats_lock);
}

static void mlx4_en_get_pauseparam(struct net_device *dev,
struct ethtool_pauseparam *pause)
{
Expand Down Expand Up @@ -2138,6 +2156,7 @@ const struct ethtool_ops mlx4_en_ethtool_ops = {
.set_msglevel = mlx4_en_set_msglevel,
.get_coalesce = mlx4_en_get_coalesce,
.set_coalesce = mlx4_en_set_coalesce,
.get_pause_stats = mlx4_en_get_pause_stats,
.get_pauseparam = mlx4_en_get_pauseparam,
.set_pauseparam = mlx4_en_set_pauseparam,
.get_ringparam = mlx4_en_get_ringparam,
Expand Down
12 changes: 12 additions & 0 deletions drivers/net/ethernet/mellanox/mlx4/mlx4_stats.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@ struct mlx4_en_flow_stats_rx {
MLX4_NUM_PRIORITIES)
};

#define FLOW_PRIORITY_STATS_IDX_RX_FRAMES (NUM_MAIN_STATS + \
NUM_PORT_STATS + \
NUM_PF_STATS + \
NUM_FLOW_PRIORITY_STATS_RX)

struct mlx4_en_flow_stats_tx {
u64 tx_pause;
u64 tx_pause_duration;
Expand All @@ -93,6 +98,13 @@ struct mlx4_en_flow_stats_tx {
MLX4_NUM_PRIORITIES)
};

#define FLOW_PRIORITY_STATS_IDX_TX_FRAMES (NUM_MAIN_STATS + \
NUM_PORT_STATS + \
NUM_PF_STATS + \
NUM_FLOW_PRIORITY_STATS_RX + \
NUM_FLOW_STATS_RX + \
NUM_FLOW_PRIORITY_STATS_TX)

#define NUM_FLOW_STATS (NUM_FLOW_STATS_RX + NUM_FLOW_STATS_TX + \
NUM_FLOW_PRIORITY_STATS_TX + \
NUM_FLOW_PRIORITY_STATS_RX)
Expand Down
9 changes: 9 additions & 0 deletions drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c
Original file line number Diff line number Diff line change
Expand Up @@ -1341,6 +1341,14 @@ static int mlx5e_set_tunable(struct net_device *dev,
return err;
}

static void mlx5e_get_pause_stats(struct net_device *netdev,
struct ethtool_pause_stats *pause_stats)
{
struct mlx5e_priv *priv = netdev_priv(netdev);

mlx5e_stats_pause_get(priv, pause_stats);
}

void mlx5e_ethtool_get_pauseparam(struct mlx5e_priv *priv,
struct ethtool_pauseparam *pauseparam)
{
Expand Down Expand Up @@ -2033,6 +2041,7 @@ const struct ethtool_ops mlx5e_ethtool_ops = {
.set_rxnfc = mlx5e_set_rxnfc,
.get_tunable = mlx5e_get_tunable,
.set_tunable = mlx5e_set_tunable,
.get_pause_stats = mlx5e_get_pause_stats,
.get_pauseparam = mlx5e_get_pauseparam,
.set_pauseparam = mlx5e_set_pauseparam,
.get_ts_info = mlx5e_get_ts_info,
Expand Down
9 changes: 9 additions & 0 deletions drivers/net/ethernet/mellanox/mlx5/core/en_rep.c
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,14 @@ static u32 mlx5e_rep_get_rxfh_indir_size(struct net_device *netdev)
return mlx5e_ethtool_get_rxfh_indir_size(priv);
}

static void mlx5e_uplink_rep_get_pause_stats(struct net_device *netdev,
struct ethtool_pause_stats *stats)
{
struct mlx5e_priv *priv = netdev_priv(netdev);

mlx5e_stats_pause_get(priv, stats);
}

static void mlx5e_uplink_rep_get_pauseparam(struct net_device *netdev,
struct ethtool_pauseparam *pauseparam)
{
Expand Down Expand Up @@ -362,6 +370,7 @@ static const struct ethtool_ops mlx5e_uplink_rep_ethtool_ops = {
.set_rxfh = mlx5e_set_rxfh,
.get_rxnfc = mlx5e_get_rxnfc,
.set_rxnfc = mlx5e_set_rxnfc,
.get_pause_stats = mlx5e_uplink_rep_get_pause_stats,
.get_pauseparam = mlx5e_uplink_rep_get_pauseparam,
.set_pauseparam = mlx5e_uplink_rep_set_pauseparam,
};
Expand Down
29 changes: 29 additions & 0 deletions drivers/net/ethernet/mellanox/mlx5/core/en_stats.c
Original file line number Diff line number Diff line change
Expand Up @@ -677,6 +677,35 @@ static MLX5E_DECLARE_STATS_GRP_OP_UPDATE_STATS(802_3)
mlx5_core_access_reg(mdev, in, sz, out, sz, MLX5_REG_PPCNT, 0, 0);
}

#define MLX5E_READ_CTR64_BE_F(ptr, c) \
be64_to_cpu(*(__be64 *)((char *)ptr + \
MLX5_BYTE_OFF(ppcnt_reg, \
counter_set.eth_802_3_cntrs_grp_data_layout.c##_high)))

void mlx5e_stats_pause_get(struct mlx5e_priv *priv,
struct ethtool_pause_stats *pause_stats)
{
u32 ppcnt_ieee_802_3[MLX5_ST_SZ_DW(ppcnt_reg)];
struct mlx5_core_dev *mdev = priv->mdev;
u32 in[MLX5_ST_SZ_DW(ppcnt_reg)] = {};
int sz = MLX5_ST_SZ_BYTES(ppcnt_reg);

if (!MLX5_BASIC_PPCNT_SUPPORTED(mdev))
return;

MLX5_SET(ppcnt_reg, in, local_port, 1);
MLX5_SET(ppcnt_reg, in, grp, MLX5_IEEE_802_3_COUNTERS_GROUP);
mlx5_core_access_reg(mdev, in, sz, ppcnt_ieee_802_3,
sz, MLX5_REG_PPCNT, 0, 0);

pause_stats->tx_pause_frames =
MLX5E_READ_CTR64_BE_F(ppcnt_ieee_802_3,
a_pause_mac_ctrl_frames_transmitted);
pause_stats->rx_pause_frames =
MLX5E_READ_CTR64_BE_F(ppcnt_ieee_802_3,
a_pause_mac_ctrl_frames_received);
}

#define PPORT_2863_OFF(c) \
MLX5_BYTE_OFF(ppcnt_reg, \
counter_set.eth_2863_cntrs_grp_data_layout.c##_high)
Expand Down
3 changes: 3 additions & 0 deletions drivers/net/ethernet/mellanox/mlx5/core/en_stats.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,9 @@ void mlx5e_stats_update(struct mlx5e_priv *priv);
void mlx5e_stats_fill(struct mlx5e_priv *priv, u64 *data, int idx);
void mlx5e_stats_fill_strings(struct mlx5e_priv *priv, u8 *data);

void mlx5e_stats_pause_get(struct mlx5e_priv *priv,
struct ethtool_pause_stats *pause_stats);

/* Concrete NIC Stats */

struct mlx5e_sw_stats {
Expand Down
2 changes: 1 addition & 1 deletion drivers/net/netdevsim/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
obj-$(CONFIG_NETDEVSIM) += netdevsim.o

netdevsim-objs := \
netdev.o dev.o fib.o bus.o health.o udp_tunnels.o
netdev.o dev.o ethtool.o fib.o bus.o health.o udp_tunnels.o

ifeq ($(CONFIG_BPF_SYSCALL),y)
netdevsim-objs += \
Expand Down
64 changes: 64 additions & 0 deletions drivers/net/netdevsim/ethtool.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2020 Facebook

#include <linux/debugfs.h>
#include <linux/ethtool.h>
#include <linux/random.h>

#include "netdevsim.h"

static void
nsim_get_pause_stats(struct net_device *dev,
struct ethtool_pause_stats *pause_stats)
{
struct netdevsim *ns = netdev_priv(dev);

if (ns->ethtool.report_stats_rx)
pause_stats->rx_pause_frames = 1;
if (ns->ethtool.report_stats_tx)
pause_stats->tx_pause_frames = 2;
}

static void
nsim_get_pauseparam(struct net_device *dev, struct ethtool_pauseparam *pause)
{
struct netdevsim *ns = netdev_priv(dev);

pause->autoneg = 0; /* We don't support ksettings, so can't pretend */
pause->rx_pause = ns->ethtool.rx;
pause->tx_pause = ns->ethtool.tx;
}

static int
nsim_set_pauseparam(struct net_device *dev, struct ethtool_pauseparam *pause)
{
struct netdevsim *ns = netdev_priv(dev);

if (pause->autoneg)
return -EINVAL;

ns->ethtool.rx = pause->rx_pause;
ns->ethtool.tx = pause->tx_pause;
return 0;
}

static const struct ethtool_ops nsim_ethtool_ops = {
.get_pause_stats = nsim_get_pause_stats,
.get_pauseparam = nsim_get_pauseparam,
.set_pauseparam = nsim_set_pauseparam,
};

void nsim_ethtool_init(struct netdevsim *ns)
{
struct dentry *ethtool, *dir;

ns->netdev->ethtool_ops = &nsim_ethtool_ops;

ethtool = debugfs_create_dir("ethtool", ns->nsim_dev->ddir);

dir = debugfs_create_dir("pause", ethtool);
debugfs_create_bool("report_stats_rx", 0600, dir,
&ns->ethtool.report_stats_rx);
debugfs_create_bool("report_stats_tx", 0600, dir,
&ns->ethtool.report_stats_tx);
}
Loading

0 comments on commit 945c570

Please sign in to comment.