@@ -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+
194211static 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+
14041479static 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