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