[PATCH net-next 11/14] net: mscc: ocelot: add support for all sorts of standardized counters present in DSA

From: Vladimir Oltean
Date: Thu Sep 08 2022 - 12:50:20 EST


DSA is integrated with the new standardized ethtool -S --groups option,
but the felix driver only exports unstructured statistics.

Reuse the array of 64-bit statistics collected by ocelot_check_stats_work(),
but just export select values from it.

Since ocelot_check_stats_work() runs periodically to avoid 32-bit
overflow, and the ethtool calling context is sleepable, we update the
64-bit stats one more time, to provide up-to-date values. The locking
scheme with a mutex followed by a spinlock is a bit hard to digest, so
we create and use a ocelot_port_stats_run() helper with a callback that
populates the ethool stats group the caller is interested in.

The exported stats are:
ethtool -S swp0 --groups eth-phy
ethtool -S swp0 --groups eth-mac
ethtool -S swp0 --groups eth-ctrl
ethtool -S swp0 --groups rmon
ethtool --include-statistics --show-pause swp0

Signed-off-by: Vladimir Oltean <vladimir.oltean@xxxxxxx>
---
drivers/net/dsa/ocelot/felix.c | 46 ++++++
drivers/net/ethernet/mscc/ocelot_stats.c | 201 +++++++++++++++++++++--
include/soc/mscc/ocelot.h | 11 ++
3 files changed, 241 insertions(+), 17 deletions(-)

diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c
index 71e22990aa67..c73ef5f7aa64 100644
--- a/drivers/net/dsa/ocelot/felix.c
+++ b/drivers/net/dsa/ocelot/felix.c
@@ -1042,6 +1042,47 @@ static void felix_get_stats64(struct dsa_switch *ds, int port,
ocelot_port_get_stats64(ocelot, port, stats);
}

