Skip to content

Commit 63845e1

Browse files
committed
nvme: add show-topology delay option for nvme top
This is to monitor the system changes. Signed-off-by: Tokunori Ikegami <[email protected]>
1 parent a2e1b83 commit 63845e1

5 files changed

Lines changed: 303 additions & 8 deletions

File tree

nvme.c

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
#include "util/suffix.h"
6565
#include "logging.h"
6666
#include "util/sighdl.h"
67+
#include "util/delay.h"
6768
#include "fabrics.h"
6869
#define CREATE_CMD
6970
#include "nvme-builtin.h"
@@ -191,6 +192,7 @@ const char *output_format = "Output format: normal|binary";
191192
const char *timeout = "timeout value, in milliseconds";
192193
const char *verbose = "Increase output verbosity";
193194
const char *dry_run = "show command instead of sending";
195+
const char *delay = "iterative delay as SECS [.TENTHS]";
194196

195197
static const char *app_tag = "app tag for end-to-end PI";
196198
static const char *app_tag_mask = "app tag mask for end-to-end PI";
@@ -262,6 +264,7 @@ struct nvme_config nvme_cfg = {
262264
.output_format = "normal",
263265
.output_format_ver = 1,
264266
.timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
267+
.delay = 0,
265268
};
266269

267270
static void *mmap_registers(struct nvme_transport_handle *hdl, bool writable);
@@ -374,7 +377,10 @@ static int parse_args(int argc, char *argv[], const char *desc,
374377
log_level = map_log_level(nvme_cfg.verbose, false);
375378
nvme_init_default_logging(stderr, log_level, false, false);
376379

377-
return 0;
380+
if (nvme_cfg.delay)
381+
ret = delay_set_stdout_file();
382+
383+
return ret;
378384
}
379385

380386
int parse_and_open(struct nvme_global_ctx **ctx,
@@ -9998,7 +10004,7 @@ static int tls_key(int argc, char **argv, struct command *acmd, struct plugin *p
999810004
OPT_FLAG("export", 'e', &cfg.export, export),
999910005
OPT_STR("revoke", 'r', &cfg.revoke, revoke));
1000010006

10001-
err = argconfig_parse(argc, argv, desc, opts);
10007+
err = parse_args(argc, argv, desc, opts);
1000210008
if (err)
1000310009
return err;
1000410010

@@ -10096,9 +10102,10 @@ static int show_topology_cmd(int argc, char **argv, struct command *acmd, struct
1009610102
};
1009710103

1009810104
NVME_ARGS(opts,
10099-
OPT_FMT("ranking", 'r', &cfg.ranking, ranking));
10105+
OPT_FMT("ranking", 'r', &cfg.ranking, ranking),
10106+
OPT_DOUBLE("delay", 'd', &nvme_cfg.delay, delay));
1010010107

10101-
err = argconfig_parse(argc, argv, desc, opts);
10108+
err = parse_args(argc, argv, desc, opts);
1010210109
if (err)
1010310110
return err;
1010410111

@@ -11026,9 +11033,15 @@ int main(int argc, char **argv)
1102611033
if (err)
1102711034
return err;
1102811035

11029-
err = handle_plugin(argc - 1, &argv[1], nvme.extensions);
11030-
if (err == -ENOTTY)
11031-
general_help(&builtin, NULL);
11036+
err = delay_remove_file();
11037+
if (err)
11038+
return err;
11039+
11040+
do {
11041+
err = handle_plugin(argc - 1, &argv[1], nvme.extensions);
11042+
if (err == -ENOTTY)
11043+
general_help(&builtin, NULL);
11044+
} while (!err && nvme_cfg.delay && delay_handle());
1103211045

1103311046
return err ? 1 : 0;
1103411047
}

nvme.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ struct nvme_config {
5959
bool dry_run;
6060
bool no_retries;
6161
unsigned int output_format_ver;
62+
double delay;
6263
};
6364

