[lvc-project] [PATCH v1 3/3] net/ethtool/ioctl: correct & simplify ethtool_get_phy_stats if checks

Daniil Tatianin d-tatianin at yandex-team.ru
Mon Nov 28 11:16:09 MSK 2022


On 11/26/22 2:25 AM, Andrew Lunn wrote:
>> I did experiment with that for a bit at the start, couldn't come up with a
>> clear solution right away so went with this instead.
> 
> How about this? The patch is a lot less readable than the end code.
> Compile tested only.
> 
> 	Andrew
> 
> --- a/net/ethtool/ioctl.c
> +++ b/net/ethtool/ioctl.c
> @@ -2077,58 +2077,88 @@ static int ethtool_get_stats(struct net_device *dev, void __user *useraddr)
>          return ret;
>   }
>   
> -static int ethtool_get_phy_stats(struct net_device *dev, void __user *useraddr)
> +static int ethtool_get_phy_stats_phydev(struct phy_device *phydev,
> +                                       struct ethtool_stats *stats,
> +                                       u64 **data)
>   {
>          const struct ethtool_phy_ops *phy_ops = ethtool_phy_ops;
> +       int n_stats;
> +
> +       if (!phy_ops || !phy_ops->get_sset_count || !phy_ops->get_stats)
> +               return -EOPNOTSUPP;
> +
> +       n_stats = phy_ops->get_sset_count(phydev);
> +       if (n_stats < 0)
> +               return n_stats;
> +       if (n_stats > S32_MAX / sizeof(u64))
> +               return -ENOMEM;
> +       if (WARN_ON_ONCE(!n_stats))
> +               return -EOPNOTSUPP;
> +
> +       stats->n_stats = n_stats;
> +
> +       *data = vzalloc(array_size(n_stats, sizeof(u64)));
> +       if (!*data)
> +               return -ENOMEM;
> +
> +       return phy_ops->get_stats(phydev, stats, *data);
> +}
> +
> +static int ethtool_get_phy_stats_ethtool(struct net_device *dev,
> +                                        struct ethtool_stats *stats,
> +                                        u64 **data)
> +{
>          const struct ethtool_ops *ops = dev->ethtool_ops;
> -       struct phy_device *phydev = dev->phydev;
> -       struct ethtool_stats stats;
> -       u64 *data;
> -       int ret, n_stats;
> +       int n_stats;
>   
> -       if (!phydev && (!ops->get_ethtool_phy_stats || !ops->get_sset_count))
> +       if (!ops || !ops->get_sset_count || ops->get_ethtool_phy_stats)
>                  return -EOPNOTSUPP;
>   
> -       if (phydev && !ops->get_ethtool_phy_stats &&
> -           phy_ops && phy_ops->get_sset_count)
> -               n_stats = phy_ops->get_sset_count(phydev);
> -       else
> -               n_stats = ops->get_sset_count(dev, ETH_SS_PHY_STATS);
> +       n_stats = ops->get_sset_count(dev, ETH_SS_PHY_STATS);
>          if (n_stats < 0)
>                  return n_stats;
>          if (n_stats > S32_MAX / sizeof(u64))
>                  return -ENOMEM;
> -       WARN_ON_ONCE(!n_stats);
> +       if (WARN_ON_ONCE(!n_stats))
> +               return -EOPNOTSUPP;
> +
> +       stats->n_stats = n_stats;
> +
> +       *data = vzalloc(array_size(n_stats, sizeof(u64)));
> +       if (!*data)
> +               return -ENOMEM;
> +
> +       ops->get_ethtool_phy_stats(dev, stats, *data);
> +
> +       return 0;
> +}
> +
> +static int ethtool_get_phy_stats(struct net_device *dev, void __user *useraddr)
> +{
> +       struct phy_device *phydev = dev->phydev;
> +       struct ethtool_stats stats;
> +       u64 *data;
> +       int ret;
>   
>          if (copy_from_user(&stats, useraddr, sizeof(stats)))
>                  return -EFAULT;
>   
> -       stats.n_stats = n_stats;
> -
> -       if (n_stats) {
> -               data = vzalloc(array_size(n_stats, sizeof(u64)));
> -               if (!data)
> -                       return -ENOMEM;
> +       if (phydev)
> +               ret = ethtool_get_phy_stats_phydev(phydev, &stats, &data);
> +       else
> +               ret = ethtool_get_phy_stats_ethtool(dev, &stats, &data);
> +       if (ret)
> +               return ret;
>   
> -               if (phydev && !ops->get_ethtool_phy_stats &&
> -                   phy_ops && phy_ops->get_stats) {
> -                       ret = phy_ops->get_stats(phydev, &stats, data);
> -                       if (ret < 0)
> -                               goto out;
> -               } else {
> -                       ops->get_ethtool_phy_stats(dev, &stats, data);
> -               }
> -       } else {
> -               data = NULL;
> +       if (copy_to_user(useraddr, &stats, sizeof(stats))) {
> +               ret = -EFAULT;
> +               goto out;
>          }
>   
> -       ret = -EFAULT;
> -       if (copy_to_user(useraddr, &stats, sizeof(stats)))
> -               goto out;
>          useraddr += sizeof(stats);
> -       if (n_stats && copy_to_user(useraddr, data, array_size(n_stats, sizeof(u64))))
> -               goto out;
> -       ret = 0;
> +       if (copy_to_user(useraddr, data,
> +                        array_size(stats.n_stats, sizeof(u64))))
> +               ret = -EFAULT;
>   
>    out:
>          vfree(data);
I guess this does indeed look cleaner although I'm not sure about the 
duplicated n_stats validation code. Maybe this could be moved to a 
separate helper as well or do you think it's okay to duplicate it in 
this case?

Thank you



More information about the lvc-project mailing list