Skip to content

Commit 71e1eab

Browse files
committed
Merge branch 'net-stats-tools-driver-tests-for-hw-gro'
Jakub Kicinski says: ==================== net: stats, tools, driver tests for HW GRO [part] Add miscellaneous pieces related to production use of HW-GRO: - report standard stats from drivers (bnxt included here, Gal recently posted patches for mlx5 which is great) - CLI tool for calculating HW GRO savings / effectiveness ==================== Link: https://patch.msgid.link/20260207003509.3927744-1-kuba@kernel.org Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2 parents 57be33f + c61a375 commit 71e1eab

3 files changed

Lines changed: 131 additions & 61 deletions

File tree

drivers/net/ethernet/broadcom/bnxt/bnxt.c

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1801,7 +1801,8 @@ static inline struct sk_buff *bnxt_gro_skb(struct bnxt *bp,
18011801
struct bnxt_tpa_info *tpa_info,
18021802
struct rx_tpa_end_cmp *tpa_end,
18031803
struct rx_tpa_end_cmp_ext *tpa_end1,
1804-
struct sk_buff *skb)
1804+
struct sk_buff *skb,
1805+
struct bnxt_rx_sw_stats *rx_stats)
18051806
{
18061807
#ifdef CONFIG_INET
18071808
int payload_off;
@@ -1811,6 +1812,9 @@ static inline struct sk_buff *bnxt_gro_skb(struct bnxt *bp,
18111812
if (segs == 1)
18121813
return skb;
18131814

1815+
rx_stats->rx_hw_gro_packets++;
1816+
rx_stats->rx_hw_gro_wire_packets += segs;
1817+
18141818
NAPI_GRO_CB(skb)->count = segs;
18151819
skb_shinfo(skb)->gso_size =
18161820
le32_to_cpu(tpa_end1->rx_tpa_end_cmp_seg_len);
@@ -1984,7 +1988,8 @@ static inline struct sk_buff *bnxt_tpa_end(struct bnxt *bp,
19841988
}
19851989

19861990
if (gro)
1987-
skb = bnxt_gro_skb(bp, tpa_info, tpa_end, tpa_end1, skb);
1991+
skb = bnxt_gro_skb(bp, tpa_info, tpa_end, tpa_end1, skb,
1992+
&cpr->sw_stats->rx);
19881993

19891994
return skb;
19901995
}
@@ -13489,6 +13494,8 @@ static void bnxt_get_one_ring_err_stats(struct bnxt *bp,
1348913494
stats->rx_total_netpoll_discards += sw_stats->rx.rx_netpoll_discards;
1349013495
stats->rx_total_ring_discards +=
1349113496
BNXT_GET_RING_STATS64(hw_stats, rx_discard_pkts);
13497+
stats->rx_total_hw_gro_packets += sw_stats->rx.rx_hw_gro_packets;
13498+
stats->rx_total_hw_gro_wire_packets += sw_stats->rx.rx_hw_gro_wire_packets;
1349213499
stats->tx_total_resets += sw_stats->tx.tx_resets;
1349313500
stats->tx_total_ring_discards +=
1349413501
BNXT_GET_RING_STATS64(hw_stats, tx_discard_pkts);
@@ -15910,6 +15917,8 @@ static void bnxt_get_queue_stats_rx(struct net_device *dev, int i,
1591015917
stats->bytes += BNXT_GET_RING_STATS64(sw, rx_bcast_bytes);
1591115918

1591215919
stats->alloc_fail = cpr->sw_stats->rx.rx_oom_discards;
15920+
stats->hw_gro_packets = cpr->sw_stats->rx.rx_hw_gro_packets;
15921+
stats->hw_gro_wire_packets = cpr->sw_stats->rx.rx_hw_gro_wire_packets;
1591315922
}
1591415923

1591515924
static void bnxt_get_queue_stats_tx(struct net_device *dev, int i,
@@ -15945,6 +15954,8 @@ static void bnxt_get_base_stats(struct net_device *dev,
1594515954
rx->packets = bp->net_stats_prev.rx_packets;
1594615955
rx->bytes = bp->net_stats_prev.rx_bytes;
1594715956
rx->alloc_fail = bp->ring_err_stats_prev.rx_total_oom_discards;
15957+
rx->hw_gro_packets = bp->ring_err_stats_prev.rx_total_hw_gro_packets;
15958+
rx->hw_gro_wire_packets = bp->ring_err_stats_prev.rx_total_hw_gro_wire_packets;
1594815959

1594915960
tx->packets = bp->net_stats_prev.tx_packets;
1595015961
tx->bytes = bp->net_stats_prev.tx_bytes;

drivers/net/ethernet/broadcom/bnxt/bnxt.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1126,8 +1126,11 @@ struct bnxt_rx_sw_stats {
11261126
u64 rx_l4_csum_errors;
11271127
u64 rx_resets;
11281128
u64 rx_buf_errors;
1129+
/* end of ethtool -S stats */
11291130
u64 rx_oom_discards;
11301131
u64 rx_netpoll_discards;
1132+
u64 rx_hw_gro_packets;
1133+
u64 rx_hw_gro_wire_packets;
11311134
};
11321135

11331136
struct bnxt_tx_sw_stats {
@@ -1154,6 +1157,9 @@ struct bnxt_total_ring_err_stats {
11541157
u64 tx_total_resets;
11551158
u64 tx_total_ring_discards;
11561159
u64 total_missed_irqs;
1160+
/* end of ethtool -S stats */
1161+
u64 rx_total_hw_gro_packets;
1162+
u64 rx_total_hw_gro_wire_packets;
11571163
};
11581164

11591165
struct bnxt_stats_mem {

tools/net/ynl/ynltool/qstats.c

Lines changed: 112 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -237,13 +237,47 @@ static void print_plain_qstats(struct netdev_qstats_get_list *qstats)
237237
}
238238
}
239239

240-
static int do_show(int argc, char **argv)
240+
static struct netdev_qstats_get_list *
241+
qstats_dump(enum netdev_qstats_scope scope)
241242
{
242243
struct netdev_qstats_get_list *qstats;
243244
struct netdev_qstats_get_req *req;
244245
struct ynl_error yerr;
245246
struct ynl_sock *ys;
246-
int ret = 0;
247+
248+
ys = ynl_sock_create(&ynl_netdev_family, &yerr);
249+
if (!ys) {
250+
p_err("YNL: %s", yerr.msg);
251+
return NULL;
252+
}
253+
254+
req = netdev_qstats_get_req_alloc();
255+
if (!req) {
256+
p_err("failed to allocate qstats request");
257+
goto err_close;
258+
}
259+
260+
if (scope)
261+
netdev_qstats_get_req_set_scope(req, scope);
262+
263+
qstats = netdev_qstats_get_dump(ys, req);
264+
netdev_qstats_get_req_free(req);
265+
if (!qstats) {
266+
p_err("failed to get queue stats: %s", ys->err.msg);
267+
goto err_close;
268+
}
269+
270+
ynl_sock_destroy(ys);
271+
return qstats;
272+
273+
err_close:
274+
ynl_sock_destroy(ys);
275+
return NULL;
276+
}
277+
278+
static int do_show(int argc, char **argv)
279+
{
280+
struct netdev_qstats_get_list *qstats;
247281

248282
/* Parse options */
249283
while (argc > 0) {
@@ -268,29 +302,9 @@ static int do_show(int argc, char **argv)
268302
}
269303
}
270304

271-
ys = ynl_sock_create(&ynl_netdev_family, &yerr);
272-
if (!ys) {
273-
p_err("YNL: %s", yerr.msg);
305+
qstats = qstats_dump(scope);
306+
if (!qstats)
274307
return -1;
275-
}
276-
277-
req = netdev_qstats_get_req_alloc();
278-
if (!req) {
279-
p_err("failed to allocate qstats request");
280-
ret = -1;
281-
goto exit_close;
282-
}
283-
284-
if (scope)
285-
netdev_qstats_get_req_set_scope(req, scope);
286-
287-
qstats = netdev_qstats_get_dump(ys, req);
288-
netdev_qstats_get_req_free(req);
289-
if (!qstats) {
290-
p_err("failed to get queue stats: %s", ys->err.msg);
291-
ret = -1;
292-
goto exit_close;
293-
}
294308

295309
/* Print the stats as returned by the kernel */
296310
if (json_output)
@@ -299,9 +313,7 @@ static int do_show(int argc, char **argv)
299313
print_plain_qstats(qstats);
300314

301315
netdev_qstats_get_list_free(qstats);
302-
exit_close:
303-
ynl_sock_destroy(ys);
304-
return ret;
316+
return 0;
305317
}
306318

307319
static void compute_stats(__u64 *values, unsigned int count,
@@ -406,10 +418,7 @@ static int cmp_ifindex_type(const void *a, const void *b)
406418
static int do_balance(int argc, char **argv __attribute__((unused)))
407419
{
408420
struct netdev_qstats_get_list *qstats;
409-
struct netdev_qstats_get_req *req;
410421
struct netdev_qstats_get_rsp **sorted;
411-
struct ynl_error yerr;
412-
struct ynl_sock *ys;
413422
unsigned int count = 0;
414423
unsigned int i, j;
415424
int ret = 0;
@@ -419,29 +428,9 @@ static int do_balance(int argc, char **argv __attribute__((unused)))
419428
return -1;
420429
}
421430

422-
ys = ynl_sock_create(&ynl_netdev_family, &yerr);
423-
if (!ys) {
424-
p_err("YNL: %s", yerr.msg);
431+
qstats = qstats_dump(NETDEV_QSTATS_SCOPE_QUEUE);
432+
if (!qstats)
425433
return -1;
426-
}
427-
428-
req = netdev_qstats_get_req_alloc();
429-
if (!req) {
430-
p_err("failed to allocate qstats request");
431-
ret = -1;
432-
goto exit_close;
433-
}
434-
435-
/* Always use queue scope for balance analysis */
436-
netdev_qstats_get_req_set_scope(req, NETDEV_QSTATS_SCOPE_QUEUE);
437-
438-
qstats = netdev_qstats_get_dump(ys, req);
439-
netdev_qstats_get_req_free(req);
440-
if (!qstats) {
441-
p_err("failed to get queue stats: %s", ys->err.msg);
442-
ret = -1;
443-
goto exit_close;
444-
}
445434

446435
/* Count and sort queues */
447436
ynl_dump_foreach(qstats, qs)
@@ -576,11 +565,68 @@ static int do_balance(int argc, char **argv __attribute__((unused)))
576565
free(sorted);
577566
exit_free_qstats:
578567
netdev_qstats_get_list_free(qstats);
579-
exit_close:
580-
ynl_sock_destroy(ys);
581568
return ret;
582569
}
583570

571+
static int do_hw_gro(int argc, char **argv __attribute__((unused)))
572+
{
573+
struct netdev_qstats_get_list *qstats;
574+
575+
if (argc > 0) {
576+
p_err("hw-gro command takes no arguments");
577+
return -1;
578+
}
579+
580+
qstats = qstats_dump(0);
581+
if (!qstats)
582+
return -1;
583+
584+
if (json_output)
585+
jsonw_start_array(json_wtr);
586+
587+
ynl_dump_foreach(qstats, qs) {
588+
char ifname[IF_NAMESIZE];
589+
const char *name;
590+
double savings;
591+
592+
if (!qs->_present.rx_packets ||
593+
!qs->_present.rx_hw_gro_packets ||
594+
!qs->_present.rx_hw_gro_wire_packets)
595+
continue;
596+
597+
if (!qs->rx_packets)
598+
continue;
599+
600+
/* How many skbs did we avoid allocating thanks to HW GRO */
601+
savings = (double)(qs->rx_hw_gro_wire_packets -
602+
qs->rx_hw_gro_packets) /
603+
qs->rx_packets * 100.0;
604+
605+
name = if_indextoname(qs->ifindex, ifname);
606+
607+
if (json_output) {
608+
jsonw_start_object(json_wtr);
609+
jsonw_uint_field(json_wtr, "ifindex", qs->ifindex);
610+
if (name)
611+
jsonw_string_field(json_wtr, "ifname", name);
612+
jsonw_float_field(json_wtr, "savings", savings);
613+
jsonw_end_object(json_wtr);
614+
} else {
615+
if (name)
616+
printf("%s", name);
617+
else
618+
printf("ifindex:%u", qs->ifindex);
619+
printf(": %.1f%% savings\n", savings);
620+
}
621+
}
622+
623+
if (json_output)
624+
jsonw_end_array(json_wtr);
625+
626+
netdev_qstats_get_list_free(qstats);
627+
return 0;
628+
}
629+
584630
static int do_help(int argc __attribute__((unused)),
585631
char **argv __attribute__((unused)))
586632
{
@@ -590,9 +636,10 @@ static int do_help(int argc __attribute__((unused)),
590636
}
591637

592638
fprintf(stderr,
593-
"Usage: %s qstats { COMMAND | help }\n"
594-
" %s qstats [ show ] [ OPTIONS ]\n"
595-
" %s qstats balance\n"
639+
"Usage: %1$s qstats { COMMAND | help }\n"
640+
" %1$s qstats [ show ] [ OPTIONS ]\n"
641+
" %1$s qstats balance\n"
642+
" %1$s qstats hw-gro\n"
596643
"\n"
597644
" OPTIONS := { scope queue | group-by { device | queue } }\n"
598645
"\n"
@@ -601,16 +648,22 @@ static int do_help(int argc __attribute__((unused)),
601648
" show scope queue - Display per-queue statistics\n"
602649
" show group-by device - Display device-aggregated statistics (default)\n"
603650
" show group-by queue - Display per-queue statistics\n"
604-
" balance - Analyze traffic distribution balance.\n"
651+
"\n"
652+
" Analysis:\n"
653+
" balance - Traffic distribution between queues.\n"
654+
" hw-gro - HW GRO effectiveness analysis\n"
655+
" - savings - delta between packets received\n"
656+
" on the wire and packets seen by the kernel.\n"
605657
"",
606-
bin_name, bin_name, bin_name);
658+
bin_name);
607659

608660
return 0;
609661
}
610662

611663
static const struct cmd qstats_cmds[] = {
612664
{ "show", do_show },
613665
{ "balance", do_balance },
666+
{ "hw-gro", do_hw_gro },
614667
{ "help", do_help },
615668
{ 0 }
616669
};

0 commit comments

Comments
 (0)