6465
/*
@@ -109,6 +110,7 @@ extern const char *output_format;
109110
extern const char *timeout;
110111
extern const char *verbose;
111112
extern const char *dry_run;
113+
extern const char *delay;
112114
extern struct nvme_config nvme_cfg;
113115

114116
int validate_output_format(const char *format, nvme_print_flags_t *flags);

util/delay.c

Lines changed: 270 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,270 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
#include "cleanup.h"
3+
#include "types.h"
4+
#include "nvme.h"
5+
#include <errno.h>
6+
#include <stdio.h>
7+
#include <unistd.h>
8+
#include <math.h>
9+
#include <termios.h>
10+
#include <sys/types.h>
11+
#include <sys/stat.h>
12+
13+
#define NVME_OUT "nvme.out"
14+
#define NVME_PREV_OUT "nvme-prev.out"
15+
16+
int delay_set_stdout_file(void)
17+
{
18+
if (!freopen(NVME_OUT, "w", stdout)) {
19+
perror("freopen");
20+
return -errno;
21+
}
22+
23+
return 0;
24+
}
25+
26+
static int get_file_line(const char *file)
27+
{
28+
_cleanup_file_ FILE *fd = fopen(file, "r");
29+
30+
_cleanup_free_ char *str = NULL;
31+
int len = STR_LEN;
32+
int line = 0;
33+
34+
if (!file || !fd)
35+
return 0;
36+
37+
str = malloc(len + 1);
38+
if (str) {
39+
while (fgets(str, len, fd))
40+
line++;
41+
}
42+
43+
return line;
44+
}
45+
46+
static bool get_window_size(int *row, int *col)
47+
{
48+
struct winsize ws;
49+
50+
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) < 0)
51+
return false;
52+
53+
*row = ws.ws_row;
54+
*col = ws.ws_col;
55+
56+
return true;
57+
}
58+
59+
static char *read_file(const char *file, size_t *len, int i, int row, int col)
60+
{
61+
_cleanup_file_ FILE *fd = fopen(file, "r");
62+
_cleanup_free_ char *str = NULL;
63+
struct stat st;
64+
char *buf;
65+
int j = 0;
66+
67+
if (!file || !len || !fd || stat(file, &st))
68+
return NULL;
69+
70+
*len = 0;
71+
72+
str = malloc(col + 1);
73+
if (!str)
74+
return NULL;
75+
76+
buf = malloc(st.st_size + 1);
77+
if (!buf)
78+
return NULL;
79+
80+
for (j = 0; j < i + row - 1; j++) {
81+
if (!fgets(str, col, fd))
82+
break;
83+
if (j >= i)
84+
*len += sprintf(&buf[*len], "%s", str);
85+
}
86+
87+
if (*len)
88+
return buf;
89+
90+
return NULL;
91+
}
92+
93+
static bool delay_compare(int i, int row, int col)
94+
{
95+
_cleanup_free_ char *prev_buf = NULL;
96+
_cleanup_free_ char *buf = NULL;
97+
bool changed = false;
98+
static int last_row;
99+
static int last_col;
100+
static int last_i;
101+
size_t prev_len;
102+
struct stat st;
103+
size_t len;
104+
105+
if (last_i != i || last_row != row || last_col != col)
106+
changed = true;
107+
108+
last_i = i;
109+
last_row = row;
110+
last_col = col;
111+
112+
if (changed || stat(NVME_PREV_OUT, &st))
113+
return true;
114+
115+
buf = read_file(NVME_OUT, &len, i, row, col);
116+
if (!buf)
117+
return true;
118+
119+
prev_buf = read_file(NVME_PREV_OUT, &prev_len, i, row, col);
120+
if (!prev_buf || len != prev_len)
121+
return true;
122+
123+
return !!memcmp(buf, prev_buf, len);
124+
}
125+
126+
static bool delay_copy(void)
127+
{
128+
_cleanup_free_ char *cmd = NULL;
129+
int err;
130+
131+
if (asprintf(&cmd, "cp %s %s", NVME_OUT, NVME_PREV_OUT) < 0)
132+
return false;
133+
134+
err = system(cmd);
135+
if (err < 0)
136+
return false;
137+
138+
return true;
139+
}
140+
141+
static bool delay_print(int i, int row, int col)
142+
{
143+
_cleanup_free_ char *buf = NULL;
144+
size_t len;
145+
int err;
146+
147+
buf = read_file(NVME_OUT, &len, i, row, col);
148+
if (!buf)
149+
return false;
150+
151+
err = system("clear");
152+
if (err < 0)
153+
return false;
154+
155+
printf("%s", buf);
156+
157+
return true;
158+
}
159+
160+
static bool check_up(char *str, int i)
161+
{
162+
static const char up[] = "\x1b[A";
163+
164+
if (memcmp(str, up, sizeof(up) - 1) || !i)
165+
return false;
166+
167+
return true;
168+
}
169+
170+
static bool check_down(char *str, int i, int row)
171+
{
172+
static const char down[] = "\x1b[B";
173+
int line = get_file_line(NVME_OUT);
174+
175+
if (memcmp(str, down, sizeof(down) - 1) || i + row >= line)
176+
return false;
177+
178+
return true;
179+
}
180+
181+
bool delay_handle(void)
182+
{
183+
_cleanup_free_ char *str = NULL;
184+
struct termios orgtty;
185+
struct termios curtty;
186+
struct timespec ts;
187+
char *prev = NULL;
188+
double delay_f;
189+
double delay_i;
190+
static int row;
191+
static int col;
192+
static int i;
193+
fd_set fds;
194+
int len = 0;
195+
char buf;
196+
int err;
197+
198+
tcgetattr(STDIN_FILENO, &orgtty);
199+
curtty = orgtty;
200+
201+
curtty.c_lflag &= ~(ECHO | ICANON);
202+
tcsetattr(STDIN_FILENO, TCSAFLUSH, &curtty);
203+
204+
FD_ZERO(&fds);
205+
FD_SET(STDIN_FILENO, &fds);
206+
207+
if (!freopen("/dev/tty", "w", stdout)) {
208+
tcsetattr(STDIN_FILENO, TCSAFLUSH, &orgtty);
209+
return false;
210+
}
211+
212+
if (!get_window_size(&row, &col) || !row || !col)
213+
return false;
214+
215+
if (delay_compare(i, row, col)) {
216+
if (!delay_print(i, row, col) || !delay_copy()) {
217+
tcsetattr(STDIN_FILENO, TCSAFLUSH, &orgtty);
218+
return false;
219+
}
220+
}
221+
222+
delay_f = modf(nvme_cfg.delay, &delay_i);
223+
ts.tv_sec = delay_i;
224+
ts.tv_nsec = delay_f * 1000000000;
225+
do {
226+
err = pselect(STDIN_FILENO + 1, &fds, NULL, NULL, &ts, NULL);
227+
if (err <= 0)
228+
break;
229+
err = read(STDIN_FILENO, &buf, sizeof(buf));
230+
if (err <= 0)
231+
break;
232+
len += err;
233+
str = malloc(len);
234+
if (!str)
235+
continue;
236+
if (prev && len > 1) {
237+
memcpy(str, prev, len - err);
238+
free(prev);
239+
}
240+
str[len - 1] = buf;
241+
prev = str;
242+
if (check_up(str, i)) {
243+
i--;
244+
break;
245+
} else if (check_down(str, i, row)) {
246+
i++;
247+
break;
248+
}
249+
} while (err > 0);
250+
251+
tcsetattr(STDIN_FILENO, TCSAFLUSH, &orgtty);
252+
253+
if (err < 0)
254+
return false;
255+
256+
return true;
257+
}
258+
259+
int delay_remove_file(void)
260+
{
261+
struct stat st;
262+
263+
if (!stat(NVME_OUT, &st) && remove(NVME_OUT))
264+
return -errno;
265+
266+
if (!stat(NVME_PREV_OUT, &st) && remove(NVME_PREV_OUT))
267+
return -errno;
268+
269+
return 0;
270+
}

util/delay.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/* SPDX-License-Identifier: GPL-2.0-or-later */
2+
#ifndef _DELAY_H
3+
#define _DELAY_H
4+
5+
int delay_set_stdout_file(void);
6+
bool delay_handle(void);
7+
int delay_remove_file(void);
8+
9+
#endif /* _DELAY_H */

util/meson.build

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ util_sources = [
99
'util/suffix.c',
1010
'util/types.c',
1111
'util/utils.c',
12-
'util/table.c'
12+
'util/table.c',
13+
'util/delay.c'
1314
]
1415

1516
if json_c_dep.found()

0 commit comments

Comments
 (0)