[PATCH v4 net-next] hv_netvsc: Add per-cpu ethtool stats for netvsc
From: Yidong Ren This patch implements following ethtool stats fields for netvsc: cpu_tx/rx_packets/bytes cpu_vf_tx/rx_packets/bytes Corresponding per-cpu counters already exist in current code. Exposing these counters will help troubleshooting performance issues. for_each_present_cpu() was used instead of for_each_possible_cpu(). for_each_possible_cpu() would create very long and useless output. It is still being used for internal buffer, but not for ethtool output. There could be an overflow if cpu was added between ethtool call netvsc_get_sset_count() and netvsc_get_ethtool_stats() and netvsc_get_strings(). (still safe if cpu was removed) ethtool makes these three function calls separately. As long as we use ethtool, I can't see any clean solution. Currently and in foreseeable short term, Hyper-V doesn't support cpu hot-plug. Plus, ethtool is for admin use. Unlikely the admin would perform such combo operations. Signed-off-by: Yidong Ren --- Changes in v2: - Remove cpp style comment - Resubmit after freeze Changes in v3: - Reimplemented with kvmalloc instead of alloc_percpu Changes in v4: - Fixed inconsistent array size - Use kvmalloc_array instead of kvmalloc drivers/net/hyperv/hyperv_net.h | 11 drivers/net/hyperv/netvsc_drv.c | 106 +++- 2 files changed, 114 insertions(+), 3 deletions(-) diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h index 4b6e308199d2..a32ded5b4f41 100644 --- a/drivers/net/hyperv/hyperv_net.h +++ b/drivers/net/hyperv/hyperv_net.h @@ -873,6 +873,17 @@ struct netvsc_ethtool_stats { unsigned long wake_queue; }; +struct netvsc_ethtool_pcpu_stats { + u64 rx_packets; + u64 rx_bytes; + u64 tx_packets; + u64 tx_bytes; + u64 vf_rx_packets; + u64 vf_rx_bytes; + u64 vf_tx_packets; + u64 vf_tx_bytes; +}; + struct netvsc_vf_pcpu_stats { u64 rx_packets; u64 rx_bytes; diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index dd1d6e115145..805388bfbce7 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -1118,6 +1118,64 @@ static void netvsc_get_vf_stats(struct net_device *net, } } +static void netvsc_get_pcpu_stats(struct net_device *net, + struct netvsc_ethtool_pcpu_stats *pcpu_tot) +{ + struct net_device_context *ndev_ctx = netdev_priv(net); + struct netvsc_device *nvdev = rcu_dereference_rtnl(ndev_ctx->nvdev); + int i; + + /* fetch percpu stats of vf */ + for_each_possible_cpu(i) { + const struct netvsc_vf_pcpu_stats *stats = + per_cpu_ptr(ndev_ctx->vf_stats, i); + struct netvsc_ethtool_pcpu_stats *this_tot = _tot[i]; + unsigned int start; + + do { + start = u64_stats_fetch_begin_irq(>syncp); + this_tot->vf_rx_packets = stats->rx_packets; + this_tot->vf_tx_packets = stats->tx_packets; + this_tot->vf_rx_bytes = stats->rx_bytes; + this_tot->vf_tx_bytes = stats->tx_bytes; + } while (u64_stats_fetch_retry_irq(>syncp, start)); + this_tot->rx_packets = this_tot->vf_rx_packets; + this_tot->tx_packets = this_tot->vf_tx_packets; + this_tot->rx_bytes = this_tot->vf_rx_bytes; + this_tot->tx_bytes = this_tot->vf_tx_bytes; + } + + /* fetch percpu stats of netvsc */ + for (i = 0; i < nvdev->num_chn; i++) { + const struct netvsc_channel *nvchan = >chan_table[i]; + const struct netvsc_stats *stats; + struct netvsc_ethtool_pcpu_stats *this_tot = + _tot[nvchan->channel->target_cpu]; + u64 packets, bytes; + unsigned int start; + + stats = >tx_stats; + do { + start = u64_stats_fetch_begin_irq(>syncp); + packets = stats->packets; + bytes = stats->bytes; + } while (u64_stats_fetch_retry_irq(>syncp, start)); + + this_tot->tx_bytes += bytes; + this_tot->tx_packets+= packets; + + stats = >rx_stats; + do { + start = u64_stats_fetch_begin_irq(>syncp); + packets = stats->packets; + bytes = stats->bytes; + } while (u64_stats_fetch_retry_irq(>syncp, start)); + + this_tot->rx_bytes += bytes; + this_tot->rx_packets+= packets; + } +} + static void netvsc_get_stats64(struct net_device *net, s
RE: [PATCH v3] hv_netvsc: Add per-cpu ethtool stats for netvsc
> From: Vitaly Kuznetsov > While you do for_each_present_cpu() in netvsc_get_ethtool_stats(), > netvsc_get_pcpu_stats() does for_each_possible_cpu(). This looks > inconsistent. I made a mistake there. Thanks for catch me. > The allocation you're doing here is short-lived so I would suggest you use > possible_cpus everywhere. Even knowing there's no CPU hotplug on Hyper- > V at this moment, it can appear later and we'll get a hard-to-find issue. > Moreover, we may consider using netvsc driver on e.g. KVM with Hyper-V > enlightenments and KVM has CPU hotplug already. That would cause the output to be very long and useless. I will submit a revision that allocates for num_possible_cpus, but only copy out stats for each present cpu. -Yidong ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
RE: [PATCH v3] hv_netvsc: Add per-cpu ethtool stats for netvsc
> From: Yidong Ren > Sent: Monday, July 23, 2018 6:26 PM > + pcpu_sum = kvmalloc(sizeof(struct netvsc_ethtool_pcpu_stats) * > + num_present_cpus(), GFP_KERNEL); Since there is no plan for CPU hotplug in Hyper-V in short term, it is fine to use num_present_cpus for now. We can move to debugfs later if necessary. ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
[PATCH v3] hv_netvsc: Add per-cpu ethtool stats for netvsc
From: Yidong Ren This patch implements following ethtool stats fields for netvsc: cpu_tx/rx_packets/bytes cpu_vf_tx/rx_packets/bytes Corresponding per-cpu counters already exist in current code. Exposing these counters will help troubleshooting performance issues. Signed-off-by: Yidong Ren --- Changes since v2: * Reimplemented with kvmalloc instead of alloc_percpu drivers/net/hyperv/hyperv_net.h | 11 drivers/net/hyperv/netvsc_drv.c | 103 +++- 2 files changed, 112 insertions(+), 2 deletions(-) diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h index 4b6e308199d2..a32ded5b4f41 100644 --- a/drivers/net/hyperv/hyperv_net.h +++ b/drivers/net/hyperv/hyperv_net.h @@ -873,6 +873,17 @@ struct netvsc_ethtool_stats { unsigned long wake_queue; }; +struct netvsc_ethtool_pcpu_stats { + u64 rx_packets; + u64 rx_bytes; + u64 tx_packets; + u64 tx_bytes; + u64 vf_rx_packets; + u64 vf_rx_bytes; + u64 vf_tx_packets; + u64 vf_tx_bytes; +}; + struct netvsc_vf_pcpu_stats { u64 rx_packets; u64 rx_bytes; diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index dd1d6e115145..51f9cab2da5b 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -1118,6 +1118,64 @@ static void netvsc_get_vf_stats(struct net_device *net, } } +static void netvsc_get_pcpu_stats(struct net_device *net, + struct netvsc_ethtool_pcpu_stats *pcpu_tot) +{ + struct net_device_context *ndev_ctx = netdev_priv(net); + struct netvsc_device *nvdev = rcu_dereference_rtnl(ndev_ctx->nvdev); + int i; + + /* fetch percpu stats of vf */ + for_each_possible_cpu(i) { + const struct netvsc_vf_pcpu_stats *stats = + per_cpu_ptr(ndev_ctx->vf_stats, i); + struct netvsc_ethtool_pcpu_stats *this_tot = _tot[i]; + unsigned int start; + + do { + start = u64_stats_fetch_begin_irq(>syncp); + this_tot->vf_rx_packets = stats->rx_packets; + this_tot->vf_tx_packets = stats->tx_packets; + this_tot->vf_rx_bytes = stats->rx_bytes; + this_tot->vf_tx_bytes = stats->tx_bytes; + } while (u64_stats_fetch_retry_irq(>syncp, start)); + this_tot->rx_packets = this_tot->vf_rx_packets; + this_tot->tx_packets = this_tot->vf_tx_packets; + this_tot->rx_bytes = this_tot->vf_rx_bytes; + this_tot->tx_bytes = this_tot->vf_tx_bytes; + } + + /* fetch percpu stats of netvsc */ + for (i = 0; i < nvdev->num_chn; i++) { + const struct netvsc_channel *nvchan = >chan_table[i]; + const struct netvsc_stats *stats; + struct netvsc_ethtool_pcpu_stats *this_tot = + _tot[nvchan->channel->target_cpu]; + u64 packets, bytes; + unsigned int start; + + stats = >tx_stats; + do { + start = u64_stats_fetch_begin_irq(>syncp); + packets = stats->packets; + bytes = stats->bytes; + } while (u64_stats_fetch_retry_irq(>syncp, start)); + + this_tot->tx_bytes += bytes; + this_tot->tx_packets+= packets; + + stats = >rx_stats; + do { + start = u64_stats_fetch_begin_irq(>syncp); + packets = stats->packets; + bytes = stats->bytes; + } while (u64_stats_fetch_retry_irq(>syncp, start)); + + this_tot->rx_bytes += bytes; + this_tot->rx_packets+= packets; + } +} + static void netvsc_get_stats64(struct net_device *net, struct rtnl_link_stats64 *t) { @@ -1215,6 +1273,23 @@ static const struct { { "rx_no_memory", offsetof(struct netvsc_ethtool_stats, rx_no_memory) }, { "stop_queue", offsetof(struct netvsc_ethtool_stats, stop_queue) }, { "wake_queue", offsetof(struct netvsc_ethtool_stats, wake_queue) }, +}, pcpu_stats[] = { + { "cpu%u_rx_packets", + offsetof(struct netvsc_ethtool_pcpu_stats, rx_packets) }, + { "cpu%u_rx_bytes", + offsetof(struct netvsc_ethtool_pcpu_stats, rx_bytes) }, + { "cpu%u_tx_packets", + offsetof(struct netvsc_ethtool_pcpu_stats, tx_packets) }, + { "cpu%u_tx_bytes", + offsetof(struct netvsc_ethtool_pcpu_stats, tx_bytes) }, + { &
RE: [PATCH v2] hv_netvsc: Add per-cpu ethtool stats for netvsc
> From: devel On Behalf > Of Stephen Hemminger > > +/* statistics per queue (rx/tx packets/bytes) */ #define > > +NETVSC_PCPU_STATS_LEN (num_present_cpus() * > ARRAY_SIZE(pcpu_stats)) > > Even though Hyper-V/Azure does not support hot plug cpu's it might be > better to num_cpu_possible to avoid any possible future surprises. That will create a very long output (num_cpu_possible = 128 on my machine) for ethtool, While doesn't provide additional info. num_present_cpus() would cause problem only if someone removed cpu between netvsc_get_sset_count() and netvsc_get_strings() and netvsc_get_ethtool_stats(). An alternative way could be: Check all stats, and only output if not zero. This need to be done in two pass. First pass to get the correct count, second pass to print the number. Is there an elegant way to do this? ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
RE: [PATCH v2] hv_netvsc: Add per-cpu ethtool stats for netvsc
> From: Eric Dumazet > You actually want to allocate memory local to this cpu, possibly in one chunk, > not spread all over the places. > > kvmalloc(nr_cpu_ids * sizeof(struct netvsc_ethtool_pcpu_stats)) should be > really better, since it would most of the time be satisfied by a single > kmalloc() Got it. I'm just trying to allocate memory for each cpu. It doesn't have to be __percpu variable. ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
[PATCH v2] hv_netvsc: Add per-cpu ethtool stats for netvsc
From: Yidong Ren This patch implements following ethtool stats fields for netvsc: cpu_tx/rx_packets/bytes cpu_vf_tx/rx_packets/bytes Corresponding per-cpu counters exist in current code. Exposing these counters will help troubleshooting performance issues. Signed-off-by: Yidong Ren --- Changes in v2: - Remove cpp style comment - Resubmit after freeze drivers/net/hyperv/hyperv_net.h | 11 + drivers/net/hyperv/netvsc_drv.c | 104 +++- 2 files changed, 113 insertions(+), 2 deletions(-) diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h index 23304ac..c825353 100644 --- a/drivers/net/hyperv/hyperv_net.h +++ b/drivers/net/hyperv/hyperv_net.h @@ -873,6 +873,17 @@ struct netvsc_ethtool_stats { unsigned long wake_queue; }; +struct netvsc_ethtool_pcpu_stats { + u64 rx_packets; + u64 rx_bytes; + u64 tx_packets; + u64 tx_bytes; + u64 vf_rx_packets; + u64 vf_rx_bytes; + u64 vf_tx_packets; + u64 vf_tx_bytes; +}; + struct netvsc_vf_pcpu_stats { u64 rx_packets; u64 rx_bytes; diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index 7b18a8c..6803aae 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -1105,6 +1105,66 @@ static void netvsc_get_vf_stats(struct net_device *net, } } +static void netvsc_get_pcpu_stats(struct net_device *net, + struct netvsc_ethtool_pcpu_stats + __percpu *pcpu_tot) +{ + struct net_device_context *ndev_ctx = netdev_priv(net); + struct netvsc_device *nvdev = rcu_dereference_rtnl(ndev_ctx->nvdev); + int i; + + /* fetch percpu stats of vf */ + for_each_possible_cpu(i) { + const struct netvsc_vf_pcpu_stats *stats = + per_cpu_ptr(ndev_ctx->vf_stats, i); + struct netvsc_ethtool_pcpu_stats *this_tot = + per_cpu_ptr(pcpu_tot, i); + unsigned int start; + + do { + start = u64_stats_fetch_begin_irq(>syncp); + this_tot->vf_rx_packets = stats->rx_packets; + this_tot->vf_tx_packets = stats->tx_packets; + this_tot->vf_rx_bytes = stats->rx_bytes; + this_tot->vf_tx_bytes = stats->tx_bytes; + } while (u64_stats_fetch_retry_irq(>syncp, start)); + this_tot->rx_packets = this_tot->vf_rx_packets; + this_tot->tx_packets = this_tot->vf_tx_packets; + this_tot->rx_bytes = this_tot->vf_rx_bytes; + this_tot->tx_bytes = this_tot->vf_tx_bytes; + } + + /* fetch percpu stats of netvsc */ + for (i = 0; i < nvdev->num_chn; i++) { + const struct netvsc_channel *nvchan = >chan_table[i]; + const struct netvsc_stats *stats; + struct netvsc_ethtool_pcpu_stats *this_tot = + per_cpu_ptr(pcpu_tot, nvchan->channel->target_cpu); + u64 packets, bytes; + unsigned int start; + + stats = >tx_stats; + do { + start = u64_stats_fetch_begin_irq(>syncp); + packets = stats->packets; + bytes = stats->bytes; + } while (u64_stats_fetch_retry_irq(>syncp, start)); + + this_tot->tx_bytes += bytes; + this_tot->tx_packets+= packets; + + stats = >rx_stats; + do { + start = u64_stats_fetch_begin_irq(>syncp); + packets = stats->packets; + bytes = stats->bytes; + } while (u64_stats_fetch_retry_irq(>syncp, start)); + + this_tot->rx_bytes += bytes; + this_tot->rx_packets+= packets; + } +} + static void netvsc_get_stats64(struct net_device *net, struct rtnl_link_stats64 *t) { @@ -1202,6 +1262,23 @@ static const struct { { "rx_no_memory", offsetof(struct netvsc_ethtool_stats, rx_no_memory) }, { "stop_queue", offsetof(struct netvsc_ethtool_stats, stop_queue) }, { "wake_queue", offsetof(struct netvsc_ethtool_stats, wake_queue) }, +}, pcpu_stats[] = { + { "cpu%u_rx_packets", + offsetof(struct netvsc_ethtool_pcpu_stats, rx_packets) }, + { "cpu%u_rx_bytes", + offsetof(struct netvsc_ethtool_pcpu_stats, rx_bytes) }, + { "cpu%u_tx_packets", + offsetof(struct netvsc_ethtool_pcpu_stats, tx_packets) }, + { "cpu%u_tx_byt
[PATCH] hv_netvsc: Add per-cpu ethtool stats for netvsc
From: Yidong Ren This patch implements following ethtool stats fields for netvsc: cpu_tx/rx_packets/bytes cpu_vf_tx/rx_packets/bytes Corresponding per-cpu counters exist in current code. Exposing these counters will help troubleshooting performance issues. Signed-off-by: Yidong Ren --- drivers/net/hyperv/hyperv_net.h | 11 drivers/net/hyperv/netvsc_drv.c | 104 +++- 2 files changed, 113 insertions(+), 2 deletions(-) diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h index 960f06141472..f8c798bf9418 100644 --- a/drivers/net/hyperv/hyperv_net.h +++ b/drivers/net/hyperv/hyperv_net.h @@ -710,6 +710,17 @@ struct netvsc_ethtool_stats { unsigned long wake_queue; }; +struct netvsc_ethtool_pcpu_stats { + u64 rx_packets; + u64 rx_bytes; + u64 tx_packets; + u64 tx_bytes; + u64 vf_rx_packets; + u64 vf_rx_bytes; + u64 vf_tx_packets; + u64 vf_tx_bytes; +}; + struct netvsc_vf_pcpu_stats { u64 rx_packets; u64 rx_bytes; diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index da07ccdf84bf..c43e64606c1a 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -1104,6 +1104,66 @@ static void netvsc_get_vf_stats(struct net_device *net, } } +static void netvsc_get_pcpu_stats(struct net_device *net, + struct netvsc_ethtool_pcpu_stats + __percpu *pcpu_tot) +{ + struct net_device_context *ndev_ctx = netdev_priv(net); + struct netvsc_device *nvdev = rcu_dereference_rtnl(ndev_ctx->nvdev); + int i; + + // fetch percpu stats of vf + for_each_possible_cpu(i) { + const struct netvsc_vf_pcpu_stats *stats = + per_cpu_ptr(ndev_ctx->vf_stats, i); + struct netvsc_ethtool_pcpu_stats *this_tot = + per_cpu_ptr(pcpu_tot, i); + unsigned int start; + + do { + start = u64_stats_fetch_begin_irq(>syncp); + this_tot->vf_rx_packets = stats->rx_packets; + this_tot->vf_tx_packets = stats->tx_packets; + this_tot->vf_rx_bytes = stats->rx_bytes; + this_tot->vf_tx_bytes = stats->tx_bytes; + } while (u64_stats_fetch_retry_irq(>syncp, start)); + this_tot->rx_packets = this_tot->vf_rx_packets; + this_tot->tx_packets = this_tot->vf_tx_packets; + this_tot->rx_bytes = this_tot->vf_rx_bytes; + this_tot->tx_bytes = this_tot->vf_tx_bytes; + } + + // fetch percpu stats of netvsc + for (i = 0; i < nvdev->num_chn; i++) { + const struct netvsc_channel *nvchan = >chan_table[i]; + const struct netvsc_stats *stats; + struct netvsc_ethtool_pcpu_stats *this_tot = + per_cpu_ptr(pcpu_tot, nvchan->channel->target_cpu); + u64 packets, bytes; + unsigned int start; + + stats = >tx_stats; + do { + start = u64_stats_fetch_begin_irq(>syncp); + packets = stats->packets; + bytes = stats->bytes; + } while (u64_stats_fetch_retry_irq(>syncp, start)); + + this_tot->tx_bytes += bytes; + this_tot->tx_packets+= packets; + + stats = >rx_stats; + do { + start = u64_stats_fetch_begin_irq(>syncp); + packets = stats->packets; + bytes = stats->bytes; + } while (u64_stats_fetch_retry_irq(>syncp, start)); + + this_tot->rx_bytes += bytes; + this_tot->rx_packets+= packets; + } +} + static void netvsc_get_stats64(struct net_device *net, struct rtnl_link_stats64 *t) { @@ -1201,6 +1261,23 @@ static const struct { { "rx_no_memory", offsetof(struct netvsc_ethtool_stats, rx_no_memory) }, { "stop_queue", offsetof(struct netvsc_ethtool_stats, stop_queue) }, { "wake_queue", offsetof(struct netvsc_ethtool_stats, wake_queue) }, +}, pcpu_stats[] = { + { "cpu%u_rx_packets", + offsetof(struct netvsc_ethtool_pcpu_stats, rx_packets) }, + { "cpu%u_rx_bytes", + offsetof(struct netvsc_ethtool_pcpu_stats, rx_bytes) }, + { "cpu%u_tx_packets", + offsetof(struct netvsc_ethtool_pcpu_stats, tx_packets) }, + { "cpu%u_tx_bytes", + offsetof(struct netvsc_ethtool_pcpu_stats
RE: [PATCH] hv_netvsc: add per-cpu ethtool stats for netvsc
Thank you, Greg. I'm sorry for didn't read the guidelines on kernel newbies. Please ignore this patch. Thanks, Yidong -Original Message- From: Greg KH Sent: Wednesday, June 6, 2018 1:34 AM To: Yidong Ren Cc: driverdev-devel@linuxdriverproject.org; Haiyang Zhang ; Stephen Hemminger ; Madhan Sivakumar Subject: Re: [PATCH] hv_netvsc: add per-cpu ethtool stats for netvsc On Tue, Jun 05, 2018 at 08:14:06PM +, Yidong Ren wrote: > This patch implements following ethtool stats fields for netvsc: > cpu_rx_packets > cpu_tx_packets > cpu_rx_bytes > cpu_tx_bytes > cpu_vf_rx_packets > cpu_vf_tx_packets > cpu_vf_rx_bytes > cpu_vf_tx_bytes > --- No signed-off-by line? Always use scripts/checkpatch.pl on your patches os you do not get grumpy kernel maintainers telling you to run checkpatch.pl on your patches... greg k-h ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
[PATCH] hv_netvsc: add per-cpu ethtool stats for netvsc
This patch implements following ethtool stats fields for netvsc: cpu_rx_packets cpu_tx_packets cpu_rx_bytes cpu_tx_bytes cpu_vf_rx_packets cpu_vf_tx_packets cpu_vf_rx_bytes cpu_vf_tx_bytes --- drivers/net/hyperv/hyperv_net.h | 11 + drivers/net/hyperv/netvsc_drv.c | 95 - 2 files changed, 104 insertions(+), 2 deletions(-) diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h index 960f061..f8c798b 100644 --- a/drivers/net/hyperv/hyperv_net.h +++ b/drivers/net/hyperv/hyperv_net.h @@ -710,6 +710,17 @@ struct netvsc_ethtool_stats { unsigned long wake_queue; }; +struct netvsc_ethtool_pcpu_stats { + u64 rx_packets; + u64 rx_bytes; + u64 tx_packets; + u64 tx_bytes; + u64 vf_rx_packets; + u64 vf_rx_bytes; + u64 vf_tx_packets; + u64 vf_tx_bytes; +}; + struct netvsc_vf_pcpu_stats { u64 rx_packets; u64 rx_bytes; diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index da07ccd..ef6ea1d 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -1104,6 +1104,66 @@ static void netvsc_get_vf_stats(struct net_device *net, } } +static void netvsc_get_pcpu_stats(struct net_device *net, + struct netvsc_ethtool_pcpu_stats __percpu *pcpu_tot) +{ + struct net_device_context *ndev_ctx = netdev_priv(net); + struct netvsc_device *nvdev = rcu_dereference_rtnl(ndev_ctx->nvdev); + int i; + + // fetch percpu stats of vf + for_each_possible_cpu(i) { + const struct netvsc_vf_pcpu_stats *stats += per_cpu_ptr(ndev_ctx->vf_stats, i); + struct netvsc_ethtool_pcpu_stats *this_tot + = per_cpu_ptr(pcpu_tot, i); + unsigned int start; + + do { + start = u64_stats_fetch_begin_irq(>syncp); + this_tot->vf_rx_packets = stats->rx_packets; + this_tot->vf_tx_packets = stats->tx_packets; + this_tot->vf_rx_bytes = stats->rx_bytes; + this_tot->vf_tx_bytes = stats->tx_bytes; + } while (u64_stats_fetch_retry_irq(>syncp, start)); + this_tot->rx_packets = this_tot->vf_rx_packets; + this_tot->tx_packets = this_tot->vf_tx_packets; + this_tot->rx_bytes = this_tot->vf_rx_bytes; + this_tot->tx_bytes = this_tot->vf_tx_bytes; + } + + // fetch percpu stats of netvsc + for (i = 0; i < nvdev->num_chn; i++) { + const struct netvsc_channel *nvchan = >chan_table[i]; + const struct netvsc_stats *stats; + struct netvsc_ethtool_pcpu_stats *this_tot + = per_cpu_ptr(pcpu_tot, nvchan->channel->target_cpu); + u64 packets, bytes; + unsigned int start; + + + stats = >tx_stats; + do { + start = u64_stats_fetch_begin_irq(>syncp); + packets = stats->packets; + bytes = stats->bytes; + } while (u64_stats_fetch_retry_irq(>syncp, start)); + + this_tot->tx_bytes += bytes; + this_tot->tx_packets+= packets; + + stats = >rx_stats; + do { + start = u64_stats_fetch_begin_irq(>syncp); + packets = stats->packets; + bytes = stats->bytes; + } while (u64_stats_fetch_retry_irq(>syncp, start)); + + this_tot->rx_bytes += bytes; + this_tot->rx_packets+= packets; + } +} + static void netvsc_get_stats64(struct net_device *net, struct rtnl_link_stats64 *t) { @@ -1201,6 +1261,15 @@ static const struct { { "rx_no_memory", offsetof(struct netvsc_ethtool_stats, rx_no_memory) }, { "stop_queue", offsetof(struct netvsc_ethtool_stats, stop_queue) }, { "wake_queue", offsetof(struct netvsc_ethtool_stats, wake_queue) }, +}, pcpu_stats[] = { + { "cpu%u_rx_packets", offsetof(struct netvsc_ethtool_pcpu_stats, rx_packets) }, + { "cpu%u_rx_bytes", offsetof(struct netvsc_ethtool_pcpu_stats, rx_bytes) }, + { "cpu%u_tx_packets", offsetof(struct netvsc_ethtool_pcpu_stats, tx_packets) }, + { "cpu%u_tx_bytes", offsetof(struct netvsc_ethtool_pcpu_stats, tx_bytes) }, + { "cpu%u_vf_rx_packets", offsetof(struct netvsc_ethtool_pcpu_stats, vf_rx_packets) }, + { "cpu%u_vf_rx_bytes", offsetof(struct netvsc_ethtool_pcpu_stats, vf_rx_bytes) }, + { "cpu%u_vf_tx_packets", offsetof(struct netvsc_ethtool_pcpu_stats, vf_tx_packets) }, + { "cpu%u_vf_tx_bytes", offsetof(struct netvsc_ethtool_pcpu_stats, vf_tx_bytes) }, }, vf_stats[]