Skip to content

Commit f3a4094

Browse files
lunndavem330
authored andcommitted
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>
1 parent a3748a9 commit f3a4094

File tree

3 files changed

+89
-1
lines changed

3 files changed

+89
-1
lines changed

include/linux/phy.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -589,6 +589,12 @@ struct phy_driver {
589589
int (*module_eeprom)(struct phy_device *dev,
590590
struct ethtool_eeprom *ee, u8 *data);
591591

592+
/* Get statistics from the phy using ethtool */
593+
int (*get_sset_count)(struct phy_device *dev);
594+
void (*get_strings)(struct phy_device *dev, u8 *data);
595+
void (*get_stats)(struct phy_device *dev,
596+
struct ethtool_stats *stats, u64 *data);
597+
592598
struct device_driver driver;
593599
};
594600
#define to_phy_driver(d) container_of(d, struct phy_driver, driver)

include/uapi/linux/ethtool.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -542,6 +542,7 @@ struct ethtool_pauseparam {
542542
* now deprecated
543543
* @ETH_SS_FEATURES: Device feature names
544544
* @ETH_SS_RSS_HASH_FUNCS: RSS hush function names
545+
* @ETH_SS_PHY_STATS: Statistic names, for use with %ETHTOOL_GPHYSTATS
545546
*/
546547
enum ethtool_stringset {
547548
ETH_SS_TEST = 0,
@@ -551,6 +552,7 @@ enum ethtool_stringset {
551552
ETH_SS_FEATURES,
552553
ETH_SS_RSS_HASH_FUNCS,
553554
ETH_SS_TUNABLES,
555+
ETH_SS_PHY_STATS,
554556
};
555557

556558
/**
@@ -1225,6 +1227,7 @@ enum ethtool_sfeatures_retval_bits {
12251227
#define ETHTOOL_SRSSH 0x00000047 /* Set RX flow hash configuration */
12261228
#define ETHTOOL_GTUNABLE 0x00000048 /* Get tunable configuration */
12271229
#define ETHTOOL_STUNABLE 0x00000049 /* Set tunable configuration */
1230+
#define ETHTOOL_GPHYSTATS 0x0000004a /* get PHY-specific statistics */
12281231

12291232
/* compatibility with older code */
12301233
#define SPARC_ETH_GSET ETHTOOL_GSET

net/core/ethtool.c

Lines changed: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,23 @@ static int ethtool_set_features(struct net_device *dev, void __user *useraddr)
191191
return ret;
192192
}
193193

194+
static 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+
194211
static int __ethtool_get_sset_count(struct net_device *dev, int sset)
195212
{
196213
const struct ethtool_ops *ops = dev->ethtool_ops;
@@ -204,6 +221,13 @@ static int __ethtool_get_sset_count(struct net_device *dev, int sset)
204221
if (sset == ETH_SS_TUNABLES)
205222
return ARRAY_SIZE(tunable_strings);
206223

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+
207231
if (ops->get_sset_count && ops->get_strings)
208232
return ops->get_sset_count(dev, sset);
209233
else
@@ -223,7 +247,17 @@ static void __ethtool_get_strings(struct net_device *dev,
223247
sizeof(rss_hash_func_strings));
224248
else if (stringset == ETH_SS_TUNABLES)
225249
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
227261
/* ops->get_strings is valid because checked earlier */
228262
ops->get_strings(dev, stringset, data);
229263
}
@@ -1401,6 +1435,47 @@ static int ethtool_get_stats(struct net_device *dev, void __user *useraddr)
14011435
return ret;
14021436
}
14031437

1438+
static 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+
14041479
static int ethtool_get_perm_addr(struct net_device *dev, void __user *useraddr)
14051480
{
14061481
struct ethtool_perm_addr epaddr;
@@ -1779,6 +1854,7 @@ int dev_ethtool(struct net *net, struct ifreq *ifr)
17791854
case ETHTOOL_GSSET_INFO:
17801855
case ETHTOOL_GSTRINGS:
17811856
case ETHTOOL_GSTATS:
1857+
case ETHTOOL_GPHYSTATS:
17821858
case ETHTOOL_GTSO:
17831859
case ETHTOOL_GPERMADDR:
17841860
case ETHTOOL_GUFO:
@@ -1991,6 +2067,9 @@ int dev_ethtool(struct net *net, struct ifreq *ifr)
19912067
case ETHTOOL_STUNABLE:
19922068
rc = ethtool_set_tunable(dev, useraddr);
19932069
break;
2070+
case ETHTOOL_GPHYSTATS:
2071+
rc = ethtool_get_phy_stats(dev, useraddr);
2072+
break;
19942073
default:
19952074
rc = -EOPNOTSUPP;
19962075
}

0 commit comments

Comments
 (0)