Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/tools/net/ynl/ynltool/qstats.c
122941 views
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
#include <math.h>
9
10
#include <ynl.h>
11
#include "netdev-user.h"
12
13
#include "main.h"
14
15
static enum netdev_qstats_scope scope; /* default - device */
16
17
struct queue_balance {
18
unsigned int ifindex;
19
enum netdev_queue_type type;
20
unsigned int queue_count;
21
__u64 *rx_packets;
22
__u64 *rx_bytes;
23
__u64 *tx_packets;
24
__u64 *tx_bytes;
25
};
26
27
static void print_json_qstats(struct netdev_qstats_get_list *qstats)
28
{
29
jsonw_start_array(json_wtr);
30
31
ynl_dump_foreach(qstats, qs) {
32
char ifname[IF_NAMESIZE];
33
const char *name;
34
35
jsonw_start_object(json_wtr);
36
37
name = if_indextoname(qs->ifindex, ifname);
38
if (name)
39
jsonw_string_field(json_wtr, "ifname", name);
40
jsonw_uint_field(json_wtr, "ifindex", qs->ifindex);
41
42
if (qs->_present.queue_type)
43
jsonw_string_field(json_wtr, "queue-type",
44
netdev_queue_type_str(qs->queue_type));
45
if (qs->_present.queue_id)
46
jsonw_uint_field(json_wtr, "queue-id", qs->queue_id);
47
48
if (qs->_present.rx_packets || qs->_present.rx_bytes ||
49
qs->_present.rx_alloc_fail || qs->_present.rx_hw_drops ||
50
qs->_present.rx_csum_complete || qs->_present.rx_hw_gro_packets) {
51
jsonw_name(json_wtr, "rx");
52
jsonw_start_object(json_wtr);
53
if (qs->_present.rx_packets)
54
jsonw_uint_field(json_wtr, "packets", qs->rx_packets);
55
if (qs->_present.rx_bytes)
56
jsonw_uint_field(json_wtr, "bytes", qs->rx_bytes);
57
if (qs->_present.rx_alloc_fail)
58
jsonw_uint_field(json_wtr, "alloc-fail", qs->rx_alloc_fail);
59
if (qs->_present.rx_hw_drops)
60
jsonw_uint_field(json_wtr, "hw-drops", qs->rx_hw_drops);
61
if (qs->_present.rx_hw_drop_overruns)
62
jsonw_uint_field(json_wtr, "hw-drop-overruns", qs->rx_hw_drop_overruns);
63
if (qs->_present.rx_hw_drop_ratelimits)
64
jsonw_uint_field(json_wtr, "hw-drop-ratelimits", qs->rx_hw_drop_ratelimits);
65
if (qs->_present.rx_csum_complete)
66
jsonw_uint_field(json_wtr, "csum-complete", qs->rx_csum_complete);
67
if (qs->_present.rx_csum_unnecessary)
68
jsonw_uint_field(json_wtr, "csum-unnecessary", qs->rx_csum_unnecessary);
69
if (qs->_present.rx_csum_none)
70
jsonw_uint_field(json_wtr, "csum-none", qs->rx_csum_none);
71
if (qs->_present.rx_csum_bad)
72
jsonw_uint_field(json_wtr, "csum-bad", qs->rx_csum_bad);
73
if (qs->_present.rx_hw_gro_packets)
74
jsonw_uint_field(json_wtr, "hw-gro-packets", qs->rx_hw_gro_packets);
75
if (qs->_present.rx_hw_gro_bytes)
76
jsonw_uint_field(json_wtr, "hw-gro-bytes", qs->rx_hw_gro_bytes);
77
if (qs->_present.rx_hw_gro_wire_packets)
78
jsonw_uint_field(json_wtr, "hw-gro-wire-packets", qs->rx_hw_gro_wire_packets);
79
if (qs->_present.rx_hw_gro_wire_bytes)
80
jsonw_uint_field(json_wtr, "hw-gro-wire-bytes", qs->rx_hw_gro_wire_bytes);
81
jsonw_end_object(json_wtr);
82
}
83
84
if (qs->_present.tx_packets || qs->_present.tx_bytes ||
85
qs->_present.tx_hw_drops || qs->_present.tx_csum_none ||
86
qs->_present.tx_hw_gso_packets) {
87
jsonw_name(json_wtr, "tx");
88
jsonw_start_object(json_wtr);
89
if (qs->_present.tx_packets)
90
jsonw_uint_field(json_wtr, "packets", qs->tx_packets);
91
if (qs->_present.tx_bytes)
92
jsonw_uint_field(json_wtr, "bytes", qs->tx_bytes);
93
if (qs->_present.tx_hw_drops)
94
jsonw_uint_field(json_wtr, "hw-drops", qs->tx_hw_drops);
95
if (qs->_present.tx_hw_drop_errors)
96
jsonw_uint_field(json_wtr, "hw-drop-errors", qs->tx_hw_drop_errors);
97
if (qs->_present.tx_hw_drop_ratelimits)
98
jsonw_uint_field(json_wtr, "hw-drop-ratelimits", qs->tx_hw_drop_ratelimits);
99
if (qs->_present.tx_csum_none)
100
jsonw_uint_field(json_wtr, "csum-none", qs->tx_csum_none);
101
if (qs->_present.tx_needs_csum)
102
jsonw_uint_field(json_wtr, "needs-csum", qs->tx_needs_csum);
103
if (qs->_present.tx_hw_gso_packets)
104
jsonw_uint_field(json_wtr, "hw-gso-packets", qs->tx_hw_gso_packets);
105
if (qs->_present.tx_hw_gso_bytes)
106
jsonw_uint_field(json_wtr, "hw-gso-bytes", qs->tx_hw_gso_bytes);
107
if (qs->_present.tx_hw_gso_wire_packets)
108
jsonw_uint_field(json_wtr, "hw-gso-wire-packets", qs->tx_hw_gso_wire_packets);
109
if (qs->_present.tx_hw_gso_wire_bytes)
110
jsonw_uint_field(json_wtr, "hw-gso-wire-bytes", qs->tx_hw_gso_wire_bytes);
111
if (qs->_present.tx_stop)
112
jsonw_uint_field(json_wtr, "stop", qs->tx_stop);
113
if (qs->_present.tx_wake)
114
jsonw_uint_field(json_wtr, "wake", qs->tx_wake);
115
jsonw_end_object(json_wtr);
116
}
117
118
jsonw_end_object(json_wtr);
119
}
120
121
jsonw_end_array(json_wtr);
122
}
123
124
static void print_one(bool present, const char *name, unsigned long long val,
125
int *line)
126
{
127
if (!present)
128
return;
129
130
if (!*line) {
131
printf(" ");
132
++(*line);
133
}
134
135
/* Don't waste space on tx- and rx- prefix, its implied by queue type */
136
if (scope == NETDEV_QSTATS_SCOPE_QUEUE &&
137
(name[0] == 'r' || name[0] == 't') &&
138
name[1] == 'x' && name[2] == '-')
139
name += 3;
140
141
printf(" %15s: %15llu", name, val);
142
143
if (++(*line) == 3) {
144
printf("\n");
145
*line = 0;
146
}
147
}
148
149
static void print_plain_qstats(struct netdev_qstats_get_list *qstats)
150
{
151
ynl_dump_foreach(qstats, qs) {
152
char ifname[IF_NAMESIZE];
153
const char *name;
154
int n;
155
156
name = if_indextoname(qs->ifindex, ifname);
157
if (name)
158
printf("%s", name);
159
else
160
printf("ifindex:%u", qs->ifindex);
161
162
if (qs->_present.queue_type && qs->_present.queue_id)
163
printf("\t%s-%-3u",
164
netdev_queue_type_str(qs->queue_type),
165
qs->queue_id);
166
else
167
printf("\t ");
168
169
n = 1;
170
171
/* Basic counters */
172
print_one(qs->_present.rx_packets, "rx-packets", qs->rx_packets, &n);
173
print_one(qs->_present.rx_bytes, "rx-bytes", qs->rx_bytes, &n);
174
print_one(qs->_present.tx_packets, "tx-packets", qs->tx_packets, &n);
175
print_one(qs->_present.tx_bytes, "tx-bytes", qs->tx_bytes, &n);
176
177
/* RX error/drop counters */
178
print_one(qs->_present.rx_alloc_fail, "rx-alloc-fail",
179
qs->rx_alloc_fail, &n);
180
print_one(qs->_present.rx_hw_drops, "rx-hw-drops",
181
qs->rx_hw_drops, &n);
182
print_one(qs->_present.rx_hw_drop_overruns, "rx-hw-drop-overruns",
183
qs->rx_hw_drop_overruns, &n);
184
print_one(qs->_present.rx_hw_drop_ratelimits, "rx-hw-drop-ratelimits",
185
qs->rx_hw_drop_ratelimits, &n);
186
187
/* RX checksum counters */
188
print_one(qs->_present.rx_csum_complete, "rx-csum-complete",
189
qs->rx_csum_complete, &n);
190
print_one(qs->_present.rx_csum_unnecessary, "rx-csum-unnecessary",
191
qs->rx_csum_unnecessary, &n);
192
print_one(qs->_present.rx_csum_none, "rx-csum-none",
193
qs->rx_csum_none, &n);
194
print_one(qs->_present.rx_csum_bad, "rx-csum-bad",
195
qs->rx_csum_bad, &n);
196
197
/* RX GRO counters */
198
print_one(qs->_present.rx_hw_gro_packets, "rx-hw-gro-packets",
199
qs->rx_hw_gro_packets, &n);
200
print_one(qs->_present.rx_hw_gro_bytes, "rx-hw-gro-bytes",
201
qs->rx_hw_gro_bytes, &n);
202
print_one(qs->_present.rx_hw_gro_wire_packets, "rx-hw-gro-wire-packets",
203
qs->rx_hw_gro_wire_packets, &n);
204
print_one(qs->_present.rx_hw_gro_wire_bytes, "rx-hw-gro-wire-bytes",
205
qs->rx_hw_gro_wire_bytes, &n);
206
207
/* TX error/drop counters */
208
print_one(qs->_present.tx_hw_drops, "tx-hw-drops",
209
qs->tx_hw_drops, &n);
210
print_one(qs->_present.tx_hw_drop_errors, "tx-hw-drop-errors",
211
qs->tx_hw_drop_errors, &n);
212
print_one(qs->_present.tx_hw_drop_ratelimits, "tx-hw-drop-ratelimits",
213
qs->tx_hw_drop_ratelimits, &n);
214
215
/* TX checksum counters */
216
print_one(qs->_present.tx_csum_none, "tx-csum-none",
217
qs->tx_csum_none, &n);
218
print_one(qs->_present.tx_needs_csum, "tx-needs-csum",
219
qs->tx_needs_csum, &n);
220
221
/* TX GSO counters */
222
print_one(qs->_present.tx_hw_gso_packets, "tx-hw-gso-packets",
223
qs->tx_hw_gso_packets, &n);
224
print_one(qs->_present.tx_hw_gso_bytes, "tx-hw-gso-bytes",
225
qs->tx_hw_gso_bytes, &n);
226
print_one(qs->_present.tx_hw_gso_wire_packets, "tx-hw-gso-wire-packets",
227
qs->tx_hw_gso_wire_packets, &n);
228
print_one(qs->_present.tx_hw_gso_wire_bytes, "tx-hw-gso-wire-bytes",
229
qs->tx_hw_gso_wire_bytes, &n);
230
231
/* TX queue control */
232
print_one(qs->_present.tx_stop, "tx-stop", qs->tx_stop, &n);
233
print_one(qs->_present.tx_wake, "tx-wake", qs->tx_wake, &n);
234
235
if (n)
236
printf("\n");
237
}
238
}
239
240
static struct netdev_qstats_get_list *
241
qstats_dump(enum netdev_qstats_scope scope)
242
{
243
struct netdev_qstats_get_list *qstats;
244
struct netdev_qstats_get_req *req;
245
struct ynl_error yerr;
246
struct ynl_sock *ys;
247
248
ys = ynl_sock_create(&ynl_netdev_family, &yerr);
249
if (!ys) {
250
p_err("YNL: %s", yerr.msg);
251
return NULL;
252
}
253
254
req = netdev_qstats_get_req_alloc();
255
if (!req) {
256
p_err("failed to allocate qstats request");
257
goto err_close;
258
}
259
260
if (scope)
261
netdev_qstats_get_req_set_scope(req, scope);
262
263
qstats = netdev_qstats_get_dump(ys, req);
264
netdev_qstats_get_req_free(req);
265
if (!qstats) {
266
p_err("failed to get queue stats: %s", ys->err.msg);
267
goto err_close;
268
}
269
270
ynl_sock_destroy(ys);
271
return qstats;
272
273
err_close:
274
ynl_sock_destroy(ys);
275
return NULL;
276
}
277
278
static int do_show(int argc, char **argv)
279
{
280
struct netdev_qstats_get_list *qstats;
281
282
/* Parse options */
283
while (argc > 0) {
284
if (is_prefix(*argv, "scope") || is_prefix(*argv, "group-by")) {
285
NEXT_ARG();
286
287
if (!REQ_ARGS(1))
288
return -1;
289
290
if (is_prefix(*argv, "queue")) {
291
scope = NETDEV_QSTATS_SCOPE_QUEUE;
292
} else if (is_prefix(*argv, "device")) {
293
scope = 0;
294
} else {
295
p_err("invalid scope value '%s'", *argv);
296
return -1;
297
}
298
NEXT_ARG();
299
} else {
300
p_err("unknown option '%s'", *argv);
301
return -1;
302
}
303
}
304
305
qstats = qstats_dump(scope);
306
if (!qstats)
307
return -1;
308
309
/* Print the stats as returned by the kernel */
310
if (json_output)
311
print_json_qstats(qstats);
312
else
313
print_plain_qstats(qstats);
314
315
netdev_qstats_get_list_free(qstats);
316
return 0;
317
}
318
319
static void compute_stats(__u64 *values, unsigned int count,
320
double *mean, double *stddev, __u64 *min, __u64 *max)
321
{
322
double sum = 0.0, variance = 0.0;
323
unsigned int i;
324
325
*min = ~0ULL;
326
*max = 0;
327
328
if (count == 0) {
329
*mean = 0;
330
*stddev = 0;
331
*min = 0;
332
return;
333
}
334
335
for (i = 0; i < count; i++) {
336
sum += values[i];
337
if (values[i] < *min)
338
*min = values[i];
339
if (values[i] > *max)
340
*max = values[i];
341
}
342
343
*mean = sum / count;
344
345
if (count > 1) {
346
for (i = 0; i < count; i++) {
347
double diff = values[i] - *mean;
348
349
variance += diff * diff;
350
}
351
*stddev = sqrt(variance / (count - 1));
352
} else {
353
*stddev = 0;
354
}
355
}
356
357
static void print_balance_stats(const char *name, enum netdev_queue_type type,
358
__u64 *values, unsigned int count)
359
{
360
double mean, stddev, cv, ns;
361
__u64 min, max;
362
363
if ((name[0] == 'r' && type != NETDEV_QUEUE_TYPE_RX) ||
364
(name[0] == 't' && type != NETDEV_QUEUE_TYPE_TX))
365
return;
366
367
compute_stats(values, count, &mean, &stddev, &min, &max);
368
369
cv = mean > 0 ? (stddev / mean) * 100.0 : 0.0;
370
ns = min + max > 0 ? (double)2 * (max - min) / (max + min) * 100 : 0.0;
371
372
printf(" %-12s: cv=%.1f%% ns=%.1f%% stddev=%.0f\n",
373
name, cv, ns, stddev);
374
printf(" %-12s min=%llu max=%llu mean=%.0f\n",
375
"", min, max, mean);
376
}
377
378
static void
379
print_balance_stats_json(const char *name, enum netdev_queue_type type,
380
__u64 *values, unsigned int count)
381
{
382
double mean, stddev, cv, ns;
383
__u64 min, max;
384
385
if ((name[0] == 'r' && type != NETDEV_QUEUE_TYPE_RX) ||
386
(name[0] == 't' && type != NETDEV_QUEUE_TYPE_TX))
387
return;
388
389
compute_stats(values, count, &mean, &stddev, &min, &max);
390
391
cv = mean > 0 ? (stddev / mean) * 100.0 : 0.0;
392
ns = min + max > 0 ? (double)2 * (max - min) / (max + min) * 100 : 0.0;
393
394
jsonw_name(json_wtr, name);
395
jsonw_start_object(json_wtr);
396
jsonw_uint_field(json_wtr, "queue-count", count);
397
jsonw_uint_field(json_wtr, "min", min);
398
jsonw_uint_field(json_wtr, "max", max);
399
jsonw_float_field(json_wtr, "mean", mean);
400
jsonw_float_field(json_wtr, "stddev", stddev);
401
jsonw_float_field(json_wtr, "coefficient-of-variation", cv);
402
jsonw_float_field(json_wtr, "normalized-spread", ns);
403
jsonw_end_object(json_wtr);
404
}
405
406
static int cmp_ifindex_type(const void *a, const void *b)
407
{
408
const struct netdev_qstats_get_rsp *qa = a;
409
const struct netdev_qstats_get_rsp *qb = b;
410
411
if (qa->ifindex != qb->ifindex)
412
return qa->ifindex - qb->ifindex;
413
if (qa->queue_type != qb->queue_type)
414
return qa->queue_type - qb->queue_type;
415
return qa->queue_id - qb->queue_id;
416
}
417
418
static int do_balance(int argc, char **argv __attribute__((unused)))
419
{
420
struct netdev_qstats_get_list *qstats;
421
struct netdev_qstats_get_rsp **sorted;
422
unsigned int count = 0;
423
unsigned int i, j;
424
int ret = 0;
425
426
if (argc > 0) {
427
p_err("balance command takes no arguments");
428
return -1;
429
}
430
431
qstats = qstats_dump(NETDEV_QSTATS_SCOPE_QUEUE);
432
if (!qstats)
433
return -1;
434
435
/* Count and sort queues */
436
ynl_dump_foreach(qstats, qs)
437
count++;
438
439
if (count == 0) {
440
if (json_output)
441
jsonw_start_array(json_wtr);
442
else
443
printf("No queue statistics available\n");
444
goto exit_free_qstats;
445
}
446
447
sorted = calloc(count, sizeof(*sorted));
448
if (!sorted) {
449
p_err("failed to allocate sorted array");
450
ret = -1;
451
goto exit_free_qstats;
452
}
453
454
i = 0;
455
ynl_dump_foreach(qstats, qs)
456
sorted[i++] = qs;
457
458
qsort(sorted, count, sizeof(*sorted), cmp_ifindex_type);
459
460
if (json_output)
461
jsonw_start_array(json_wtr);
462
463
/* Process each device/queue-type combination */
464
i = 0;
465
while (i < count) {
466
__u64 *rx_packets, *rx_bytes, *tx_packets, *tx_bytes;
467
enum netdev_queue_type type = sorted[i]->queue_type;
468
unsigned int ifindex = sorted[i]->ifindex;
469
unsigned int queue_count = 0;
470
char ifname[IF_NAMESIZE];
471
const char *name;
472
473
/* Count queues for this device/type */
474
for (j = i; j < count && sorted[j]->ifindex == ifindex &&
475
sorted[j]->queue_type == type; j++)
476
queue_count++;
477
478
/* Skip if no packets/bytes (inactive queues) */
479
if (!sorted[i]->_present.rx_packets &&
480
!sorted[i]->_present.rx_bytes &&
481
!sorted[i]->_present.tx_packets &&
482
!sorted[i]->_present.tx_bytes)
483
goto next_ifc;
484
485
/* Allocate arrays for statistics */
486
rx_packets = calloc(queue_count, sizeof(*rx_packets));
487
rx_bytes = calloc(queue_count, sizeof(*rx_bytes));
488
tx_packets = calloc(queue_count, sizeof(*tx_packets));
489
tx_bytes = calloc(queue_count, sizeof(*tx_bytes));
490
491
if (!rx_packets || !rx_bytes || !tx_packets || !tx_bytes) {
492
p_err("failed to allocate statistics arrays");
493
free(rx_packets);
494
free(rx_bytes);
495
free(tx_packets);
496
free(tx_bytes);
497
ret = -1;
498
goto exit_free_sorted;
499
}
500
501
/* Collect statistics */
502
for (j = 0; j < queue_count; j++) {
503
rx_packets[j] = sorted[i + j]->_present.rx_packets ?
504
sorted[i + j]->rx_packets : 0;
505
rx_bytes[j] = sorted[i + j]->_present.rx_bytes ?
506
sorted[i + j]->rx_bytes : 0;
507
tx_packets[j] = sorted[i + j]->_present.tx_packets ?
508
sorted[i + j]->tx_packets : 0;
509
tx_bytes[j] = sorted[i + j]->_present.tx_bytes ?
510
sorted[i + j]->tx_bytes : 0;
511
}
512
513
name = if_indextoname(ifindex, ifname);
514
515
if (json_output) {
516
jsonw_start_object(json_wtr);
517
if (name)
518
jsonw_string_field(json_wtr, "ifname", name);
519
jsonw_uint_field(json_wtr, "ifindex", ifindex);
520
jsonw_string_field(json_wtr, "queue-type",
521
netdev_queue_type_str(type));
522
523
print_balance_stats_json("rx-packets", type,
524
rx_packets, queue_count);
525
print_balance_stats_json("rx-bytes", type,
526
rx_bytes, queue_count);
527
print_balance_stats_json("tx-packets", type,
528
tx_packets, queue_count);
529
print_balance_stats_json("tx-bytes", type,
530
tx_bytes, queue_count);
531
532
jsonw_end_object(json_wtr);
533
} else {
534
if (name)
535
printf("%s", name);
536
else
537
printf("ifindex:%u", ifindex);
538
printf(" %s %d queues:\n",
539
netdev_queue_type_str(type), queue_count);
540
541
print_balance_stats("rx-packets", type,
542
rx_packets, queue_count);
543
print_balance_stats("rx-bytes", type,
544
rx_bytes, queue_count);
545
print_balance_stats("tx-packets", type,
546
tx_packets, queue_count);
547
print_balance_stats("tx-bytes", type,
548
tx_bytes, queue_count);
549
printf("\n");
550
}
551
552
free(rx_packets);
553
free(rx_bytes);
554
free(tx_packets);
555
free(tx_bytes);
556
557
next_ifc:
558
i += queue_count;
559
}
560
561
if (json_output)
562
jsonw_end_array(json_wtr);
563
564
exit_free_sorted:
565
free(sorted);
566
exit_free_qstats:
567
netdev_qstats_get_list_free(qstats);
568
return ret;
569
}
570
571
static int do_hw_gro(int argc, char **argv __attribute__((unused)))
572
{
573
struct netdev_qstats_get_list *qstats;
574
575
if (argc > 0) {
576
p_err("hw-gro command takes no arguments");
577
return -1;
578
}
579
580
qstats = qstats_dump(0);
581
if (!qstats)
582
return -1;
583
584
if (json_output)
585
jsonw_start_array(json_wtr);
586
587
ynl_dump_foreach(qstats, qs) {
588
char ifname[IF_NAMESIZE];
589
const char *name;
590
double savings;
591
592
if (!qs->_present.rx_packets ||
593
!qs->_present.rx_hw_gro_packets ||
594
!qs->_present.rx_hw_gro_wire_packets)
595
continue;
596
597
if (!qs->rx_packets)
598
continue;
599
600
/* How many skbs did we avoid allocating thanks to HW GRO */
601
savings = (double)(qs->rx_hw_gro_wire_packets -
602
qs->rx_hw_gro_packets) /
603
qs->rx_packets * 100.0;
604
605
name = if_indextoname(qs->ifindex, ifname);
606
607
if (json_output) {
608
jsonw_start_object(json_wtr);
609
jsonw_uint_field(json_wtr, "ifindex", qs->ifindex);
610
if (name)
611
jsonw_string_field(json_wtr, "ifname", name);
612
jsonw_float_field(json_wtr, "savings", savings);
613
jsonw_end_object(json_wtr);
614
} else {
615
if (name)
616
printf("%s", name);
617
else
618
printf("ifindex:%u", qs->ifindex);
619
printf(": %.1f%% savings\n", savings);
620
}
621
}
622
623
if (json_output)
624
jsonw_end_array(json_wtr);
625
626
netdev_qstats_get_list_free(qstats);
627
return 0;
628
}
629
630
static int do_help(int argc __attribute__((unused)),
631
char **argv __attribute__((unused)))
632
{
633
if (json_output) {
634
jsonw_null(json_wtr);
635
return 0;
636
}
637
638
fprintf(stderr,
639
"Usage: %1$s qstats { COMMAND | help }\n"
640
" %1$s qstats [ show ] [ OPTIONS ]\n"
641
" %1$s qstats balance\n"
642
" %1$s qstats hw-gro\n"
643
"\n"
644
" OPTIONS := { scope queue | group-by { device | queue } }\n"
645
"\n"
646
" show - Display queue statistics (default)\n"
647
" Statistics are aggregated for the entire device.\n"
648
" show scope queue - Display per-queue statistics\n"
649
" show group-by device - Display device-aggregated statistics (default)\n"
650
" show group-by queue - Display per-queue statistics\n"
651
"\n"
652
" Analysis:\n"
653
" balance - Traffic distribution between queues.\n"
654
" hw-gro - HW GRO effectiveness analysis\n"
655
" - savings - delta between packets received\n"
656
" on the wire and packets seen by the kernel.\n"
657
"",
658
bin_name);
659
660
return 0;
661
}
662
663
static const struct cmd qstats_cmds[] = {
664
{ "show", do_show },
665
{ "balance", do_balance },
666
{ "hw-gro", do_hw_gro },
667
{ "help", do_help },
668
{ 0 }
669
};
670
671
int do_qstats(int argc, char **argv)
672
{
673
return cmd_select(qstats_cmds, argc, argv, do_help);
674
}
675
676