mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	net: mscc: ocelot: add support for all sorts of standardized counters present in DSA
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@nxp.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									d3e75f1665
								
							
						
					
					
						commit
						e32036e1ae
					
				
					 3 changed files with 241 additions and 17 deletions
				
			
		| 
						 | 
				
			
			@ -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,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue