net/ethtool: Introduce link_ksettings API for virtual network devices
authorCris Forno <cforno12@linux.vnet.ibm.com>
Fri, 28 Feb 2020 20:12:05 +0000 (14:12 -0600)
committerDavid S. Miller <davem@davemloft.net>
Sun, 1 Mar 2020 05:48:55 +0000 (21:48 -0800)
With the ethtool_virtdev_set_link_ksettings function in core/ethtool.c,
ibmveth, netvsc, and virtio now use the core's helper function.

Funtionality changes that pertain to ibmveth driver include:

  1. Changed the initial hardcoded link speed to 1GB.

  2. Added support for allowing a user to change the reported link
  speed via ethtool.

Functionality changes to the netvsc driver include:

  1. When netvsc_get_link_ksettings is called, it will defer to the VF
  device if it exists to pull accelerated networking values, otherwise
  pull default or user-defined values.

  2. Similarly, if netvsc_set_link_ksettings called and a VF device
  exists, the real values of speed and duplex are changed.

Signed-off-by: Cris Forno <cforno12@linux.vnet.ibm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/ibm/ibmveth.c
drivers/net/ethernet/ibm/ibmveth.h
drivers/net/hyperv/netvsc_drv.c
drivers/net/virtio_net.c

index 84121aab7ff1a3e05d3756d2b7968e7f8ff332ea..1fdbd7649f0fa7c91eb3b539da25d7d900a910bd 100644 (file)
@@ -712,29 +712,36 @@ static int ibmveth_close(struct net_device *netdev)
        return 0;
 }
 
-static int netdev_get_link_ksettings(struct net_device *dev,
-                                    struct ethtool_link_ksettings *cmd)
+static int ibmveth_set_link_ksettings(struct net_device *dev,
+                                     const struct ethtool_link_ksettings *cmd)
 {
-       u32 supported, advertising;
-
-       supported = (SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg |
-                               SUPPORTED_FIBRE);
-       advertising = (ADVERTISED_1000baseT_Full | ADVERTISED_Autoneg |
-                               ADVERTISED_FIBRE);
-       cmd->base.speed = SPEED_1000;
-       cmd->base.duplex = DUPLEX_FULL;
-       cmd->base.port = PORT_FIBRE;
-       cmd->base.phy_address = 0;
-       cmd->base.autoneg = AUTONEG_ENABLE;
-
-       ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported,
-                                               supported);
-       ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising,
-                                               advertising);
+       struct ibmveth_adapter *adapter = netdev_priv(dev);
+
+       return ethtool_virtdev_set_link_ksettings(dev, cmd,
+                                                 &adapter->speed,
+                                                 &adapter->duplex);
+}
+
+static int ibmveth_get_link_ksettings(struct net_device *dev,
+                                     struct ethtool_link_ksettings *cmd)
+{
+       struct ibmveth_adapter *adapter = netdev_priv(dev);
+
+       cmd->base.speed = adapter->speed;
+       cmd->base.duplex = adapter->duplex;
+       cmd->base.port = PORT_OTHER;
 
        return 0;
 }
 
