Commit de301810 authored by David S. Miller's avatar David S. Miller

Merge branch 'net-ethtool-Introduce-link_ksettings-API-for-virtual-network-devices'

Cris Forno says:

====================
net/ethtool: Introduce link_ksettings API for virtual network devices

This series provides an API for drivers of virtual network devices that
allows users to alter initial device speed and duplex settings to reflect
the actual capabilities of underlying hardware. The changes made include
a helper function ethtool_virtdev_set_link_ksettings, which is used to
retrieve alterable link settings. In addition, there is a new ethtool
function defined to validate those settings. These changes resolve code
duplication for existing virtual network drivers that have already
implemented this behavior.  In the case of the ibmveth driver, this API is
used to provide this capability for the first time.

---
v7:  - removed ethtool_validate_cmd function pointer parameter from
      ethtool_virtdev_set_link_ksettings since none of the virtual drivers
      pass in a custom validate function as suggested by Michal Kubecek.

v6:  - removed netvsc_validate_ethtool_ss_cmd(). netvsc_drv now uses
     ethtool_virtdev_validate_cmd() instead as suggested by Michal Kubecek
     and approved by Haiyang Zhang.

     - matched handler argument name of ethtool_virtdev_set_link_ksettings
     in declaration and definition as suggested by Michal Kubecek.

     - shortened validate variable assignment in
     ethtool_virtdev_set_link_ksettings as suggested by Michal Kubecek.

v5:  - virtdev_validate_link_ksettings is taken out of the ethtool global
     structure and is instead added as an argument to
     ethtool_virtdev_set_link_ksettings as suggested by Jakub Kicinski.

v4:  - Cleaned up return statement in ethtool_virtdev_validate_cmd based
     off of Michal Kubecek's and Thomas Falcon's suggestion.

     - If the netvsc driver is using the VF device in order to get
     accelerated networking, the real speed and duplex is reported by using
     the VF device as suggested by Stephen Hemminger.

     - The speed and duplex variables are now passed by value rather than
     passed by pointer as suggested by Willem de Bruijin and Michal
     Kubecek.

     - Removed ethtool_virtdev_get_link_ksettings since it was too simple
     to warrant a helper function.

v3:  - Factored out duplicated code to core/ethtool to provide API to
     virtual drivers

