Skip to content

Commit 3f0a638

Browse files
kuba-mooPaolo Abeni
authored andcommitted
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 <[email protected]> Link: https://patch.msgid.link/[email protected] Acked-by: Stanislav Fomichev <[email protected]> Signed-off-by: Paolo Abeni <[email protected]>
1 parent 124dac9 commit 3f0a638

File tree

3 files changed

+333
-1
lines changed

3 files changed

+333
-1
lines changed

tools/net/ynl/ynltool/main.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ static int do_help(int argc __attribute__((unused)),
4747
"Usage: %s [OPTIONS] OBJECT { COMMAND | help }\n"
4848
" %s version\n"
4949
"\n"
50-
" OBJECT := { page-pool }\n"
50+
" OBJECT := { page-pool | qstats }\n"
5151
" " HELP_SPEC_OPTIONS "\n"
5252
"",
5353
bin_name, bin_name);
@@ -72,6 +72,7 @@ static int do_version(int argc __attribute__((unused)),
7272
static const struct cmd commands[] = {
7373
{ "help", do_help },
7474
{ "page-pool", do_page_pool },
75+
{ "qstats", do_qstats },
7576
{ "version", do_version },
7677
{ 0 }
7778
};

tools/net/ynl/ynltool/main.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,5 +61,6 @@ int cmd_select(const struct cmd *cmds, int argc, char **argv,
6161

6262
/* subcommands */
6363
int do_page_pool(int argc, char **argv);
64+
int do_qstats(int argc, char **argv);
6465

6566
#endif /* __YNLTOOL_H */

tools/net/ynl/ynltool/qstats.c

Lines changed: 330 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,330 @@
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+
}

0 commit comments

Comments
 (0)