Skip to content

Commit

Permalink
virtio_net: 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
virtio_net device via ethtool. Having this functionality is very helpful
for simulating different environments and also enables the virtio_net
device to participate in operations where proper speed and duplex are
required (e.g. currently bonding lacp mode requires full duplex). Custom
speed and duplex are not allowed, the user-supplied settings are validated
before applying.

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

Based on a patch by Roopa Prabhu.

Signed-off-by: Nikolay Aleksandrov <nikolay@cumulusnetworks.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Nikolay Aleksandrov authored and David S. Miller committed Feb 7, 2016
1 parent 103a8ad commit 16032be
Showing 1 changed file with 60 additions and 0 deletions.
60 changes: 60 additions & 0 deletions drivers/net/virtio_net.c
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,10 @@ struct virtnet_info {
virtio_net_ctrl_ack ctrl_status;
u8 ctrl_promisc;
u8 ctrl_allmulti;

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

struct padded_vnet_hdr {
Expand Down Expand Up @@ -1376,13 +1380,67 @@ 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_cmd *cmd)
{
struct ethtool_cmd diff1 = *cmd;
struct ethtool_cmd diff2 = {};

/* advertising and cmd are usually set, ignore port because we set it */
ethtool_cmd_speed_set(&diff1, 0);
diff1.advertising = 0;
diff1.duplex = 0;
diff1.port = 0;
diff1.cmd = 0;

return !memcmp(&diff1, &diff2, sizeof(diff1));
}

static int virtnet_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{
struct virtnet_info *vi = netdev_priv(dev);
u32 speed;

speed = ethtool_cmd_speed(cmd);
/* don't allow custom speed and duplex */
if (!ethtool_validate_speed(speed) ||
!ethtool_validate_duplex(cmd->duplex) ||
!virtnet_validate_ethtool_cmd(cmd))
return -EINVAL;
vi->speed = speed;
vi->duplex = cmd->duplex;

return 0;
}

static int virtnet_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{
struct virtnet_info *vi = netdev_priv(dev);

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

return 0;
}

static void virtnet_init_settings(struct net_device *dev)
{
struct virtnet_info *vi = netdev_priv(dev);

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

static const struct ethtool_ops virtnet_ethtool_ops = {
.get_drvinfo = virtnet_get_drvinfo,
.get_link = ethtool_op_get_link,
.get_ringparam = virtnet_get_ringparam,
.set_channels = virtnet_set_channels,
.get_channels = virtnet_get_channels,
.get_ts_info = ethtool_op_get_ts_info,
.get_settings = virtnet_get_settings,
.set_settings = virtnet_set_settings,
};

#define MIN_MTU 68
Expand Down Expand Up @@ -1855,6 +1913,8 @@ static int virtnet_probe(struct virtio_device *vdev)
netif_set_real_num_tx_queues(dev, vi->curr_queue_pairs);
netif_set_real_num_rx_queues(dev, vi->curr_queue_pairs);

virtnet_init_settings(dev);

err = register_netdev(dev);
if (err) {
pr_debug("virtio_net: registering device failed\n");
Expand Down

0 comments on commit 16032be

Please sign in to comment.