mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	net: dsa: Allow providing PHY statistics from CPU port
Implement the same type of ethtool diversion that we have for ETH_SS_STATS and make it work with ETH_SS_PHY_STATS. This allows providing PHY level statistics for CPU ports that are directly connecting to a PHY device. Signed-off-by: Florian Fainelli <f.fainelli@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									6207a78c09
								
							
						
					
					
						commit
						cf96357303
					
				
					 3 changed files with 106 additions and 5 deletions
				
			
		| 
						 | 
				
			
			@ -361,6 +361,8 @@ struct dsa_switch_ops {
 | 
			
		|||
	void	(*get_ethtool_stats)(struct dsa_switch *ds,
 | 
			
		||||
				     int port, uint64_t *data);
 | 
			
		||||
	int	(*get_sset_count)(struct dsa_switch *ds, int port, int sset);
 | 
			
		||||
	void	(*get_ethtool_phy_stats)(struct dsa_switch *ds,
 | 
			
		||||
					 int port, uint64_t *data);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * ethtool Wake-on-LAN
 | 
			
		||||
| 
						 | 
				
			
			@ -589,4 +591,9 @@ static inline int call_dsa_notifiers(unsigned long val, struct net_device *dev,
 | 
			
		|||
#define BRCM_TAG_GET_PORT(v)		((v) >> 8)
 | 
			
		||||
#define BRCM_TAG_GET_QUEUE(v)		((v) & 0xff)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int dsa_port_get_phy_strings(struct dsa_port *dp, uint8_t *data);
 | 
			
		||||
int dsa_port_get_ethtool_phy_stats(struct dsa_port *dp, uint64_t *data);
 | 
			
		||||
int dsa_port_get_phy_sset_count(struct dsa_port *dp);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -31,6 +31,32 @@ static void dsa_master_get_ethtool_stats(struct net_device *dev,
 | 
			
		|||
		ds->ops->get_ethtool_stats(ds, port, data + count);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void dsa_master_get_ethtool_phy_stats(struct net_device *dev,
 | 
			
		||||
					     struct ethtool_stats *stats,
 | 
			
		||||
					     uint64_t *data)
 | 
			
		||||
{
 | 
			
		||||
	struct dsa_port *cpu_dp = dev->dsa_ptr;
 | 
			
		||||
	const struct ethtool_ops *ops = cpu_dp->orig_ethtool_ops;
 | 
			
		||||
	struct dsa_switch *ds = cpu_dp->ds;
 | 
			
		||||
	int port = cpu_dp->index;
 | 
			
		||||
	int count = 0;
 | 
			
		||||
 | 
			
		||||
	if (dev->phydev && !ops->get_ethtool_phy_stats) {
 | 
			
		||||
		count = phy_ethtool_get_sset_count(dev->phydev);
 | 
			
		||||
		if (count >= 0)
 | 
			
		||||
			phy_ethtool_get_stats(dev->phydev, stats, data);
 | 
			
		||||
	} else if (ops->get_sset_count && ops->get_ethtool_phy_stats) {
 | 
			
		||||
		count = ops->get_sset_count(dev, ETH_SS_PHY_STATS);
 | 
			
		||||
		ops->get_ethtool_phy_stats(dev, stats, data);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (count < 0)
 | 
			
		||||
		count = 0;
 | 
			
		||||
 | 
			
		||||
	if (ds->ops->get_ethtool_phy_stats)
 | 
			
		||||
		ds->ops->get_ethtool_phy_stats(ds, port, data + count);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int dsa_master_get_sset_count(struct net_device *dev, int sset)
 | 
			
		||||
{
 | 
			
		||||
	struct dsa_port *cpu_dp = dev->dsa_ptr;
 | 
			
		||||
| 
						 | 
				
			
			@ -38,11 +64,14 @@ static int dsa_master_get_sset_count(struct net_device *dev, int sset)
 | 
			
		|||
	struct dsa_switch *ds = cpu_dp->ds;
 | 
			
		||||
	int count = 0;
 | 
			
		||||
 | 
			
		||||
	if (ops->get_sset_count) {
 | 
			
		||||
	if (sset == ETH_SS_PHY_STATS && dev->phydev &&
 | 
			
		||||
	    !ops->get_ethtool_phy_stats)
 | 
			
		||||
		count = phy_ethtool_get_sset_count(dev->phydev);
 | 
			
		||||
	else if (ops->get_sset_count)
 | 
			
		||||
		count = ops->get_sset_count(dev, sset);
 | 
			
		||||
		if (count < 0)
 | 
			
		||||
			count = 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (count < 0)
 | 
			
		||||
		count = 0;
 | 
			
		||||
 | 
			
		||||
	if (ds->ops->get_sset_count)
 | 
			
		||||
		count += ds->ops->get_sset_count(ds, cpu_dp->index, sset);
 | 
			
		||||
| 
						 | 
				
			
			@ -67,7 +96,14 @@ static void dsa_master_get_strings(struct net_device *dev, uint32_t stringset,
 | 
			
		|||
	/* We do not want to be NULL-terminated, since this is a prefix */
 | 
			
		||||
	pfx[sizeof(pfx) - 1] = '_';
 | 
			
		||||
 | 
			
		||||
	if (ops->get_sset_count && ops->get_strings) {
 | 
			
		||||
	if (stringset == ETH_SS_PHY_STATS && dev->phydev &&
 | 
			
		||||
	    !ops->get_ethtool_phy_stats) {
 | 
			
		||||
		mcount = phy_ethtool_get_sset_count(dev->phydev);
 | 
			
		||||
		if (mcount < 0)
 | 
			
		||||
			mcount = 0;
 | 
			
		||||
		else
 | 
			
		||||
			phy_ethtool_get_strings(dev->phydev, data);
 | 
			
		||||
	} else if (ops->get_sset_count && ops->get_strings) {
 | 
			
		||||
		mcount = ops->get_sset_count(dev, stringset);
 | 
			
		||||
		if (mcount < 0)
 | 
			
		||||
			mcount = 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -107,6 +143,7 @@ static int dsa_master_ethtool_setup(struct net_device *dev)
 | 
			
		|||
	ops->get_sset_count = dsa_master_get_sset_count;
 | 
			
		||||
	ops->get_ethtool_stats = dsa_master_get_ethtool_stats;
 | 
			
		||||
	ops->get_strings = dsa_master_get_strings;
 | 
			
		||||
	ops->get_ethtool_phy_stats = dsa_master_get_ethtool_phy_stats;
 | 
			
		||||
 | 
			
		||||
	dev->ethtool_ops = ops;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -383,3 +383,60 @@ void dsa_port_link_unregister_of(struct dsa_port *dp)
 | 
			
		|||
	else
 | 
			
		||||
		dsa_port_setup_phy_of(dp, false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int dsa_port_get_phy_strings(struct dsa_port *dp, uint8_t *data)
 | 
			
		||||
{
 | 
			
		||||
	struct phy_device *phydev;
 | 
			
		||||
	int ret = -EOPNOTSUPP;
 | 
			
		||||
 | 
			
		||||
	if (of_phy_is_fixed_link(dp->dn))
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	phydev = dsa_port_get_phy_device(dp);
 | 
			
		||||
	if (IS_ERR_OR_NULL(phydev))
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	ret = phy_ethtool_get_strings(phydev, data);
 | 
			
		||||
	put_device(&phydev->mdio.dev);
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL_GPL(dsa_port_get_phy_strings);
 | 
			
		||||
 | 
			
		||||
int dsa_port_get_ethtool_phy_stats(struct dsa_port *dp, uint64_t *data)
 | 
			
		||||
{
 | 
			
		||||
	struct phy_device *phydev;
 | 
			
		||||
	int ret = -EOPNOTSUPP;
 | 
			
		||||
 | 
			
		||||
	if (of_phy_is_fixed_link(dp->dn))
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	phydev = dsa_port_get_phy_device(dp);
 | 
			
		||||
	if (IS_ERR_OR_NULL(phydev))
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	ret = phy_ethtool_get_stats(phydev, NULL, data);
 | 
			
		||||
	put_device(&phydev->mdio.dev);
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL_GPL(dsa_port_get_ethtool_phy_stats);
 | 
			
		||||
 | 
			
		||||
int dsa_port_get_phy_sset_count(struct dsa_port *dp)
 | 
			
		||||
{
 | 
			
		||||
	struct phy_device *phydev;
 | 
			
		||||
	int ret = -EOPNOTSUPP;
 | 
			
		||||
 | 
			
		||||
	if (of_phy_is_fixed_link(dp->dn))
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	phydev = dsa_port_get_phy_device(dp);
 | 
			
		||||
	if (IS_ERR_OR_NULL(phydev))
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	ret = phy_ethtool_get_sset_count(phydev);
 | 
			
		||||
	put_device(&phydev->mdio.dev);
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL_GPL(dsa_port_get_phy_sset_count);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue