Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6c80f3fc authored by Simon Xiao's avatar Simon Xiao Committed by David S. Miller
Browse files

netvsc: report per-channel stats in ethtool statistics



Report packets and bytes transferred through a vmbus channel via ethtool.
This supersedes need for per-cpu statistics.

Example:
$ ethtool -S eth0
NIC statistics:
...
     tx_queue_0_packets: 3523179
     tx_queue_0_bytes: 505370920
     rx_queue_0_packets: 41430490
     rx_queue_0_bytes: 62714661254
     tx_queue_1_packets: 0
     tx_queue_1_bytes: 0
     rx_queue_1_packets: 0
     rx_queue_1_bytes: 0
...

Reviewed-by: default avatarLong Li <longli@microsoft.com>
Reviewed-by: default avatarK. Y. Srinivasan <kys@microsoft.com>
Reviewed-by: default avatarHaiyang Zhang <haiyangz@microsoft.com>
Signed-off-by: default avatarSimon Xiao <sixiao@microsoft.com>
Signed-off-by: default avatarStephen Hemminger <sthemmin@microsoft.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 793e3955
Loading
Loading
Loading
Loading
+3 −2
Original line number Original line Diff line number Diff line
@@ -699,8 +699,6 @@ struct net_device_context {
	u32 msg_enable; /* debug level */
	u32 msg_enable; /* debug level */


	u32 tx_checksum_mask;
	u32 tx_checksum_mask;
	struct netvsc_stats __percpu *tx_stats;
	struct netvsc_stats __percpu *rx_stats;


	/* Ethtool settings */
	/* Ethtool settings */
	u8 duplex;
	u8 duplex;
@@ -725,6 +723,9 @@ struct netvsc_channel {
	struct multi_send_data msd;
	struct multi_send_data msd;
	struct multi_recv_comp mrc;
	struct multi_recv_comp mrc;
	atomic_t queue_sends;
	atomic_t queue_sends;

	struct netvsc_stats tx_stats;
	struct netvsc_stats rx_stats;
};
};


