Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

tools: ynltool: add qstats support

$ ynltool qstat
eth0 rx-packets: 493192163 rx-bytes: 1442544543997
tx-packets: 745999838 tx-bytes: 4574215826482
tx-stop: 7033 tx-wake: 7033

$ ynltool qstat show group-by queue
eth0 rx-0 packets: 70196880 bytes: 178633973750
eth0 rx-1 packets: 63623419 bytes: 197274745250
...
eth0 tx-1 packets: 98645810 bytes: 631247647938
stop: 1048 wake: 1048
eth0 tx-2 packets: 86775824 bytes: 563930471952
stop: 1126 wake: 1126
...

$ ynltool -j qstat | jq
[
{
"ifname": "eth0",
"ifindex": 2,
"rx": {
"packets": 493396439,
"bytes": 1443608198921
},
"tx": {
"packets": 746239978,
"bytes": 4574333772645,
"stop": 7072,
"wake": 7072
}
}
]

Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Link: https://patch.msgid.link/20251107162227.980672-4-kuba@kernel.org
Acked-by: Stanislav Fomichev <sdf@fomichev.me>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>

authored by

Jakub Kicinski and committed by
Paolo Abeni
3f0a638d 124dac9b

+333 -1
+2 -1
tools/net/ynl/ynltool/main.c
··· 47 47 "Usage: %s [OPTIONS] OBJECT { COMMAND | help }\n" 48 48 " %s version\n" 49 49 "\n" 50 - " OBJECT := { page-pool }\n" 50 + " OBJECT := { page-pool | qstats }\n" 51 51 " " HELP_SPEC_OPTIONS "\n" 52 52 "", 53 53 bin_name, bin_name); ··· 72 72 static const struct cmd commands[] = { 73 73 { "help", do_help }, 74 74 { "page-pool", do_page_pool }, 75 + { "qstats", do_qstats }, 75 76 { "version", do_version }, 76 77 { 0 } 77 78 };
+1
tools/net/ynl/ynltool/main.h
··· 61 61 62 62 /* subcommands */ 63 63 int do_page_pool(int argc, char **argv); 64 + int do_qstats(int argc, char **argv); 64 65 65 66 #endif /* __YNLTOOL_H */
+330
tools/net/ynl/ynltool/qstats.c
··· 1 + // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 2 + 3 + #include <stdio.h> 4 + #include <stdlib.h> 5 + #include <string.h> 6 + #include <errno.h> 7 + #include <net/if.h> 8 + 9 + #include <ynl.h> 10 + #include "netdev-user.h" 11 + 12 + #include "main.h" 13 + 14 + static enum netdev_qstats_scope scope; /* default - device */ 15 + 16 + static void print_json_qstats(struct netdev_qstats_get_list *qstats) 17 + { 18 + jsonw_start_array(json_wtr); 19 + 20 + ynl_dump_foreach(qstats, qs) { 21 + char ifname[IF_NAMESIZE]; 22 + const char *name; 23 + 24 + jsonw_start_object(json_wtr); 25 + 26 + name = if_indextoname(qs->ifindex, ifname); 27 + if (name) 28 + jsonw_string_field(json_wtr, "ifname", name); 29 + jsonw_uint_field(json_wtr, "ifindex", qs->ifindex); 30 + 31 + if (qs->_present.queue_type) 32 + jsonw_string_field(json_wtr, "queue-type", 33 + netdev_queue_type_str(qs->queue_type)); 34 + if (qs->_present.queue_id) 35 + jsonw_uint_field(json_wtr, "queue-id", qs->queue_id); 36 + 37 + if (qs->_present.rx_packets || qs->_present.rx_bytes || 38 + qs->_present.rx_alloc_fail || qs->_present.rx_hw_drops || 39 + qs->_present.rx_csum_complete || qs->_present.rx_hw_gro_packets) { 40 + jsonw_name(json_wtr, "rx"); 41 + jsonw_start_object(json_wtr); 42 + if (qs->_present.rx_packets) 43 + jsonw_uint_field(json_wtr, "packets", qs->rx_packets); 44 + if (qs->_present.rx_bytes) 45 + jsonw_uint_field(json_wtr, "bytes", qs->rx_bytes); 46 + if (qs->_present.rx_alloc_fail) 47 + jsonw_uint_field(json_wtr, "alloc-fail", qs->rx_alloc_fail); 48 + if (qs->_present.rx_hw_drops) 49 + jsonw_uint_field(json_wtr, "hw-drops", qs->rx_hw_drops); 50 + if (qs->_present.rx_hw_drop_overruns) 51 + jsonw_uint_field(json_wtr, "hw-drop-overruns", qs->rx_hw_drop_overruns); 52 + if (qs->_present.rx_hw_drop_ratelimits) 53 + jsonw_uint_field(json_wtr, "hw-drop-ratelimits", qs->rx_hw_drop_ratelimits); 54 + if (qs->_present.rx_csum_complete) 55 + jsonw_uint_field(json_wtr, "csum-complete", qs->rx_csum_complete); 56 + if (qs->_present.rx_csum_unnecessary) 57 + jsonw_uint_field(json_wtr, "csum-unnecessary", qs->rx_csum_unnecessary); 58 + if (qs->_present.rx_csum_none) 59 + jsonw_uint_field(json_wtr, "csum-none", qs->rx_csum_none); 60 + if (qs->_present.rx_csum_bad) 61 + jsonw_uint_field(json_wtr, "csum-bad", qs->rx_csum_bad); 62 + if (qs->_present.rx_hw_gro_packets) 63 + jsonw_uint_field(json_wtr, "hw-gro-packets", qs->rx_hw_gro_packets); 64 + if (qs->_present.rx_hw_gro_bytes) 65 + jsonw_uint_field(json_wtr, "hw-gro-bytes", qs->rx_hw_gro_bytes); 66 + if (qs->_present.rx_hw_gro_wire_packets) 67 + jsonw_uint_field(json_wtr, "hw-gro-wire-packets", qs->rx_hw_gro_wire_packets); 68 + if (qs->_present.rx_hw_gro_wire_bytes) 69 + jsonw_uint_field(json_wtr, "hw-gro-wire-bytes", qs->rx_hw_gro_wire_bytes); 70 + jsonw_end_object(json_wtr); 71 + } 72 + 73 + if (qs->_present.tx_packets || qs->_present.tx_bytes || 74 + qs->_present.tx_hw_drops || qs->_present.tx_csum_none || 75 + qs->_present.tx_hw_gso_packets) { 76 + jsonw_name(json_wtr, "tx"); 77 + jsonw_start_object(json_wtr); 78 + if (qs->_present.tx_packets) 79 + jsonw_uint_field(json_wtr, "packets", qs->tx_packets); 80 + if (qs->_present.tx_bytes) 81 + jsonw_uint_field(json_wtr, "bytes", qs->tx_bytes); 82 + if (qs->_present.tx_hw_drops) 83 + jsonw_uint_field(json_wtr, "hw-drops", qs->tx_hw_drops); 84 + if (qs->_present.tx_hw_drop_errors) 85 + jsonw_uint_field(json_wtr, "hw-drop-errors", qs->tx_hw_drop_errors); 86 + if (qs->_present.tx_hw_drop_ratelimits) 87 + jsonw_uint_field(json_wtr, "hw-drop-ratelimits", qs->tx_hw_drop_ratelimits); 88 + if (qs->_present.tx_csum_none) 89 + jsonw_uint_field(json_wtr, "csum-none", qs->tx_csum_none); 90 + if (qs->_present.tx_needs_csum) 91 + jsonw_uint_field(json_wtr, "needs-csum", qs->tx_needs_csum); 92 + if (qs->_present.tx_hw_gso_packets) 93 + jsonw_uint_field(json_wtr, "hw-gso-packets", qs->tx_hw_gso_packets); 94 + if (qs->_present.tx_hw_gso_bytes) 95 + jsonw_uint_field(json_wtr, "hw-gso-bytes", qs->tx_hw_gso_bytes); 96 + if (qs->_present.tx_hw_gso_wire_packets) 97 + jsonw_uint_field(json_wtr, "hw-gso-wire-packets", qs->tx_hw_gso_wire_packets); 98 + if (qs->_present.tx_hw_gso_wire_bytes) 99 + jsonw_uint_field(json_wtr, "hw-gso-wire-bytes", qs->tx_hw_gso_wire_bytes); 100 + if (qs->_present.tx_stop) 101 + jsonw_uint_field(json_wtr, "stop", qs->tx_stop); 102 + if (qs->_present.tx_wake) 103 + jsonw_uint_field(json_wtr, "wake", qs->tx_wake); 104 + jsonw_end_object(json_wtr); 105 + } 106 + 107 + jsonw_end_object(json_wtr); 108 + } 109 + 110 + jsonw_end_array(json_wtr); 111 + } 112 + 113 + static void print_one(bool present, const char *name, unsigned long long val, 114 + int *line) 115 + { 116 + if (!present) 117 + return; 118 + 119 + if (!*line) { 120 + printf(" "); 121 + ++(*line); 122 + } 123 + 124 + /* Don't waste space on tx- and rx- prefix, its implied by queue type */ 125 + if (scope == NETDEV_QSTATS_SCOPE_QUEUE && 126 + (name[0] == 'r' || name[0] == 't') && 127 + name[1] == 'x' && name[2] == '-') 128 + name += 3; 129 + 130 + printf(" %15s: %15llu", name, val); 131 + 132 + if (++(*line) == 3) { 133 + printf("\n"); 134 + *line = 0; 135 + } 136 + } 137 + 138 + static void print_plain_qstats(struct netdev_qstats_get_list *qstats) 139 + { 140 + ynl_dump_foreach(qstats, qs) { 141 + char ifname[IF_NAMESIZE]; 142 + const char *name; 143 + int n; 144 + 145 + name = if_indextoname(qs->ifindex, ifname); 146 + if (name) 147 + printf("%s", name); 148 + else 149 + printf("ifindex:%u", qs->ifindex); 150 + 151 + if (qs->_present.queue_type && qs->_present.queue_id) 152 + printf("\t%s-%-3u", 153 + netdev_queue_type_str(qs->queue_type), 154 + qs->queue_id); 155 + else 156 + printf("\t "); 157 + 158 + n = 1; 159 + 160 + /* Basic counters */ 161 + print_one(qs->_present.rx_packets, "rx-packets", qs->rx_packets, &n); 162 + print_one(qs->_present.rx_bytes, "rx-bytes", qs->rx_bytes, &n); 163 + print_one(qs->_present.tx_packets, "tx-packets", qs->tx_packets, &n); 164 + print_one(qs->_present.tx_bytes, "tx-bytes", qs->tx_bytes, &n); 165 + 166 + /* RX error/drop counters */ 167 + print_one(qs->_present.rx_alloc_fail, "rx-alloc-fail", 168 + qs->rx_alloc_fail, &n); 169 + print_one(qs->_present.rx_hw_drops, "rx-hw-drops", 170 + qs->rx_hw_drops, &n); 171 + print_one(qs->_present.rx_hw_drop_overruns, "rx-hw-drop-overruns", 172 + qs->rx_hw_drop_overruns, &n); 173 + print_one(qs->_present.rx_hw_drop_ratelimits, "rx-hw-drop-ratelimits", 174 + qs->rx_hw_drop_ratelimits, &n); 175 + 176 + /* RX checksum counters */ 177 + print_one(qs->_present.rx_csum_complete, "rx-csum-complete", 178 + qs->rx_csum_complete, &n); 179 + print_one(qs->_present.rx_csum_unnecessary, "rx-csum-unnecessary", 180 + qs->rx_csum_unnecessary, &n); 181 + print_one(qs->_present.rx_csum_none, "rx-csum-none", 182 + qs->rx_csum_none, &n); 183 + print_one(qs->_present.rx_csum_bad, "rx-csum-bad", 184 + qs->rx_csum_bad, &n); 185 + 186 + /* RX GRO counters */ 187 + print_one(qs->_present.rx_hw_gro_packets, "rx-hw-gro-packets", 188 + qs->rx_hw_gro_packets, &n); 189 + print_one(qs->_present.rx_hw_gro_bytes, "rx-hw-gro-bytes", 190 + qs->rx_hw_gro_bytes, &n); 191 + print_one(qs->_present.rx_hw_gro_wire_packets, "rx-hw-gro-wire-packets", 192 + qs->rx_hw_gro_wire_packets, &n); 193 + print_one(qs->_present.rx_hw_gro_wire_bytes, "rx-hw-gro-wire-bytes", 194 + qs->rx_hw_gro_wire_bytes, &n); 195 + 196 + /* TX error/drop counters */ 197 + print_one(qs->_present.tx_hw_drops, "tx-hw-drops", 198 + qs->tx_hw_drops, &n); 199 + print_one(qs->_present.tx_hw_drop_errors, "tx-hw-drop-errors", 200 + qs->tx_hw_drop_errors, &n); 201 + print_one(qs->_present.tx_hw_drop_ratelimits, "tx-hw-drop-ratelimits", 202 + qs->tx_hw_drop_ratelimits, &n); 203 + 204 + /* TX checksum counters */ 205 + print_one(qs->_present.tx_csum_none, "tx-csum-none", 206 + qs->tx_csum_none, &n); 207 + print_one(qs->_present.tx_needs_csum, "tx-needs-csum", 208 + qs->tx_needs_csum, &n); 209 + 210 + /* TX GSO counters */ 211 + print_one(qs->_present.tx_hw_gso_packets, "tx-hw-gso-packets", 212 + qs->tx_hw_gso_packets, &n); 213 + print_one(qs->_present.tx_hw_gso_bytes, "tx-hw-gso-bytes", 214 + qs->tx_hw_gso_bytes, &n); 215 + print_one(qs->_present.tx_hw_gso_wire_packets, "tx-hw-gso-wire-packets", 216 + qs->tx_hw_gso_wire_packets, &n); 217 + print_one(qs->_present.tx_hw_gso_wire_bytes, "tx-hw-gso-wire-bytes", 218 + qs->tx_hw_gso_wire_bytes, &n); 219 + 220 + /* TX queue control */ 221 + print_one(qs->_present.tx_stop, "tx-stop", qs->tx_stop, &n); 222 + print_one(qs->_present.tx_wake, "tx-wake", qs->tx_wake, &n); 223 + 224 + if (n) 225 + printf("\n"); 226 + } 227 + } 228 + 229 + static int do_show(int argc, char **argv) 230 + { 231 + struct netdev_qstats_get_list *qstats; 232 + struct netdev_qstats_get_req *req; 233 + struct ynl_error yerr; 234 + struct ynl_sock *ys; 235 + int ret = 0; 236 + 237 + /* Parse options */ 238 + while (argc > 0) { 239 + if (is_prefix(*argv, "scope") || is_prefix(*argv, "group-by")) { 240 + NEXT_ARG(); 241 + 242 + if (!REQ_ARGS(1)) 243 + return -1; 244 + 245 + if (is_prefix(*argv, "queue")) { 246 + scope = NETDEV_QSTATS_SCOPE_QUEUE; 247 + } else if (is_prefix(*argv, "device")) { 248 + scope = 0; 249 + } else { 250 + p_err("invalid scope value '%s'", *argv); 251 + return -1; 252 + } 253 + NEXT_ARG(); 254 + } else { 255 + p_err("unknown option '%s'", *argv); 256 + return -1; 257 + } 258 + } 259 + 260 + ys = ynl_sock_create(&ynl_netdev_family, &yerr); 261 + if (!ys) { 262 + p_err("YNL: %s", yerr.msg); 263 + return -1; 264 + } 265 + 266 + req = netdev_qstats_get_req_alloc(); 267 + if (!req) { 268 + p_err("failed to allocate qstats request"); 269 + ret = -1; 270 + goto exit_close; 271 + } 272 + 273 + if (scope) 274 + netdev_qstats_get_req_set_scope(req, scope); 275 + 276 + qstats = netdev_qstats_get_dump(ys, req); 277 + netdev_qstats_get_req_free(req); 278 + if (!qstats) { 279 + p_err("failed to get queue stats: %s", ys->err.msg); 280 + ret = -1; 281 + goto exit_close; 282 + } 283 + 284 + /* Print the stats as returned by the kernel */ 285 + if (json_output) 286 + print_json_qstats(qstats); 287 + else 288 + print_plain_qstats(qstats); 289 + 290 + netdev_qstats_get_list_free(qstats); 291 + exit_close: 292 + ynl_sock_destroy(ys); 293 + return ret; 294 + } 295 + 296 + static int do_help(int argc __attribute__((unused)), 297 + char **argv __attribute__((unused))) 298 + { 299 + if (json_output) { 300 + jsonw_null(json_wtr); 301 + return 0; 302 + } 303 + 304 + fprintf(stderr, 305 + "Usage: %s qstats { COMMAND | help }\n" 306 + " %s qstats [ show ] [ OPTIONS ]\n" 307 + "\n" 308 + " OPTIONS := { scope queue | group-by { device | queue } }\n" 309 + "\n" 310 + " show - Display queue statistics (default)\n" 311 + " Statistics are aggregated for the entire device.\n" 312 + " show scope queue - Display per-queue statistics\n" 313 + " show group-by device - Display device-aggregated statistics (default)\n" 314 + " show group-by queue - Display per-queue statistics\n" 315 + "", 316 + bin_name, bin_name); 317 + 318 + return 0; 319 + } 320 + 321 + static const struct cmd qstats_cmds[] = { 322 + { "show", do_show }, 323 + { "help", do_help }, 324 + { 0 } 325 + }; 326 + 327 + int do_qstats(int argc, char **argv) 328 + { 329 + return cmd_select(qstats_cmds, argc, argv, do_help); 330 + }