const struct ndis_pkt_8021q_info *vlan)
{
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 sk_buff *skb;
struct netvsc_stats *rx_stats;
+ u16 q_idx = channel->offermsg.offer.sub_channel_index;
+
if (net->reg_state != NETREG_REGISTERED)
return NVSP_STAT_FAIL;
}
if (net != vf_netdev)
- skb_record_rx_queue(skb,
- channel->offermsg.offer.sub_channel_index);
+ skb_record_rx_queue(skb, q_idx);
/*
* Even if injecting the packet, record the statistics
* on the synthetic device because modifying the VF device
* 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);
rx_stats->packets++;
rx_stats->bytes += len;
struct rtnl_link_stats64 *t)
{
struct net_device_context *ndev_ctx = netdev_priv(net);
- int cpu;
-
- for_each_possible_cpu(cpu) {
- struct netvsc_stats *tx_stats = per_cpu_ptr(ndev_ctx->tx_stats,
- cpu);
- struct netvsc_stats *rx_stats = per_cpu_ptr(ndev_ctx->rx_stats,
- cpu);
- u64 tx_packets, tx_bytes, rx_packets, rx_bytes, rx_multicast;
+ struct netvsc_device *nvdev = ndev_ctx->nvdev;
+ int i;
+
+ if (!nvdev)
+ return;
+
+ for (i = 0; i < nvdev->num_chn; i++) {
+ const struct netvsc_channel *nvchan = &nvdev->chan_table[i];
+ const struct netvsc_stats *stats;
+ u64 packets, bytes, multicast;
unsigned int start;
+ stats = &nvchan->tx_stats;
do {
- start = u64_stats_fetch_begin_irq(&tx_stats->syncp);
- tx_packets = tx_stats->packets;
- tx_bytes = tx_stats->bytes;
- } while (u64_stats_fetch_retry_irq(&tx_stats->syncp, start));
+ start = u64_stats_fetch_begin_irq(&stats->syncp);
+ packets = stats->packets;
+ bytes = stats->bytes;
+ } while (u64_stats_fetch_retry_irq(&stats->syncp, start));
+
+ t->tx_bytes += bytes;
+ t->tx_packets += packets;
+ stats = &nvchan->rx_stats;
do {
- start = u64_stats_fetch_begin_irq(&rx_stats->syncp);
- rx_packets = rx_stats->packets;
- rx_bytes = rx_stats->bytes;
- rx_multicast = rx_stats->multicast + rx_stats->broadcast;
- } while (u64_stats_fetch_retry_irq(&rx_stats->syncp, start));
-
- t->tx_bytes += tx_bytes;
- t->tx_packets += tx_packets;
- t->rx_bytes += rx_bytes;
- t->rx_packets += rx_packets;
- t->multicast += rx_multicast;
+ start = u64_stats_fetch_begin_irq(&stats->syncp);
+ packets = stats->packets;
+ bytes = stats->bytes;
+ multicast = stats->multicast + stats->broadcast;
+ } while (u64_stats_fetch_retry_irq(&stats->syncp, start));
+
+ t->rx_bytes += bytes;
+ t->rx_packets += packets;
+ t->multicast += multicast;
}
t->tx_dropped = net->stats.tx_dropped;
{ "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)
{
+ struct net_device_context *ndc = netdev_priv(dev);
+ struct netvsc_device *nvdev = ndc->nvdev;
+
switch (string_set) {
case ETH_SS_STATS:
- return ARRAY_SIZE(netvsc_stats);
+ return NETVSC_GLOBAL_STATS_LEN + NETVSC_QUEUE_STATS_LEN(nvdev);
default:
return -EINVAL;
}
struct ethtool_stats *stats, u64 *data)
{
struct net_device_context *ndc = netdev_priv(dev);
+ struct netvsc_device *nvdev = ndc->nvdev;
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);
+
+ 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)
{
+ struct net_device_context *ndc = netdev_priv(dev);
+ struct netvsc_device *nvdev = ndc->nvdev;
+ u8 *p = data;
int i;
switch (stringset) {
case ETH_SS_STATS:
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);
+
+ 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;
}
}
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)
{
struct net_device *dev;
netdev_dbg(net, "netvsc msg_enable: %d\n",
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);
net_device_ctx->start_remove = false;
ret = rndis_filter_device_add(dev, &device_info);
if (ret != 0) {
netdev_err(net, "unable to add netvsc device (ret %d)\n", ret);
- netvsc_free_netdev(net);
+ free_netdev(net);
hv_set_drvdata(dev, NULL);
return ret;
}
if (ret != 0) {
pr_err("Unable to register netdev.\n");
rndis_filter_device_remove(dev, nvdev);
- netvsc_free_netdev(net);
+ free_netdev(net);
}
return ret;
hv_set_drvdata(dev, NULL);
- netvsc_free_netdev(net);
+ free_netdev(net);
return 0;
}