Skip to content

Commit c95a10b

Browse files
Yidong Renmerwick
authored andcommitted
hv_netvsc: Add per-cpu ethtool stats for netvsc
This patch implements following ethtool stats fields for netvsc: cpu<n>_tx/rx_packets/bytes cpu<n>_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. 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 Signed-off-by: Yidong Ren <[email protected]> Reviewed-by: Stephen Hemminger <[email protected]> Signed-off-by: David S. Miller <[email protected]> (cherry picked from commit 6ae7467) Orabug: 28671425 Signed-off-by: Liam Merwick <[email protected]> Reviewed-by: Darren Kenny <[email protected]> Reviewed-by: Alejandro Jimenez <[email protected]> Tested-by: Vijay Balakrishna <[email protected]>
1 parent 96dda42 commit c95a10b

File tree

2 files changed

+114
-3
lines changed

2 files changed

+114
-3
lines changed

drivers/net/hyperv/hyperv_net.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -873,6 +873,17 @@ struct netvsc_ethtool_stats {
873873
unsigned long wake_queue;
874874
};
875875

876+
struct netvsc_ethtool_pcpu_stats {
877+
u64 rx_packets;
878+
u64 rx_bytes;
879+
u64 tx_packets;
880+
u64 tx_bytes;
881+
u64 vf_rx_packets;
882+
u64 vf_rx_bytes;
883+
u64 vf_tx_packets;
884+
u64 vf_tx_bytes;
885+
};
886+
876887
struct netvsc_vf_pcpu_stats {
877888
u64 rx_packets;
878889
u64 rx_bytes;

drivers/net/hyperv/netvsc_drv.c

Lines changed: 103 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1118,6 +1118,64 @@ static void netvsc_get_vf_stats(struct net_device *net,
11181118
}
11191119
}
11201120

