aboutsummaryrefslogtreecommitdiffstats
path: root/net/core
diff options
context:
space:
mode:
authorAndrew Lunn <andrew@lunn.ch>2015-12-30 10:28:25 -0500
committerDavid S. Miller <davem@davemloft.net>2015-12-31 00:53:10 -0500
commitf3a4094558ddf8afa8bb58250d548e15e059c65a (patch)
tree081f2d64b4eef8094fef2b8e8858364f3c74155f /net/core
parenta3748a9c7fc0e33ed1a5bcd6104098d9f63a61ed (diff)
ethtool: Add phy statistics
Ethernet PHYs can maintain statistics, for example errors while idle and receive errors. Add an ethtool mechanism to retrieve these statistics, using the same model as MAC statistics. Signed-off-by: Andrew Lunn <andrew@lunn.ch> Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/core')
-rw-r--r--net/core/ethtool.c81
1 files changed, 80 insertions, 1 deletions
diff --git a/net/core/ethtool.c b/net/core/ethtool.c
index 09948a726347..daf04709dd3c 100644
--- a/net/core/ethtool.c
+++ b/net/core/ethtool.c
@@ -191,6 +191,23 @@ static int ethtool_set_features(struct net_device *dev, void __user *useraddr)
191 return ret; 191 return ret;
192} 192}
193 193
194static int phy_get_sset_count(struct phy_device *phydev)
195{
196 int ret;
197
198 if (phydev->drv->get_sset_count &&
199 phydev->drv->get_strings &&
200 phydev->drv->get_stats) {
201 mutex_lock(&phydev->lock);
202 ret = phydev->drv->get_sset_count(phydev);
203 mutex_unlock(&phydev->lock);
204
205 return ret;
206 }
207
208 return -EOPNOTSUPP;
209}
210
194static int __ethtool_get_sset_count(struct net_device *dev, int sset) 211static int __ethtool_get_sset_count(struct net_device *dev, int sset)
195{ 212{
196 const struct ethtool_ops *ops = dev->ethtool_ops; 213 const struct ethtool_ops *ops = dev->ethtool_ops;
@@ -204,6 +221,13 @@ static int __ethtool_get_sset_count(struct net_device *dev, int sset)
204 if (sset == ETH_SS_TUNABLES) 221 if (sset == ETH_SS_TUNABLES)
205 return ARRAY_SIZE(tunable_strings); 222 return ARRAY_SIZE(tunable_strings);
206 223
224 if (sset == ETH_SS_PHY_STATS) {
225 if (dev->phydev)
226 return phy_get_sset_count(dev->phydev);
227 else
228 return -EOPNOTSUPP;
229 }
230
207 if (ops->get_sset_count && ops->get_strings) 231 if (ops->get_sset_count && ops->get_strings)
208 return ops->get_sset_count(dev, sset); 232 return ops->get_sset_count(dev, sset);
209 else 233 else
@@ -223,7 +247,17 @@ static void __ethtool_get_strings(struct net_device *dev,
223 sizeof(rss_hash_func_strings)); 247 sizeof(rss_hash_func_strings));
224 else if (stringset == ETH_SS_TUNABLES) 248 else if (stringset == ETH_SS_TUNABLES)
225 memcpy(data, tunable_strings, sizeof(tunable_strings)); 249 memcpy(data, tunable_strings, sizeof(tunable_strings));
226 else 250 else if (stringset == ETH_SS_PHY_STATS) {
251 struct phy_device *phydev = dev->phydev;
252
253 if (phydev) {
254 mutex_lock(&phydev->lock);
255 phydev->drv->get_strings(phydev, data);
256 mutex_unlock(&phydev->lock);
257 } else {
258 return;
259 }
260 } else
227 /* ops->get_strings is valid because checked earlier */ 261 /* ops->get_strings is valid because checked earlier */
228 ops->get_strings(dev, stringset, data); 262 ops->get_strings(dev, stringset, data);
229} 263}
@@ -1401,6 +1435,47 @@ static int ethtool_get_stats(struct net_device *dev, void __user *useraddr)
1401 return ret; 1435 return ret;
1402} 1436}
1403 1437
1438static int ethtool_get_phy_stats(struct net_device *dev, void __user *useraddr)
1439{
1440 struct ethtool_stats stats;
1441 struct phy_device *phydev = dev->phydev;
1442 u64 *data;
1443 int ret, n_stats;
1444
1445 if (!phydev)
1446 return -EOPNOTSUPP;
1447
1448 n_stats = phy_get_sset_count(phydev);
1449
1450 if (n_stats < 0)
1451 return n_stats;
1452 WARN_ON(n_stats == 0);
1453
1454 if (copy_from_user(&stats, useraddr, sizeof(stats)))
1455 return -EFAULT;
1456
1457 stats.n_stats = n_stats;
1458 data = kmalloc_array(n_stats, sizeof(u64), GFP_USER);
1459 if (!data)
1460 return -ENOMEM;
1461
1462 mutex_lock(&phydev->lock);
1463 phydev->drv->get_stats(phydev, &stats, data);
1464 mutex_unlock(&phydev->lock);
1465
1466 ret = -EFAULT;
1467 if (copy_to_user(useraddr, &stats, sizeof(stats)))
1468 goto out;
1469 useraddr += sizeof(stats);
1470 if (copy_to_user(useraddr, data, stats.n_stats * sizeof(u64)))
1471 goto out;
1472 ret = 0;
1473
1474 out:
1475 kfree(data);
1476 return ret;
1477}
1478
1404static int ethtool_get_perm_addr(struct net_device *dev, void __user *useraddr) 1479static int ethtool_get_perm_addr(struct net_device *dev, void __user *useraddr)
1405{ 1480{
1406 struct ethtool_perm_addr epaddr; 1481 struct ethtool_perm_addr epaddr;
@@ -1779,6 +1854,7 @@ int dev_ethtool(struct net *net, struct ifreq *ifr)
1779 case ETHTOOL_GSSET_INFO: 1854 case ETHTOOL_GSSET_INFO:
1780 case ETHTOOL_GSTRINGS: 1855 case ETHTOOL_GSTRINGS:
1781 case ETHTOOL_GSTATS: 1856 case ETHTOOL_GSTATS:
1857 case ETHTOOL_GPHYSTATS:
1782 case ETHTOOL_GTSO: 1858 case ETHTOOL_GTSO:
1783 case ETHTOOL_GPERMADDR: 1859 case ETHTOOL_GPERMADDR:
1784 case ETHTOOL_GUFO: 1860 case ETHTOOL_GUFO:
@@ -1991,6 +2067,9 @@ int dev_ethtool(struct net *net, struct ifreq *ifr)
1991 case ETHTOOL_STUNABLE: 2067 case ETHTOOL_STUNABLE:
1992 rc = ethtool_set_tunable(dev, useraddr); 2068 rc = ethtool_set_tunable(dev, useraddr);
1993 break; 2069 break;
2070 case ETHTOOL_GPHYSTATS:
2071 rc = ethtool_get_phy_stats(dev, useraddr);
2072 break;
1994 default: 2073 default:
1995 rc = -EOPNOTSUPP; 2074 rc = -EOPNOTSUPP;
1996 } 2075 }