+static void felix_get_pause_stats(struct dsa_switch *ds, int port,
+ struct ethtool_pause_stats *pause_stats)
+{
+ struct ocelot *ocelot = ds->priv;
+
+ ocelot_port_get_pause_stats(ocelot, port, pause_stats);
+}
+
+static void felix_get_rmon_stats(struct dsa_switch *ds, int port,
+ struct ethtool_rmon_stats *rmon_stats,
+ const struct ethtool_rmon_hist_range **ranges)
+{
+ struct ocelot *ocelot = ds->priv;
+
+ ocelot_port_get_rmon_stats(ocelot, port, rmon_stats, ranges);
+}
+
+static void felix_get_eth_ctrl_stats(struct dsa_switch *ds, int port,
+ struct ethtool_eth_ctrl_stats *ctrl_stats)
+{
+ struct ocelot *ocelot = ds->priv;
+
+ ocelot_port_get_eth_ctrl_stats(ocelot, port, ctrl_stats);
+}
+
+static void felix_get_eth_mac_stats(struct dsa_switch *ds, int port,
+ struct ethtool_eth_mac_stats *mac_stats)
+{
+ struct ocelot *ocelot = ds->priv;
+
+ ocelot_port_get_eth_mac_stats(ocelot, port, mac_stats);
+}
+
+static void felix_get_eth_phy_stats(struct dsa_switch *ds, int port,
+ struct ethtool_eth_phy_stats *phy_stats)
+{
+ struct ocelot *ocelot = ds->priv;
+
+ ocelot_port_get_eth_phy_stats(ocelot, port, phy_stats);
+}
+
static void felix_get_strings(struct dsa_switch *ds, int port,
u32 stringset, u8 *data)
{
@@ -1857,6 +1898,11 @@ const struct dsa_switch_ops felix_switch_ops = {
.teardown = felix_teardown,
.set_ageing_time = felix_set_ageing_time,
.get_stats64 = felix_get_stats64,
+ .get_pause_stats = felix_get_pause_stats,
+ .get_rmon_stats = felix_get_rmon_stats,
+ .get_eth_ctrl_stats = felix_get_eth_ctrl_stats,
+ .get_eth_mac_stats = felix_get_eth_mac_stats,
+ .get_eth_phy_stats = felix_get_eth_phy_stats,
.get_strings = felix_get_strings,
.get_ethtool_stats = felix_get_ethtool_stats,
.get_sset_count = felix_get_sset_count,
diff --git a/drivers/net/ethernet/mscc/ocelot_stats.c b/drivers/net/ethernet/mscc/ocelot_stats.c
index 2926d2661af4..dbd20b125cea 100644
--- a/drivers/net/ethernet/mscc/ocelot_stats.c
+++ b/drivers/net/ethernet/mscc/ocelot_stats.c
@@ -2,6 +2,7 @@
/* Statistics for Ocelot switch family
*
* Copyright (c) 2017 Microsemi Corporation
+ * Copyright 2022 NXP
*/
#include <linux/spinlock.h>
#include <linux/mutex.h>
@@ -101,37 +102,32 @@ void ocelot_get_strings(struct ocelot *ocelot, int port, u32 sset, u8 *data)
}
EXPORT_SYMBOL(ocelot_get_strings);

-void ocelot_get_ethtool_stats(struct ocelot *ocelot, int port, u64 *data)
+/* Update ocelot->stats for the given port and run the given callback */
+static void ocelot_port_stats_run(struct ocelot *ocelot, int port, void *priv,
+ void (*cb)(struct ocelot *ocelot, int port,
+ void *priv))
{
- int i, err;
+ int err;

mutex_lock(&ocelot->stat_view_lock);

- /* check and update now */
err = ocelot_port_update_stats(ocelot, port);
+ if (err) {
+ dev_err(ocelot->dev, "Failed to update port %d stats: %pe\n",
+ port, ERR_PTR(err));
+ goto out_unlock;
+ }

spin_lock(&ocelot->stats_lock);

ocelot_port_transfer_stats(ocelot, port);
-
- /* Copy all supported counters */
- for (i = 0; i < OCELOT_NUM_STATS; i++) {
- int index = port * OCELOT_NUM_STATS + i;
-
- if (ocelot->stats_layout[i].name[0] == '\0')
- continue;
-
- *data++ = ocelot->stats[index];
- }
+ cb(ocelot, port, priv);

spin_unlock(&ocelot->stats_lock);

+out_unlock:
mutex_unlock(&ocelot->stat_view_lock);
-
- if (err)
- dev_err(ocelot->dev, "Error %d updating ethtool stats\n", err);
}
-EXPORT_SYMBOL(ocelot_get_ethtool_stats);

int ocelot_get_sset_count(struct ocelot *ocelot, int port, int sset)
{
@@ -148,6 +144,177 @@ int ocelot_get_sset_count(struct ocelot *ocelot, int port, int sset)
}
EXPORT_SYMBOL(ocelot_get_sset_count);

+static void ocelot_port_ethtool_stats_cb(struct ocelot *ocelot, int port,
+ void *priv)
+{
+ u64 *data = priv;
+ int i;
+
+ /* Copy all supported counters */
+ for (i = 0; i < OCELOT_NUM_STATS; i++) {
+ int index = port * OCELOT_NUM_STATS + i;
+
+ if (ocelot->stats_layout[i].name[0] == '\0')
+ continue;
+
+ *data++ = ocelot->stats[index];
+ }
+}
+
+void ocelot_get_ethtool_stats(struct ocelot *ocelot, int port, u64 *data)
+{
+ ocelot_port_stats_run(ocelot, port, data, ocelot_port_ethtool_stats_cb);
+}
+EXPORT_SYMBOL(ocelot_get_ethtool_stats);
+
+static void ocelot_port_pause_stats_cb(struct ocelot *ocelot, int port, void *priv)
+{
+ u64 *s = &ocelot->stats[port * OCELOT_NUM_STATS];
+ struct ethtool_pause_stats *pause_stats = priv;
+
+ pause_stats->tx_pause_frames = s[OCELOT_STAT_TX_PAUSE];
+ pause_stats->rx_pause_frames = s[OCELOT_STAT_RX_PAUSE];
+}
+
+void ocelot_port_get_pause_stats(struct ocelot *ocelot, int port,
+ struct ethtool_pause_stats *pause_stats)
+{
+ ocelot_port_stats_run(ocelot, port, pause_stats,
+ ocelot_port_pause_stats_cb);
+}
+EXPORT_SYMBOL_GPL(ocelot_port_get_pause_stats);
+
+static const struct ethtool_rmon_hist_range ocelot_rmon_ranges[] = {
+ { 64, 64 },
+ { 65, 127 },
+ { 128, 255 },
+ { 256, 511 },
+ { 512, 1023 },
+ { 1024, 1526 },
+ { 1527, 65535 },
+ {},
+};
+
+static void ocelot_port_rmon_stats_cb(struct ocelot *ocelot, int port, void *priv)
+{
+ u64 *s = &ocelot->stats[port * OCELOT_NUM_STATS];
+ struct ethtool_rmon_stats *rmon_stats = priv;
+
+ rmon_stats->undersize_pkts = s[OCELOT_STAT_RX_SHORTS];
+ rmon_stats->oversize_pkts = s[OCELOT_STAT_RX_LONGS];
+ rmon_stats->fragments = s[OCELOT_STAT_RX_FRAGMENTS];
+ rmon_stats->jabbers = s[OCELOT_STAT_RX_JABBERS];
+
+ rmon_stats->hist[0] = s[OCELOT_STAT_RX_64];
+ rmon_stats->hist[1] = s[OCELOT_STAT_RX_65_127];
+ rmon_stats->hist[2] = s[OCELOT_STAT_RX_128_255];
+ rmon_stats->hist[3] = s[OCELOT_STAT_RX_256_511];
+ rmon_stats->hist[4] = s[OCELOT_STAT_RX_512_1023];
+ rmon_stats->hist[5] = s[OCELOT_STAT_RX_1024_1526];
+ rmon_stats->hist[6] = s[OCELOT_STAT_RX_1527_MAX];
+
+ rmon_stats->hist_tx[0] = s[OCELOT_STAT_TX_64];
+ rmon_stats->hist_tx[1] = s[OCELOT_STAT_TX_65_127];
+ rmon_stats->hist_tx[2] = s[OCELOT_STAT_TX_128_255];
+ rmon_stats->hist_tx[3] = s[OCELOT_STAT_TX_128_255];
+ rmon_stats->hist_tx[4] = s[OCELOT_STAT_TX_256_511];
+ rmon_stats->hist_tx[5] = s[OCELOT_STAT_TX_512_1023];
+ rmon_stats->hist_tx[6] = s[OCELOT_STAT_TX_1024_1526];
+}
+
+void ocelot_port_get_rmon_stats(struct ocelot *ocelot, int port,
+ struct ethtool_rmon_stats *rmon_stats,
+ const struct ethtool_rmon_hist_range **ranges)
+{
+ *ranges = ocelot_rmon_ranges;
+
+ ocelot_port_stats_run(ocelot, port, rmon_stats,
+ ocelot_port_rmon_stats_cb);
+}
+EXPORT_SYMBOL_GPL(ocelot_port_get_rmon_stats);
+
+static void ocelot_port_ctrl_stats_cb(struct ocelot *ocelot, int port, void *priv)
+{
+ u64 *s = &ocelot->stats[port * OCELOT_NUM_STATS];
+ struct ethtool_eth_ctrl_stats *ctrl_stats = priv;
+
+ ctrl_stats->MACControlFramesReceived = s[OCELOT_STAT_RX_CONTROL];
+}
+
+void ocelot_port_get_eth_ctrl_stats(struct ocelot *ocelot, int port,
+ struct ethtool_eth_ctrl_stats *ctrl_stats)
+{
+ ocelot_port_stats_run(ocelot, port, ctrl_stats,
+ ocelot_port_ctrl_stats_cb);
+}
+EXPORT_SYMBOL_GPL(ocelot_port_get_eth_ctrl_stats);
+
+static void ocelot_port_mac_stats_cb(struct ocelot *ocelot, int port, void *priv)
+{
+ u64 *s = &ocelot->stats[port * OCELOT_NUM_STATS];
+ struct ethtool_eth_mac_stats *mac_stats = priv;
+
+ mac_stats->OctetsTransmittedOK = s[OCELOT_STAT_TX_OCTETS];
+ mac_stats->FramesTransmittedOK = s[OCELOT_STAT_TX_64] +
+ s[OCELOT_STAT_TX_65_127] +
+ s[OCELOT_STAT_TX_128_255] +
+ s[OCELOT_STAT_TX_256_511] +
+ s[OCELOT_STAT_TX_512_1023] +
+ s[OCELOT_STAT_TX_1024_1526] +
+ s[OCELOT_STAT_TX_1527_MAX];
+ mac_stats->OctetsReceivedOK = s[OCELOT_STAT_RX_OCTETS];
+ mac_stats->FramesReceivedOK = s[OCELOT_STAT_RX_GREEN_PRIO_0] +
+ s[OCELOT_STAT_RX_GREEN_PRIO_1] +
+ s[OCELOT_STAT_RX_GREEN_PRIO_2] +
+ s[OCELOT_STAT_RX_GREEN_PRIO_3] +
+ s[OCELOT_STAT_RX_GREEN_PRIO_4] +
+ s[OCELOT_STAT_RX_GREEN_PRIO_5] +
+ s[OCELOT_STAT_RX_GREEN_PRIO_6] +
+ s[OCELOT_STAT_RX_GREEN_PRIO_7] +
+ s[OCELOT_STAT_RX_YELLOW_PRIO_0] +
+ s[OCELOT_STAT_RX_YELLOW_PRIO_1] +
+ s[OCELOT_STAT_RX_YELLOW_PRIO_2] +
+ s[OCELOT_STAT_RX_YELLOW_PRIO_3] +
+ s[OCELOT_STAT_RX_YELLOW_PRIO_4] +
+ s[OCELOT_STAT_RX_YELLOW_PRIO_5] +
+ s[OCELOT_STAT_RX_YELLOW_PRIO_6] +
+ s[OCELOT_STAT_RX_YELLOW_PRIO_7];
+ mac_stats->MulticastFramesXmittedOK = s[OCELOT_STAT_TX_MULTICAST];
+ mac_stats->BroadcastFramesXmittedOK = s[OCELOT_STAT_TX_BROADCAST];
+ mac_stats->MulticastFramesReceivedOK = s[OCELOT_STAT_RX_MULTICAST];
+ mac_stats->BroadcastFramesReceivedOK = s[OCELOT_STAT_RX_BROADCAST];
+ mac_stats->FrameTooLongErrors = s[OCELOT_STAT_RX_LONGS];
+ /* Sadly, C_RX_CRC is the sum of FCS and alignment errors, they are not
+ * counted individually.
+ */
+ mac_stats->FrameCheckSequenceErrors = s[OCELOT_STAT_RX_CRC_ALIGN_ERRS];
+ mac_stats->AlignmentErrors = s[OCELOT_STAT_RX_CRC_ALIGN_ERRS];
+}
+
+void ocelot_port_get_eth_mac_stats(struct ocelot *ocelot, int port,
+ struct ethtool_eth_mac_stats *mac_stats)
+{
+ ocelot_port_stats_run(ocelot, port, mac_stats,
+ ocelot_port_mac_stats_cb);
+}
+EXPORT_SYMBOL_GPL(ocelot_port_get_eth_mac_stats);
+
+static void ocelot_port_phy_stats_cb(struct ocelot *ocelot, int port, void *priv)
+{
+ u64 *s = &ocelot->stats[port * OCELOT_NUM_STATS];
+ struct ethtool_eth_phy_stats *phy_stats = priv;
+
+ phy_stats->SymbolErrorDuringCarrier = s[OCELOT_STAT_RX_SYM_ERRS];
+}
+
+void ocelot_port_get_eth_phy_stats(struct ocelot *ocelot, int port,
+ struct ethtool_eth_phy_stats *phy_stats)
+{
+ ocelot_port_stats_run(ocelot, port, phy_stats,
+ ocelot_port_phy_stats_cb);
+}
+EXPORT_SYMBOL_GPL(ocelot_port_get_eth_phy_stats);
+
void ocelot_port_get_stats64(struct ocelot *ocelot, int port,
struct rtnl_link_stats64 *stats)
{
diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h
index 2f639ef88f8f..050e142518e6 100644
--- a/include/soc/mscc/ocelot.h
+++ b/include/soc/mscc/ocelot.h
@@ -1045,6 +1045,17 @@ void ocelot_get_ethtool_stats(struct ocelot *ocelot, int port, u64 *data);
int ocelot_get_sset_count(struct ocelot *ocelot, int port, int sset);
void ocelot_port_get_stats64(struct ocelot *ocelot, int port,
struct rtnl_link_stats64 *stats);
+void ocelot_port_get_pause_stats(struct ocelot *ocelot, int port,
+ struct ethtool_pause_stats *pause_stats);
+void ocelot_port_get_rmon_stats(struct ocelot *ocelot, int port,
+ struct ethtool_rmon_stats *rmon_stats,
+ const struct ethtool_rmon_hist_range **ranges);
+void ocelot_port_get_eth_ctrl_stats(struct ocelot *ocelot, int port,
+ struct ethtool_eth_ctrl_stats *ctrl_stats);
+void ocelot_port_get_eth_mac_stats(struct ocelot *ocelot, int port,
+ struct ethtool_eth_mac_stats *mac_stats);
+void ocelot_port_get_eth_phy_stats(struct ocelot *ocelot, int port,
+ struct ethtool_eth_phy_stats *phy_stats);
int ocelot_get_ts_info(struct ocelot *ocelot, int port,
struct ethtool_ts_info *info);
void ocelot_set_ageing_time(struct ocelot *ocelot, unsigned int msecs);
--
2.34.1