1121+
static void netvsc_get_pcpu_stats(struct net_device *net,
1122+
struct netvsc_ethtool_pcpu_stats *pcpu_tot)
1123+
{
1124+
struct net_device_context *ndev_ctx = netdev_priv(net);
1125+
struct netvsc_device *nvdev = rcu_dereference_rtnl(ndev_ctx->nvdev);
1126+
int i;
1127+
1128+
/* fetch percpu stats of vf */
1129+
for_each_possible_cpu(i) {
1130+
const struct netvsc_vf_pcpu_stats *stats =
1131+
per_cpu_ptr(ndev_ctx->vf_stats, i);
1132+
struct netvsc_ethtool_pcpu_stats *this_tot = &pcpu_tot[i];
1133+
unsigned int start;
1134+
1135+
do {
1136+
start = u64_stats_fetch_begin_irq(&stats->syncp);
1137+
this_tot->vf_rx_packets = stats->rx_packets;
1138+
this_tot->vf_tx_packets = stats->tx_packets;
1139+
this_tot->vf_rx_bytes = stats->rx_bytes;
1140+
this_tot->vf_tx_bytes = stats->tx_bytes;
1141+
} while (u64_stats_fetch_retry_irq(&stats->syncp, start));
1142+
this_tot->rx_packets = this_tot->vf_rx_packets;
1143+
this_tot->tx_packets = this_tot->vf_tx_packets;
1144+
this_tot->rx_bytes = this_tot->vf_rx_bytes;
1145+
this_tot->tx_bytes = this_tot->vf_tx_bytes;
1146+
}
1147+
1148+
/* fetch percpu stats of netvsc */
1149+
for (i = 0; i < nvdev->num_chn; i++) {
1150+
const struct netvsc_channel *nvchan = &nvdev->chan_table[i];
1151+
const struct netvsc_stats *stats;
1152+
struct netvsc_ethtool_pcpu_stats *this_tot =
1153+
&pcpu_tot[nvchan->channel->target_cpu];
1154+
u64 packets, bytes;
1155+
unsigned int start;
1156+
1157+
stats = &nvchan->tx_stats;
1158+
do {
1159+
start = u64_stats_fetch_begin_irq(&stats->syncp);
1160+
packets = stats->packets;
1161+
bytes = stats->bytes;
1162+
} while (u64_stats_fetch_retry_irq(&stats->syncp, start));
1163+
1164+
this_tot->tx_bytes += bytes;
1165+
this_tot->tx_packets += packets;
1166+
1167+
stats = &nvchan->rx_stats;
1168+
do {
1169+
start = u64_stats_fetch_begin_irq(&stats->syncp);
1170+
packets = stats->packets;
1171+
bytes = stats->bytes;
1172+
} while (u64_stats_fetch_retry_irq(&stats->syncp, start));
1173+
1174+
this_tot->rx_bytes += bytes;
1175+
this_tot->rx_packets += packets;
1176+
}
1177+
}
1178+
11211179
static void netvsc_get_stats64(struct net_device *net,
11221180
struct rtnl_link_stats64 *t)
11231181
{
@@ -1215,6 +1273,23 @@ static const struct {
12151273
{ "rx_no_memory", offsetof(struct netvsc_ethtool_stats, rx_no_memory) },
12161274
{ "stop_queue", offsetof(struct netvsc_ethtool_stats, stop_queue) },
12171275
{ "wake_queue", offsetof(struct netvsc_ethtool_stats, wake_queue) },
1276+
}, pcpu_stats[] = {
1277+
{ "cpu%u_rx_packets",
1278+
offsetof(struct netvsc_ethtool_pcpu_stats, rx_packets) },
1279+
{ "cpu%u_rx_bytes",
1280+
offsetof(struct netvsc_ethtool_pcpu_stats, rx_bytes) },
1281+
{ "cpu%u_tx_packets",
1282+
offsetof(struct netvsc_ethtool_pcpu_stats, tx_packets) },
1283+
{ "cpu%u_tx_bytes",
1284+
offsetof(struct netvsc_ethtool_pcpu_stats, tx_bytes) },
1285+
{ "cpu%u_vf_rx_packets",
1286+
offsetof(struct netvsc_ethtool_pcpu_stats, vf_rx_packets) },
1287+
{ "cpu%u_vf_rx_bytes",
1288+
offsetof(struct netvsc_ethtool_pcpu_stats, vf_rx_bytes) },
1289+
{ "cpu%u_vf_tx_packets",
1290+
offsetof(struct netvsc_ethtool_pcpu_stats, vf_tx_packets) },
1291+
{ "cpu%u_vf_tx_bytes",
1292+
offsetof(struct netvsc_ethtool_pcpu_stats, vf_tx_bytes) },
12181293
}, vf_stats[] = {
12191294
{ "vf_rx_packets", offsetof(struct netvsc_vf_pcpu_stats, rx_packets) },
12201295
{ "vf_rx_bytes", offsetof(struct netvsc_vf_pcpu_stats, rx_bytes) },
@@ -1226,6 +1301,9 @@ static const struct {
12261301
#define NETVSC_GLOBAL_STATS_LEN ARRAY_SIZE(netvsc_stats)
12271302
#define NETVSC_VF_STATS_LEN ARRAY_SIZE(vf_stats)
12281303

1304+
/* statistics per queue (rx/tx packets/bytes) */
1305+
#define NETVSC_PCPU_STATS_LEN (num_present_cpus() * ARRAY_SIZE(pcpu_stats))
1306+
12291307
/* 4 statistics per queue (rx/tx packets/bytes) */
12301308
#define NETVSC_QUEUE_STATS_LEN(dev) ((dev)->num_chn * 4)
12311309

@@ -1241,7 +1319,8 @@ static int netvsc_get_sset_count(struct net_device *dev, int string_set)
12411319
case ETH_SS_STATS:
12421320
return NETVSC_GLOBAL_STATS_LEN
12431321
+ NETVSC_VF_STATS_LEN
1244-
+ NETVSC_QUEUE_STATS_LEN(nvdev);
1322+
+ NETVSC_QUEUE_STATS_LEN(nvdev)
1323+
+ NETVSC_PCPU_STATS_LEN;
12451324
default:
12461325
return -EINVAL;
12471326
}
@@ -1255,9 +1334,10 @@ static void netvsc_get_ethtool_stats(struct net_device *dev,
12551334
const void *nds = &ndc->eth_stats;
12561335
const struct netvsc_stats *qstats;
12571336
struct netvsc_vf_pcpu_stats sum;
1337+
struct netvsc_ethtool_pcpu_stats *pcpu_sum;
12581338
unsigned int start;
12591339
u64 packets, bytes;
1260-
int i, j;
1340+
int i, j, cpu;
12611341

12621342
if (!nvdev)
12631343
return;
@@ -1289,14 +1369,27 @@ static void netvsc_get_ethtool_stats(struct net_device *dev,
12891369
data[i++] = packets;
12901370
data[i++] = bytes;
12911371
}
1372+
1373+
pcpu_sum = kvmalloc_array(num_possible_cpus(),
1374+
sizeof(struct netvsc_ethtool_pcpu_stats),
1375+
GFP_KERNEL);
1376+
netvsc_get_pcpu_stats(dev, pcpu_sum);
1377+
for_each_present_cpu(cpu) {
1378+
struct netvsc_ethtool_pcpu_stats *this_sum = &pcpu_sum[cpu];
1379+
1380+
for (j = 0; j < ARRAY_SIZE(pcpu_stats); j++)
1381+
data[i++] = *(u64 *)((void *)this_sum
1382+
+ pcpu_stats[j].offset);
1383+
}
1384+
kvfree(pcpu_sum);
12921385
}
12931386

12941387
static void netvsc_get_strings(struct net_device *dev, u32 stringset, u8 *data)
12951388
{
12961389
struct net_device_context *ndc = netdev_priv(dev);
12971390
struct netvsc_device *nvdev = rtnl_dereference(ndc->nvdev);
12981391
u8 *p = data;
1299-
int i;
1392+
int i, cpu;
13001393

13011394
if (!nvdev)
13021395
return;
@@ -1324,6 +1417,13 @@ static void netvsc_get_strings(struct net_device *dev, u32 stringset, u8 *data)
13241417
p += ETH_GSTRING_LEN;
13251418
}
13261419

1420+
for_each_present_cpu(cpu) {
1421+
for (i = 0; i < ARRAY_SIZE(pcpu_stats); i++) {
1422+
sprintf(p, pcpu_stats[i].name, cpu);
1423+
p += ETH_GSTRING_LEN;
1424+
}
1425+
}
1426+
13271427
break;
13281428
}
13291429
}

0 commit comments

Comments
 (0)