net: Allow network devices to have PHY statistics

Add a new callback: get_ethtool_phy_stats() which allows network device
drivers not making use of the PHY library to return PHY statistics.
Update ethtool_get_phy_stats(), __ethtool_get_sset_count() and
__ethtool_get_strings() accordingly to interogate the network device
about ETH_SS_PHY_STATS.

Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Florian Fainelli 2018-04-25 12:12:48 -07:00 committed by David S. Miller
parent c59530d0d5
commit 9994338227
2 changed files with 26 additions and 18 deletions

View File

@ -312,6 +312,9 @@ bool ethtool_convert_link_mode_to_legacy_u32(u32 *legacy_u32,
* by kernel. Returns a negative error code or zero. * by kernel. Returns a negative error code or zero.
* @get_fecparam: Get the network device Forward Error Correction parameters. * @get_fecparam: Get the network device Forward Error Correction parameters.
* @set_fecparam: Set the network device Forward Error Correction parameters. * @set_fecparam: Set the network device Forward Error Correction parameters.
* @get_ethtool_phy_stats: Return extended statistics about the PHY device.
* This is only useful if the device maintains PHY statistics and
* cannot use the standard PHY library helpers.
* *
* All operations are optional (i.e. the function pointer may be set * All operations are optional (i.e. the function pointer may be set
* to %NULL) and callers must take this into account. Callers must * to %NULL) and callers must take this into account. Callers must
@ -407,5 +410,7 @@ struct ethtool_ops {
struct ethtool_fecparam *); struct ethtool_fecparam *);
int (*set_fecparam)(struct net_device *, int (*set_fecparam)(struct net_device *,
struct ethtool_fecparam *); struct ethtool_fecparam *);
void (*get_ethtool_phy_stats)(struct net_device *,
struct ethtool_stats *, u64 *);
}; };
#endif /* _LINUX_ETHTOOL_H */ #endif /* _LINUX_ETHTOOL_H */

View File

@ -227,12 +227,9 @@ static int __ethtool_get_sset_count(struct net_device *dev, int sset)
if (sset == ETH_SS_PHY_TUNABLES) if (sset == ETH_SS_PHY_TUNABLES)
return ARRAY_SIZE(phy_tunable_strings); return ARRAY_SIZE(phy_tunable_strings);
if (sset == ETH_SS_PHY_STATS) { if (sset == ETH_SS_PHY_STATS && dev->phydev &&
if (dev->phydev) !ops->get_ethtool_phy_stats)
return phy_ethtool_get_sset_count(dev->phydev); return phy_ethtool_get_sset_count(dev->phydev);
else
return -EOPNOTSUPP;
}
if (ops->get_sset_count && ops->get_strings) if (ops->get_sset_count && ops->get_strings)
return ops->get_sset_count(dev, sset); return ops->get_sset_count(dev, sset);
@ -255,12 +252,10 @@ static void __ethtool_get_strings(struct net_device *dev,
memcpy(data, tunable_strings, sizeof(tunable_strings)); memcpy(data, tunable_strings, sizeof(tunable_strings));
else if (stringset == ETH_SS_PHY_TUNABLES) else if (stringset == ETH_SS_PHY_TUNABLES)
memcpy(data, phy_tunable_strings, sizeof(phy_tunable_strings)); memcpy(data, phy_tunable_strings, sizeof(phy_tunable_strings));
else if (stringset == ETH_SS_PHY_STATS) { else if (stringset == ETH_SS_PHY_STATS && dev->phydev &&
if (dev->phydev) !ops->get_ethtool_phy_stats)
phy_ethtool_get_strings(dev->phydev, data); phy_ethtool_get_strings(dev->phydev, data);
else else
return;
} else
/* ops->get_strings is valid because checked earlier */ /* ops->get_strings is valid because checked earlier */
ops->get_strings(dev, stringset, data); ops->get_strings(dev, stringset, data);
} }
@ -1972,15 +1967,19 @@ static int ethtool_get_stats(struct net_device *dev, void __user *useraddr)
static int ethtool_get_phy_stats(struct net_device *dev, void __user *useraddr) static int ethtool_get_phy_stats(struct net_device *dev, void __user *useraddr)
{ {
struct ethtool_stats stats; const struct ethtool_ops *ops = dev->ethtool_ops;
struct phy_device *phydev = dev->phydev; struct phy_device *phydev = dev->phydev;
struct ethtool_stats stats;
u64 *data; u64 *data;
int ret, n_stats; int ret, n_stats;
if (!phydev) if (!phydev && (!ops->get_ethtool_phy_stats || !ops->get_sset_count))
return -EOPNOTSUPP; return -EOPNOTSUPP;
n_stats = phy_ethtool_get_sset_count(dev->phydev); if (dev->phydev && !ops->get_ethtool_phy_stats)
n_stats = phy_ethtool_get_sset_count(dev->phydev);
else
n_stats = ops->get_sset_count(dev, ETH_SS_PHY_STATS);
if (n_stats < 0) if (n_stats < 0)
return n_stats; return n_stats;
if (n_stats > S32_MAX / sizeof(u64)) if (n_stats > S32_MAX / sizeof(u64))
@ -1995,9 +1994,13 @@ static int ethtool_get_phy_stats(struct net_device *dev, void __user *useraddr)
if (n_stats && !data) if (n_stats && !data)
return -ENOMEM; return -ENOMEM;
ret = phy_ethtool_get_stats(dev->phydev, &stats, data); if (dev->phydev && !ops->get_ethtool_phy_stats) {
if (ret < 0) ret = phy_ethtool_get_stats(dev->phydev, &stats, data);
return ret; if (ret < 0)
return ret;
} else {
ops->get_ethtool_phy_stats(dev, &stats, data);
}
ret = -EFAULT; ret = -EFAULT;
if (copy_to_user(useraddr, &stats, sizeof(stats))) if (copy_to_user(useraddr, &stats, sizeof(stats)))