/* Per netvsc device */
/* Per netvsc device */
+1 −1
Original line number Original line Diff line number Diff line
@@ -621,7 +621,7 @@ static void netvsc_send_tx_complete(struct netvsc_device *net_device,
		q_idx = packet->q_idx;
		q_idx = packet->q_idx;
		channel = incoming_channel;
		channel = incoming_channel;


		tx_stats = this_cpu_ptr(net_device_ctx->tx_stats);
		tx_stats = &net_device->chan_table[q_idx].tx_stats;


		u64_stats_update_begin(&tx_stats->syncp);
		u64_stats_update_begin(&tx_stats->syncp);
		tx_stats->packets += packet->total_packets;
		tx_stats->packets += packet->total_packets;
+89 −54
Original line number Original line Diff line number Diff line
@@ -641,9 +641,12 @@ int netvsc_recv_callback(struct net_device *net,
			 const struct ndis_pkt_8021q_info *vlan)
			 const struct ndis_pkt_8021q_info *vlan)
{
{
	struct net_device_context *net_device_ctx = netdev_priv(net);
	struct net_device_context *net_device_ctx = netdev_priv(net);
	struct netvsc_device *net_device = net_device_ctx->nvdev;
	struct net_device *vf_netdev;
	struct net_device *vf_netdev;
	struct sk_buff *skb;
	struct sk_buff *skb;
	struct netvsc_stats *rx_stats;
	struct netvsc_stats *rx_stats;
	u16 q_idx = channel->offermsg.offer.sub_channel_index;



	if (net->reg_state != NETREG_REGISTERED)
	if (net->reg_state != NETREG_REGISTERED)
		return NVSP_STAT_FAIL;
		return NVSP_STAT_FAIL;
@@ -669,15 +672,14 @@ int netvsc_recv_callback(struct net_device *net,
	}
	}


	if (net != vf_netdev)
	if (net != vf_netdev)
		skb_record_rx_queue(skb,
		skb_record_rx_queue(skb, q_idx);
				    channel->offermsg.offer.sub_channel_index);


	/*
	/*
	 * Even if injecting the packet, record the statistics
	 * Even if injecting the packet, record the statistics
	 * on the synthetic device because modifying the VF device
	 * on the synthetic device because modifying the VF device
	 * statistics will not work correctly.
	 * statistics will not work correctly.
	 */
	 */
	rx_stats = this_cpu_ptr(net_device_ctx->rx_stats);
	rx_stats = &net_device->chan_table[q_idx].rx_stats;
	u64_stats_update_begin(&rx_stats->syncp);
	u64_stats_update_begin(&rx_stats->syncp);
	rx_stats->packets++;
	rx_stats->packets++;
	rx_stats->bytes += len;
	rx_stats->bytes += len;
@@ -882,34 +884,39 @@ static void netvsc_get_stats64(struct net_device *net,
			       struct rtnl_link_stats64 *t)
			       struct rtnl_link_stats64 *t)
{
{
	struct net_device_context *ndev_ctx = netdev_priv(net);
	struct net_device_context *ndev_ctx = netdev_priv(net);
	int cpu;
	struct netvsc_device *nvdev = ndev_ctx->nvdev;

	int i;
	for_each_possible_cpu(cpu) {

		struct netvsc_stats *tx_stats = per_cpu_ptr(ndev_ctx->tx_stats,
	if (!nvdev)
							    cpu);
		return;
		struct netvsc_stats *rx_stats = per_cpu_ptr(ndev_ctx->rx_stats,

							    cpu);
	for (i = 0; i < nvdev->num_chn; i++) {
		u64 tx_packets, tx_bytes, rx_packets, rx_bytes, rx_multicast;
		const struct netvsc_channel *nvchan = &nvdev->chan_table[i];
		const struct netvsc_stats *stats;
		u64 packets, bytes, multicast;
		unsigned int start;
		unsigned int start;


		stats = &nvchan->tx_stats;
		do {
		do {
			start = u64_stats_fetch_begin_irq(&tx_stats->syncp);
			start = u64_stats_fetch_begin_irq(&stats->syncp);
			tx_packets = tx_stats->packets;
			packets = stats->packets;
			tx_bytes = tx_stats->bytes;
			bytes = stats->bytes;
		} while (u64_stats_fetch_retry_irq(&tx_stats->syncp, start));
		} while (u64_stats_fetch_retry_irq(&stats->syncp, start));

		t->tx_bytes	+= bytes;
		t->tx_packets	+= packets;


		stats = &nvchan->rx_stats;
		do {
		do {
			start = u64_stats_fetch_begin_irq(&rx_stats->syncp);
			start = u64_stats_fetch_begin_irq(&stats->syncp);
			rx_packets = rx_stats->packets;
			packets = stats->packets;
			rx_bytes = rx_stats->bytes;
			bytes = stats->bytes;
			rx_multicast = rx_stats->multicast + rx_stats->broadcast;
			multicast = stats->multicast + stats->broadcast;
		} while (u64_stats_fetch_retry_irq(&rx_stats->syncp, start));
		} while (u64_stats_fetch_retry_irq(&stats->syncp, start));


		t->tx_bytes	+= tx_bytes;
		t->rx_bytes	+= bytes;
		t->tx_packets	+= tx_packets;
		t->rx_packets	+= packets;
		t->rx_bytes	+= rx_bytes;
		t->multicast	+= multicast;
		t->rx_packets	+= rx_packets;
		t->multicast	+= rx_multicast;
	}
	}


	t->tx_dropped	= net->stats.tx_dropped;
	t->tx_dropped	= net->stats.tx_dropped;
@@ -954,11 +961,19 @@ static const struct {
	{ "tx_busy",	  offsetof(struct netvsc_ethtool_stats, tx_busy) },
	{ "tx_busy",	  offsetof(struct netvsc_ethtool_stats, tx_busy) },
};
};


#define NETVSC_GLOBAL_STATS_LEN	ARRAY_SIZE(netvsc_stats)

/* 4 statistics per queue (rx/tx packets/bytes) */
#define NETVSC_QUEUE_STATS_LEN(dev) ((dev)->num_chn * 4)

static int netvsc_get_sset_count(struct net_device *dev, int string_set)
static int netvsc_get_sset_count(struct net_device *dev, int string_set)
{
{
	struct net_device_context *ndc = netdev_priv(dev);
	struct netvsc_device *nvdev = ndc->nvdev;

	switch (string_set) {
	switch (string_set) {
	case ETH_SS_STATS:
	case ETH_SS_STATS:
		return ARRAY_SIZE(netvsc_stats);
		return NETVSC_GLOBAL_STATS_LEN + NETVSC_QUEUE_STATS_LEN(nvdev);
	default:
	default:
		return -EINVAL;
		return -EINVAL;
	}
	}
@@ -968,22 +983,63 @@ static void netvsc_get_ethtool_stats(struct net_device *dev,
				     struct ethtool_stats *stats, u64 *data)
				     struct ethtool_stats *stats, u64 *data)
{
{
	struct net_device_context *ndc = netdev_priv(dev);
	struct net_device_context *ndc = netdev_priv(dev);
	struct netvsc_device *nvdev = ndc->nvdev;
	const void *nds = &ndc->eth_stats;
	const void *nds = &ndc->eth_stats;
	int i;
	const struct netvsc_stats *qstats;
	unsigned int start;
	u64 packets, bytes;
	int i, j;


	for (i = 0; i < ARRAY_SIZE(netvsc_stats); i++)
	for (i = 0; i < NETVSC_GLOBAL_STATS_LEN; i++)
		data[i] = *(unsigned long *)(nds + netvsc_stats[i].offset);
		data[i] = *(unsigned long *)(nds + netvsc_stats[i].offset);

	for (j = 0; j < nvdev->num_chn; j++) {
		qstats = &nvdev->chan_table[j].tx_stats;

		do {
			start = u64_stats_fetch_begin_irq(&qstats->syncp);
			packets = qstats->packets;
			bytes = qstats->bytes;
		} while (u64_stats_fetch_retry_irq(&qstats->syncp, start));
		data[i++] = packets;
		data[i++] = bytes;

		qstats = &nvdev->chan_table[j].rx_stats;
		do {
			start = u64_stats_fetch_begin_irq(&qstats->syncp);
			packets = qstats->packets;
			bytes = qstats->bytes;
		} while (u64_stats_fetch_retry_irq(&qstats->syncp, start));
		data[i++] = packets;
		data[i++] = bytes;
	}
}
}


static void netvsc_get_strings(struct net_device *dev, u32 stringset, u8 *data)
static void netvsc_get_strings(struct net_device *dev, u32 stringset, u8 *data)
{
{
	struct net_device_context *ndc = netdev_priv(dev);
	struct netvsc_device *nvdev = ndc->nvdev;
	u8 *p = data;
	int i;
	int i;


	switch (stringset) {
	switch (stringset) {
	case ETH_SS_STATS:
	case ETH_SS_STATS:
		for (i = 0; i < ARRAY_SIZE(netvsc_stats); i++)
		for (i = 0; i < ARRAY_SIZE(netvsc_stats); i++)
			memcpy(data + i * ETH_GSTRING_LEN,
			memcpy(p + i * ETH_GSTRING_LEN,
			       netvsc_stats[i].name, ETH_GSTRING_LEN);
			       netvsc_stats[i].name, ETH_GSTRING_LEN);

		p += i * ETH_GSTRING_LEN;
		for (i = 0; i < nvdev->num_chn; i++) {
			sprintf(p, "tx_queue_%u_packets", i);
			p += ETH_GSTRING_LEN;
			sprintf(p, "tx_queue_%u_bytes", i);
			p += ETH_GSTRING_LEN;
			sprintf(p, "rx_queue_%u_packets", i);
			p += ETH_GSTRING_LEN;
			sprintf(p, "rx_queue_%u_bytes", i);
			p += ETH_GSTRING_LEN;
		}

		break;
		break;
	}
	}
}
}
@@ -1237,15 +1293,6 @@ static void netvsc_link_change(struct work_struct *w)
	rtnl_unlock();
	rtnl_unlock();
}
}


