提交 6c80f3fc 编写于 作者: S Simon Xiao 提交者: David S. Miller

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: NLong Li <longli@microsoft.com>
Reviewed-by: NK. Y. Srinivasan <kys@microsoft.com>
Reviewed-by: NHaiyang Zhang <haiyangz@microsoft.com>
Signed-off-by: NSimon Xiao <sixiao@microsoft.com>
Signed-off-by: NStephen Hemminger <sthemmin@microsoft.com>
Signed-off-by: NDavid S. Miller <davem@davemloft.net>
上级 793e3955
...@@ -699,8 +699,6 @@ struct net_device_context { ...@@ -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 { ...@@ -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 */
......
...@@ -621,7 +621,7 @@ static void netvsc_send_tx_complete(struct netvsc_device *net_device, ...@@ -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;
......
...@@ -641,9 +641,12 @@ int netvsc_recv_callback(struct net_device *net, ...@@ -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, ...@@ -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, ...@@ -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 { ...@@ -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, ...@@ -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) ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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) ...@@ -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;
} }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册