xref: /linux/tools/net/ynl/ynltool/qstats.c (revision 24f171c7e145f43b9f187578e89b0982ce87e54c)
13f0a638dSJakub Kicinski // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
23f0a638dSJakub Kicinski 
33f0a638dSJakub Kicinski #include <stdio.h>
43f0a638dSJakub Kicinski #include <stdlib.h>
53f0a638dSJakub Kicinski #include <string.h>
63f0a638dSJakub Kicinski #include <errno.h>
73f0a638dSJakub Kicinski #include <net/if.h>
8*9eef97a9SJakub Kicinski #include <math.h>
93f0a638dSJakub Kicinski 
103f0a638dSJakub Kicinski #include <ynl.h>
113f0a638dSJakub Kicinski #include "netdev-user.h"
123f0a638dSJakub Kicinski 
133f0a638dSJakub Kicinski #include "main.h"
143f0a638dSJakub Kicinski 
153f0a638dSJakub Kicinski static enum netdev_qstats_scope scope; /* default - device */
163f0a638dSJakub Kicinski 
17*9eef97a9SJakub Kicinski struct queue_balance {
18*9eef97a9SJakub Kicinski 	unsigned int ifindex;
19*9eef97a9SJakub Kicinski 	enum netdev_queue_type type;
20*9eef97a9SJakub Kicinski 	unsigned int queue_count;
21*9eef97a9SJakub Kicinski 	__u64 *rx_packets;
22*9eef97a9SJakub Kicinski 	__u64 *rx_bytes;
23*9eef97a9SJakub Kicinski 	__u64 *tx_packets;
24*9eef97a9SJakub Kicinski 	__u64 *tx_bytes;
25*9eef97a9SJakub Kicinski };
26*9eef97a9SJakub Kicinski 
273f0a638dSJakub Kicinski static void print_json_qstats(struct netdev_qstats_get_list *qstats)
283f0a638dSJakub Kicinski {
293f0a638dSJakub Kicinski 	jsonw_start_array(json_wtr);
303f0a638dSJakub Kicinski 
313f0a638dSJakub Kicinski 	ynl_dump_foreach(qstats, qs) {
323f0a638dSJakub Kicinski 		char ifname[IF_NAMESIZE];
333f0a638dSJakub Kicinski 		const char *name;
343f0a638dSJakub Kicinski 
353f0a638dSJakub Kicinski 		jsonw_start_object(json_wtr);
363f0a638dSJakub Kicinski 
373f0a638dSJakub Kicinski 		name = if_indextoname(qs->ifindex, ifname);
383f0a638dSJakub Kicinski 		if (name)
393f0a638dSJakub Kicinski 			jsonw_string_field(json_wtr, "ifname", name);
403f0a638dSJakub Kicinski 		jsonw_uint_field(json_wtr, "ifindex", qs->ifindex);
413f0a638dSJakub Kicinski 
423f0a638dSJakub Kicinski 		if (qs->_present.queue_type)
433f0a638dSJakub Kicinski 			jsonw_string_field(json_wtr, "queue-type",
443f0a638dSJakub Kicinski 					   netdev_queue_type_str(qs->queue_type));
453f0a638dSJakub Kicinski 		if (qs->_present.queue_id)
463f0a638dSJakub Kicinski 			jsonw_uint_field(json_wtr, "queue-id", qs->queue_id);
473f0a638dSJakub Kicinski 
483f0a638dSJakub Kicinski 		if (qs->_present.rx_packets || qs->_present.rx_bytes ||
493f0a638dSJakub Kicinski 		    qs->_present.rx_alloc_fail || qs->_present.rx_hw_drops ||
503f0a638dSJakub Kicinski 		    qs->_present.rx_csum_complete || qs->_present.rx_hw_gro_packets) {
513f0a638dSJakub Kicinski 			jsonw_name(json_wtr, "rx");
523f0a638dSJakub Kicinski 			jsonw_start_object(json_wtr);
533f0a638dSJakub Kicinski 			if (qs->_present.rx_packets)
543f0a638dSJakub Kicinski 				jsonw_uint_field(json_wtr, "packets", qs->rx_packets);
553f0a638dSJakub Kicinski 			if (qs->_present.rx_bytes)
563f0a638dSJakub Kicinski 				jsonw_uint_field(json_wtr, "bytes", qs->rx_bytes);
573f0a638dSJakub Kicinski 			if (qs->_present.rx_alloc_fail)
583f0a638dSJakub Kicinski 				jsonw_uint_field(json_wtr, "alloc-fail", qs->rx_alloc_fail);
593f0a638dSJakub Kicinski 			if (qs->_present.rx_hw_drops)
603f0a638dSJakub Kicinski 				jsonw_uint_field(json_wtr, "hw-drops", qs->rx_hw_drops);
613f0a638dSJakub Kicinski 			if (qs->_present.rx_hw_drop_overruns)
623f0a638dSJakub Kicinski 				jsonw_uint_field(json_wtr, "hw-drop-overruns", qs->rx_hw_drop_overruns);
633f0a638dSJakub Kicinski 			if (qs->_present.rx_hw_drop_ratelimits)
643f0a638dSJakub Kicinski 				jsonw_uint_field(json_wtr, "hw-drop-ratelimits", qs->rx_hw_drop_ratelimits);
653f0a638dSJakub Kicinski 			if (qs->_present.rx_csum_complete)
663f0a638dSJakub Kicinski 				jsonw_uint_field(json_wtr, "csum-complete", qs->rx_csum_complete);
673f0a638dSJakub Kicinski 			if (qs->_present.rx_csum_unnecessary)
683f0a638dSJakub Kicinski 				jsonw_uint_field(json_wtr, "csum-unnecessary", qs->rx_csum_unnecessary);
693f0a638dSJakub Kicinski 			if (qs->_present.rx_csum_none)
703f0a638dSJakub Kicinski 				jsonw_uint_field(json_wtr, "csum-none", qs->rx_csum_none);
713f0a638dSJakub Kicinski 			if (qs->_present.rx_csum_bad)
723f0a638dSJakub Kicinski 				jsonw_uint_field(json_wtr, "csum-bad", qs->rx_csum_bad);
733f0a638dSJakub Kicinski 			if (qs->_present.rx_hw_gro_packets)
743f0a638dSJakub Kicinski 				jsonw_uint_field(json_wtr, "hw-gro-packets", qs->rx_hw_gro_packets);
753f0a638dSJakub Kicinski 			if (qs->_present.rx_hw_gro_bytes)
763f0a638dSJakub Kicinski 				jsonw_uint_field(json_wtr, "hw-gro-bytes", qs->rx_hw_gro_bytes);
773f0a638dSJakub Kicinski 			if (qs->_present.rx_hw_gro_wire_packets)
783f0a638dSJakub Kicinski 				jsonw_uint_field(json_wtr, "hw-gro-wire-packets", qs->rx_hw_gro_wire_packets);
793f0a638dSJakub Kicinski 			if (qs->_present.rx_hw_gro_wire_bytes)
803f0a638dSJakub Kicinski 				jsonw_uint_field(json_wtr, "hw-gro-wire-bytes", qs->rx_hw_gro_wire_bytes);
813f0a638dSJakub Kicinski 			jsonw_end_object(json_wtr);
823f0a638dSJakub Kicinski 		}
833f0a638dSJakub Kicinski 
843f0a638dSJakub Kicinski 		if (qs->_present.tx_packets || qs->_present.tx_bytes ||
853f0a638dSJakub Kicinski 		    qs->_present.tx_hw_drops || qs->_present.tx_csum_none ||
863f0a638dSJakub Kicinski 		    qs->_present.tx_hw_gso_packets) {
873f0a638dSJakub Kicinski 			jsonw_name(json_wtr, "tx");
883f0a638dSJakub Kicinski 			jsonw_start_object(json_wtr);
893f0a638dSJakub Kicinski 			if (qs->_present.tx_packets)
903f0a638dSJakub Kicinski 				jsonw_uint_field(json_wtr, "packets", qs->tx_packets);
913f0a638dSJakub Kicinski 			if (qs->_present.tx_bytes)
923f0a638dSJakub Kicinski 				jsonw_uint_field(json_wtr, "bytes", qs->tx_bytes);
933f0a638dSJakub Kicinski 			if (qs->_present.tx_hw_drops)
943f0a638dSJakub Kicinski 				jsonw_uint_field(json_wtr, "hw-drops", qs->tx_hw_drops);
953f0a638dSJakub Kicinski 			if (qs->_present.tx_hw_drop_errors)
963f0a638dSJakub Kicinski 				jsonw_uint_field(json_wtr, "hw-drop-errors", qs->tx_hw_drop_errors);
973f0a638dSJakub Kicinski 			if (qs->_present.tx_hw_drop_ratelimits)
983f0a638dSJakub Kicinski 				jsonw_uint_field(json_wtr, "hw-drop-ratelimits", qs->tx_hw_drop_ratelimits);
993f0a638dSJakub Kicinski 			if (qs->_present.tx_csum_none)
1003f0a638dSJakub Kicinski 				jsonw_uint_field(json_wtr, "csum-none", qs->tx_csum_none);
1013f0a638dSJakub Kicinski 			if (qs->_present.tx_needs_csum)
1023f0a638dSJakub Kicinski 				jsonw_uint_field(json_wtr, "needs-csum", qs->tx_needs_csum);
1033f0a638dSJakub Kicinski 			if (qs->_present.tx_hw_gso_packets)
1043f0a638dSJakub Kicinski 				jsonw_uint_field(json_wtr, "hw-gso-packets", qs->tx_hw_gso_packets);
1053f0a638dSJakub Kicinski 			if (qs->_present.tx_hw_gso_bytes)
1063f0a638dSJakub Kicinski 				jsonw_uint_field(json_wtr, "hw-gso-bytes", qs->tx_hw_gso_bytes);
1073f0a638dSJakub Kicinski 			if (qs->_present.tx_hw_gso_wire_packets)
1083f0a638dSJakub Kicinski 				jsonw_uint_field(json_wtr, "hw-gso-wire-packets", qs->tx_hw_gso_wire_packets);
1093f0a638dSJakub Kicinski 			if (qs->_present.tx_hw_gso_wire_bytes)
1103f0a638dSJakub Kicinski 				jsonw_uint_field(json_wtr, "hw-gso-wire-bytes", qs->tx_hw_gso_wire_bytes);
1113f0a638dSJakub Kicinski 			if (qs->_present.tx_stop)
1123f0a638dSJakub Kicinski 				jsonw_uint_field(json_wtr, "stop", qs->tx_stop);
1133f0a638dSJakub Kicinski 			if (qs->_present.tx_wake)
1143f0a638dSJakub Kicinski 				jsonw_uint_field(json_wtr, "wake", qs->tx_wake);
1153f0a638dSJakub Kicinski 			jsonw_end_object(json_wtr);
1163f0a638dSJakub Kicinski 		}
1173f0a638dSJakub Kicinski 
1183f0a638dSJakub Kicinski 		jsonw_end_object(json_wtr);
1193f0a638dSJakub Kicinski 	}
1203f0a638dSJakub Kicinski 
1213f0a638dSJakub Kicinski 	jsonw_end_array(json_wtr);
1223f0a638dSJakub Kicinski }
1233f0a638dSJakub Kicinski 
1243f0a638dSJakub Kicinski static void print_one(bool present, const char *name, unsigned long long val,
1253f0a638dSJakub Kicinski 		      int *line)
1263f0a638dSJakub Kicinski {
1273f0a638dSJakub Kicinski 	if (!present)
1283f0a638dSJakub Kicinski 		return;
1293f0a638dSJakub Kicinski 
1303f0a638dSJakub Kicinski 	if (!*line) {
1313f0a638dSJakub Kicinski 		printf("              ");
1323f0a638dSJakub Kicinski 		++(*line);
1333f0a638dSJakub Kicinski 	}
1343f0a638dSJakub Kicinski 
1353f0a638dSJakub Kicinski 	/* Don't waste space on tx- and rx- prefix, its implied by queue type */
1363f0a638dSJakub Kicinski 	if (scope == NETDEV_QSTATS_SCOPE_QUEUE &&
1373f0a638dSJakub Kicinski 	    (name[0] == 'r' || name[0] == 't') &&
1383f0a638dSJakub Kicinski 	    name[1] == 'x' && name[2] == '-')
1393f0a638dSJakub Kicinski 		name += 3;
1403f0a638dSJakub Kicinski 
1413f0a638dSJakub Kicinski 	printf(" %15s: %15llu", name, val);
1423f0a638dSJakub Kicinski 
1433f0a638dSJakub Kicinski 	if (++(*line) == 3) {
1443f0a638dSJakub Kicinski 		printf("\n");
1453f0a638dSJakub Kicinski 		*line = 0;
1463f0a638dSJakub Kicinski 	}
1473f0a638dSJakub Kicinski }
1483f0a638dSJakub Kicinski 
1493f0a638dSJakub Kicinski static void print_plain_qstats(struct netdev_qstats_get_list *qstats)
1503f0a638dSJakub Kicinski {
1513f0a638dSJakub Kicinski 	ynl_dump_foreach(qstats, qs) {
1523f0a638dSJakub Kicinski 		char ifname[IF_NAMESIZE];
1533f0a638dSJakub Kicinski 		const char *name;
1543f0a638dSJakub Kicinski 		int n;
1553f0a638dSJakub Kicinski 
1563f0a638dSJakub Kicinski 		name = if_indextoname(qs->ifindex, ifname);
1573f0a638dSJakub Kicinski 		if (name)
1583f0a638dSJakub Kicinski 			printf("%s", name);
1593f0a638dSJakub Kicinski 		else
1603f0a638dSJakub Kicinski 			printf("ifindex:%u", qs->ifindex);
1613f0a638dSJakub Kicinski 
1623f0a638dSJakub Kicinski 		if (qs->_present.queue_type && qs->_present.queue_id)
1633f0a638dSJakub Kicinski 			printf("\t%s-%-3u",
1643f0a638dSJakub Kicinski 			       netdev_queue_type_str(qs->queue_type),
1653f0a638dSJakub Kicinski 			       qs->queue_id);
1663f0a638dSJakub Kicinski 		else
1673f0a638dSJakub Kicinski 			printf("\t      ");
1683f0a638dSJakub Kicinski 
1693f0a638dSJakub Kicinski 		n = 1;
1703f0a638dSJakub Kicinski 
1713f0a638dSJakub Kicinski 		/* Basic counters */
1723f0a638dSJakub Kicinski 		print_one(qs->_present.rx_packets, "rx-packets", qs->rx_packets, &n);
1733f0a638dSJakub Kicinski 		print_one(qs->_present.rx_bytes, "rx-bytes", qs->rx_bytes, &n);
1743f0a638dSJakub Kicinski 		print_one(qs->_present.tx_packets, "tx-packets", qs->tx_packets, &n);
1753f0a638dSJakub Kicinski 		print_one(qs->_present.tx_bytes, "tx-bytes", qs->tx_bytes, &n);
1763f0a638dSJakub Kicinski 
1773f0a638dSJakub Kicinski 		/* RX error/drop counters */
1783f0a638dSJakub Kicinski 		print_one(qs->_present.rx_alloc_fail, "rx-alloc-fail",
1793f0a638dSJakub Kicinski 			  qs->rx_alloc_fail, &n);
1803f0a638dSJakub Kicinski 		print_one(qs->_present.rx_hw_drops, "rx-hw-drops",
1813f0a638dSJakub Kicinski 			  qs->rx_hw_drops, &n);
1823f0a638dSJakub Kicinski 		print_one(qs->_present.rx_hw_drop_overruns, "rx-hw-drop-overruns",
1833f0a638dSJakub Kicinski 			  qs->rx_hw_drop_overruns, &n);
1843f0a638dSJakub Kicinski 		print_one(qs->_present.rx_hw_drop_ratelimits, "rx-hw-drop-ratelimits",
1853f0a638dSJakub Kicinski 			  qs->rx_hw_drop_ratelimits, &n);
1863f0a638dSJakub Kicinski 
1873f0a638dSJakub Kicinski 		/* RX checksum counters */
1883f0a638dSJakub Kicinski 		print_one(qs->_present.rx_csum_complete, "rx-csum-complete",
1893f0a638dSJakub Kicinski 			  qs->rx_csum_complete, &n);
1903f0a638dSJakub Kicinski 		print_one(qs->_present.rx_csum_unnecessary, "rx-csum-unnecessary",
1913f0a638dSJakub Kicinski 			  qs->rx_csum_unnecessary, &n);
1923f0a638dSJakub Kicinski 		print_one(qs->_present.rx_csum_none, "rx-csum-none",
1933f0a638dSJakub Kicinski 			  qs->rx_csum_none, &n);
1943f0a638dSJakub Kicinski 		print_one(qs->_present.rx_csum_bad, "rx-csum-bad",
1953f0a638dSJakub Kicinski 			  qs->rx_csum_bad, &n);
1963f0a638dSJakub Kicinski 
1973f0a638dSJakub Kicinski 		/* RX GRO counters */
1983f0a638dSJakub Kicinski 		print_one(qs->_present.rx_hw_gro_packets, "rx-hw-gro-packets",
1993f0a638dSJakub Kicinski 			  qs->rx_hw_gro_packets, &n);
2003f0a638dSJakub Kicinski 		print_one(qs->_present.rx_hw_gro_bytes, "rx-hw-gro-bytes",
2013f0a638dSJakub Kicinski 			  qs->rx_hw_gro_bytes, &n);
2023f0a638dSJakub Kicinski 		print_one(qs->_present.rx_hw_gro_wire_packets, "rx-hw-gro-wire-packets",
2033f0a638dSJakub Kicinski 			  qs->rx_hw_gro_wire_packets, &n);
2043f0a638dSJakub Kicinski 		print_one(qs->_present.rx_hw_gro_wire_bytes, "rx-hw-gro-wire-bytes",
2053f0a638dSJakub Kicinski 			  qs->rx_hw_gro_wire_bytes, &n);
2063f0a638dSJakub Kicinski 
2073f0a638dSJakub Kicinski 		/* TX error/drop counters */
2083f0a638dSJakub Kicinski 		print_one(qs->_present.tx_hw_drops, "tx-hw-drops",
2093f0a638dSJakub Kicinski 			  qs->tx_hw_drops, &n);
2103f0a638dSJakub Kicinski 		print_one(qs->_present.tx_hw_drop_errors, "tx-hw-drop-errors",
2113f0a638dSJakub Kicinski 			  qs->tx_hw_drop_errors, &n);
2123f0a638dSJakub Kicinski 		print_one(qs->_present.tx_hw_drop_ratelimits, "tx-hw-drop-ratelimits",
2133f0a638dSJakub Kicinski 			  qs->tx_hw_drop_ratelimits, &n);
2143f0a638dSJakub Kicinski 
2153f0a638dSJakub Kicinski 		/* TX checksum counters */
2163f0a638dSJakub Kicinski 		print_one(qs->_present.tx_csum_none, "tx-csum-none",
2173f0a638dSJakub Kicinski 			  qs->tx_csum_none, &n);
2183f0a638dSJakub Kicinski 		print_one(qs->_present.tx_needs_csum, "tx-needs-csum",
2193f0a638dSJakub Kicinski 			  qs->tx_needs_csum, &n);
2203f0a638dSJakub Kicinski 
2213f0a638dSJakub Kicinski 		/* TX GSO counters */
2223f0a638dSJakub Kicinski 		print_one(qs->_present.tx_hw_gso_packets, "tx-hw-gso-packets",
2233f0a638dSJakub Kicinski 			  qs->tx_hw_gso_packets, &n);
2243f0a638dSJakub Kicinski 		print_one(qs->_present.tx_hw_gso_bytes, "tx-hw-gso-bytes",
2253f0a638dSJakub Kicinski 			  qs->tx_hw_gso_bytes, &n);
2263f0a638dSJakub Kicinski 		print_one(qs->_present.tx_hw_gso_wire_packets, "tx-hw-gso-wire-packets",
2273f0a638dSJakub Kicinski 			  qs->tx_hw_gso_wire_packets, &n);
2283f0a638dSJakub Kicinski 		print_one(qs->_present.tx_hw_gso_wire_bytes, "tx-hw-gso-wire-bytes",
2293f0a638dSJakub Kicinski 			  qs->tx_hw_gso_wire_bytes, &n);
2303f0a638dSJakub Kicinski 
2313f0a638dSJakub Kicinski 		/* TX queue control */
2323f0a638dSJakub Kicinski 		print_one(qs->_present.tx_stop, "tx-stop", qs->tx_stop, &n);
2333f0a638dSJakub Kicinski 		print_one(qs->_present.tx_wake, "tx-wake", qs->tx_wake, &n);
2343f0a638dSJakub Kicinski 
2353f0a638dSJakub Kicinski 		if (n)
2363f0a638dSJakub Kicinski 			printf("\n");
2373f0a638dSJakub Kicinski 	}
2383f0a638dSJakub Kicinski }
2393f0a638dSJakub Kicinski 
2403f0a638dSJakub Kicinski static int do_show(int argc, char **argv)
2413f0a638dSJakub Kicinski {
2423f0a638dSJakub Kicinski 	struct netdev_qstats_get_list *qstats;
2433f0a638dSJakub Kicinski 	struct netdev_qstats_get_req *req;
2443f0a638dSJakub Kicinski 	struct ynl_error yerr;
2453f0a638dSJakub Kicinski 	struct ynl_sock *ys;
2463f0a638dSJakub Kicinski 	int ret = 0;
2473f0a638dSJakub Kicinski 
2483f0a638dSJakub Kicinski 	/* Parse options */
2493f0a638dSJakub Kicinski 	while (argc > 0) {
2503f0a638dSJakub Kicinski 		if (is_prefix(*argv, "scope") || is_prefix(*argv, "group-by")) {
2513f0a638dSJakub Kicinski 			NEXT_ARG();
2523f0a638dSJakub Kicinski 
2533f0a638dSJakub Kicinski 			if (!REQ_ARGS(1))
2543f0a638dSJakub Kicinski 				return -1;
2553f0a638dSJakub Kicinski 
2563f0a638dSJakub Kicinski 			if (is_prefix(*argv, "queue")) {
2573f0a638dSJakub Kicinski 				scope = NETDEV_QSTATS_SCOPE_QUEUE;
2583f0a638dSJakub Kicinski 			} else if (is_prefix(*argv, "device")) {
2593f0a638dSJakub Kicinski 				scope = 0;
2603f0a638dSJakub Kicinski 			} else {
2613f0a638dSJakub Kicinski 				p_err("invalid scope value '%s'", *argv);
2623f0a638dSJakub Kicinski 				return -1;
2633f0a638dSJakub Kicinski 			}
2643f0a638dSJakub Kicinski 			NEXT_ARG();
2653f0a638dSJakub Kicinski 		} else {
2663f0a638dSJakub Kicinski 			p_err("unknown option '%s'", *argv);
2673f0a638dSJakub Kicinski 			return -1;
2683f0a638dSJakub Kicinski 		}
2693f0a638dSJakub Kicinski 	}
2703f0a638dSJakub Kicinski 
2713f0a638dSJakub Kicinski 	ys = ynl_sock_create(&ynl_netdev_family, &yerr);
2723f0a638dSJakub Kicinski 	if (!ys) {
2733f0a638dSJakub Kicinski 		p_err("YNL: %s", yerr.msg);
2743f0a638dSJakub Kicinski 		return -1;
2753f0a638dSJakub Kicinski 	}
2763f0a638dSJakub Kicinski 
2773f0a638dSJakub Kicinski 	req = netdev_qstats_get_req_alloc();
2783f0a638dSJakub Kicinski 	if (!req) {
2793f0a638dSJakub Kicinski 		p_err("failed to allocate qstats request");
2803f0a638dSJakub Kicinski 		ret = -1;
2813f0a638dSJakub Kicinski 		goto exit_close;
2823f0a638dSJakub Kicinski 	}
2833f0a638dSJakub Kicinski 
2843f0a638dSJakub Kicinski 	if (scope)
2853f0a638dSJakub Kicinski 		netdev_qstats_get_req_set_scope(req, scope);
2863f0a638dSJakub Kicinski 
2873f0a638dSJakub Kicinski 	qstats = netdev_qstats_get_dump(ys, req);
2883f0a638dSJakub Kicinski 	netdev_qstats_get_req_free(req);
2893f0a638dSJakub Kicinski 	if (!qstats) {
2903f0a638dSJakub Kicinski 		p_err("failed to get queue stats: %s", ys->err.msg);
2913f0a638dSJakub Kicinski 		ret = -1;
2923f0a638dSJakub Kicinski 		goto exit_close;
2933f0a638dSJakub Kicinski 	}
2943f0a638dSJakub Kicinski 
2953f0a638dSJakub Kicinski 	/* Print the stats as returned by the kernel */
2963f0a638dSJakub Kicinski 	if (json_output)
2973f0a638dSJakub Kicinski 		print_json_qstats(qstats);
2983f0a638dSJakub Kicinski 	else
2993f0a638dSJakub Kicinski 		print_plain_qstats(qstats);
3003f0a638dSJakub Kicinski 
3013f0a638dSJakub Kicinski 	netdev_qstats_get_list_free(qstats);
3023f0a638dSJakub Kicinski exit_close:
3033f0a638dSJakub Kicinski 	ynl_sock_destroy(ys);
3043f0a638dSJakub Kicinski 	return ret;
3053f0a638dSJakub Kicinski }
3063f0a638dSJakub Kicinski 
307*9eef97a9SJakub Kicinski static void compute_stats(__u64 *values, unsigned int count,
308*9eef97a9SJakub Kicinski 			  double *mean, double *stddev, __u64 *min, __u64 *max)
309*9eef97a9SJakub Kicinski {
310*9eef97a9SJakub Kicinski 	double sum = 0.0, variance = 0.0;
311*9eef97a9SJakub Kicinski 	unsigned int i;
312*9eef97a9SJakub Kicinski 
313*9eef97a9SJakub Kicinski 	*min = ~0ULL;
314*9eef97a9SJakub Kicinski 	*max = 0;
315*9eef97a9SJakub Kicinski 
316*9eef97a9SJakub Kicinski 	if (count == 0) {
317*9eef97a9SJakub Kicinski 		*mean = 0;
318*9eef97a9SJakub Kicinski 		*stddev = 0;
319*9eef97a9SJakub Kicinski 		*min = 0;
320*9eef97a9SJakub Kicinski 		return;
321*9eef97a9SJakub Kicinski 	}
322*9eef97a9SJakub Kicinski 
323*9eef97a9SJakub Kicinski 	for (i = 0; i < count; i++) {
324*9eef97a9SJakub Kicinski 		sum += values[i];
325*9eef97a9SJakub Kicinski 		if (values[i] < *min)
326*9eef97a9SJakub Kicinski 			*min = values[i];
327*9eef97a9SJakub Kicinski 		if (values[i] > *max)
328*9eef97a9SJakub Kicinski 			*max = values[i];
329*9eef97a9SJakub Kicinski 	}
330*9eef97a9SJakub Kicinski 
331*9eef97a9SJakub Kicinski 	*mean = sum / count;
332*9eef97a9SJakub Kicinski 
333*9eef97a9SJakub Kicinski 	if (count > 1) {
334*9eef97a9SJakub Kicinski 		for (i = 0; i < count; i++) {
335*9eef97a9SJakub Kicinski 			double diff = values[i] - *mean;
336*9eef97a9SJakub Kicinski 
337*9eef97a9SJakub Kicinski 			variance += diff * diff;
338*9eef97a9SJakub Kicinski 		}
339*9eef97a9SJakub Kicinski 		*stddev = sqrt(variance / (count - 1));
340*9eef97a9SJakub Kicinski 	} else {
341*9eef97a9SJakub Kicinski 		*stddev = 0;
342*9eef97a9SJakub Kicinski 	}
343*9eef97a9SJakub Kicinski }
344*9eef97a9SJakub Kicinski 
345*9eef97a9SJakub Kicinski static void print_balance_stats(const char *name, enum netdev_queue_type type,
346*9eef97a9SJakub Kicinski 				__u64 *values, unsigned int count)
347*9eef97a9SJakub Kicinski {
348*9eef97a9SJakub Kicinski 	double mean, stddev, cv, ns;
349*9eef97a9SJakub Kicinski 	__u64 min, max;
350*9eef97a9SJakub Kicinski 
351*9eef97a9SJakub Kicinski 	if ((name[0] == 'r' && type != NETDEV_QUEUE_TYPE_RX) ||
352*9eef97a9SJakub Kicinski 	    (name[0] == 't' && type != NETDEV_QUEUE_TYPE_TX))
353*9eef97a9SJakub Kicinski 		return;
354*9eef97a9SJakub Kicinski 
355*9eef97a9SJakub Kicinski 	compute_stats(values, count, &mean, &stddev, &min, &max);
356*9eef97a9SJakub Kicinski 
357*9eef97a9SJakub Kicinski 	cv = mean > 0 ? (stddev / mean) * 100.0 : 0.0;
358*9eef97a9SJakub Kicinski 	ns = min + max > 0 ? (double)2 * (max - min) / (max + min) * 100 : 0.0;
359*9eef97a9SJakub Kicinski 
360*9eef97a9SJakub Kicinski 	printf("  %-12s: cv=%.1f%% ns=%.1f%% stddev=%.0f\n",
361*9eef97a9SJakub Kicinski 	       name, cv, ns, stddev);
362*9eef97a9SJakub Kicinski 	printf("  %-12s  min=%llu max=%llu mean=%.0f\n",
363*9eef97a9SJakub Kicinski 	       "", min, max, mean);
364*9eef97a9SJakub Kicinski }
365*9eef97a9SJakub Kicinski 
366*9eef97a9SJakub Kicinski static void
367*9eef97a9SJakub Kicinski print_balance_stats_json(const char *name, enum netdev_queue_type type,
368*9eef97a9SJakub Kicinski 			 __u64 *values, unsigned int count)
369*9eef97a9SJakub Kicinski {
370*9eef97a9SJakub Kicinski 	double mean, stddev, cv, ns;
371*9eef97a9SJakub Kicinski 	__u64 min, max;
372*9eef97a9SJakub Kicinski 
373*9eef97a9SJakub Kicinski 	if ((name[0] == 'r' && type != NETDEV_QUEUE_TYPE_RX) ||
374*9eef97a9SJakub Kicinski 	    (name[0] == 't' && type != NETDEV_QUEUE_TYPE_TX))
375*9eef97a9SJakub Kicinski 		return;
376*9eef97a9SJakub Kicinski 
377*9eef97a9SJakub Kicinski 	compute_stats(values, count, &mean, &stddev, &min, &max);
378*9eef97a9SJakub Kicinski 
379*9eef97a9SJakub Kicinski 	cv = mean > 0 ? (stddev / mean) * 100.0 : 0.0;
380*9eef97a9SJakub Kicinski 	ns = min + max > 0 ? (double)2 * (max - min) / (max + min) * 100 : 0.0;
381*9eef97a9SJakub Kicinski 
382*9eef97a9SJakub Kicinski 	jsonw_name(json_wtr, name);
383*9eef97a9SJakub Kicinski 	jsonw_start_object(json_wtr);
384*9eef97a9SJakub Kicinski 	jsonw_uint_field(json_wtr, "queue-count", count);
385*9eef97a9SJakub Kicinski 	jsonw_uint_field(json_wtr, "min", min);
386*9eef97a9SJakub Kicinski 	jsonw_uint_field(json_wtr, "max", max);
387*9eef97a9SJakub Kicinski 	jsonw_float_field(json_wtr, "mean", mean);
388*9eef97a9SJakub Kicinski 	jsonw_float_field(json_wtr, "stddev", stddev);
389*9eef97a9SJakub Kicinski 	jsonw_float_field(json_wtr, "coefficient-of-variation", cv);
390*9eef97a9SJakub Kicinski 	jsonw_float_field(json_wtr, "normalized-spread", ns);
391*9eef97a9SJakub Kicinski 	jsonw_end_object(json_wtr);
392*9eef97a9SJakub Kicinski }
393*9eef97a9SJakub Kicinski 
394*9eef97a9SJakub Kicinski static int cmp_ifindex_type(const void *a, const void *b)
395*9eef97a9SJakub Kicinski {
396*9eef97a9SJakub Kicinski 	const struct netdev_qstats_get_rsp *qa = a;
397*9eef97a9SJakub Kicinski 	const struct netdev_qstats_get_rsp *qb = b;
398*9eef97a9SJakub Kicinski 
399*9eef97a9SJakub Kicinski 	if (qa->ifindex != qb->ifindex)
400*9eef97a9SJakub Kicinski 		return qa->ifindex - qb->ifindex;
401*9eef97a9SJakub Kicinski 	if (qa->queue_type != qb->queue_type)
402*9eef97a9SJakub Kicinski 		return qa->queue_type - qb->queue_type;
403*9eef97a9SJakub Kicinski 	return qa->queue_id - qb->queue_id;
404*9eef97a9SJakub Kicinski }
405*9eef97a9SJakub Kicinski 
406*9eef97a9SJakub Kicinski static int do_balance(int argc, char **argv __attribute__((unused)))
407*9eef97a9SJakub Kicinski {
408*9eef97a9SJakub Kicinski 	struct netdev_qstats_get_list *qstats;
409*9eef97a9SJakub Kicinski 	struct netdev_qstats_get_req *req;
410*9eef97a9SJakub Kicinski 	struct netdev_qstats_get_rsp **sorted;
411*9eef97a9SJakub Kicinski 	struct ynl_error yerr;
412*9eef97a9SJakub Kicinski 	struct ynl_sock *ys;
413*9eef97a9SJakub Kicinski 	unsigned int count = 0;
414*9eef97a9SJakub Kicinski 	unsigned int i, j;
415*9eef97a9SJakub Kicinski 	int ret = 0;
416*9eef97a9SJakub Kicinski 
417*9eef97a9SJakub Kicinski 	if (argc > 0) {
418*9eef97a9SJakub Kicinski 		p_err("balance command takes no arguments");
419*9eef97a9SJakub Kicinski 		return -1;
420*9eef97a9SJakub Kicinski 	}
421*9eef97a9SJakub Kicinski 
422*9eef97a9SJakub Kicinski 	ys = ynl_sock_create(&ynl_netdev_family, &yerr);
423*9eef97a9SJakub Kicinski 	if (!ys) {
424*9eef97a9SJakub Kicinski 		p_err("YNL: %s", yerr.msg);
425*9eef97a9SJakub Kicinski 		return -1;
426*9eef97a9SJakub Kicinski 	}
427*9eef97a9SJakub Kicinski 
428*9eef97a9SJakub Kicinski 	req = netdev_qstats_get_req_alloc();
429*9eef97a9SJakub Kicinski 	if (!req) {
430*9eef97a9SJakub Kicinski 		p_err("failed to allocate qstats request");
431*9eef97a9SJakub Kicinski 		ret = -1;
432*9eef97a9SJakub Kicinski 		goto exit_close;
433*9eef97a9SJakub Kicinski 	}
434*9eef97a9SJakub Kicinski 
435*9eef97a9SJakub Kicinski 	/* Always use queue scope for balance analysis */
436*9eef97a9SJakub Kicinski 	netdev_qstats_get_req_set_scope(req, NETDEV_QSTATS_SCOPE_QUEUE);
437*9eef97a9SJakub Kicinski 
438*9eef97a9SJakub Kicinski 	qstats = netdev_qstats_get_dump(ys, req);
439*9eef97a9SJakub Kicinski 	netdev_qstats_get_req_free(req);
440*9eef97a9SJakub Kicinski 	if (!qstats) {
441*9eef97a9SJakub Kicinski 		p_err("failed to get queue stats: %s", ys->err.msg);
442*9eef97a9SJakub Kicinski 		ret = -1;
443*9eef97a9SJakub Kicinski 		goto exit_close;
444*9eef97a9SJakub Kicinski 	}
445*9eef97a9SJakub Kicinski 
446*9eef97a9SJakub Kicinski 	/* Count and sort queues */
447*9eef97a9SJakub Kicinski 	ynl_dump_foreach(qstats, qs)
448*9eef97a9SJakub Kicinski 		count++;
449*9eef97a9SJakub Kicinski 
450*9eef97a9SJakub Kicinski 	if (count == 0) {
451*9eef97a9SJakub Kicinski 		if (json_output)
452*9eef97a9SJakub Kicinski 			jsonw_start_array(json_wtr);
453*9eef97a9SJakub Kicinski 		else
454*9eef97a9SJakub Kicinski 			printf("No queue statistics available\n");
455*9eef97a9SJakub Kicinski 		goto exit_free_qstats;
456*9eef97a9SJakub Kicinski 	}
457*9eef97a9SJakub Kicinski 
458*9eef97a9SJakub Kicinski 	sorted = calloc(count, sizeof(*sorted));
459*9eef97a9SJakub Kicinski 	if (!sorted) {
460*9eef97a9SJakub Kicinski 		p_err("failed to allocate sorted array");
461*9eef97a9SJakub Kicinski 		ret = -1;
462*9eef97a9SJakub Kicinski 		goto exit_free_qstats;
463*9eef97a9SJakub Kicinski 	}
464*9eef97a9SJakub Kicinski 
465*9eef97a9SJakub Kicinski 	i = 0;
466*9eef97a9SJakub Kicinski 	ynl_dump_foreach(qstats, qs)
467*9eef97a9SJakub Kicinski 		sorted[i++] = qs;
468*9eef97a9SJakub Kicinski 
469*9eef97a9SJakub Kicinski 	qsort(sorted, count, sizeof(*sorted), cmp_ifindex_type);
470*9eef97a9SJakub Kicinski 
471*9eef97a9SJakub Kicinski 	if (json_output)
472*9eef97a9SJakub Kicinski 		jsonw_start_array(json_wtr);
473*9eef97a9SJakub Kicinski 
474*9eef97a9SJakub Kicinski 	/* Process each device/queue-type combination */
475*9eef97a9SJakub Kicinski 	i = 0;
476*9eef97a9SJakub Kicinski 	while (i < count) {
477*9eef97a9SJakub Kicinski 		__u64 *rx_packets, *rx_bytes, *tx_packets, *tx_bytes;
478*9eef97a9SJakub Kicinski 		enum netdev_queue_type type = sorted[i]->queue_type;
479*9eef97a9SJakub Kicinski 		unsigned int ifindex = sorted[i]->ifindex;
480*9eef97a9SJakub Kicinski 		unsigned int queue_count = 0;
481*9eef97a9SJakub Kicinski 		char ifname[IF_NAMESIZE];
482*9eef97a9SJakub Kicinski 		const char *name;
483*9eef97a9SJakub Kicinski 
484*9eef97a9SJakub Kicinski 		/* Count queues for this device/type */
485*9eef97a9SJakub Kicinski 		for (j = i; j < count && sorted[j]->ifindex == ifindex &&
486*9eef97a9SJakub Kicinski 		     sorted[j]->queue_type == type; j++)
487*9eef97a9SJakub Kicinski 			queue_count++;
488*9eef97a9SJakub Kicinski 
489*9eef97a9SJakub Kicinski 		/* Skip if no packets/bytes (inactive queues) */
490*9eef97a9SJakub Kicinski 		if (!sorted[i]->_present.rx_packets &&
491*9eef97a9SJakub Kicinski 		    !sorted[i]->_present.rx_bytes &&
492*9eef97a9SJakub Kicinski 		    !sorted[i]->_present.tx_packets &&
493*9eef97a9SJakub Kicinski 		    !sorted[i]->_present.tx_bytes)
494*9eef97a9SJakub Kicinski 			goto next_ifc;
495*9eef97a9SJakub Kicinski 
496*9eef97a9SJakub Kicinski 		/* Allocate arrays for statistics */
497*9eef97a9SJakub Kicinski 		rx_packets = calloc(queue_count, sizeof(*rx_packets));
498*9eef97a9SJakub Kicinski 		rx_bytes   = calloc(queue_count, sizeof(*rx_bytes));
499*9eef97a9SJakub Kicinski 		tx_packets = calloc(queue_count, sizeof(*tx_packets));
500*9eef97a9SJakub Kicinski 		tx_bytes   = calloc(queue_count, sizeof(*tx_bytes));
501*9eef97a9SJakub Kicinski 
502*9eef97a9SJakub Kicinski 		if (!rx_packets || !rx_bytes || !tx_packets || !tx_bytes) {
503*9eef97a9SJakub Kicinski 			p_err("failed to allocate statistics arrays");
504*9eef97a9SJakub Kicinski 			free(rx_packets);
505*9eef97a9SJakub Kicinski 			free(rx_bytes);
506*9eef97a9SJakub Kicinski 			free(tx_packets);
507*9eef97a9SJakub Kicinski 			free(tx_bytes);
508*9eef97a9SJakub Kicinski 			ret = -1;
509*9eef97a9SJakub Kicinski 			goto exit_free_sorted;
510*9eef97a9SJakub Kicinski 		}
511*9eef97a9SJakub Kicinski 
512*9eef97a9SJakub Kicinski 		/* Collect statistics */
513*9eef97a9SJakub Kicinski 		for (j = 0; j < queue_count; j++) {
514*9eef97a9SJakub Kicinski 			rx_packets[j] = sorted[i + j]->_present.rx_packets ?
515*9eef97a9SJakub Kicinski 					sorted[i + j]->rx_packets : 0;
516*9eef97a9SJakub Kicinski 			rx_bytes[j] = sorted[i + j]->_present.rx_bytes ?
517*9eef97a9SJakub Kicinski 				      sorted[i + j]->rx_bytes : 0;
518*9eef97a9SJakub Kicinski 			tx_packets[j] = sorted[i + j]->_present.tx_packets ?
519*9eef97a9SJakub Kicinski 					sorted[i + j]->tx_packets : 0;
520*9eef97a9SJakub Kicinski 			tx_bytes[j] = sorted[i + j]->_present.tx_bytes ?
521*9eef97a9SJakub Kicinski 				      sorted[i + j]->tx_bytes : 0;
522*9eef97a9SJakub Kicinski 		}
523*9eef97a9SJakub Kicinski 
524*9eef97a9SJakub Kicinski 		name = if_indextoname(ifindex, ifname);
525*9eef97a9SJakub Kicinski 
526*9eef97a9SJakub Kicinski 		if (json_output) {
527*9eef97a9SJakub Kicinski 			jsonw_start_object(json_wtr);
528*9eef97a9SJakub Kicinski 			if (name)
529*9eef97a9SJakub Kicinski 				jsonw_string_field(json_wtr, "ifname", name);
530*9eef97a9SJakub Kicinski 			jsonw_uint_field(json_wtr, "ifindex", ifindex);
531*9eef97a9SJakub Kicinski 			jsonw_string_field(json_wtr, "queue-type",
532*9eef97a9SJakub Kicinski 					   netdev_queue_type_str(type));
533*9eef97a9SJakub Kicinski 
534*9eef97a9SJakub Kicinski 			print_balance_stats_json("rx-packets", type,
535*9eef97a9SJakub Kicinski 						 rx_packets, queue_count);
536*9eef97a9SJakub Kicinski 			print_balance_stats_json("rx-bytes", type,
537*9eef97a9SJakub Kicinski 						 rx_bytes, queue_count);
538*9eef97a9SJakub Kicinski 			print_balance_stats_json("tx-packets", type,
539*9eef97a9SJakub Kicinski 						 tx_packets, queue_count);
540*9eef97a9SJakub Kicinski 			print_balance_stats_json("tx-bytes", type,
541*9eef97a9SJakub Kicinski 						 tx_bytes, queue_count);
542*9eef97a9SJakub Kicinski 
543*9eef97a9SJakub Kicinski 			jsonw_end_object(json_wtr);
544*9eef97a9SJakub Kicinski 		} else {
545*9eef97a9SJakub Kicinski 			if (name)
546*9eef97a9SJakub Kicinski 				printf("%s", name);
547*9eef97a9SJakub Kicinski 			else
548*9eef97a9SJakub Kicinski 				printf("ifindex:%u", ifindex);
549*9eef97a9SJakub Kicinski 			printf(" %s %d queues:\n",
550*9eef97a9SJakub Kicinski 			       netdev_queue_type_str(type), queue_count);
551*9eef97a9SJakub Kicinski 
552*9eef97a9SJakub Kicinski 			print_balance_stats("rx-packets", type,
553*9eef97a9SJakub Kicinski 					    rx_packets, queue_count);
554*9eef97a9SJakub Kicinski 			print_balance_stats("rx-bytes", type,
555*9eef97a9SJakub Kicinski 					    rx_bytes, queue_count);
556*9eef97a9SJakub Kicinski 			print_balance_stats("tx-packets", type,
557*9eef97a9SJakub Kicinski 					    tx_packets, queue_count);
558*9eef97a9SJakub Kicinski 			print_balance_stats("tx-bytes", type,
559*9eef97a9SJakub Kicinski 					    tx_bytes, queue_count);
560*9eef97a9SJakub Kicinski 			printf("\n");
561*9eef97a9SJakub Kicinski 		}
562*9eef97a9SJakub Kicinski 
563*9eef97a9SJakub Kicinski 		free(rx_packets);
564*9eef97a9SJakub Kicinski 		free(rx_bytes);
565*9eef97a9SJakub Kicinski 		free(tx_packets);
566*9eef97a9SJakub Kicinski 		free(tx_bytes);
567*9eef97a9SJakub Kicinski 
568*9eef97a9SJakub Kicinski next_ifc:
569*9eef97a9SJakub Kicinski 		i += queue_count;
570*9eef97a9SJakub Kicinski 	}
571*9eef97a9SJakub Kicinski 
572*9eef97a9SJakub Kicinski 	if (json_output)
573*9eef97a9SJakub Kicinski 		jsonw_end_array(json_wtr);
574*9eef97a9SJakub Kicinski 
575*9eef97a9SJakub Kicinski exit_free_sorted:
576*9eef97a9SJakub Kicinski 	free(sorted);
577*9eef97a9SJakub Kicinski exit_free_qstats:
578*9eef97a9SJakub Kicinski 	netdev_qstats_get_list_free(qstats);
579*9eef97a9SJakub Kicinski exit_close:
580*9eef97a9SJakub Kicinski 	ynl_sock_destroy(ys);
581*9eef97a9SJakub Kicinski 	return ret;
582*9eef97a9SJakub Kicinski }
583*9eef97a9SJakub Kicinski 
5843f0a638dSJakub Kicinski static int do_help(int argc __attribute__((unused)),
5853f0a638dSJakub Kicinski 		   char **argv __attribute__((unused)))
5863f0a638dSJakub Kicinski {
5873f0a638dSJakub Kicinski 	if (json_output) {
5883f0a638dSJakub Kicinski 		jsonw_null(json_wtr);
5893f0a638dSJakub Kicinski 		return 0;
5903f0a638dSJakub Kicinski 	}
5913f0a638dSJakub Kicinski 
5923f0a638dSJakub Kicinski 	fprintf(stderr,
5933f0a638dSJakub Kicinski 		"Usage: %s qstats { COMMAND | help }\n"
5943f0a638dSJakub Kicinski 		"       %s qstats [ show ] [ OPTIONS ]\n"
595*9eef97a9SJakub Kicinski 		"       %s qstats balance\n"
5963f0a638dSJakub Kicinski 		"\n"
5973f0a638dSJakub Kicinski 		"       OPTIONS := { scope queue | group-by { device | queue } }\n"
5983f0a638dSJakub Kicinski 		"\n"
5993f0a638dSJakub Kicinski 		"       show                  - Display queue statistics (default)\n"
6003f0a638dSJakub Kicinski 		"                               Statistics are aggregated for the entire device.\n"
6013f0a638dSJakub Kicinski 		"       show scope queue      - Display per-queue statistics\n"
6023f0a638dSJakub Kicinski 		"       show group-by device  - Display device-aggregated statistics (default)\n"
6033f0a638dSJakub Kicinski 		"       show group-by queue   - Display per-queue statistics\n"
604*9eef97a9SJakub Kicinski 		"       balance               - Analyze traffic distribution balance.\n"
6053f0a638dSJakub Kicinski 		"",
606*9eef97a9SJakub Kicinski 		bin_name, bin_name, bin_name);
6073f0a638dSJakub Kicinski 
6083f0a638dSJakub Kicinski 	return 0;
6093f0a638dSJakub Kicinski }
6103f0a638dSJakub Kicinski 
6113f0a638dSJakub Kicinski static const struct cmd qstats_cmds[] = {
6123f0a638dSJakub Kicinski 	{ "show",	do_show },
613*9eef97a9SJakub Kicinski 	{ "balance",	do_balance },
6143f0a638dSJakub Kicinski 	{ "help",	do_help },
6153f0a638dSJakub Kicinski 	{ 0 }
6163f0a638dSJakub Kicinski };
6173f0a638dSJakub Kicinski 
6183f0a638dSJakub Kicinski int do_qstats(int argc, char **argv)
6193f0a638dSJakub Kicinski {
6203f0a638dSJakub Kicinski 	return cmd_select(qstats_cmds, argc, argv, do_help);
6213f0a638dSJakub Kicinski }
622