static void netvsc_free_netdev(struct net_device *netdev)
{
	struct net_device_context *net_device_ctx = netdev_priv(netdev);

	free_percpu(net_device_ctx->tx_stats);
	free_percpu(net_device_ctx->rx_stats);
	free_netdev(netdev);
}

static struct net_device *get_netvsc_bymac(const u8 *mac)
static struct net_device *get_netvsc_bymac(const u8 *mac)
{
{
	struct net_device *dev;
	struct net_device *dev;
@@ -1423,18 +1470,6 @@ static int netvsc_probe(struct hv_device *dev,
		netdev_dbg(net, "netvsc msg_enable: %d\n",
		netdev_dbg(net, "netvsc msg_enable: %d\n",
			   net_device_ctx->msg_enable);
			   net_device_ctx->msg_enable);


	net_device_ctx->tx_stats = netdev_alloc_pcpu_stats(struct netvsc_stats);
	if (!net_device_ctx->tx_stats) {
		free_netdev(net);
		return -ENOMEM;
	}
	net_device_ctx->rx_stats = netdev_alloc_pcpu_stats(struct netvsc_stats);
	if (!net_device_ctx->rx_stats) {
		free_percpu(net_device_ctx->tx_stats);
		free_netdev(net);
		return -ENOMEM;
	}

	hv_set_drvdata(dev, net);
	hv_set_drvdata(dev, net);


	net_device_ctx->start_remove = false;
	net_device_ctx->start_remove = false;
@@ -1460,7 +1495,7 @@ static int netvsc_probe(struct hv_device *dev,
	ret = rndis_filter_device_add(dev, &device_info);
	ret = rndis_filter_device_add(dev, &device_info);
	if (ret != 0) {
	if (ret != 0) {
		netdev_err(net, "unable to add netvsc device (ret %d)\n", ret);
		netdev_err(net, "unable to add netvsc device (ret %d)\n", ret);
		netvsc_free_netdev(net);
		free_netdev(net);
		hv_set_drvdata(dev, NULL);
		hv_set_drvdata(dev, NULL);
		return ret;
		return ret;
	}
	}
@@ -1487,7 +1522,7 @@ static int netvsc_probe(struct hv_device *dev,
	if (ret != 0) {
	if (ret != 0) {
		pr_err("Unable to register netdev.\n");
		pr_err("Unable to register netdev.\n");
		rndis_filter_device_remove(dev, nvdev);
		rndis_filter_device_remove(dev, nvdev);
		netvsc_free_netdev(net);
		free_netdev(net);
	}
	}


	return ret;
	return ret;
@@ -1530,7 +1565,7 @@ static int netvsc_remove(struct hv_device *dev)


	hv_set_drvdata(dev, NULL);
	hv_set_drvdata(dev, NULL);


	netvsc_free_netdev(net);
	free_netdev(net);
	return 0;
	return 0;
}
}