Skip to content

Commit

Permalink
hv_netvsc: add ethtool support for set and get of settings
Browse files Browse the repository at this point in the history
This patch allows the user to set and retrieve speed and duplex of the
hv_netvsc device via ethtool.

Example:
$ ethtool eth0
Settings for eth0:
...
    Speed: Unknown!
    Duplex: Unknown! (255)
...
$ ethtool -s eth0 speed 1000 duplex full
$ ethtool eth0
Settings for eth0:
...
    Speed: 1000Mb/s
    Duplex: Full
...

This is based on patches by Roopa Prabhu and Nikolay Aleksandrov.

Signed-off-by: Simon Xiao <sixiao@microsoft.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
sixiao@microsoft.com authored and David S. Miller committed Feb 29, 2016
1 parent c0affa1 commit 49eb938
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 0 deletions.
4 changes: 4 additions & 0 deletions drivers/net/hyperv/hyperv_net.h
Original file line number Diff line number Diff line change
Expand Up @@ -658,6 +658,10 @@ struct net_device_context {

struct netvsc_stats __percpu *tx_stats;
struct netvsc_stats __percpu *rx_stats;

/* Ethtool settings */
u8 duplex;
u32 speed;
};

/* Per netvsc device */
Expand Down
56 changes: 56 additions & 0 deletions drivers/net/hyperv/netvsc_drv.c
Original file line number Diff line number Diff line change
Expand Up @@ -799,6 +799,58 @@ static int netvsc_set_channels(struct net_device *net,
goto do_set;
}

static bool netvsc_validate_ethtool_ss_cmd(const struct ethtool_cmd *cmd)
{
struct ethtool_cmd diff1 = *cmd;
struct ethtool_cmd diff2 = {};

ethtool_cmd_speed_set(&diff1, 0);
diff1.duplex = 0;
/* advertising and cmd are usually set */
diff1.advertising = 0;
diff1.cmd = 0;
/* We set port to PORT_OTHER */
diff2.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);

ndc->speed = SPEED_UNKNOWN;
ndc->duplex = DUPLEX_UNKNOWN;
}

static int netvsc_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{
struct net_device_context *ndc = netdev_priv(dev);

ethtool_cmd_speed_set(cmd, ndc->speed);
cmd->duplex = ndc->duplex;
cmd->port = PORT_OTHER;

return 0;
}

static int netvsc_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{
struct net_device_context *ndc = netdev_priv(dev);
u32 speed;

speed = ethtool_cmd_speed(cmd);
if (!ethtool_validate_speed(speed) ||
!ethtool_validate_duplex(cmd->duplex) ||
!netvsc_validate_ethtool_ss_cmd(cmd))
return -EINVAL;

ndc->speed = speed;
ndc->duplex = cmd->duplex;

return 0;
}

static int netvsc_change_mtu(struct net_device *ndev, int mtu)
{
struct net_device_context *ndevctx = netdev_priv(ndev);
Expand Down Expand Up @@ -923,6 +975,8 @@ static const struct ethtool_ops ethtool_ops = {
.get_channels = netvsc_get_channels,
.set_channels = netvsc_set_channels,
.get_ts_info = ethtool_op_get_ts_info,
.get_settings = netvsc_get_settings,
.set_settings = netvsc_set_settings,
};

static const struct net_device_ops device_ops = {
Expand Down Expand Up @@ -1115,6 +1169,8 @@ static int netvsc_probe(struct hv_device *dev,
netif_set_real_num_tx_queues(net, nvdev->num_chn);
netif_set_real_num_rx_queues(net, nvdev->num_chn);

netvsc_init_settings(net);

ret = register_netdev(net);
if (ret != 0) {
pr_err("Unable to register netdev.\n");
Expand Down

0 comments on commit 49eb938

Please sign in to comment.