aboutsummaryrefslogtreecommitdiffstats
path: root/net/core/ethtool.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/core/ethtool.c')
-rw-r--r--net/core/ethtool.c85
1 files changed, 82 insertions, 3 deletions
diff --git a/net/core/ethtool.c b/net/core/ethtool.c
index 29edf74846fc..daf04709dd3c 100644
--- a/net/core/ethtool.c
+++ b/net/core/ethtool.c
@@ -87,7 +87,7 @@ static const char netdev_features_strings[NETDEV_FEATURE_COUNT][ETH_GSTRING_LEN]
87 [NETIF_F_GSO_UDP_TUNNEL_BIT] = "tx-udp_tnl-segmentation", 87 [NETIF_F_GSO_UDP_TUNNEL_BIT] = "tx-udp_tnl-segmentation",
88 88
89 [NETIF_F_FCOE_CRC_BIT] = "tx-checksum-fcoe-crc", 89 [NETIF_F_FCOE_CRC_BIT] = "tx-checksum-fcoe-crc",
90 [NETIF_F_SCTP_CSUM_BIT] = "tx-checksum-sctp", 90 [NETIF_F_SCTP_CRC_BIT] = "tx-checksum-sctp",
91 [NETIF_F_FCOE_MTU_BIT] = "fcoe-mtu", 91 [NETIF_F_FCOE_MTU_BIT] = "fcoe-mtu",
92 [NETIF_F_NTUPLE_BIT] = "rx-ntuple-filter", 92 [NETIF_F_NTUPLE_BIT] = "rx-ntuple-filter",
93 [NETIF_F_RXHASH_BIT] = "rx-hashing", 93 [NETIF_F_RXHASH_BIT] = "rx-hashing",
@@ -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}
@@ -235,7 +269,7 @@ static netdev_features_t ethtool_get_feature_mask(u32 eth_cmd)
235 switch (eth_cmd) { 269 switch (eth_cmd) {
236 case ETHTOOL_GTXCSUM: 270 case ETHTOOL_GTXCSUM:
237 case ETHTOOL_STXCSUM: 271 case ETHTOOL_STXCSUM:
238 return NETIF_F_ALL_CSUM | NETIF_F_SCTP_CSUM; 272 return NETIF_F_CSUM_MASK | NETIF_F_SCTP_CRC;
239 case ETHTOOL_GRXCSUM: 273 case ETHTOOL_GRXCSUM:
240 case ETHTOOL_SRXCSUM: 274 case ETHTOOL_SRXCSUM:
241 return NETIF_F_RXCSUM; 275 return NETIF_F_RXCSUM;
@@ -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 }