diff options
Diffstat (limited to 'drivers/net/virtio_net.c')
| -rw-r--r-- | drivers/net/virtio_net.c | 64 | 
1 files changed, 63 insertions, 1 deletions
| diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index 767ab11a6e9f..49d84e540343 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -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 { @@ -256,7 +260,7 @@ static struct sk_buff *page_to_skb(struct virtnet_info *vi,  	p = page_address(page) + offset;  	/* copy small packet so we can reuse these pages for small data */ -	skb = netdev_alloc_skb_ip_align(vi->dev, GOOD_COPY_LEN); +	skb = napi_alloc_skb(&rq->napi, GOOD_COPY_LEN);  	if (unlikely(!skb))  		return NULL; @@ -1376,6 +1380,60 @@ 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 = {}; + +	/* cmd is always set so we need to clear it, validate the port type +	 * and also without autonegotiation we can ignore advertising +	 */ +	ethtool_cmd_speed_set(&diff1, 0); +	diff2.port = PORT_OTHER; +	diff1.advertising = 0; +	diff1.duplex = 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, @@ -1383,6 +1441,8 @@ static const struct ethtool_ops virtnet_ethtool_ops = {  	.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 @@ -1855,6 +1915,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"); | 
