@@ -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
307319static void compute_stats (__u64 * values , unsigned int count ,
@@ -406,10 +418,7 @@ static int cmp_ifindex_type(const void *a, const void *b)
406418static 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 );
577566exit_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+
584630static 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
611663static 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