v2:  - Updated default driver speed/duplex settings to avoid breaking
     existing setups
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 68e2c376 9aedc6e2
...@@ -712,29 +712,36 @@ static int ibmveth_close(struct net_device *netdev) ...@@ -712,29 +712,36 @@ static int ibmveth_close(struct net_device *netdev)
return 0; return 0;
} }
static int netdev_get_link_ksettings(struct net_device *dev, static int ibmveth_set_link_ksettings(struct net_device *dev,
struct ethtool_link_ksettings *cmd) const struct ethtool_link_ksettings *cmd)
{ {
u32 supported, advertising; struct ibmveth_adapter *adapter = netdev_priv(dev);
supported = (SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg | return ethtool_virtdev_set_link_ksettings(dev, cmd,
SUPPORTED_FIBRE); &adapter->speed,
advertising = (ADVERTISED_1000baseT_Full | ADVERTISED_Autoneg | &adapter->duplex);
ADVERTISED_FIBRE); }
cmd->base.speed = SPEED_1000;
cmd->base.duplex = DUPLEX_FULL; static int ibmveth_get_link_ksettings(struct net_device *dev,
cmd->base.port = PORT_FIBRE; struct ethtool_link_ksettings *cmd)
cmd->base.phy_address = 0; {
cmd->base.autoneg = AUTONEG_ENABLE; struct ibmveth_adapter *adapter = netdev_priv(dev);
ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported, cmd->base.speed = adapter->speed;
supported); cmd->base.duplex = adapter->duplex;
ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising, cmd->base.port = PORT_OTHER;
advertising);
return 0; 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, static void netdev_get_drvinfo(struct net_device *dev,
struct ethtool_drvinfo *info) struct ethtool_drvinfo *info)
{ {
...@@ -965,12 +972,13 @@ static void ibmveth_get_ethtool_stats(struct net_device *dev, ...@@ -965,12 +972,13 @@ static void ibmveth_get_ethtool_stats(struct net_device *dev,
} }
static const struct ethtool_ops netdev_ethtool_ops = { static const struct ethtool_ops netdev_ethtool_ops = {
.get_drvinfo = netdev_get_drvinfo, .get_drvinfo = netdev_get_drvinfo,
.get_link = ethtool_op_get_link, .get_link = ethtool_op_get_link,
.get_strings = ibmveth_get_strings, .get_strings = ibmveth_get_strings,
.get_sset_count = ibmveth_get_sset_count, .get_sset_count = ibmveth_get_sset_count,
.get_ethtool_stats = ibmveth_get_ethtool_stats, .get_ethtool_stats = ibmveth_get_ethtool_stats,
.get_link_ksettings = netdev_get_link_ksettings, .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) 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) ...@@ -1674,6 +1682,7 @@ static int ibmveth_probe(struct vio_dev *dev, const struct vio_device_id *id)
adapter->netdev = netdev; adapter->netdev = netdev;
adapter->mcastFilterSize = be32_to_cpu(*mcastFilterSize_p); adapter->mcastFilterSize = be32_to_cpu(*mcastFilterSize_p);
adapter->pool_config = 0; adapter->pool_config = 0;
ibmveth_init_link_settings(netdev);
netif_napi_add(netdev, &adapter->napi, ibmveth_poll, 16); netif_napi_add(netdev, &adapter->napi, ibmveth_poll, 16);
......
...@@ -162,6 +162,9 @@ struct ibmveth_adapter { ...@@ -162,6 +162,9 @@ struct ibmveth_adapter {
u64 tx_send_failed; u64 tx_send_failed;
u64 tx_large_packets; u64 tx_large_packets;
u64 rx_large_packets; u64 rx_large_packets;
/* Ethtool settings */
u8 duplex;
u32 speed;
}; };
/* /*
......
...@@ -1140,23 +1140,6 @@ static int netvsc_set_channels(struct net_device *net, ...@@ -1140,23 +1140,6 @@ static int netvsc_set_channels(struct net_device *net,
return ret; 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) static void netvsc_init_settings(struct net_device *dev)
{ {
struct net_device_context *ndc = netdev_priv(dev); struct net_device_context *ndc = netdev_priv(dev);
...@@ -1173,6 +1156,12 @@ static int netvsc_get_link_ksettings(struct net_device *dev, ...@@ -1173,6 +1156,12 @@ static int netvsc_get_link_ksettings(struct net_device *dev,
struct ethtool_link_ksettings *cmd) struct ethtool_link_ksettings *cmd)
{ {
struct net_device_context *ndc = netdev_priv(dev); 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.speed = ndc->speed;
cmd->base.duplex = ndc->duplex; cmd->base.duplex = ndc->duplex;
...@@ -1185,18 +1174,18 @@ static int netvsc_set_link_ksettings(struct net_device *dev, ...@@ -1185,18 +1174,18 @@ static int netvsc_set_link_ksettings(struct net_device *dev,
const struct ethtool_link_ksettings *cmd) const struct ethtool_link_ksettings *cmd)
{ {
struct net_device_context *ndc = netdev_priv(dev); 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 (vf_netdev) {
if (!ethtool_validate_speed(speed) || if (!vf_netdev->ethtool_ops->set_link_ksettings)
!ethtool_validate_duplex(cmd->base.duplex) || return -EOPNOTSUPP;
!netvsc_validate_ethtool_ss_cmd(cmd))
return -EINVAL;
ndc->speed = speed; return vf_netdev->ethtool_ops->set_link_ksettings(vf_netdev,
ndc->duplex = cmd->base.duplex; 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) static int netvsc_change_mtu(struct net_device *ndev, int mtu)
......
...@@ -2178,48 +2178,13 @@ static void virtnet_get_channels(struct net_device *dev, ...@@ -2178,48 +2178,13 @@ static void virtnet_get_channels(struct net_device *dev,
channels->other_count = 0; 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, static int virtnet_set_link_ksettings(struct net_device *dev,
const struct ethtool_link_ksettings *cmd) const struct ethtool_link_ksettings *cmd)
{ {
struct virtnet_info *vi = netdev_priv(dev); 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, static int virtnet_get_link_ksettings(struct net_device *dev,
......
...@@ -420,4 +420,10 @@ struct ethtool_rx_flow_rule * ...@@ -420,4 +420,10 @@ struct ethtool_rx_flow_rule *
ethtool_rx_flow_rule_create(const struct ethtool_rx_flow_spec_input *input); ethtool_rx_flow_rule_create(const struct ethtool_rx_flow_spec_input *input);
void ethtool_rx_flow_rule_destroy(struct ethtool_rx_flow_rule *rule); void ethtool_rx_flow_rule_destroy(struct ethtool_rx_flow_rule *rule);
bool ethtool_virtdev_validate_cmd(const struct ethtool_link_ksettings *cmd);
int ethtool_virtdev_set_link_ksettings(struct net_device *dev,
const struct ethtool_link_ksettings *cmd,
u32 *dev_speed, u8 *dev_duplex);
#endif /* _LINUX_ETHTOOL_H */ #endif /* _LINUX_ETHTOOL_H */
...@@ -459,6 +459,24 @@ static int load_link_ksettings_from_user(struct ethtool_link_ksettings *to, ...@@ -459,6 +459,24 @@ static int load_link_ksettings_from_user(struct ethtool_link_ksettings *to,
return 0; return 0;
} }
/* Check if the user is trying to change anything besides speed/duplex */
bool ethtool_virtdev_validate_cmd(const struct ethtool_link_ksettings *cmd)
{
struct ethtool_link_settings base2 = {};
base2.speed = cmd->base.speed;
base2.port = PORT_OTHER;
base2.duplex = cmd->base.duplex;
base2.cmd = cmd->base.cmd;
base2.link_mode_masks_nwords = cmd->base.link_mode_masks_nwords;
return !memcmp(&base2, &cmd->base, sizeof(base2)) &&
bitmap_empty(cmd->link_modes.supported,
__ETHTOOL_LINK_MODE_MASK_NBITS) &&
bitmap_empty(cmd->link_modes.lp_advertising,
__ETHTOOL_LINK_MODE_MASK_NBITS);
}
/* convert a kernel internal ethtool_link_ksettings to /* convert a kernel internal ethtool_link_ksettings to
* ethtool_link_usettings in user space. return 0 on success, errno on * ethtool_link_usettings in user space. return 0 on success, errno on
* error. * error.
...@@ -581,6 +599,27 @@ static int ethtool_set_link_ksettings(struct net_device *dev, ...@@ -581,6 +599,27 @@ static int ethtool_set_link_ksettings(struct net_device *dev,
return err; return err;
} }
int ethtool_virtdev_set_link_ksettings(struct net_device *dev,
const struct ethtool_link_ksettings *cmd,
u32 *dev_speed, u8 *dev_duplex)
{
u32 speed;
u8 duplex;
speed = cmd->base.speed;
duplex = cmd->base.duplex;
/* don't allow custom speed and duplex */
if (!ethtool_validate_speed(speed) ||
!ethtool_validate_duplex(duplex) ||
!ethtool_virtdev_validate_cmd(cmd))
return -EINVAL;
*dev_speed = speed;
*dev_duplex = duplex;
return 0;
}
EXPORT_SYMBOL(ethtool_virtdev_set_link_ksettings);
/* Query device for its ethtool_cmd settings. /* Query device for its ethtool_cmd settings.
* *
* Backward compatibility note: for compatibility with legacy ethtool, this is * Backward compatibility note: for compatibility with legacy ethtool, this is
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment