Commit | Line | Data |
---|---|---|
50d16976 JA |
1 | #include <stdio.h> |
2 | #include <stdlib.h> | |
142575e6 | 3 | #include <stdarg.h> |
50d16976 JA |
4 | #include <unistd.h> |
5 | #include <limits.h> | |
50d16976 JA |
6 | #include <errno.h> |
7 | #include <fcntl.h> | |
8 | #include <sys/poll.h> | |
50d16976 JA |
9 | #include <sys/types.h> |
10 | #include <sys/wait.h> | |
d05c4a03 | 11 | #include <sys/socket.h> |
87aa8f19 JA |
12 | #include <sys/stat.h> |
13 | #include <sys/un.h> | |
50d16976 JA |
14 | #include <netinet/in.h> |
15 | #include <arpa/inet.h> | |
16 | #include <netdb.h> | |
e46d8091 | 17 | #include <syslog.h> |
9e22ecb0 | 18 | #include <signal.h> |
50d16976 JA |
19 | |
20 | #include "fio.h" | |
132159a5 | 21 | #include "server.h" |
fcee5ff6 | 22 | #include "crc/crc16.h" |
802ad4a8 | 23 | #include "ieee754.h" |
50d16976 | 24 | |
132159a5 | 25 | int fio_net_port = 8765; |
50d16976 | 26 | |
009b1be4 JA |
27 | int exit_backend = 0; |
28 | ||
46c48f1f | 29 | static int server_fd = -1; |
87aa8f19 JA |
30 | static char *fio_server_arg; |
31 | static char *bind_sock; | |
32 | static struct sockaddr_in saddr_in; | |
37db14fe | 33 | |
132159a5 JA |
34 | int fio_send_data(int sk, const void *p, unsigned int len) |
35 | { | |
794d69ca JA |
36 | assert(len <= sizeof(struct fio_net_cmd) + FIO_SERVER_MAX_PDU); |
37 | ||
132159a5 JA |
38 | do { |
39 | int ret = send(sk, p, len, 0); | |
40 | ||
41 | if (ret > 0) { | |
42 | len -= ret; | |
43 | if (!len) | |
44 | break; | |
45 | p += ret; | |
46 | continue; | |
47 | } else if (!ret) | |
48 | break; | |
49 | else if (errno == EAGAIN || errno == EINTR) | |
50 | continue; | |
51 | } while (!exit_backend); | |
52 | ||
53 | if (!len) | |
54 | return 0; | |
55 | ||
56 | return 1; | |
57 | } | |
58 | ||
59 | int fio_recv_data(int sk, void *p, unsigned int len) | |
60 | { | |
61 | do { | |
62 | int ret = recv(sk, p, len, MSG_WAITALL); | |
63 | ||
64 | if (ret > 0) { | |
65 | len -= ret; | |
66 | if (!len) | |
67 | break; | |
68 | p += ret; | |
69 | continue; | |
70 | } else if (!ret) | |
71 | break; | |
72 | else if (errno == EAGAIN || errno == EINTR) | |
73 | continue; | |
74 | } while (!exit_backend); | |
75 | ||
76 | if (!len) | |
77 | return 0; | |
78 | ||
79 | return -1; | |
80 | } | |
81 | ||
82 | static int verify_convert_cmd(struct fio_net_cmd *cmd) | |
83 | { | |
fcee5ff6 | 84 | uint16_t crc; |
132159a5 | 85 | |
fcee5ff6 JA |
86 | cmd->cmd_crc16 = le16_to_cpu(cmd->cmd_crc16); |
87 | cmd->pdu_crc16 = le16_to_cpu(cmd->pdu_crc16); | |
132159a5 | 88 | |
fcee5ff6 JA |
89 | crc = crc16(cmd, FIO_NET_CMD_CRC_SZ); |
90 | if (crc != cmd->cmd_crc16) { | |
132159a5 | 91 | log_err("fio: server bad crc on command (got %x, wanted %x)\n", |
fcee5ff6 | 92 | cmd->cmd_crc16, crc); |
132159a5 JA |
93 | return 1; |
94 | } | |
95 | ||
96 | cmd->version = le16_to_cpu(cmd->version); | |
97 | cmd->opcode = le16_to_cpu(cmd->opcode); | |
98 | cmd->flags = le32_to_cpu(cmd->flags); | |
99 | cmd->serial = le64_to_cpu(cmd->serial); | |
100 | cmd->pdu_len = le32_to_cpu(cmd->pdu_len); | |
101 | ||
102 | switch (cmd->version) { | |
178cde9f | 103 | case FIO_SERVER_VER2: |
132159a5 JA |
104 | break; |
105 | default: | |
106 | log_err("fio: bad server cmd version %d\n", cmd->version); | |
107 | return 1; | |
108 | } | |
109 | ||
110 | if (cmd->pdu_len > FIO_SERVER_MAX_PDU) { | |
111 | log_err("fio: command payload too large: %u\n", cmd->pdu_len); | |
112 | return 1; | |
113 | } | |
114 | ||
115 | return 0; | |
116 | } | |
117 | ||
a64e88da JA |
118 | /* |
119 | * Read (and defragment, if necessary) incoming commands | |
120 | */ | |
e951bdc4 | 121 | struct fio_net_cmd *fio_net_recv_cmd(int sk) |
132159a5 | 122 | { |
a64e88da JA |
123 | struct fio_net_cmd cmd, *cmdret = NULL; |
124 | size_t cmd_size = 0, pdu_offset = 0; | |
fcee5ff6 | 125 | uint16_t crc; |
a64e88da JA |
126 | int ret, first = 1; |
127 | void *pdu = NULL; | |
132159a5 | 128 | |
a64e88da JA |
129 | do { |
130 | ret = fio_recv_data(sk, &cmd, sizeof(cmd)); | |
131 | if (ret) | |
132 | break; | |
132159a5 | 133 | |
a64e88da JA |
134 | /* We have a command, verify it and swap if need be */ |
135 | ret = verify_convert_cmd(&cmd); | |
136 | if (ret) | |
137 | break; | |
132159a5 | 138 | |
0b8f30a5 JA |
139 | if (first) { |
140 | /* if this is text, add room for \0 at the end */ | |
141 | cmd_size = sizeof(cmd) + cmd.pdu_len + 1; | |
142 | assert(!cmdret); | |
143 | } else | |
a64e88da | 144 | cmd_size += cmd.pdu_len; |
132159a5 | 145 | |
a64e88da | 146 | cmdret = realloc(cmdret, cmd_size); |
132159a5 | 147 | |
a64e88da JA |
148 | if (first) |
149 | memcpy(cmdret, &cmd, sizeof(cmd)); | |
150 | else | |
151 | assert(cmdret->opcode == cmd.opcode); | |
152 | ||
153 | if (!cmd.pdu_len) | |
154 | break; | |
155 | ||
156 | /* There's payload, get it */ | |
157 | pdu = (void *) cmdret->payload + pdu_offset; | |
158 | ret = fio_recv_data(sk, pdu, cmd.pdu_len); | |
159 | if (ret) | |
160 | break; | |
161 | ||
162 | /* Verify payload crc */ | |
163 | crc = crc16(pdu, cmd.pdu_len); | |
164 | if (crc != cmd.pdu_crc16) { | |
165 | log_err("fio: server bad crc on payload "); | |
166 | log_err("(got %x, wanted %x)\n", cmd.pdu_crc16, crc); | |
167 | ret = 1; | |
168 | break; | |
169 | } | |
170 | ||
171 | pdu_offset += cmd.pdu_len; | |
817f06bb JA |
172 | if (!first) |
173 | cmdret->pdu_len += cmd.pdu_len; | |
a64e88da JA |
174 | first = 0; |
175 | } while (cmd.flags & FIO_NET_CMD_F_MORE); | |
132159a5 | 176 | |
a64e88da JA |
177 | if (ret) { |
178 | free(cmdret); | |
179 | cmdret = NULL; | |
0b8f30a5 JA |
180 | } else if (cmdret) { |
181 | /* zero-terminate text input */ | |
182 | if (cmdret->pdu_len && (cmdret->opcode == FIO_NET_CMD_TEXT || | |
183 | cmdret->opcode == FIO_NET_CMD_JOB)) { | |
184 | char *buf = (char *) cmdret->payload; | |
185 | ||
186 | buf[cmdret->pdu_len ] = '\0'; | |
187 | } | |
188 | /* frag flag is internal */ | |
a64e88da | 189 | cmdret->flags &= ~FIO_NET_CMD_F_MORE; |
0b8f30a5 | 190 | } |
a64e88da JA |
191 | |
192 | return cmdret; | |
132159a5 JA |
193 | } |
194 | ||
195 | void fio_net_cmd_crc(struct fio_net_cmd *cmd) | |
196 | { | |
197 | uint32_t pdu_len; | |
198 | ||
ddcc0b69 | 199 | cmd->cmd_crc16 = __cpu_to_le16(crc16(cmd, FIO_NET_CMD_CRC_SZ)); |
132159a5 JA |
200 | |
201 | pdu_len = le32_to_cpu(cmd->pdu_len); | |
202 | if (pdu_len) | |
ddcc0b69 | 203 | cmd->pdu_crc16 = __cpu_to_le16(crc16(cmd->payload, pdu_len)); |
132159a5 JA |
204 | } |
205 | ||
a64e88da | 206 | int fio_net_send_cmd(int fd, uint16_t opcode, const void *buf, off_t size) |
794d69ca JA |
207 | { |
208 | struct fio_net_cmd *cmd; | |
209 | size_t this_len; | |
210 | int ret; | |
211 | ||
212 | do { | |
213 | this_len = size; | |
214 | if (this_len > FIO_SERVER_MAX_PDU) | |
215 | this_len = FIO_SERVER_MAX_PDU; | |
216 | ||
217 | cmd = malloc(sizeof(*cmd) + this_len); | |
218 | ||
219 | fio_init_net_cmd(cmd, opcode, buf, this_len); | |
220 | ||
221 | if (this_len < size) | |
ddcc0b69 | 222 | cmd->flags = __cpu_to_le32(FIO_NET_CMD_F_MORE); |
794d69ca JA |
223 | |
224 | fio_net_cmd_crc(cmd); | |
225 | ||
226 | ret = fio_send_data(fd, cmd, sizeof(*cmd) + this_len); | |
227 | free(cmd); | |
228 | size -= this_len; | |
229 | buf += this_len; | |
230 | } while (!ret && size); | |
231 | ||
232 | return ret; | |
233 | } | |
234 | ||
cc0df00a | 235 | int fio_net_send_simple_cmd(int sk, uint16_t opcode, uint64_t serial) |
132159a5 | 236 | { |
178cde9f | 237 | struct fio_net_cmd cmd; |
132159a5 | 238 | |
178cde9f | 239 | fio_init_net_cmd(&cmd, opcode, NULL, 0); |
132159a5 JA |
240 | fio_net_cmd_crc(&cmd); |
241 | ||
242 | return fio_send_data(sk, &cmd, sizeof(cmd)); | |
243 | } | |
244 | ||
9abea48b | 245 | static int fio_server_send_quit_cmd(void) |
437377e1 | 246 | { |
46c48f1f | 247 | dprint(FD_NET, "server: sending quit\n"); |
cc0df00a | 248 | return fio_net_send_simple_cmd(server_fd, FIO_NET_CMD_QUIT, 0); |
437377e1 JA |
249 | } |
250 | ||
0b8f30a5 | 251 | static int handle_job_cmd(struct fio_net_cmd *cmd) |
132159a5 | 252 | { |
0b8f30a5 | 253 | char *buf = (char *) cmd->payload; |
a64e88da | 254 | int ret; |
132159a5 | 255 | |
e6d1c668 JA |
256 | if (parse_jobs_ini(buf, 1, 0)) { |
257 | fio_server_send_quit_cmd(); | |
81179eec | 258 | return -1; |
e6d1c668 | 259 | } |
81179eec JA |
260 | |
261 | fio_net_send_simple_cmd(server_fd, FIO_NET_CMD_START, 0); | |
262 | ||
263 | ret = exec_run(); | |
9abea48b | 264 | fio_server_send_quit_cmd(); |
81179eec JA |
265 | reset_fio_state(); |
266 | return ret; | |
267 | } | |
268 | ||
269 | static int handle_jobline_cmd(struct fio_net_cmd *cmd) | |
270 | { | |
271 | struct cmd_line_pdu *pdu = (struct cmd_line_pdu *) cmd->payload; | |
272 | char *argv[FIO_NET_CMD_JOBLINE_ARGV]; | |
273 | int ret, i; | |
274 | ||
275 | pdu->argc = le16_to_cpu(pdu->argc); | |
276 | ||
39e8e016 JA |
277 | dprint(FD_NET, "server: %d command line args\n", pdu->argc); |
278 | ||
279 | for (i = 0; i < pdu->argc; i++) { | |
81179eec | 280 | argv[i] = (char *) pdu->argv[i]; |
39e8e016 JA |
281 | dprint(FD_NET, "server: %d: %s\n", i, argv[i]); |
282 | } | |
81179eec | 283 | |
e6d1c668 JA |
284 | if (parse_cmd_line(pdu->argc, argv)) { |
285 | fio_server_send_quit_cmd(); | |
81179eec | 286 | return -1; |
e6d1c668 | 287 | } |
81179eec JA |
288 | |
289 | fio_net_send_simple_cmd(server_fd, FIO_NET_CMD_START, 0); | |
290 | ||
794d69ca | 291 | ret = exec_run(); |
9abea48b | 292 | fio_server_send_quit_cmd(); |
794d69ca | 293 | reset_fio_state(); |
132159a5 JA |
294 | return ret; |
295 | } | |
296 | ||
c28e8e8c JA |
297 | static int handle_probe_cmd(struct fio_net_cmd *cmd) |
298 | { | |
299 | struct cmd_probe_pdu probe; | |
300 | ||
301 | memset(&probe, 0, sizeof(probe)); | |
302 | gethostname((char *) probe.hostname, sizeof(probe.hostname)); | |
6eb24791 JA |
303 | #ifdef FIO_BIG_ENDIAN |
304 | probe.bigendian = 1; | |
305 | #endif | |
81179eec JA |
306 | probe.fio_major = FIO_MAJOR; |
307 | probe.fio_minor = FIO_MINOR; | |
308 | probe.fio_patch = FIO_PATCH; | |
c28e8e8c JA |
309 | |
310 | return fio_net_send_cmd(server_fd, FIO_NET_CMD_PROBE, &probe, sizeof(probe)); | |
311 | } | |
312 | ||
132159a5 JA |
313 | static int handle_command(struct fio_net_cmd *cmd) |
314 | { | |
315 | int ret; | |
316 | ||
46c48f1f JA |
317 | dprint(FD_NET, "server: got opcode %d\n", cmd->opcode); |
318 | ||
132159a5 JA |
319 | switch (cmd->opcode) { |
320 | case FIO_NET_CMD_QUIT: | |
cc0df00a | 321 | fio_terminate_threads(TERMINATE_ALL); |
c28e8e8c | 322 | return -1; |
d7959186 | 323 | case FIO_NET_CMD_EXIT: |
132159a5 | 324 | exit_backend = 1; |
c28e8e8c | 325 | return -1; |
132159a5 | 326 | case FIO_NET_CMD_JOB: |
0b8f30a5 | 327 | ret = handle_job_cmd(cmd); |
132159a5 | 328 | break; |
81179eec JA |
329 | case FIO_NET_CMD_JOBLINE: |
330 | ret = handle_jobline_cmd(cmd); | |
331 | break; | |
c28e8e8c JA |
332 | case FIO_NET_CMD_PROBE: |
333 | ret = handle_probe_cmd(cmd); | |
334 | break; | |
132159a5 JA |
335 | default: |
336 | log_err("fio: unknown opcode: %d\n", cmd->opcode); | |
337 | ret = 1; | |
338 | } | |
339 | ||
340 | return ret; | |
341 | } | |
342 | ||
70e0c316 | 343 | static int handle_connection(int sk, int block) |
132159a5 JA |
344 | { |
345 | struct fio_net_cmd *cmd = NULL; | |
346 | int ret = 0; | |
347 | ||
348 | /* read forever */ | |
349 | while (!exit_backend) { | |
e951bdc4 JA |
350 | struct pollfd pfd = { |
351 | .fd = sk, | |
352 | .events = POLLIN, | |
353 | }; | |
354 | ||
355 | ret = 0; | |
356 | do { | |
357 | ret = poll(&pfd, 1, 100); | |
358 | if (ret < 0) { | |
359 | if (errno == EINTR) | |
360 | break; | |
361 | log_err("fio: poll: %s\n", strerror(errno)); | |
362 | break; | |
19c65179 JA |
363 | } else if (!ret) { |
364 | if (!block) | |
365 | return 0; | |
e951bdc4 | 366 | continue; |
19c65179 | 367 | } |
e951bdc4 JA |
368 | |
369 | if (pfd.revents & POLLIN) | |
370 | break; | |
371 | if (pfd.revents & (POLLERR|POLLHUP)) { | |
372 | ret = 1; | |
373 | break; | |
374 | } | |
19c65179 | 375 | } while (!exit_backend); |
e951bdc4 JA |
376 | |
377 | if (ret < 0) | |
378 | break; | |
379 | ||
380 | cmd = fio_net_recv_cmd(sk); | |
132159a5 | 381 | if (!cmd) { |
c28e8e8c | 382 | ret = -1; |
132159a5 JA |
383 | break; |
384 | } | |
385 | ||
132159a5 JA |
386 | ret = handle_command(cmd); |
387 | if (ret) | |
388 | break; | |
389 | ||
390 | free(cmd); | |
c77a99e7 | 391 | cmd = NULL; |
132159a5 JA |
392 | } |
393 | ||
394 | if (cmd) | |
395 | free(cmd); | |
396 | ||
397 | return ret; | |
398 | } | |
399 | ||
cc0df00a JA |
400 | void fio_server_idle_loop(void) |
401 | { | |
402 | if (server_fd != -1) | |
70e0c316 | 403 | handle_connection(server_fd, 0); |
cc0df00a JA |
404 | } |
405 | ||
50d16976 JA |
406 | static int accept_loop(int listen_sk) |
407 | { | |
bb447a27 | 408 | struct sockaddr_in addr; |
5ba13ea6 | 409 | fio_socklen_t len = sizeof(addr); |
009b1be4 | 410 | struct pollfd pfd; |
132159a5 | 411 | int ret, sk, flags, exitval = 0; |
50d16976 | 412 | |
60efd14e JA |
413 | dprint(FD_NET, "server enter accept loop\n"); |
414 | ||
009b1be4 JA |
415 | flags = fcntl(listen_sk, F_GETFL); |
416 | flags |= O_NONBLOCK; | |
417 | fcntl(listen_sk, F_SETFL, flags); | |
50d16976 | 418 | again: |
009b1be4 JA |
419 | pfd.fd = listen_sk; |
420 | pfd.events = POLLIN; | |
421 | do { | |
422 | ret = poll(&pfd, 1, 100); | |
423 | if (ret < 0) { | |
424 | if (errno == EINTR) | |
425 | break; | |
fcee5ff6 | 426 | log_err("fio: poll: %s\n", strerror(errno)); |
009b1be4 JA |
427 | goto out; |
428 | } else if (!ret) | |
429 | continue; | |
430 | ||
431 | if (pfd.revents & POLLIN) | |
432 | break; | |
433 | } while (!exit_backend); | |
434 | ||
435 | if (exit_backend) | |
436 | goto out; | |
437 | ||
6b976bfc | 438 | sk = accept(listen_sk, (struct sockaddr *) &addr, &len); |
50d16976 | 439 | if (sk < 0) { |
690e09ae | 440 | log_err("fio: accept: %s\n", strerror(errno)); |
50d16976 JA |
441 | return -1; |
442 | } | |
443 | ||
bb447a27 | 444 | dprint(FD_NET, "server: connect from %s\n", inet_ntoa(addr.sin_addr)); |
46c48f1f | 445 | |
37db14fe JA |
446 | server_fd = sk; |
447 | ||
70e0c316 | 448 | exitval = handle_connection(sk, 1); |
50d16976 | 449 | |
37db14fe | 450 | server_fd = -1; |
50d16976 | 451 | close(sk); |
5c341e9a | 452 | |
009b1be4 | 453 | if (!exit_backend) |
5c341e9a JA |
454 | goto again; |
455 | ||
009b1be4 | 456 | out: |
132159a5 | 457 | return exitval; |
50d16976 JA |
458 | } |
459 | ||
142575e6 | 460 | int fio_server_text_output(const char *buf, unsigned int len) |
37db14fe | 461 | { |
337d75a8 JA |
462 | if (server_fd != -1) |
463 | return fio_net_send_cmd(server_fd, FIO_NET_CMD_TEXT, buf, len); | |
464 | ||
5037b845 | 465 | return fwrite(buf, len, 1, f_err); |
142575e6 JA |
466 | } |
467 | ||
a64e88da JA |
468 | static void convert_io_stat(struct io_stat *dst, struct io_stat *src) |
469 | { | |
470 | dst->max_val = cpu_to_le64(src->max_val); | |
471 | dst->min_val = cpu_to_le64(src->min_val); | |
472 | dst->samples = cpu_to_le64(src->samples); | |
802ad4a8 JA |
473 | |
474 | /* | |
475 | * Encode to IEEE 754 for network transfer | |
476 | */ | |
477 | dst->mean.u.i = __cpu_to_le64(fio_double_to_uint64(src->mean.u.f)); | |
478 | dst->S.u.i = __cpu_to_le64(fio_double_to_uint64(src->S.u.f)); | |
a64e88da JA |
479 | } |
480 | ||
481 | static void convert_gs(struct group_run_stats *dst, struct group_run_stats *src) | |
482 | { | |
483 | int i; | |
484 | ||
485 | for (i = 0; i < 2; i++) { | |
486 | dst->max_run[i] = cpu_to_le64(src->max_run[i]); | |
487 | dst->min_run[i] = cpu_to_le64(src->min_run[i]); | |
488 | dst->max_bw[i] = cpu_to_le64(src->max_bw[i]); | |
489 | dst->min_bw[i] = cpu_to_le64(src->min_bw[i]); | |
490 | dst->io_kb[i] = cpu_to_le64(src->io_kb[i]); | |
491 | dst->agg[i] = cpu_to_le64(src->agg[i]); | |
492 | } | |
493 | ||
494 | dst->kb_base = cpu_to_le32(src->kb_base); | |
495 | dst->groupid = cpu_to_le32(src->groupid); | |
496 | } | |
497 | ||
498 | /* | |
499 | * Send a CMD_TS, which packs struct thread_stat and group_run_stats | |
500 | * into a single payload. | |
501 | */ | |
502 | void fio_server_send_ts(struct thread_stat *ts, struct group_run_stats *rs) | |
503 | { | |
504 | struct cmd_ts_pdu p; | |
505 | int i, j; | |
506 | ||
60efd14e JA |
507 | dprint(FD_NET, "server sending end stats\n"); |
508 | ||
317b3c8b JA |
509 | memset(&p, 0, sizeof(p)); |
510 | ||
a64e88da JA |
511 | strcpy(p.ts.name, ts->name); |
512 | strcpy(p.ts.verror, ts->verror); | |
513 | strcpy(p.ts.description, ts->description); | |
514 | ||
ddcc0b69 | 515 | p.ts.error = cpu_to_le32(ts->error); |
a64e88da | 516 | p.ts.groupid = cpu_to_le32(ts->groupid); |
ddcc0b69 | 517 | p.ts.pid = cpu_to_le32(ts->pid); |
a64e88da JA |
518 | p.ts.members = cpu_to_le32(ts->members); |
519 | ||
520 | for (i = 0; i < 2; i++) { | |
521 | convert_io_stat(&p.ts.clat_stat[i], &ts->clat_stat[i]); | |
522 | convert_io_stat(&p.ts.slat_stat[i], &ts->slat_stat[i]); | |
523 | convert_io_stat(&p.ts.lat_stat[i], &ts->lat_stat[i]); | |
524 | convert_io_stat(&p.ts.bw_stat[i], &ts->bw_stat[i]); | |
525 | } | |
526 | ||
527 | p.ts.usr_time = cpu_to_le64(ts->usr_time); | |
528 | p.ts.sys_time = cpu_to_le64(ts->sys_time); | |
529 | p.ts.ctx = cpu_to_le64(ts->ctx); | |
530 | p.ts.minf = cpu_to_le64(ts->minf); | |
531 | p.ts.majf = cpu_to_le64(ts->majf); | |
532 | p.ts.clat_percentiles = cpu_to_le64(ts->clat_percentiles); | |
802ad4a8 JA |
533 | |
534 | for (i = 0; i < FIO_IO_U_LIST_MAX_LEN; i++) { | |
535 | fio_fp64_t *fp = &p.ts.percentile_list[i]; | |
536 | ||
537 | fp->u.i = __cpu_to_le64(fio_double_to_uint64(fp->u.f)); | |
538 | } | |
a64e88da JA |
539 | |
540 | for (i = 0; i < FIO_IO_U_MAP_NR; i++) { | |
541 | p.ts.io_u_map[i] = cpu_to_le32(ts->io_u_map[i]); | |
542 | p.ts.io_u_submit[i] = cpu_to_le32(ts->io_u_submit[i]); | |
543 | p.ts.io_u_complete[i] = cpu_to_le32(ts->io_u_complete[i]); | |
544 | } | |
545 | ||
546 | for (i = 0; i < FIO_IO_U_LAT_U_NR; i++) { | |
547 | p.ts.io_u_lat_u[i] = cpu_to_le32(ts->io_u_lat_u[i]); | |
548 | p.ts.io_u_lat_m[i] = cpu_to_le32(ts->io_u_lat_m[i]); | |
549 | } | |
550 | ||
551 | for (i = 0; i < 2; i++) | |
552 | for (j = 0; j < FIO_IO_U_PLAT_NR; j++) | |
553 | p.ts.io_u_plat[i][j] = cpu_to_le32(ts->io_u_plat[i][j]); | |
554 | ||
555 | for (i = 0; i < 3; i++) { | |
556 | p.ts.total_io_u[i] = cpu_to_le64(ts->total_io_u[i]); | |
93eee04a | 557 | p.ts.short_io_u[i] = cpu_to_le64(ts->short_io_u[i]); |
a64e88da JA |
558 | } |
559 | ||
93eee04a | 560 | p.ts.total_submit = cpu_to_le64(ts->total_submit); |
a64e88da JA |
561 | p.ts.total_complete = cpu_to_le64(ts->total_complete); |
562 | ||
563 | for (i = 0; i < 2; i++) { | |
564 | p.ts.io_bytes[i] = cpu_to_le64(ts->io_bytes[i]); | |
565 | p.ts.runtime[i] = cpu_to_le64(ts->runtime[i]); | |
566 | } | |
567 | ||
568 | p.ts.total_run_time = cpu_to_le64(ts->total_run_time); | |
569 | p.ts.continue_on_error = cpu_to_le16(ts->continue_on_error); | |
570 | p.ts.total_err_count = cpu_to_le64(ts->total_err_count); | |
ddcc0b69 JA |
571 | p.ts.first_error = cpu_to_le32(ts->first_error); |
572 | p.ts.kb_base = cpu_to_le32(ts->kb_base); | |
a64e88da JA |
573 | |
574 | convert_gs(&p.rs, rs); | |
575 | ||
576 | fio_net_send_cmd(server_fd, FIO_NET_CMD_TS, &p, sizeof(p)); | |
577 | } | |
578 | ||
579 | void fio_server_send_gs(struct group_run_stats *rs) | |
580 | { | |
581 | struct group_run_stats gs; | |
582 | ||
60efd14e JA |
583 | dprint(FD_NET, "server sending group run stats\n"); |
584 | ||
a64e88da JA |
585 | convert_gs(&gs, rs); |
586 | fio_net_send_cmd(server_fd, FIO_NET_CMD_GS, &gs, sizeof(gs)); | |
587 | } | |
588 | ||
cf451d1e JA |
589 | void fio_server_send_status(void) |
590 | { | |
1d1f45ae JA |
591 | struct jobs_eta *je; |
592 | size_t size; | |
593 | void *buf; | |
cf451d1e JA |
594 | int i; |
595 | ||
1d1f45ae JA |
596 | size = sizeof(*je) + thread_number * sizeof(char); |
597 | buf = malloc(size); | |
598 | memset(buf, 0, size); | |
599 | je = buf; | |
600 | ||
601 | if (!calc_thread_status(je)) { | |
602 | free(je); | |
cf451d1e | 603 | return; |
1d1f45ae | 604 | } |
cf451d1e | 605 | |
60efd14e JA |
606 | dprint(FD_NET, "server sending status\n"); |
607 | ||
1d1f45ae JA |
608 | je->nr_running = cpu_to_le32(je->nr_running); |
609 | je->nr_ramp = cpu_to_le32(je->nr_ramp); | |
610 | je->nr_pending = cpu_to_le32(je->nr_pending); | |
611 | je->files_open = cpu_to_le32(je->files_open); | |
612 | je->m_rate = cpu_to_le32(je->m_rate); | |
613 | je->t_rate = cpu_to_le32(je->t_rate); | |
614 | je->m_iops = cpu_to_le32(je->m_iops); | |
615 | je->t_iops = cpu_to_le32(je->t_iops); | |
cf451d1e JA |
616 | |
617 | for (i = 0; i < 2; i++) { | |
1d1f45ae JA |
618 | je->rate[i] = cpu_to_le32(je->rate[i]); |
619 | je->iops[i] = cpu_to_le32(je->iops[i]); | |
cf451d1e JA |
620 | } |
621 | ||
1d1f45ae JA |
622 | je->elapsed_sec = cpu_to_le32(je->nr_running); |
623 | je->eta_sec = cpu_to_le64(je->eta_sec); | |
cf451d1e | 624 | |
1d1f45ae JA |
625 | fio_net_send_cmd(server_fd, FIO_NET_CMD_ETA, buf, size); |
626 | free(je); | |
cf451d1e JA |
627 | } |
628 | ||
142575e6 JA |
629 | int fio_server_log(const char *format, ...) |
630 | { | |
631 | char buffer[1024]; | |
632 | va_list args; | |
82fa6b21 | 633 | size_t len; |
142575e6 | 634 | |
60efd14e JA |
635 | dprint(FD_NET, "server log\n"); |
636 | ||
142575e6 | 637 | va_start(args, format); |
82fa6b21 | 638 | len = vsnprintf(buffer, sizeof(buffer), format, args); |
142575e6 JA |
639 | va_end(args); |
640 | ||
82fa6b21 | 641 | return fio_server_text_output(buffer, len); |
37db14fe | 642 | } |
e46d8091 | 643 | |
87aa8f19 | 644 | static int fio_init_server_ip(void) |
81179eec | 645 | { |
87aa8f19 | 646 | int sk, opt; |
81179eec JA |
647 | |
648 | sk = socket(AF_INET, SOCK_STREAM, 0); | |
649 | if (sk < 0) { | |
650 | log_err("fio: socket: %s\n", strerror(errno)); | |
651 | return -1; | |
652 | } | |
653 | ||
654 | opt = 1; | |
655 | if (setsockopt(sk, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) { | |
656 | log_err("fio: setsockopt: %s\n", strerror(errno)); | |
b94cba47 | 657 | close(sk); |
81179eec JA |
658 | return -1; |
659 | } | |
660 | #ifdef SO_REUSEPORT | |
6eb24791 | 661 | if (setsockopt(sk, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt)) < 0) { |
81179eec | 662 | log_err("fio: setsockopt: %s\n", strerror(errno)); |
b94cba47 | 663 | close(sk); |
81179eec JA |
664 | return -1; |
665 | } | |
666 | #endif | |
667 | ||
668 | saddr_in.sin_family = AF_INET; | |
81179eec JA |
669 | saddr_in.sin_port = htons(fio_net_port); |
670 | ||
671 | if (bind(sk, (struct sockaddr *) &saddr_in, sizeof(saddr_in)) < 0) { | |
672 | log_err("fio: bind: %s\n", strerror(errno)); | |
b94cba47 | 673 | close(sk); |
81179eec JA |
674 | return -1; |
675 | } | |
676 | ||
87aa8f19 JA |
677 | return sk; |
678 | } | |
679 | ||
680 | static int fio_init_server_sock(void) | |
681 | { | |
682 | struct sockaddr_un addr; | |
683 | fio_socklen_t len; | |
684 | mode_t mode; | |
685 | int sk; | |
686 | ||
687 | sk = socket(AF_UNIX, SOCK_STREAM, 0); | |
688 | if (sk < 0) { | |
689 | log_err("fio: socket: %s\n", strerror(errno)); | |
690 | return -1; | |
691 | } | |
692 | ||
693 | mode = umask(000); | |
694 | ||
695 | memset(&addr, 0, sizeof(addr)); | |
696 | addr.sun_family = AF_UNIX; | |
697 | strcpy(addr.sun_path, bind_sock); | |
698 | unlink(bind_sock); | |
699 | ||
700 | len = sizeof(addr.sun_family) + strlen(bind_sock) + 1; | |
701 | ||
702 | if (bind(sk, (struct sockaddr *) &addr, len) < 0) { | |
703 | log_err("fio: bind: %s\n", strerror(errno)); | |
b94cba47 | 704 | close(sk); |
87aa8f19 JA |
705 | return -1; |
706 | } | |
707 | ||
708 | umask(mode); | |
709 | return sk; | |
710 | } | |
711 | ||
712 | static int fio_init_server_connection(void) | |
713 | { | |
714 | int sk; | |
715 | ||
716 | dprint(FD_NET, "starting server\n"); | |
717 | ||
718 | if (!bind_sock) | |
719 | sk = fio_init_server_ip(); | |
720 | else | |
721 | sk = fio_init_server_sock(); | |
722 | ||
723 | if (sk < 0) | |
724 | return sk; | |
725 | ||
81179eec JA |
726 | if (listen(sk, 1) < 0) { |
727 | log_err("fio: listen: %s\n", strerror(errno)); | |
728 | return -1; | |
729 | } | |
730 | ||
87aa8f19 JA |
731 | return sk; |
732 | } | |
733 | ||
734 | /* | |
735 | * Server arg should be one of: | |
736 | * | |
737 | * sock:/path/to/socket | |
738 | * ip:1.2.3.4 | |
739 | * 1.2.3.4 | |
740 | * | |
741 | * Where sock uses unix domain sockets, and ip binds the server to | |
742 | * a specific interface. If no arguments are given to the server, it | |
743 | * uses IP and binds to 0.0.0.0. | |
744 | * | |
745 | */ | |
746 | static int fio_handle_server_arg(void) | |
747 | { | |
748 | saddr_in.sin_addr.s_addr = htonl(INADDR_ANY); | |
749 | ||
750 | if (!fio_server_arg) | |
751 | return 0; | |
752 | if (!strncmp(fio_server_arg, "sock:", 5)) { | |
194d05ce | 753 | bind_sock = fio_server_arg + 5; |
87aa8f19 JA |
754 | return 0; |
755 | } else { | |
756 | char *host = fio_server_arg; | |
757 | ||
758 | if (!strncmp(host, "ip:", 3)) | |
759 | host += 3; | |
760 | ||
761 | if (inet_aton(host, &saddr_in.sin_addr) != 1) { | |
762 | struct hostent *hent; | |
763 | ||
764 | hent = gethostbyname(host); | |
765 | if (hent) | |
766 | memcpy(&saddr_in.sin_addr, hent->h_addr, 4); | |
767 | } | |
768 | return 0; | |
81179eec | 769 | } |
87aa8f19 JA |
770 | } |
771 | ||
772 | static int fio_server(void) | |
773 | { | |
774 | int sk, ret; | |
775 | ||
776 | dprint(FD_NET, "starting server\n"); | |
777 | ||
778 | if (fio_handle_server_arg()) | |
779 | return -1; | |
780 | ||
781 | sk = fio_init_server_connection(); | |
782 | if (sk < 0) | |
783 | return -1; | |
81179eec JA |
784 | |
785 | ret = accept_loop(sk); | |
87aa8f19 | 786 | |
81179eec | 787 | close(sk); |
87aa8f19 JA |
788 | |
789 | if (fio_server_arg) { | |
790 | free(fio_server_arg); | |
791 | fio_server_arg = NULL; | |
792 | } | |
793 | ||
81179eec JA |
794 | return ret; |
795 | } | |
796 | ||
9abea48b JA |
797 | static void sig_int(int sig) |
798 | { | |
799 | fio_terminate_threads(TERMINATE_ALL); | |
800 | exit_backend = 1; | |
801 | } | |
802 | ||
803 | static void server_signal_handler(void) | |
804 | { | |
805 | struct sigaction act; | |
806 | ||
807 | memset(&act, 0, sizeof(act)); | |
808 | act.sa_handler = sig_int; | |
809 | act.sa_flags = SA_RESTART; | |
810 | sigaction(SIGINT, &act, NULL); | |
811 | ||
812 | memset(&act, 0, sizeof(act)); | |
813 | act.sa_handler = sig_int; | |
814 | act.sa_flags = SA_RESTART; | |
815 | sigaction(SIGTERM, &act, NULL); | |
816 | } | |
817 | ||
e46d8091 JA |
818 | int fio_start_server(int daemonize) |
819 | { | |
820 | pid_t pid; | |
821 | ||
9abea48b JA |
822 | server_signal_handler(); |
823 | ||
e46d8091 JA |
824 | if (!daemonize) |
825 | return fio_server(); | |
826 | ||
827 | openlog("fio", LOG_NDELAY|LOG_NOWAIT|LOG_PID, LOG_USER); | |
828 | pid = fork(); | |
829 | if (pid < 0) { | |
830 | syslog(LOG_ERR, "failed server fork"); | |
c28e8e8c | 831 | return -1; |
e46d8091 JA |
832 | } else if (pid) |
833 | exit(0); | |
834 | ||
835 | setsid(); | |
836 | close(STDIN_FILENO); | |
837 | close(STDOUT_FILENO); | |
838 | close(STDERR_FILENO); | |
839 | f_out = NULL; | |
840 | f_err = NULL; | |
841 | log_syslog = 1; | |
842 | return fio_server(); | |
843 | } | |
87aa8f19 JA |
844 | |
845 | void fio_server_add_arg(const char *arg) | |
846 | { | |
847 | fio_server_arg = strdup(arg); | |
848 | } |