Bug Summary

File:.build-ci/../src/nvme-print-stdout-top.c
Warning:line 1613, column 12
Potential leak of memory pointed to by 'subsys_name'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-redhat-linux-gnu -O3 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name nvme-print-stdout-top.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model static -mframe-pointer=none -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/__w/nvme-cli/nvme-cli/.build-ci -fcoverage-compilation-dir=/__w/nvme-cli/nvme-cli/.build-ci -resource-dir /usr/bin/../lib/clang/22 -include /__w/nvme-cli/nvme-cli/.build-ci/nvme-config.h -I nvme.p -I . -I .. -I src -I ../src -I ccan -I ../ccan -I libnvme/src -I ../libnvme/src -I /usr/include/json-c -D _FILE_OFFSET_BITS=64 -D _GNU_SOURCE -U NDEBUG -internal-isystem /usr/bin/../lib/clang/22/include -internal-isystem /usr/local/include -internal-isystem /usr/bin/../lib/gcc/x86_64-redhat-linux/16/../../../../x86_64-redhat-linux/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -std=gnu11 -ferror-limit 19 -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -fcolor-diagnostics -vectorize-loops -vectorize-slp -analyzer-opt-analyze-headers -analyzer-output=html -faddrsig -fdwarf2-cfi-asm -o /__w/nvme-cli/nvme-cli/.build-ci/scan-results/2026-09-23-073103-589-1 -x c ../src/nvme-print-stdout-top.c
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Implements nvme top dashboard
4 *
5 * Copyright (c) 2026 Nilay Shroff, IBM
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 */
17
18#define IOPS_UNIT_NONE"" ""
19#define IOPS_UNIT_KB"k" "k"
20
21#define BW_UNIT_BYTES_PER_SEC"B/s" "B/s"
22#define BW_UNIT_KIB_PER_SEC"KiB/s" "KiB/s"
23#define BW_UNIT_MIB_PER_SEC"MiB/s" "MiB/s"
24
25#define BW_KIB1024 1024
26#define BW_MIB(1024 * 1024) (BW_KIB1024 * 1024)
27
28#include <stddef.h>
29#include <stdio.h>
30
31#include <libnvme.h>
32
33#include <ccan/array_size/array_size.h>
34#include <ccan/minmax/minmax.h>
35#include <shared/table-util.h>
36
37#include "cleanup.h"
38#include "dashboard.h"
39#include "global-ctx.h"
40#include "nvme-print.h"
41
42static double nvme_calc_util_percent(unsigned int ticks, double interval_ms)
43{
44 if (!interval_ms)
45 return 0;
46
47 return (ticks / interval_ms) * 100;
48}
49
50static double nvme_path_calc_util_percent(struct libnvme_path *p, double interval_ms)
51{
52 unsigned int ticks;
53
54 ticks = libnvme_path_get_io_ticks(p);
55 return nvme_calc_util_percent(ticks, interval_ms);
56}
57
58static double nvme_ns_calc_util_percent(struct libnvme_ns *n, double interval_ms)
59{
60 unsigned int ticks;
61
62 ticks = libnvme_ns_get_io_ticks(n);
63 return nvme_calc_util_percent(ticks, interval_ms);
64}
65
66static double nvme_calc_iops(unsigned long ios, double interval_ms)
67{
68 double interval_sec;
69
70 if (interval_ms < 1000)
71 return 0;
72
73 interval_sec = interval_ms / 1000;
74 return (ios / interval_sec);
75}
76
77static double nvme_path_calc_read_iops(struct libnvme_path *p, double interval_ms)
78{
79 unsigned long read_ios;
80
81 read_ios = libnvme_path_get_read_ios(p);
82 return nvme_calc_iops(read_ios, interval_ms);
83}
84
85static double nvme_path_calc_write_iops(struct libnvme_path *p, double interval_ms)
86{
87 unsigned long write_ios;
88
89 write_ios = libnvme_path_get_write_ios(p);
90 return nvme_calc_iops(write_ios, interval_ms);
91}
92
93static double nvme_ns_calc_read_iops(struct libnvme_ns *n, double interval_ms)
94{
95 unsigned long read_ios;
96
97 read_ios = libnvme_ns_get_read_ios(n);
98 return nvme_calc_iops(read_ios, interval_ms);
99}
100
101static double nvme_ns_calc_write_iops(struct libnvme_ns *n, double interval_ms)
102{
103 unsigned long write_ios;
104
105 write_ios = libnvme_ns_get_write_ios(n);
106 return nvme_calc_iops(write_ios, interval_ms);
107}
108
109static double nvme_calc_latency(unsigned long ticks, unsigned long ios)
110{
111 if (!ios)
112 return 0;
113
114 return ((double)ticks/ios);
115}
116
117static double nvme_path_calc_read_latency(struct libnvme_path *p)
118{
119 unsigned int ticks;
120 unsigned long ios;
121
122 ticks = libnvme_path_get_read_ticks(p);
123 ios = libnvme_path_get_read_ios(p);
124
125 return nvme_calc_latency(ticks, ios);
126}
127
128static double nvme_path_calc_write_latency(struct libnvme_path *p)
129{
130 unsigned int ticks;
131 unsigned long ios;
132
133 ticks = libnvme_path_get_write_ticks(p);
134 ios = libnvme_path_get_write_ios(p);
135
136 return nvme_calc_latency(ticks, ios);
137}
138
139static double nvme_ns_calc_read_latency(struct libnvme_ns *n)
140{
141 unsigned int ticks;
142 unsigned long ios;
143
144 ticks = libnvme_ns_get_read_ticks(n);
145 ios = libnvme_ns_get_read_ios(n);
146
147 return nvme_calc_latency(ticks, ios);
148}
149
150static double nvme_ns_calc_write_latency(struct libnvme_ns *n)
151{
152 unsigned int ticks;
153 unsigned long ios;
154
155 ticks = libnvme_ns_get_write_ticks(n);
156 ios = libnvme_ns_get_write_ios(n);
157
158 return nvme_calc_latency(ticks, ios);
159}
160
161static double nvme_calc_bandwidth(unsigned long long sectors,
162 double interval_ms)
163{
164 double bytes;
165 double sec;
166
167 if (interval_ms < 1000)
168 return 0;
169
170 sec = interval_ms / 1000;
171 bytes = (double)sectors * 512;
172 return (bytes / sec);
173}
174
175static double nvme_path_calc_read_bw(struct libnvme_path *p, double interval_ms)
176{
177 unsigned long long sectors;
178
179 sectors = libnvme_path_get_read_sectors(p);
180 return nvme_calc_bandwidth(sectors, interval_ms);
181}
182
183static double nvme_path_calc_write_bw(struct libnvme_path *p, double interval_ms)
184{
185 unsigned long long sectors;
186
187 sectors = libnvme_path_get_write_sectors(p);
188 return nvme_calc_bandwidth(sectors, interval_ms);
189}
190
191static double nvme_ns_calc_read_bw(struct libnvme_ns *n, double interval_ms)
192{
193 unsigned long long sectors;
194
195 sectors = libnvme_ns_get_read_sectors(n);
196 return nvme_calc_bandwidth(sectors, interval_ms);
197}
198
199static double nvme_ns_calc_write_bw(struct libnvme_ns *n, double interval_ms)
200{
201 unsigned long long sectors;
202
203 sectors = libnvme_ns_get_write_sectors(n);
204 return nvme_calc_bandwidth(sectors, interval_ms);
205}
206
207static int nvme_format_iops(double iops, char *buf, size_t size)
208{
209 char *unit;
210
211 if (iops < 1000)
212 unit = IOPS_UNIT_NONE"";
213 else {
214 iops /= 1000;
215 unit = IOPS_UNIT_KB"k";
216 }
217
218 return snprintf(buf, size, "%.2f%s", iops, unit);
219}
220
221static int nvme_format_bw(double bw, char *buf, size_t size)
222{
223 char *unit = "";
224
225 if (!bw)
226 goto out;
227
228 if (bw < BW_KIB1024)
229 unit = BW_UNIT_BYTES_PER_SEC"B/s";
230 else if (bw < BW_MIB(1024 * 1024)) {
231 bw /= BW_KIB1024;
232 unit = BW_UNIT_KIB_PER_SEC"KiB/s";
233 } else {
234 bw /= BW_MIB(1024 * 1024);
235 unit = BW_UNIT_MIB_PER_SEC"MiB/s";
236 }
237
238out:
239 return snprintf(buf, size, "%.2f%s", bw, unit);
240}
241
242static int nvme_format_lat(double lat, char *buf, size_t size)
243{
244 return snprintf(buf, size, "%.2f", lat);
245}
246
247static void nvme_ns_calc_aggr_stat(struct libnvme_ns *n,
248 double *r_iops, double *w_iops,
249 double *r_bw, double *w_bw,
250 double *max_rlat, double *max_wlat,
251 double *max_util)
252{
253 double interval_ms, rlat, wlat, util;
254
255 interval_ms = libnvme_ns_get_stat_interval(n);
256 if (!interval_ms)
257 return;
258
259 *r_iops += nvme_ns_calc_read_iops(n, interval_ms);
260 *w_iops += nvme_ns_calc_write_iops(n, interval_ms);
261
262 *r_bw += nvme_ns_calc_read_bw(n, interval_ms);
263 *w_bw += nvme_ns_calc_write_bw(n, interval_ms);
264
265 rlat = nvme_ns_calc_read_latency(n);
266 if (rlat > *max_rlat)
267 *max_rlat = rlat;
268
269 wlat = nvme_ns_calc_write_latency(n);
270 if (wlat > *max_wlat)
271 *max_wlat = wlat;
272
273 util = nvme_ns_calc_util_percent(n, interval_ms);
274 if (util > *max_util)
275 *max_util = util;
276}
277
278static void nvme_path_calc_aggr_stat(struct libnvme_path *p,
279 double *r_iops, double *w_iops,
280 double *r_bw, double *w_bw,
281 double *max_rlat, double *max_wlat,
282 double *max_util)
283{
284 double interval_ms, rlat, wlat, util;
285
286 interval_ms = libnvme_path_get_stat_interval(p);
287 if (!interval_ms)
288 return;
289
290 *r_iops += nvme_path_calc_read_iops(p, interval_ms);
291 *w_iops += nvme_path_calc_write_iops(p, interval_ms);
292
293 *r_bw += nvme_path_calc_read_bw(p, interval_ms);
294 *w_bw += nvme_path_calc_write_bw(p, interval_ms);
295
296 rlat = nvme_path_calc_read_latency(p);
297 if (rlat > *max_rlat)
298 *max_rlat = rlat;
299
300 wlat = nvme_path_calc_write_latency(p);
301 if (wlat > *max_wlat)
302 *max_wlat = wlat;
303
304 util = nvme_path_calc_util_percent(p, interval_ms);
305 if (util > *max_util)
306 *max_util = util;
307}
308
309static void nvme_ns_calc_stat(struct libnvme_ns *n,
310 double *r_iops, double *w_iops,
311 double *r_lat, double *w_lat,
312 double *r_bw, double *w_bw,
313 double *util,
314 unsigned int *inflights)
315{
316 double interval_ms;
317
318 interval_ms = libnvme_ns_get_stat_interval(n);
319 if (!interval_ms)
320 return;
321
322 /* calculate R/W IOPS */
323 *r_iops = nvme_ns_calc_read_iops(n, interval_ms);
324 *w_iops = nvme_ns_calc_write_iops(n, interval_ms);
325
326 /* calculate R/W latency */
327 *r_lat = nvme_ns_calc_read_latency(n);
328 *w_lat = nvme_ns_calc_write_latency(n);
329
330 /* calculate R/W bandwidth */
331 *r_bw = nvme_ns_calc_read_bw(n, interval_ms);
332 *w_bw = nvme_ns_calc_write_bw(n, interval_ms);
333
334 /* get inflights counter */
335 *inflights = libnvme_ns_get_inflights(n);
336
337 /* calculate util percent */
338 *util = nvme_ns_calc_util_percent(n, interval_ms);
339}
340
341static void nvme_path_calc_stat(struct libnvme_path *p,
342 double *r_iops, double *w_iops,
343 double *r_lat, double *w_lat,
344 double *r_bw, double *w_bw,
345 double *util,
346 unsigned int *inflights)
347{
348 double interval_ms;
349
350 interval_ms = libnvme_path_get_stat_interval(p);
351 if (!interval_ms)
352 return;
353
354 /* calculate R/W IOPS */
355 *r_iops = nvme_path_calc_read_iops(p, interval_ms);
356 *w_iops = nvme_path_calc_write_iops(p, interval_ms);
357
358 /* calculate R/W latency */
359 *r_lat = nvme_path_calc_read_latency(p);
360 *w_lat = nvme_path_calc_write_latency(p);
361
362 /* calculate R/W bandwidth */
363 *r_bw = nvme_path_calc_read_bw(p, interval_ms);
364 *w_bw = nvme_path_calc_write_bw(p, interval_ms);
365
366 /* get inflights counter */
367 *inflights = libnvme_path_get_inflights(p);
368
369 /* calculate util percent */
370 *util = nvme_path_calc_util_percent(p, interval_ms);
371}
372
373static bool_Bool stdout_top_nvme_ctrl_is_fabric(struct libnvme_ctrl *c)
374{
375 if (strcmp(libnvme_ctrl_get_transport(c), "pcie"))
376 return true1;
377 else
378 return false0;
379}
380
381static bool_Bool stdout_top_print_ctrl_summary_tbl_filter(const char *name,
382 void *arg)
383{
384 struct libnvme_ctrl *c;
385 struct libnvme_subsystem *s = arg;
386 bool_Bool multipath = nvme_is_multipath(s);
387
388 if (!strcmp(name, "Paths")) {
389 if (!multipath)
390 return false0;
391 }
392
393 if (!strcmp(name, "Reconnects")) {
394 c = libnvme_subsystem_first_ctrl(s);
395 if (c) {
396 if (stdout_top_nvme_ctrl_is_fabric(c))
397 return true1;
398 else
399 return false0;
400 }
401 }
402
403 return true1;
404}
405
406static int stdout_top_print_path_health(FILE *stream, struct libnvme_subsystem *s)
407{
408 int ret = 0;
409 int col, row;
410 struct libnvme_ns *n;
411 struct libnvme_path *p;
412 struct shr_table *t;
413 struct shr_table_column columns[] = {
414 {"NSPath", LEFT, AUTO_WIDTH2147483647},
415 {"ANAState", LEFT, AUTO_WIDTH2147483647},
416 {"Retries", LEFT, AUTO_WIDTH2147483647},
417 {"Failovers", LEFT, AUTO_WIDTH2147483647},
418 {"Errors", LEFT, AUTO_WIDTH2147483647}
419 };
420
421 t = shr_table_create();
422 if (!t) {
423 nvme_show_error("Failed to init path health table\n")nvme_show_message(1, "Failed to init path health table\n");
424 return 1;
425 }
426
427 if (shr_table_add_columns(t, columns, ARRAY_SIZE(columns)(sizeof(columns) / sizeof((columns)[0]) + 0)) < 0) {
428 nvme_show_error("Failed to add columns to path health table\n")nvme_show_message(1, "Failed to add columns to path health table\n"
)
;
429 ret = 1;
430 goto free_tbl;
431 }
432
433 fprintf(stream, "\n------------ Path Health -------------\n\n");
434 libnvme_subsystem_for_each_ns(s, n)for (n = libnvme_subsystem_first_ns(s); n != ((void*)0); n = libnvme_subsystem_next_ns
(s, n))
{
435 libnvme_namespace_for_each_path(n, p)for (p = libnvme_namespace_first_path(n); p != ((void*)0); p =
libnvme_namespace_next_path(n, p))
{
436 const char *ana_state;
437 long command_retry_count;
438 long multipath_failover_count;
439 long command_error_count;
440
441 row = shr_table_get_row_id(t);
442 if (row < 0) {
443 nvme_show_error("Failed to add row to path health table\n")nvme_show_message(1, "Failed to add row to path health table\n"
)
;
444 goto free_tbl;
445 }
446
447 libnvme_path_get_ana_state(p, &ana_state, "");
448 libnvme_path_get_command_retry_count(p,
449 &command_retry_count, 0);
450 libnvme_path_get_multipath_failover_count(p,
451 &multipath_failover_count, 0);
452 libnvme_path_get_command_error_count(p,
453 &command_error_count, 0);
454
455 col = -1;
456
457 shr_table_set_value_str(t, ++col, row,
458 libnvme_path_get_name(p), LEFT);
459 shr_table_set_value_str(t, ++col, row,
460 ana_state, LEFT);
461 shr_table_set_value_long(t, ++col, row,
462 command_retry_count, LEFT);
463 shr_table_set_value_long(t, ++col, row,
464 multipath_failover_count, LEFT);
465 shr_table_set_value_int(t, ++col, row,
466 command_error_count, LEFT);
467
468 shr_table_add_row(t, row);
469 }
470 }
471
472 shr_table_print_stream(stream, t);
473free_tbl:
474 shr_table_free(t);
475 return ret;
476}
477
478static int stdout_top_print_ctrl_summary(FILE *stream,
479 struct libnvme_subsystem *s, bool_Bool multipath)
480{
481 int ret = 0;
482 int row, col, npaths;
483 struct libnvme_ctrl *c;
484 struct libnvme_path *p;
485 struct libnvme_ns *n;
486 double max_util, max_rlat, max_wlat;
487 double r_iops, w_iops, r_bw, w_bw;
488 char r_bw_str[16], w_bw_str[16];
489 char r_iops_str[16], w_iops_str[16];
490 char r_clat_str[16], w_clat_str[16];
491 const char *node;
492 long reset_count, reconnect_count, command_error_count;
493 struct shr_table *t;
494 bool_Bool is_fabric = false0;
495 struct shr_table_column columns[] = {
496 {"Ctrl", LEFT, AUTO_WIDTH2147483647},
497 {"Paths", LEFT, AUTO_WIDTH2147483647},
498 {"Node", LEFT, AUTO_WIDTH2147483647},
499 {"Trtype", LEFT, AUTO_WIDTH2147483647},
500 {"Address", LEFT, AUTO_WIDTH2147483647},
501 {"State", LEFT, AUTO_WIDTH2147483647},
502 {"Resets", LEFT, AUTO_WIDTH2147483647},
503 {"Reconnects", LEFT, AUTO_WIDTH2147483647},
504 {"Errors", LEFT, AUTO_WIDTH2147483647},
505 {"r_IOPS", LEFT, 9},
506 {"w_IOPS", LEFT, 9},
507 {"r_clat", LEFT, 8},
508 {"w_clat", LEFT, 8},
509 {"r_bw", LEFT, 13},
510 {"w_bw", LEFT, 13},
511 {"Util%", LEFT, 6},
512 };
513
514 t = shr_table_create();
515 if (!t) {
516 nvme_show_error("Failed to init ctrl summary table")nvme_show_message(1, "Failed to init ctrl summary table");
517 return 1;
518 }
519
520 if (shr_table_add_columns_filter(t, columns, ARRAY_SIZE(columns)(sizeof(columns) / sizeof((columns)[0]) + 0),
521 stdout_top_print_ctrl_summary_tbl_filter, (void *)s) < 0) {
522 nvme_show_error("Failed to add columns to ctrl summary table")nvme_show_message(1, "Failed to add columns to ctrl summary table"
)
;
523 ret = 1;
524 goto free_tbl;
525 }
526
527 c = libnvme_subsystem_first_ctrl(s);
528 if (c)
529 is_fabric = stdout_top_nvme_ctrl_is_fabric(c);
530
531 fprintf(stream, "\n---------- Controller Summary --------\n\n");
532 libnvme_subsystem_for_each_ctrl(s, c)for (c = libnvme_subsystem_first_ctrl(s); c != ((void*)0); c =
libnvme_subsystem_next_ctrl(s, c))
{
533 npaths = 0;
534 r_iops = w_iops = 0;
535 r_bw = w_bw = 0;
536 max_util = max_rlat = max_wlat = 0;
537
538 row = shr_table_get_row_id(t);
539 if (row < 0) {
540 nvme_show_error("Failed to add row to ctrl summary table")nvme_show_message(1, "Failed to add row to ctrl summary table"
)
;
541 ret = 1;
542 goto free_tbl;
543 }
544
545 if (multipath) {
546 libnvme_ctrl_for_each_path(c, p)for (p = libnvme_ctrl_first_path(c); p != ((void*)0); p = libnvme_ctrl_next_path
(c, p))
{
547
548 /* count num of paths per controller */
549 npaths++;
550
551 nvme_path_calc_aggr_stat(p,
552 &r_iops, &w_iops,
553 &r_bw, &w_bw,
554 &max_rlat, &max_wlat,
555 &max_util);
556 }
557 } else {
558 libnvme_ctrl_for_each_ns(c, n)for (n = libnvme_ctrl_first_ns(c); n != ((void*)0); n = libnvme_ctrl_next_ns
(c, n))
{
559 nvme_ns_calc_aggr_stat(n,
560 &r_iops, &w_iops,
561 &r_bw, &w_bw,
562 &max_rlat, &max_wlat,
563 &max_util);
564 }
565 }
566
567 nvme_format_iops(r_iops, r_iops_str, sizeof(r_iops_str));
568 nvme_format_iops(w_iops, w_iops_str, sizeof(w_iops_str));
569
570 nvme_format_bw(r_bw, r_bw_str, sizeof(r_bw_str));
571 nvme_format_bw(w_bw, w_bw_str, sizeof(w_bw_str));
572
573 nvme_format_lat(max_rlat, r_clat_str, sizeof(r_clat_str));
574 nvme_format_lat(max_wlat, w_clat_str, sizeof(w_clat_str));
575
576 libnvme_ctrl_get_numa_node(c, &node, "-1");
577 if (!strcmp(node, "-1"))
578 node = "NUMA_NO_NODE";
579
580 col = -1;
581
582 shr_table_set_value_str(t, ++col, row,
583 libnvme_ctrl_get_name(c), LEFT);
584 if (multipath)
585 shr_table_set_value_int(t, ++col, row, npaths, LEFT);
586
587 shr_table_set_value_str(t, ++col, row, node, LEFT);
588 shr_table_set_value_str(t, ++col, row,
589 libnvme_ctrl_get_transport(c), LEFT);
590 shr_table_set_value_str(t, ++col, row,
591 libnvme_ctrl_get_traddr(c), LEFT);
592 shr_table_set_value_str(t, ++col, row,
593 libnvme_ctrl_get_state(c), LEFT);
594
595 libnvme_ctrl_get_reset_count(c, &reset_count, 0);
596 shr_table_set_value_long(t, ++col, row, reset_count, LEFT);
597
598 if (is_fabric) {
599 libnvme_ctrl_get_reconnect_count(c, &reconnect_count,
600 0);
601 shr_table_set_value_long(t, ++col, row,
602 reconnect_count, LEFT);
603 }
604
605 libnvme_ctrl_get_command_error_count(c, &command_error_count,
606 0);
607 shr_table_set_value_long(t, ++col, row, command_error_count, LEFT);
608 shr_table_set_value_str(t, ++col, row, r_iops_str, LEFT);
609 shr_table_set_value_str(t, ++col, row, w_iops_str, LEFT);
610 shr_table_set_value_str(t, ++col, row, r_clat_str, LEFT);
611 shr_table_set_value_str(t, ++col, row, w_clat_str, LEFT);
612 shr_table_set_value_str(t, ++col, row, r_bw_str, LEFT);
613 shr_table_set_value_str(t, ++col, row, w_bw_str, LEFT);
614 shr_table_set_value_double(t, ++col, row, max_util, LEFT);
615
616 shr_table_add_row(t, row);
617 }
618
619 shr_table_print_stream(stream, t);
620free_tbl:
621 shr_table_free(t);
622 return ret;
623}
624
625static int stdout_top_print_ns_stat(FILE *stream, struct libnvme_subsystem *s)
626{
627 int ret = 0;
628 struct libnvme_ns *n;
629 struct libnvme_ctrl *c;
630 int col, row;
631 unsigned int inflights;
632 double r_iops, w_iops, r_lat, w_lat, r_bw, w_bw, util;
633 char r_bw_str[16], w_bw_str[16];
634 char r_iops_str[16], w_iops_str[16];
635 char r_clat_str[16], w_clat_str[16];
636 struct shr_table *t;
637 struct shr_table_column columns[] = {
638 {"Namespace", LEFT, AUTO_WIDTH2147483647},
639 {"NSID", LEFT, AUTO_WIDTH2147483647},
640 {"Ctrl", LEFT, AUTO_WIDTH2147483647},
641 {"Retries", LEFT, AUTO_WIDTH2147483647},
642 {"Errors", LEFT, AUTO_WIDTH2147483647},
643 {"r_IOPS", LEFT, 9},
644 {"w_IOPS", LEFT, 9},
645 {"r_clat", LEFT, 8},
646 {"w_clat", LEFT, 8},
647 {"r_bw", LEFT, 13},
648 {"w_bw", LEFT, 13},
649 {"Inflights", LEFT, AUTO_WIDTH2147483647},
650 {"Util%", LEFT, 6},
651 };
652
653 t = shr_table_create();
654 if (!t) {
655 nvme_show_error("Failed to init ns stat table\n")nvme_show_message(1, "Failed to init ns stat table\n");
656 return 1;
657 }
658
659 if (shr_table_add_columns(t, columns, ARRAY_SIZE(columns)(sizeof(columns) / sizeof((columns)[0]) + 0)) < 0) {
660 nvme_show_error("Failed to add columns to ns stat table\n")nvme_show_message(1, "Failed to add columns to ns stat table\n"
)
;
661 ret = 1;
662 goto free_tbl;
663 }
664
665 fprintf(stream, "----------- Namespace Stat -----------\n\n");
666 libnvme_subsystem_for_each_ctrl(s, c)for (c = libnvme_subsystem_first_ctrl(s); c != ((void*)0); c =
libnvme_subsystem_next_ctrl(s, c))
{
667 libnvme_ctrl_for_each_ns(c, n)for (n = libnvme_ctrl_first_ns(c); n != ((void*)0); n = libnvme_ctrl_next_ns
(c, n))
{
668 long command_retry_count, command_error_count;
669
670 r_iops = r_lat = r_bw = 0;
671 w_iops = w_lat = w_bw = 0;
672 util = inflights = 0;
673
674 nvme_ns_calc_stat(n,
675 &r_iops, &w_iops,
676 &r_lat, &w_lat,
677 &r_bw, &w_bw,
678 &util, &inflights);
679
680 nvme_format_iops(r_iops, r_iops_str,
681 sizeof(r_iops_str));
682 nvme_format_iops(w_iops, w_iops_str,
683 sizeof(w_iops_str));
684
685 nvme_format_bw(r_bw, r_bw_str, sizeof(r_bw_str));
686 nvme_format_bw(w_bw, w_bw_str, sizeof(w_bw_str));
687
688 nvme_format_lat(r_lat, r_clat_str, sizeof(r_clat_str));
689 nvme_format_lat(w_lat, w_clat_str, sizeof(w_clat_str));
690
691 row = shr_table_get_row_id(t);
692 if (row < 0) {
693 nvme_show_error("Failed to add row to ns stat table\n")nvme_show_message(1, "Failed to add row to ns stat table\n");
694 ret = 1;
695 goto free_tbl;
696 }
697
698 col = -1;
699
700 shr_table_set_value_str(t, ++col, row,
701 libnvme_ns_get_name(n), LEFT);
702 shr_table_set_value_int(t, ++col, row,
703 libnvme_ns_get_nsid(n), LEFT);
704 shr_table_set_value_str(t, ++col, row,
705 libnvme_ctrl_get_name(c), LEFT);
706 libnvme_ns_get_command_retry_count(n,
707 &command_retry_count, 0);
708 libnvme_ns_get_command_error_count(n,
709 &command_error_count, 0);
710 shr_table_set_value_long(t, ++col, row,
711 command_retry_count, LEFT);
712 shr_table_set_value_long(t, ++col, row,
713 command_error_count, LEFT);
714 shr_table_set_value_str(t, ++col, row, r_iops_str, LEFT);
715 shr_table_set_value_str(t, ++col, row, w_iops_str, LEFT);
716 shr_table_set_value_str(t, ++col, row, r_clat_str, LEFT);
717 shr_table_set_value_str(t, ++col, row, w_clat_str, LEFT);
718 shr_table_set_value_str(t, ++col, row, r_bw_str, LEFT);
719 shr_table_set_value_str(t, ++col, row, w_bw_str, LEFT);
720 shr_table_set_value_unsigned(t, ++col, row, inflights,
721 LEFT);
722 shr_table_set_value_double(t, ++col, row, util, LEFT);
723
724 shr_table_add_row(t, row);
725 }
726 }
727
728 shr_table_print_stream(stream, t);
729free_tbl:
730 shr_table_free(t);
731 return ret;
732}
733
734static int stdout_top_print_nshead_stat(FILE *stream, struct libnvme_subsystem *s)
735{
736 int ret = 0;
737 struct libnvme_ns *n;
738 struct libnvme_path *p;
739 double r_iops, w_iops, r_lat, w_lat, r_bw, w_bw, util;
740 unsigned int inflights;
741 int col, row, npaths;
742 char r_iops_str[16], w_iops_str[16];
743 char r_clat_str[16], w_clat_str[16];
744 char r_bw_str[16], w_bw_str[16];
745 struct shr_table *t;
746 struct shr_table_column columns[] = {
747 {"NSHead", LEFT, AUTO_WIDTH2147483647},
748 {"NSID", LEFT, AUTO_WIDTH2147483647},
749 {"Paths", LEFT, AUTO_WIDTH2147483647},
750 {"Requeue-IO", LEFT, AUTO_WIDTH2147483647},
751 {"Fail-IO", LEFT, AUTO_WIDTH2147483647},
752 {"r_IOPS", LEFT, 9},
753 {"w_IOPS", LEFT, 9},
754 {"r_clat", LEFT, 8},
755 {"w_clat", LEFT, 8},
756 {"r_bw", LEFT, 13},
757 {"w_bw", LEFT, 13},
758 {"Inflights", LEFT, AUTO_WIDTH2147483647},
759 {"Util%", LEFT, 6},
760 };
761
762 t = shr_table_create();
763 if (!t) {
764 nvme_show_error("Failed to init nshead stat table\n")nvme_show_message(1, "Failed to init nshead stat table\n");
765 return 1;
766 }
767
768 if (shr_table_add_columns(t, columns, ARRAY_SIZE(columns)(sizeof(columns) / sizeof((columns)[0]) + 0)) < 0) {
769 nvme_show_error("Failed to add columns to nshead stat table\n")nvme_show_message(1, "Failed to add columns to nshead stat table\n"
)
;
770 ret = 1;
771 goto free_tbl;
772 }
773
774 fprintf(stream, "------------ NSHead Stat -------------\n\n");
775 libnvme_subsystem_for_each_ns(s, n)for (n = libnvme_subsystem_first_ns(s); n != ((void*)0); n = libnvme_subsystem_next_ns
(s, n))
{
776 long io_requeue_no_usable_path_count;
777 long io_fail_no_available_path_count;
778
779 npaths = 0;
780 r_iops = r_lat = r_bw = 0;
781 w_iops = w_lat = w_bw = 0;
782 util = inflights = 0;
783
784 nvme_ns_calc_stat(n,
785 &r_iops, &w_iops,
786 &r_lat, &w_lat,
787 &r_bw, &w_bw,
788 &util, &inflights);
789
790 nvme_format_iops(r_iops, r_iops_str, sizeof(r_iops_str));
791 nvme_format_iops(w_iops, w_iops_str, sizeof(w_iops_str));
792
793 nvme_format_bw(r_bw, r_bw_str, sizeof(r_bw_str));
794 nvme_format_bw(w_bw, w_bw_str, sizeof(w_bw_str));
795
796 nvme_format_lat(r_lat, r_clat_str, sizeof(r_clat_str));
797 nvme_format_lat(w_lat, w_clat_str, sizeof(w_clat_str));
798
799 libnvme_namespace_for_each_path(n, p)for (p = libnvme_namespace_first_path(n); p != ((void*)0); p =
libnvme_namespace_next_path(n, p))
800 npaths++;
801
802 row = shr_table_get_row_id(t);
803 if (row < 0) {
804 nvme_show_error("Failed to add row to nshead stat table\n")nvme_show_message(1, "Failed to add row to nshead stat table\n"
)
;
805 ret = 1;
806 goto free_tbl;
807 }
808
809 col = -1;
810
811 shr_table_set_value_str(t, ++col, row, libnvme_ns_get_name(n),
812 LEFT);
813 shr_table_set_value_int(t, ++col, row, libnvme_ns_get_nsid(n),
814 LEFT);
815 shr_table_set_value_int(t, ++col, row, npaths, LEFT);
816 libnvme_ns_get_io_requeue_no_usable_path_count(n,
817 &io_requeue_no_usable_path_count, 0);
818 libnvme_ns_get_io_fail_no_available_path_count(n,
819 &io_fail_no_available_path_count, 0);
820 shr_table_set_value_long(t, ++col, row,
821 io_requeue_no_usable_path_count, LEFT);
822 shr_table_set_value_long(t, ++col, row,
823 io_fail_no_available_path_count, LEFT);
824 shr_table_set_value_str(t, ++col, row, r_iops_str, LEFT);
825 shr_table_set_value_str(t, ++col, row, w_iops_str, LEFT);
826 shr_table_set_value_str(t, ++col, row, r_clat_str, LEFT);
827 shr_table_set_value_str(t, ++col, row, w_clat_str, LEFT);
828 shr_table_set_value_str(t, ++col, row, r_bw_str, LEFT);
829 shr_table_set_value_str(t, ++col, row, w_bw_str, LEFT);
830 shr_table_set_value_unsigned(t, ++col, row, inflights, LEFT);
831 shr_table_set_value_double(t, ++col, row, util, LEFT);
832
833 shr_table_add_row(t, row);
834 }
835
836 shr_table_print_stream(stream, t);
837free_tbl:
838 shr_table_free(t);
839 return ret;
840}
841
842static int stdout_top_print_path_perf(FILE *stream, struct libnvme_subsystem *s)
843{
844 int ret = 0;
845 struct libnvme_ns *n;
846 struct libnvme_path *p;
847 struct libnvme_ctrl *c;
848 unsigned int inflights;
849 int row, col;
850 double util, r_iops, w_iops, r_lat, w_lat, r_bw, w_bw;
851 char r_iops_str[16], w_iops_str[16];
852 char r_clat_str[16], w_clat_str[16];
853 char r_bw_str[16], w_bw_str[16];
854 bool_Bool first;
855 struct shr_table *t;
856 const char *iopolicy;
857 struct shr_table_column columns[] = {
858 {"NSHead", LEFT, AUTO_WIDTH2147483647},
859 {"NSID", LEFT, AUTO_WIDTH2147483647},
860 {"NSPath", LEFT, AUTO_WIDTH2147483647},
861 {"Nodes", LEFT, AUTO_WIDTH2147483647},
862 {"Qdepth", LEFT, AUTO_WIDTH2147483647},
863 {"Ctrl", LEFT, AUTO_WIDTH2147483647},
864 {"r_IOPS", LEFT, 9},
865 {"w_IOPS", LEFT, 9},
866 {"r_clat", LEFT, 8},
867 {"w_clat", LEFT, 8},
868 {"r_bw", LEFT, 13},
869 {"w_bw", LEFT, 13},
870 {"Inflights", LEFT, AUTO_WIDTH2147483647},
871 {"Util%", LEFT, 6},
872 };
873
874 t = shr_table_create();
875 if (!t) {
876 nvme_show_error("Failed to init path perf table")nvme_show_message(1, "Failed to init path perf table");
877 return 1;
878 }
879
880 if (shr_table_add_columns_filter(t, columns, ARRAY_SIZE(columns)(sizeof(columns) / sizeof((columns)[0]) + 0),
881 subsystem_iopolicy_filter, (void *)s) < 0) {
882 nvme_show_error("Failed to add columns to path perf table")nvme_show_message(1, "Failed to add columns to path perf table"
)
;
883 ret = 1;
884 goto free_tbl;
885 }
886
887 libnvme_subsystem_get_iopolicy(s, &iopolicy, "");
888
889 fprintf(stream, "\n---------- Path Performance ----------\n\n");
890 libnvme_subsystem_for_each_ns(s, n)for (n = libnvme_subsystem_first_ns(s); n != ((void*)0); n = libnvme_subsystem_next_ns
(s, n))
{
891 first = true1;
892 libnvme_namespace_for_each_path(n, p)for (p = libnvme_namespace_first_path(n); p != ((void*)0); p =
libnvme_namespace_next_path(n, p))
{
893 r_iops = r_lat = r_bw = 0;
894 w_iops = w_lat = w_bw = 0;
895 util = inflights = 0;
896
897 nvme_path_calc_stat(p,
898 &r_iops, &w_iops,
899 &r_lat, &w_lat,
900 &r_bw, &w_bw,
901 &util, &inflights);
902
903 nvme_format_iops(r_iops, r_iops_str,
904 sizeof(r_iops_str));
905 nvme_format_iops(w_iops, w_iops_str,
906 sizeof(w_iops_str));
907
908 nvme_format_bw(r_bw, r_bw_str, sizeof(r_bw_str));
909 nvme_format_bw(w_bw, w_bw_str, sizeof(w_bw_str));
910
911 nvme_format_lat(r_lat, r_clat_str, sizeof(r_clat_str));
912 nvme_format_lat(w_lat, w_clat_str, sizeof(w_clat_str));
913
914 /* get controller associated with the path */
915 c = libnvme_path_get_ctrl(p);
916
917 row = shr_table_get_row_id(t);
918 if (row < 0) {
919 nvme_show_error("Failed to add row to path perf table")nvme_show_message(1, "Failed to add row to path perf table");
920 ret = 1;
921 goto free_tbl;
922 }
923
924 /*
925 * For the first row we print actual NSHead name,
926 * however, for the subsequent rows we print "arrow"
927 * ("-->") symbol for NSHead. This "arrow" style makes
928 * it visually obvious that subsequent entries (if
929 * present) are a path under the first NSHead.
930 */
931 col = -1;
932
933 if (first) {
934 shr_table_set_value_str(t, ++col, row,
935 libnvme_ns_get_name(n), LEFT);
936 first = false0;
937 } else
938 shr_table_set_value_str(t, ++col, row,
939 "-->", CENTERED);
940
941 shr_table_set_value_int(t, ++col, row,
942 libnvme_ns_get_nsid(n), CENTERED);
943 shr_table_set_value_str(t, ++col, row,
944 libnvme_path_get_name(p), LEFT);
945
946 if (!strcmp(iopolicy, "numa")) {
947 const char *numa_nodes;
948
949 libnvme_path_get_numa_nodes(p, &numa_nodes, "");
950 shr_table_set_value_str(t, ++col, row,
951 numa_nodes, CENTERED);
952 } else if (!strcmp(iopolicy, "queue-depth")) {
953 int queue_depth;
954
955 libnvme_path_get_queue_depth(p, &queue_depth,
956 0);
957 shr_table_set_value_int(t, ++col, row,
958 queue_depth, CENTERED);
959 }
960
961 shr_table_set_value_str(t, ++col, row,
962 libnvme_ctrl_get_name(c), LEFT);
963 shr_table_set_value_str(t, ++col, row, r_iops_str, LEFT);
964 shr_table_set_value_str(t, ++col, row, w_iops_str, LEFT);
965 shr_table_set_value_str(t, ++col, row, r_clat_str, LEFT);
966 shr_table_set_value_str(t, ++col, row, w_clat_str, LEFT);
967 shr_table_set_value_str(t, ++col, row, r_bw_str, LEFT);
968 shr_table_set_value_str(t, ++col, row, w_bw_str, LEFT);
969 shr_table_set_value_unsigned(t, ++col, row,
970 inflights, LEFT);
971 shr_table_set_value_double(t, ++col, row, util, LEFT);
972
973 shr_table_add_row(t, row);
974 }
975 }
976 shr_table_print_stream(stream, t);
977free_tbl:
978 shr_table_free(t);
979 return ret;
980}
981
982static void stdout_top_print_subsys_topology_config(FILE *stream,
983 struct libnvme_subsystem *s)
984{
985 int len = strlen(libnvme_subsystem_get_name(s));
986 const char *iopolicy;
987 const char *model;
988 const char *serial;
989 const char *firmware;
990
991 libnvme_subsystem_get_iopolicy(s, &iopolicy, "");
992 libnvme_subsystem_get_model(s, &model, "undefined");
993 libnvme_subsystem_get_serial(s, &serial, "");
994 libnvme_subsystem_get_firmware(s, &firmware, "");
995
996 fprintf(stream, "%s - NQN=%s\n", libnvme_subsystem_get_name(s),
997 libnvme_subsystem_get_subsysnqn(s));
998 fprintf(stream, "%*s hostnqn=%s\n", len, " ",
999 libnvme_host_get_hostnqn(libnvme_subsystem_get_host(s)));
1000 fprintf(stream, "%*s iopolicy=%s\n", len, " ", iopolicy);
1001
1002 fprintf(stream, "%*s model=%s\n", len, " ", model);
1003 fprintf(stream, "%*s serial=%s\n", len, " ", serial);
1004 fprintf(stream, "%*s firmware=%s\n", len, " ", firmware);
1005 fprintf(stream, "%*s type=%s\n", len, " ",
1006 libnvme_subsystem_get_subsystype(s));
1007
1008 fprintf(stream, "\n");
1009}
1010
1011static int stdout_top_update_stat(struct libnvme_subsystem *s)
1012{
1013 struct libnvme_ctrl *c;
1014 struct libnvme_ns *n;
1015 struct libnvme_path *p;
1016 int ret;
1017
1018 if (nvme_is_multipath(s)) {
1019 libnvme_subsystem_for_each_ns(s, n)for (n = libnvme_subsystem_first_ns(s); n != ((void*)0); n = libnvme_subsystem_next_ns
(s, n))
{
1020 ret = libnvme_ns_update_stat(n, true1);
1021 if (ret < 0) {
1022 nvme_show_error("Failed to update namespace stat")nvme_show_message(1, "Failed to update namespace stat");
1023 return ret;
1024 }
1025
1026 libnvme_namespace_for_each_path(n, p)for (p = libnvme_namespace_first_path(n); p != ((void*)0); p =
libnvme_namespace_next_path(n, p))
{
1027 ret = libnvme_path_update_stat(p, true1);
1028 if (ret < 0) {
1029 nvme_show_error("Failed to update path stat")nvme_show_message(1, "Failed to update path stat");
1030 return ret;
1031 }
1032 }
1033 }
1034 } else {
1035 libnvme_subsystem_for_each_ctrl(s, c)for (c = libnvme_subsystem_first_ctrl(s); c != ((void*)0); c =
libnvme_subsystem_next_ctrl(s, c))
{
1036 libnvme_ctrl_for_each_ns(c, n)for (n = libnvme_ctrl_first_ns(c); n != ((void*)0); n = libnvme_ctrl_next_ns
(c, n))
{
1037 ret = libnvme_ns_update_stat(n, true1);
1038 if (ret < 0) {
1039 nvme_show_error("Failed to update namespace stat")nvme_show_message(1, "Failed to update namespace stat");
1040 return ret;
1041 }
1042 }
1043 }
1044 }
1045
1046 return 0;
1047}
1048
1049static void stdout_top_reset_stat(struct libnvme_subsystem *s)
1050{
1051 struct libnvme_ctrl *c;
1052 struct libnvme_ns *n;
1053 struct libnvme_path *p;
1054
1055 if (nvme_is_multipath(s)) {
1056 libnvme_subsystem_for_each_ns(s, n)for (n = libnvme_subsystem_first_ns(s); n != ((void*)0); n = libnvme_subsystem_next_ns
(s, n))
{
1057 libnvme_ns_reset_stat(n);
1058
1059 libnvme_namespace_for_each_path(n, p)for (p = libnvme_namespace_first_path(n); p != ((void*)0); p =
libnvme_namespace_next_path(n, p))
1060 libnvme_path_reset_stat(p);
1061 }
1062 } else {
1063 libnvme_subsystem_for_each_ctrl(s, c)for (c = libnvme_subsystem_first_ctrl(s); c != ((void*)0); c =
libnvme_subsystem_next_ctrl(s, c))
{
1064
1065 libnvme_ctrl_for_each_ns(c, n)for (n = libnvme_ctrl_first_ns(c); n != ((void*)0); n = libnvme_ctrl_next_ns
(c, n))
1066 libnvme_ns_reset_stat(n);
1067 }
1068 }
1069}
1070
1071static int stdout_top_print_subsys_topology(struct dashboard_ctx *db_ctx,
1072 FILE *stream, struct libnvme_subsystem *s)
1073{
1074 int ret = 0;
1075 bool_Bool multipath = nvme_is_multipath(s);
1076
1077 ret = stdout_top_update_stat(s);
1078 if (ret)
1079 return ret;
1080
1081 stdout_top_print_subsys_topology_config(stream, s);
1082
1083 if (multipath) {
1084 ret = stdout_top_print_nshead_stat(stream, s);
1085 if (ret)
1086 return ret;
1087
1088 ret = stdout_top_print_path_perf(stream, s);
1089 if (ret)
1090 return ret;
1091
1092 ret = stdout_top_print_path_health(stream, s);
1093 if (ret)
1094 return ret;
1095 } else {
1096 ret = stdout_top_print_ns_stat(stream, s);
1097 if (ret)
1098 return ret;
1099 }
1100
1101 ret = stdout_top_print_ctrl_summary(stream, s, multipath);
1102
1103 return ret;
1104}
1105
1106static void stdout_top_print_subsys_topology_header(
1107 struct dashboard_ctx *db_ctx, FILE *stream)
1108{
1109 fprintf(stream, "---- nvme-top - Refresh: %d Second ----\n",
1110 dashboard_get_interval(db_ctx));
1111
1112 dashboard_set_header_rows(db_ctx, 1);
1113
1114 /* highlight the header row */
1115 dashboard_set_header_row_reverse(db_ctx, 0);
1116}
1117
1118static void stdout_top_print_subsys_topology_footer(
1119 struct dashboard_ctx *db_ctx, FILE *stream)
1120{
1121 fprintf(stream, "\n--------------------------------------\n");
1122 fprintf(stream, "[up/down keys to navigate, ESC to go back to the previous screen, q to quit]\n");
1123
1124 dashboard_set_footer_rows(db_ctx, 3);
1125
1126 /* hightligh the last footer row */
1127 dashboard_set_footer_row_reverse(db_ctx, 2);
1128}
1129
1130static struct libnvme_subsystem *stdout_top_search_subsystem(
1131 struct libnvme_global_ctx *ctx, const char *subsys_name)
1132{
1133 struct libnvme_host *h;
1134 struct libnvme_subsystem *s;
1135
1136 libnvme_for_each_host(ctx, h)for (h = libnvme_first_host(ctx); h != ((void*)0); h = libnvme_next_host
(ctx, h))
{
1137 libnvme_for_each_subsystem(h, s)for (s = libnvme_first_subsystem(h); s != ((void*)0); s = libnvme_next_subsystem
(h, s))
{
1138 if (!strcmp(libnvme_subsystem_get_name(s), subsys_name))
1139 return s;
1140 }
1141 }
1142
1143 return NULL((void*)0);
1144}
1145
1146static struct libnvme_subsystem **stdout_top_build_subsys_arr(
1147 struct libnvme_global_ctx *ctx, int *num_subsys)
1148{
1149 struct libnvme_host *h;
1150 struct libnvme_subsystem *s;
1151 struct libnvme_subsystem **subsys_arr;
1152 int subsys_idx = 0;
1153 int n = 0;
1154
1155 libnvme_for_each_host(ctx, h)for (h = libnvme_first_host(ctx); h != ((void*)0); h = libnvme_next_host
(ctx, h))
1156 libnvme_for_each_subsystem(h, s)for (s = libnvme_first_subsystem(h); s != ((void*)0); s = libnvme_next_subsystem
(h, s))
1157 n++;
1158 if (!n) {
1159 nvme_show_error("Can't find any NVMe subsystem on the host\n")nvme_show_message(1, "Can't find any NVMe subsystem on the host\n"
)
;
1160 return NULL((void*)0);
1161 }
1162
1163 subsys_arr = calloc(n, sizeof(struct libnvme_subsystem *));
1164 if (!subsys_arr) {
1165 nvme_show_error("Failed to allocate memory for subsys array\n")nvme_show_message(1, "Failed to allocate memory for subsys array\n"
)
;
1166 return NULL((void*)0);
1167 }
1168
1169 libnvme_for_each_host(ctx, h)for (h = libnvme_first_host(ctx); h != ((void*)0); h = libnvme_next_host
(ctx, h))
{
1170 libnvme_for_each_subsystem(h, s)for (s = libnvme_first_subsystem(h); s != ((void*)0); s = libnvme_next_subsystem
(h, s))
1171 subsys_arr[subsys_idx++] = s;
1172 }
1173
1174 *num_subsys = n;
1175 return subsys_arr;
1176}
1177
1178static int stdout_top_find_subsys_by_name(struct libnvme_subsystem **subsys_arr,
1179 int num_subsys, const char *subsys_name)
1180{
1181 int idx;
1182 struct libnvme_subsystem *s;
1183
1184 for (idx = 0; idx < num_subsys; idx++) {
1185 s = subsys_arr[idx];
1186
1187 if (!strcmp(libnvme_subsystem_get_name(s), subsys_name))
1188 return idx;
1189 }
1190
1191 return -1;
1192}
1193
1194static int handle_event_page_down(struct dashboard_ctx *db_ctx)
1195{
1196 int data_start, data_rows, frame_rows;
1197 int scroll_down, scroll = 0;
1198
1199 data_start = dashboard_get_data_start(db_ctx);
1200 data_rows = dashboard_get_data_rows(db_ctx);
1201 frame_rows = dashboard_get_frame_data_rows(db_ctx);
1202
1203 /*
1204 * Scroll down max up to one page frame from current data_start index.
1205 * While scrolling down, ensure we don't move past the max available
1206 * data rows. If we are already on the last page frame or there's no
1207 * data if move past the current page frame then ignore the key press.
1208 */
1209 scroll_down = data_start + frame_rows;
1210
1211 if (scroll_down < data_rows) {
1212 dashboard_set_data_start(db_ctx, scroll_down);
1213 scroll = 1;
1214 }
1215
1216 return scroll;
1217}
1218
1219static int handle_event_page_up(struct dashboard_ctx *db_ctx)
1220{
1221 int data_start, frame_rows, off;
1222 int scroll_up, scroll = 0;
1223
1224 data_start = dashboard_get_data_start(db_ctx);
1225 frame_rows = dashboard_get_frame_data_rows(db_ctx);
1226
1227 /*
1228 * Scroll back up max up to one page frame. Determine the num of data
1229 * rows we can scroll back up based on current data start index and max
1230 * num of rows which could be drawn in one page frame. If we're already
1231 * on the first page frame and hence we can't scroll back then ignore
1232 * the key press.
1233 */
1234 off = min(data_start, frame_rows)({ typeof(data_start) _a = (data_start); typeof(frame_rows) _b
= (frame_rows); do { } while (0); _a < _b ? _a : _b; })
;
1235 scroll_up = data_start - off;
1236
1237 if (scroll_up != data_start) {
1238 dashboard_set_data_start(db_ctx, scroll_up);
1239 scroll = 1;
1240 }
1241
1242 return scroll;
1243}
1244
1245/*
1246 * Draws subsys topology screen of susbystem @s
1247 * Returns: 0 if ESC key is pressed or needs to draw subsystem selection screen
1248 * 1 if 'q' is pressed or in case of error
1249 */
1250static int stdout_top_draw_subsys_topology_screen(
1251 struct libnvme_global_ctx *ctx, struct dashboard_ctx *db_ctx,
1252 FILE *stream, struct libnvme_subsystem *_s)
1253{
1254 enum event_type event;
1255 int ret, scroll = 0;
1256 int data_start, data_rows;
1257 __cleanup_free__attribute__((cleanup(shr_freep))) const char *subsys_name;
1258 struct libnvme_subsystem *s = _s;
1259
1260 subsys_name = strdup(libnvme_subsystem_get_name(s));
1261 if (!subsys_name)
1262 return 1; /* force quit */
1263
1264 while (1) {
1265 stdout_top_print_subsys_topology_header(db_ctx, stream);
1266 ret = stdout_top_print_subsys_topology(db_ctx, stream, s);
1267 if (ret)
1268 break;
1269 stdout_top_print_subsys_topology_footer(db_ctx, stream);
1270
1271draw:
1272 ret = dashboard_draw_frame(db_ctx, scroll);
1273 if (ret)
1274 break;
1275wait_for_event:
1276 event = dashboard_wait_for_event(db_ctx);
1277 if (event == EVENT_TYPE_KEY_ESC) {
1278 ret = 0;
1279 dashboard_reset(db_ctx);
1280 break;
1281 } else if (event == EVENT_TYPE_KEY_UP) {
1282 data_start = dashboard_get_data_start(db_ctx);
1283 /*
1284 * If we don't move past the first data row by shifting
1285 * one data row up then do so, otherwise ignore the key
1286 * press.
1287 */
1288 if (data_start - 1 >= 0) {
1289 dashboard_set_data_start(db_ctx,
1290 data_start - 1);
1291 scroll = 1;
1292 goto draw;
1293 }
1294 goto wait_for_event;
1295 } else if (event == EVENT_TYPE_KEY_DOWN) {
1296 data_start = dashboard_get_data_start(db_ctx);
1297 data_rows = dashboard_get_data_rows(db_ctx);
1298 /*
1299 * If we don't move past the max data rows shifting one
1300 * row down then do so, otherwise ignore the key press.
1301 */
1302 if (data_start + 1 < data_rows) {
1303 dashboard_set_data_start(db_ctx,
1304 data_start + 1);
1305 scroll = 1;
1306 goto draw;
1307 }
1308 goto wait_for_event;
1309 } else if (event == EVENT_TYPE_TIMEOUT) { /* screen timed out */
1310 scroll = 0;
1311 } else if (event == EVENT_TYPE_KEY_QUIT ||
1312 event == EVENT_TYPE_ERROR) {
1313 ret = 1;
1314 break;
1315 } else if (event == EVENT_TYPE_NVME_UEVENT) {
1316
1317 if (libnvme_refresh_topology(ctx)) {
1318 ret = 1; /* force quit */
1319 break;
1320 }
1321
1322 s = stdout_top_search_subsystem(ctx, subsys_name);
1323 if (!s) {
1324 ret = 0; /* draw subsys selection screen */
1325 break;
1326 }
1327 scroll = 0;
1328 } else if (event == EVENT_TYPE_SIGWINCH) {
1329 /*
1330 * Window size would have changed so re-draw the subsys
1331 * topology screen.
1332 */
1333 scroll = 0;
1334 } else if (event == EVENT_TYPE_KEY_PAGE_DOWN) {
1335
1336 scroll = handle_event_page_down(db_ctx);
1337 if (scroll)
1338 goto draw;
1339
1340 goto wait_for_event;
1341
1342 } else if (event == EVENT_TYPE_KEY_PAGE_UP) {
1343
1344 scroll = handle_event_page_up(db_ctx);
1345 if (scroll)
1346 goto draw;
1347
1348 goto wait_for_event;
1349 } /* else ignore */
1350 }
1351
1352 return ret;
1353}
1354
1355static int stdout_top_draw_subsys_screen(struct dashboard_ctx *db_ctx,
1356 FILE *stream, struct libnvme_subsystem **subsys_arr, int num_subsys)
1357{
1358 int ret = 0;
1359 struct libnvme_subsystem *s;
1360 struct libnvme_ctrl *c;
1361 struct libnvme_ns *n;
1362 struct libnvme_path *p;
1363 int i, row, col, num_ns, num_path, num_ctrl;
1364 double r_iops, w_iops;
1365 double r_bw, w_bw;
1366 double max_rlat, max_wlat, max_util;
1367 char r_bw_str[16], w_bw_str[16];
1368 char r_iops_str[16], w_iops_str[16];
1369 char r_clat_str[16], w_clat_str[16];
1370 const char *iopolicy;
1371 struct shr_table *t;
1372 struct shr_table_column columns[] = {
1373 {"Subsystem", LEFT, AUTO_WIDTH2147483647},
1374 {"Namespaces", LEFT, AUTO_WIDTH2147483647},
1375 {"Paths", LEFT, AUTO_WIDTH2147483647},
1376 {"Ctrls", LEFT, AUTO_WIDTH2147483647},
1377 {"IOPolicy", LEFT, AUTO_WIDTH2147483647},
1378 {"r_IOPS", LEFT, 9},
1379 {"w_IOPS", LEFT, 9},
1380 {"r_clat", LEFT, 8},
1381 {"w_clat", LEFT, 8},
1382 {"r_bw", LEFT, 13},
1383 {"w_bw", LEFT, 13},
1384 {"Util%", LEFT, 6},
1385 };
1386
1387 fprintf(stream, "---- nvme-top - Refresh: %d Second ----\n",
1388 dashboard_get_interval(db_ctx));
1389 fprintf(stream, "\n--------- Subsystem Summary ----------\n\n");
1390
1391 t = shr_table_create();
1392 if (!t) {
1393 nvme_show_error("Failed to init subsys screen table\n")nvme_show_message(1, "Failed to init subsys screen table\n");
1394 return -1;
1395 }
1396
1397 if (shr_table_add_columns(t, columns, ARRAY_SIZE(columns)(sizeof(columns) / sizeof((columns)[0]) + 0)) < 0) {
1398 nvme_show_error("Failed to add columns to subsys screen table\n")nvme_show_message(1, "Failed to add columns to subsys screen table\n"
)
;
1399 ret = -1;
1400 goto free_tbl;
1401 }
1402 /*
1403 * Header row count is calculated manually. The first row displays the
1404 * refresh interval, followed by an empty row. The third row displays
1405 * the heading followed by another empty row. The fifth row is for
1406 * displaying shr_table columns and then another row for dashes underneath
1407 * the shr_table columns.
1408 */
1409 dashboard_set_header_rows(db_ctx, 6);
1410
1411 /* highlight the first header row */
1412 dashboard_set_header_row_reverse(db_ctx, 0);
1413
1414 for (i = 0; i < num_subsys; i++) {
1415 s = subsys_arr[i];
1416 num_ctrl = num_ns = num_path = 0;
1417 r_iops = w_iops = 0;
1418 r_bw = w_bw = 0;
1419 max_rlat = max_wlat = 0;
1420 max_util = 0;
1421 libnvme_subsystem_get_iopolicy(s, &iopolicy, "NA");
1422
1423 libnvme_subsystem_for_each_ctrl(s, c)for (c = libnvme_subsystem_first_ctrl(s); c != ((void*)0); c =
libnvme_subsystem_next_ctrl(s, c))
1424 num_ctrl++;
1425
1426 ret = stdout_top_update_stat(s);
1427 if (ret)
1428 goto free_tbl;
1429
1430 if (nvme_is_multipath(s)) {
1431 libnvme_subsystem_for_each_ns(s, n)for (n = libnvme_subsystem_first_ns(s); n != ((void*)0); n = libnvme_subsystem_next_ns
(s, n))
{
1432 num_ns++;
1433
1434 libnvme_namespace_for_each_path(n, p)for (p = libnvme_namespace_first_path(n); p != ((void*)0); p =
libnvme_namespace_next_path(n, p))
1435 num_path++;
1436
1437 nvme_ns_calc_aggr_stat(n,
1438 &r_iops, &w_iops,
1439 &r_bw, &w_bw,
1440 &max_rlat, &max_wlat,
1441 &max_util);
1442 }
1443 } else {
1444 libnvme_subsystem_for_each_ctrl(s, c)for (c = libnvme_subsystem_first_ctrl(s); c != ((void*)0); c =
libnvme_subsystem_next_ctrl(s, c))
{
1445 libnvme_ctrl_for_each_ns(c, n)for (n = libnvme_ctrl_first_ns(c); n != ((void*)0); n = libnvme_ctrl_next_ns
(c, n))
{
1446 num_ns++;
1447
1448 nvme_ns_calc_aggr_stat(n,
1449 &r_iops, &w_iops,
1450 &r_bw, &w_bw,
1451 &max_rlat, &max_wlat,
1452 &max_util);
1453 }
1454 }
1455 }
1456
1457 nvme_format_iops(r_iops, r_iops_str, sizeof(r_iops_str));
1458 nvme_format_iops(w_iops, w_iops_str, sizeof(w_iops_str));
1459
1460 nvme_format_bw(r_bw, r_bw_str, sizeof(r_bw_str));
1461 nvme_format_bw(w_bw, w_bw_str, sizeof(w_bw_str));
1462
1463 nvme_format_lat(max_rlat, r_clat_str, sizeof(r_clat_str));
1464 nvme_format_lat(max_wlat, w_clat_str, sizeof(w_clat_str));
1465
1466 row = shr_table_get_row_id(t);
1467 if (row < 0) {
1468 nvme_show_error("Failed to add row to subsys screen table\n")nvme_show_message(1, "Failed to add row to subsys screen table\n"
)
;
1469 ret = -1;
1470 goto free_tbl;
1471 }
1472
1473 col = -1;
1474
1475 shr_table_set_value_str(t, ++col, row,
1476 libnvme_subsystem_get_name(s), LEFT);
1477 shr_table_set_value_int(t, ++col, row, num_ns, LEFT);
1478 shr_table_set_value_int(t, ++col, row, num_path, LEFT);
1479 shr_table_set_value_int(t, ++col, row, num_ctrl, LEFT);
1480 shr_table_set_value_str(t, ++col, row, iopolicy, LEFT);
1481 shr_table_set_value_str(t, ++col, row, r_iops_str, LEFT);
1482 shr_table_set_value_str(t, ++col, row, w_iops_str, LEFT);
1483 shr_table_set_value_str(t, ++col, row, r_clat_str, LEFT);
1484 shr_table_set_value_str(t, ++col, row, w_clat_str, LEFT);
1485 shr_table_set_value_str(t, ++col, row, r_bw_str, LEFT);
1486 shr_table_set_value_str(t, ++col, row, w_bw_str, LEFT);
1487 shr_table_set_value_double(t, ++col, row, max_util, LEFT);
1488
1489 shr_table_add_row(t, row);
1490 }
1491
1492 shr_table_print_stream(stream, t);
1493
1494 fprintf(stream, "\n--------------------------------------\n");
1495 fprintf(stream, "[up/down keys to navigate, Enter to view, q to quit]\n");
1496
1497 /*
1498 * Footer rows are calculated manually.
1499 * The first row is empty (adds spaces) followed by another row for
1500 * dashes and the last row adds footer string.
1501 */
1502 dashboard_set_footer_rows(db_ctx, 3);
1503
1504 /* highlight the last footer row */
1505 dashboard_set_footer_row_reverse(db_ctx, 2);
1506
1507free_tbl:
1508 shr_table_free(t);
1509 return ret;
1510}
1511
1512void stdout_top(int refresh_interval)
1513{
1514 FILE *stream;
1515 enum event_type event;
1516 struct dashboard_ctx *db_ctx;
1517 struct libnvme_host *h;
1518 struct libnvme_subsystem *s;
1519 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
1520 __cleanup_free__attribute__((cleanup(shr_freep))) struct libnvme_subsystem **subsys_arr = NULL((void*)0);
1521 int data_start, frame_rows, quit = 0, scroll = 0;
1522 int num_subsys = 0, subsys_idx = 0;
1523 int err;
1524
1525 err = nvme_create_global_ctx(&ctx);
1526 if (err)
1
Assuming 'err' is 0
2
Taking false branch
1527 return;
1528
1529 if (libnvme_scan_topology(ctx, NULL((void*)0), NULL((void*)0))) {
3
Assuming the condition is false
4
Taking false branch
1530 nvme_show_error("Failed to scan topology")nvme_show_message(1, "Failed to scan topology");
1531 return;
1532 }
1533
1534 subsys_arr = stdout_top_build_subsys_arr(ctx, &num_subsys);
1535 if (!subsys_arr
4.1
'subsys_arr' is non-null
)
5
Taking false branch
1536 return;
1537
1538 stream = dashboard_init(&db_ctx, refresh_interval);
1539 if (!stream)
6
Assuming 'stream' is non-null
7
Taking false branch
1540 return;
1541
1542 libnvme_for_each_host(ctx, h)for (h = libnvme_first_host(ctx); h != ((void*)0); h = libnvme_next_host
(ctx, h))
{
8
Assuming 'h' is not equal to null
9
Loop condition is true. Entering loop body
14
Assuming 'h' is equal to null
15
Loop condition is false. Execution continues on line 1550
1543 libnvme_for_each_subsystem(h, s)for (s = libnvme_first_subsystem(h); s != ((void*)0); s = libnvme_next_subsystem
(h, s))
10
Assuming 's' is not equal to null
11
Loop condition is true. Entering loop body
12
Assuming 's' is equal to null
13
Loop condition is false. Execution continues on line 1542
1544 stdout_top_reset_stat(s);
1545 }
1546
1547 /*
1548 * We start with first subsystem highlited, so set subsystem index to 0.
1549 */
1550 subsys_idx = 0;
1551 while (!quit) {
16
Loop condition is true. Entering loop body
1552 if (stdout_top_draw_subsys_screen(db_ctx, stream, subsys_arr,
17
Assuming the condition is false
18
Taking false branch
1553 num_subsys) < 0)
1554 break;
1555draw:
1556 /* highlight the selected @subsys_idx row */
1557 dashboard_set_data_row_reverse(db_ctx, subsys_idx);
1558 if (dashboard_draw_frame(db_ctx, scroll) < 0)
19
Assuming the condition is false
20
Taking false branch
1559 break;
1560wait_for_event:
1561 event = dashboard_wait_for_event(db_ctx);
1562 switch (event) {
21
Control jumps to 'case EVENT_TYPE_NVME_UEVENT:' at line 1599
1563 case EVENT_TYPE_KEY_QUIT:
1564 case EVENT_TYPE_ERROR:
1565 quit = 1;
1566 break;
1567 case EVENT_TYPE_KEY_RETURN: {
1568 dashboard_reset(db_ctx);
1569 s = subsys_arr[subsys_idx];
1570
1571 quit = stdout_top_draw_subsys_topology_screen(ctx,
1572 db_ctx, stream, s);
1573 if (quit)
1574 break;
1575
1576 scroll = 0;
1577 free(subsys_arr);
1578 subsys_arr = NULL((void*)0);
1579
1580 if (libnvme_refresh_topology(ctx)) {
1581 quit = 1;
1582 break;
1583 }
1584
1585 /*
1586 * The topology may have changed while the subsystem
1587 * dashboard was active. Restart from the first
1588 * subsystem instead of trying to restore the previous
1589 * screen position.
1590 */
1591 subsys_idx = 0;
1592 subsys_arr = stdout_top_build_subsys_arr(ctx,
1593 &num_subsys);
1594 if (!subsys_arr)
1595 quit = 1;
1596
1597 break;
1598 }
1599 case EVENT_TYPE_NVME_UEVENT: {
1600 __cleanup_free__attribute__((cleanup(shr_freep))) char *subsys_name = NULL((void*)0);
1601 struct libnvme_subsystem *s = subsys_arr[subsys_idx];
1602
1603 subsys_name = strdup(libnvme_subsystem_get_name(s));
22
Memory is allocated
1604 if (!subsys_name) {
23
Assuming 'subsys_name' is non-null
24
Taking false branch
1605 quit = 1;
1606 break;
1607 }
1608
1609 free(subsys_arr);
1610 subsys_arr = NULL((void*)0);
1611
1612 if (libnvme_refresh_topology(ctx)) {
25
Assuming the condition is true
26
Taking true branch
1613 quit = 1;
27
Potential leak of memory pointed to by 'subsys_name'
1614 break;
1615 }
1616
1617 subsys_arr = stdout_top_build_subsys_arr(ctx,
1618 &num_subsys);
1619 if (!subsys_arr) {
1620 quit = 1;
1621 break;
1622 }
1623
1624 subsys_idx = stdout_top_find_subsys_by_name(subsys_arr,
1625 num_subsys, subsys_name);
1626 if (subsys_idx < 0)
1627 subsys_idx = 0;
1628
1629 scroll = 0;
1630 break;
1631 }
1632 case EVENT_TYPE_KEY_DOWN:
1633 /*
1634 * The @num_subsys should be equal to @data_rows, so we
1635 * evaluate here that we don't move pass the last data
1636 * row (or the last subsys) if we were to shift (focus)
1637 * one row down. In case it's not possible to shift
1638 * because we are already down to the last row then
1639 * ignore key press.
1640 */
1641 if (subsys_idx + 1 < num_subsys) {
1642 subsys_idx++; /* we'll highlight this row */
1643
1644 data_start = dashboard_get_data_start(db_ctx);
1645 frame_rows = dashboard_get_frame_data_rows(
1646 db_ctx);
1647 /*
1648 * If moving to next row requires shifting the
1649 * window frame buffer by one position down then
1650 * do so.
1651 */
1652 if (subsys_idx >= data_start + frame_rows) {
1653 dashboard_set_data_start(db_ctx,
1654 data_start + 1);
1655 }
1656 /*
1657 * As we are scrolling one row down, we need to
1658 * re-draw the frame.
1659 */
1660 scroll = 1;
1661 goto draw;
1662 }
1663 goto wait_for_event;
1664 case EVENT_TYPE_KEY_UP:
1665 /*
1666 * If it's possible to move one row above the current
1667 * subsys (higlighted) row then decrease the subsys_idx
1668 * by one.
1669 */
1670 if (subsys_idx - 1 >= 0) {
1671 subsys_idx--;
1672 /*
1673 * If moving one row up requires us to shift
1674 * the window frame buffer by one position up
1675 * then do so.
1676 */
1677 data_start = dashboard_get_data_start(db_ctx);
1678 if (subsys_idx < data_start) {
1679 dashboard_set_data_start(db_ctx,
1680 data_start - 1);
1681 }
1682 /*
1683 * As we are scrolling one row up, we need to
1684 * re-draw the frame.
1685 */
1686 scroll = 1;
1687 goto draw;
1688 }
1689 goto wait_for_event;
1690 case EVENT_TYPE_TIMEOUT:
1691 /* subsys screen timed out */
1692 scroll = 0;
1693 break;
1694 case EVENT_TYPE_SIGWINCH:
1695 /*
1696 * Window size would have changed so re-draw the subsys
1697 * selection screen.
1698 */
1699 scroll = 0;
1700 break;
1701 case EVENT_TYPE_KEY_PAGE_DOWN:
1702 scroll = handle_event_page_down(db_ctx);
1703 if (scroll) {
1704 subsys_idx = dashboard_get_data_start(db_ctx);
1705 goto draw;
1706 }
1707
1708 goto wait_for_event;
1709 case EVENT_TYPE_KEY_PAGE_UP:
1710 scroll = handle_event_page_up(db_ctx);
1711 if (scroll) {
1712 subsys_idx = dashboard_get_data_start(db_ctx);
1713 goto draw;
1714 }
1715
1716 goto wait_for_event;
1717 default: /* unknown event, ignore */
1718 continue;
1719 }
1720 }
1721
1722 dashboard_exit(db_ctx);
1723}