+static void ibmveth_init_link_settings(struct net_device *dev)
+{
+       struct ibmveth_adapter *adapter = netdev_priv(dev);
+
+       adapter->speed = SPEED_1000;
+       adapter->duplex = DUPLEX_FULL;
+}
+
 static void netdev_get_drvinfo(struct net_device *dev,
                               struct ethtool_drvinfo *info)
 {
@@ -965,12 +972,13 @@ static void ibmveth_get_ethtool_stats(struct net_device *dev,
 }
 
 static const struct ethtool_ops netdev_ethtool_ops = {
-       .get_drvinfo            = netdev_get_drvinfo,
-       .get_link               = ethtool_op_get_link,
-       .get_strings            = ibmveth_get_strings,
-       .get_sset_count         = ibmveth_get_sset_count,
-       .get_ethtool_stats      = ibmveth_get_ethtool_stats,
-       .get_link_ksettings     = netdev_get_link_ksettings,
+       .get_drvinfo                     = netdev_get_drvinfo,
+       .get_link                        = ethtool_op_get_link,
+       .get_strings                     = ibmveth_get_strings,
+       .get_sset_count                  = ibmveth_get_sset_count,
+       .get_ethtool_stats               = ibmveth_get_ethtool_stats,
+       .get_link_ksettings              = ibmveth_get_link_ksettings,
+       .set_link_ksettings              = ibmveth_set_link_ksettings,
 };
 
 static int ibmveth_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
@@ -1674,6 +1682,7 @@ static int ibmveth_probe(struct vio_dev *dev, const struct vio_device_id *id)
        adapter->netdev = netdev;
        adapter->mcastFilterSize = be32_to_cpu(*mcastFilterSize_p);
        adapter->pool_config = 0;
+       ibmveth_init_link_settings(netdev);
 
        netif_napi_add(netdev, &adapter->napi, ibmveth_poll, 16);
 
index 4e9bf3421f4fa7b13aa17c55b7d8bb41b0f9a570..27dfff2001662c6efa49ec037481209e916effd5 100644 (file)
@@ -162,6 +162,9 @@ struct ibmveth_adapter {
     u64 tx_send_failed;
     u64 tx_large_packets;
     u64 rx_large_packets;
+    /* Ethtool settings */
+       u8 duplex;
+       u32 speed;
 };
 
 /*
index 245ce2374035b788e9982139474080901726e74b..d8e86bdbfba1e68d861d7c5bf9b91dd947cc0a65 100644 (file)
@@ -1140,23 +1140,6 @@ out:
        return ret;
 }
 
-static bool
-netvsc_validate_ethtool_ss_cmd(const struct ethtool_link_ksettings *cmd)
-{
-       struct ethtool_link_ksettings diff1 = *cmd;
-       struct ethtool_link_ksettings diff2 = {};
-
-       diff1.base.speed = 0;
-       diff1.base.duplex = 0;
-       /* advertising and cmd are usually set */
-       ethtool_link_ksettings_zero_link_mode(&diff1, advertising);
-       diff1.base.cmd = 0;
-       /* We set port to PORT_OTHER */
-       diff2.base.port = PORT_OTHER;
-
-       return !memcmp(&diff1, &diff2, sizeof(diff1));
-}
-
 static void netvsc_init_settings(struct net_device *dev)
 {
        struct net_device_context *ndc = netdev_priv(dev);
@@ -1173,6 +1156,12 @@ static int netvsc_get_link_ksettings(struct net_device *dev,
                                     struct ethtool_link_ksettings *cmd)
 {
        struct net_device_context *ndc = netdev_priv(dev);
+       struct net_device *vf_netdev;
+
+       vf_netdev = rtnl_dereference(ndc->vf_netdev);
+
+       if (vf_netdev)
+               return __ethtool_get_link_ksettings(vf_netdev, cmd);
 
        cmd->base.speed = ndc->speed;
        cmd->base.duplex = ndc->duplex;
@@ -1185,18 +1174,18 @@ static int netvsc_set_link_ksettings(struct net_device *dev,
                                     const struct ethtool_link_ksettings *cmd)
 {
        struct net_device_context *ndc = netdev_priv(dev);
-       u32 speed;
+       struct net_device *vf_netdev = rtnl_dereference(ndc->vf_netdev);
 
-       speed = cmd->base.speed;
-       if (!ethtool_validate_speed(speed) ||
-           !ethtool_validate_duplex(cmd->base.duplex) ||
-           !netvsc_validate_ethtool_ss_cmd(cmd))
-               return -EINVAL;
+       if (vf_netdev) {
+               if (!vf_netdev->ethtool_ops->set_link_ksettings)
+                       return -EOPNOTSUPP;
 
-       ndc->speed = speed;
-       ndc->duplex = cmd->base.duplex;
+               return vf_netdev->ethtool_ops->set_link_ksettings(vf_netdev,
+                                                                 cmd);
+       }
 
-       return 0;
+       return ethtool_virtdev_set_link_ksettings(dev, cmd,
+                                                 &ndc->speed, &ndc->duplex);
 }
 
 static int netvsc_change_mtu(struct net_device *ndev, int mtu)
index 12d115ef5e74e238d26eb4308ddbd252d21950a0..84c0d9581f93d5a1bf64369e0c63fb29fe2bf204 100644 (file)
@@ -2178,48 +2178,13 @@ static void virtnet_get_channels(struct net_device *dev,
        channels->other_count = 0;
 }
 
-/* Check if the user is trying to change anything besides speed/duplex */
-static bool
-virtnet_validate_ethtool_cmd(const struct ethtool_link_ksettings *cmd)
-{
-       struct ethtool_link_ksettings diff1 = *cmd;
-       struct ethtool_link_ksettings diff2 = {};
-
-       /* cmd is always set so we need to clear it, validate the port type
-        * and also without autonegotiation we can ignore advertising
-        */
-       diff1.base.speed = 0;
-       diff2.base.port = PORT_OTHER;
-       ethtool_link_ksettings_zero_link_mode(&diff1, advertising);
-       diff1.base.duplex = 0;
-       diff1.base.cmd = 0;
-       diff1.base.link_mode_masks_nwords = 0;
-
-       return !memcmp(&diff1.base, &diff2.base, sizeof(diff1.base)) &&
-               bitmap_empty(diff1.link_modes.supported,
-                            __ETHTOOL_LINK_MODE_MASK_NBITS) &&
-               bitmap_empty(diff1.link_modes.advertising,
-                            __ETHTOOL_LINK_MODE_MASK_NBITS) &&
-               bitmap_empty(diff1.link_modes.lp_advertising,
-                            __ETHTOOL_LINK_MODE_MASK_NBITS);
-}
-
 static int virtnet_set_link_ksettings(struct net_device *dev,
                                      const struct ethtool_link_ksettings *cmd)
 {
        struct virtnet_info *vi = netdev_priv(dev);
-       u32 speed;
-
-       speed = cmd->base.speed;
-       /* don't allow custom speed and duplex */
-       if (!ethtool_validate_speed(speed) ||
-           !ethtool_validate_duplex(cmd->base.duplex) ||
-           !virtnet_validate_ethtool_cmd(cmd))
-               return -EINVAL;
-       vi->speed = speed;
-       vi->duplex = cmd->base.duplex;
 
-       return 0;
+       return ethtool_virtdev_set_link_ksettings(dev, cmd,
+                                                 &vi->speed, &vi->duplex);
 }
 
 static int virtnet_get_link_ksettings(struct net_device *dev,