X-Git-Url: https://git.kernel.dk/?p=fio.git;a=blobdiff_plain;f=server.c;h=519a7ee681af5ebe5361e10264d069a146e85ab2;hp=b7e9922425c0443798d54a52973fc6a90552c918;hb=68d96e515825185b232bbc8d2e5b5136b90514be;hpb=62232d0618e3f5aa4805b4be34e872895ba1beb2 diff --git a/server.c b/server.c index b7e99224..519a7ee6 100644 --- a/server.c +++ b/server.c @@ -4,24 +4,29 @@ #include #include #include -#include #include #include #include #include #include #include +#include #include #include #include #include #include +#ifdef CONFIG_ZLIB #include +#endif #include "fio.h" +#include "options.h" #include "server.h" #include "crc/crc16.h" #include "lib/ieee754.h" +#include "verify.h" +#include "smalloc.h" int fio_net_port = FIO_NET_PORT; @@ -33,6 +38,13 @@ static char *bind_sock; static struct sockaddr_in saddr_in; static struct sockaddr_in6 saddr_in6; static int use_ipv6; +#ifdef CONFIG_ZLIB +static unsigned int has_zlib = 1; +#else +static unsigned int has_zlib = 0; +#endif +static unsigned int use_zlib; +static char me[128]; struct fio_fork_item { struct flist_head list; @@ -42,11 +54,12 @@ struct fio_fork_item { pid_t pid; }; -/* Created on fork on new connection */ -static FLIST_HEAD(conn_list); - -/* Created on job fork from connection */ -static FLIST_HEAD(job_list); +struct cmd_reply { + struct fio_mutex lock; + void *data; + size_t size; + int error; +}; static const char *fio_server_ops[FIO_NET_CMD_NR] = { "", @@ -65,8 +78,12 @@ static const char *fio_server_ops[FIO_NET_CMD_NR] = { "DISK_UTIL", "SERVER_START", "ADD_JOB", - "CMD_RUN" - "CMD_IOLOG", + "RUN", + "IOLOG", + "UPDATE_JOB", + "LOAD_FILE", + "VTRIGGER", + "SENDFILE", }; const char *fio_server_op(unsigned int op) @@ -206,7 +223,7 @@ static int verify_convert_cmd(struct fio_net_cmd *cmd) */ struct fio_net_cmd *fio_net_recv_cmd(int sk) { - struct fio_net_cmd cmd, *cmdret = NULL; + struct fio_net_cmd cmd, *tmp, *cmdret = NULL; size_t cmd_size = 0, pdu_offset = 0; uint16_t crc; int ret, first = 1; @@ -229,7 +246,19 @@ struct fio_net_cmd *fio_net_recv_cmd(int sk) } else cmd_size += cmd.pdu_len; - cmdret = realloc(cmdret, cmd_size); + if (cmd_size / 1024 > FIO_SERVER_MAX_CMD_MB * 1024) { + log_err("fio: cmd+pdu too large (%llu)\n", (unsigned long long) cmd_size); + ret = 1; + break; + } + + tmp = realloc(cmdret, cmd_size); + if (!tmp) { + log_err("fio: server failed allocating cmd\n"); + ret = 1; + break; + } + cmdret = tmp; if (first) memcpy(cmdret, &cmd, sizeof(cmd)); @@ -271,14 +300,14 @@ struct fio_net_cmd *fio_net_recv_cmd(int sk) /* zero-terminate text input */ if (cmdret->pdu_len) { if (cmdret->opcode == FIO_NET_CMD_TEXT) { - struct cmd_text_pdu *pdu = (struct cmd_text_pdu *) cmdret->payload; - char *buf = (char *) pdu->buf; + struct cmd_text_pdu *__pdu = (struct cmd_text_pdu *) cmdret->payload; + char *buf = (char *) __pdu->buf; - buf[pdu->buf_len] = '\0'; + buf[__pdu->buf_len] = '\0'; } else if (cmdret->opcode == FIO_NET_CMD_JOB) { - struct cmd_job_pdu *pdu = (struct cmd_job_pdu *) cmdret->payload; - char *buf = (char *) pdu->buf; - int len = le32_to_cpu(pdu->buf_len); + struct cmd_job_pdu *__pdu = (struct cmd_job_pdu *) cmdret->payload; + char *buf = (char *) __pdu->buf; + int len = le32_to_cpu(__pdu->buf_len); buf[len] = '\0'; } @@ -293,8 +322,9 @@ struct fio_net_cmd *fio_net_recv_cmd(int sk) static void add_reply(uint64_t tag, struct flist_head *list) { - struct fio_net_cmd_reply *reply = (struct fio_net_cmd_reply *) tag; + struct fio_net_cmd_reply *reply; + reply = (struct fio_net_cmd_reply *) (uintptr_t) tag; flist_add_tail(&reply->list, list); } @@ -304,7 +334,7 @@ static uint64_t alloc_reply(uint64_t tag, uint16_t opcode) reply = calloc(1, sizeof(*reply)); INIT_FLIST_HEAD(&reply->list); - gettimeofday(&reply->tv, NULL); + fio_gettime(&reply->tv, NULL); reply->saved_tag = tag; reply->opcode = opcode; @@ -313,8 +343,9 @@ static uint64_t alloc_reply(uint64_t tag, uint16_t opcode) static void free_reply(uint64_t tag) { - struct fio_net_cmd_reply *reply = (struct fio_net_cmd_reply *) tag; + struct fio_net_cmd_reply *reply; + reply = (struct fio_net_cmd_reply *) (uintptr_t) tag; free(reply); } @@ -460,16 +491,16 @@ static void fio_server_add_fork_item(pid_t pid, struct flist_head *list) flist_add_tail(&ffi->list, list); } -static void fio_server_add_conn_pid(pid_t pid) +static void fio_server_add_conn_pid(struct flist_head *conn_list, pid_t pid) { - dprint(FD_NET, "server: forked off connection job (pid=%u)\n", pid); - fio_server_add_fork_item(pid, &conn_list); + dprint(FD_NET, "server: forked off connection job (pid=%u)\n", (int) pid); + fio_server_add_fork_item(pid, conn_list); } -static void fio_server_add_job_pid(pid_t pid) +static void fio_server_add_job_pid(struct flist_head *job_list, pid_t pid) { - dprint(FD_NET, "server: forked off job job (pid=%u)\n", pid); - fio_server_add_fork_item(pid, &job_list); + dprint(FD_NET, "server: forked off job job (pid=%u)\n", (int) pid); + fio_server_add_fork_item(pid, job_list); } static void fio_server_check_fork_item(struct fio_fork_item *ffi) @@ -479,7 +510,7 @@ static void fio_server_check_fork_item(struct fio_fork_item *ffi) ret = waitpid(ffi->pid, &status, WNOHANG); if (ret < 0) { if (errno == ECHILD) { - log_err("fio: connection pid %u disappeared\n", ffi->pid); + log_err("fio: connection pid %u disappeared\n", (int) ffi->pid); ffi->exited = 1; } else log_err("fio: waitpid: %s\n", strerror(errno)); @@ -498,7 +529,7 @@ static void fio_server_check_fork_item(struct fio_fork_item *ffi) static void fio_server_fork_item_done(struct fio_fork_item *ffi) { - dprint(FD_NET, "pid %u exited, sig=%u, exitval=%d\n", ffi->pid, ffi->signal, ffi->exitval); + dprint(FD_NET, "pid %u exited, sig=%u, exitval=%d\n", (int) ffi->pid, ffi->signal, ffi->exitval); /* * Fold STOP and QUIT... @@ -524,26 +555,49 @@ static void fio_server_check_fork_items(struct flist_head *list) } } -static void fio_server_check_jobs(void) +static void fio_server_check_jobs(struct flist_head *job_list) { - fio_server_check_fork_items(&job_list); + fio_server_check_fork_items(job_list); } -static void fio_server_check_conns(void) +static void fio_server_check_conns(struct flist_head *conn_list) { - fio_server_check_fork_items(&conn_list); + fio_server_check_fork_items(conn_list); } -static int handle_run_cmd(struct fio_net_cmd *cmd) +static int handle_load_file_cmd(struct fio_net_cmd *cmd) +{ + struct cmd_load_file_pdu *pdu = (struct cmd_load_file_pdu *) cmd->payload; + void *file_name = pdu->file; + struct cmd_start_pdu spdu; + + dprint(FD_NET, "server: loading local file %s\n", (char *) file_name); + + pdu->name_len = le16_to_cpu(pdu->name_len); + pdu->client_type = le16_to_cpu(pdu->client_type); + + if (parse_jobs_ini(file_name, 0, 0, pdu->client_type)) { + fio_net_send_quit(server_fd); + return -1; + } + + spdu.jobs = cpu_to_le32(thread_number); + spdu.stat_outputs = cpu_to_le32(stat_number); + fio_net_send_cmd(server_fd, FIO_NET_CMD_START, &spdu, sizeof(spdu), NULL, NULL); + return 0; +} + +static int handle_run_cmd(struct flist_head *job_list, struct fio_net_cmd *cmd) { pid_t pid; int ret; + fio_time_init(); set_genesis_time(); pid = fork(); if (pid) { - fio_server_add_job_pid(pid); + fio_server_add_job_pid(job_list, pid); return 0; } @@ -614,11 +668,14 @@ static int handle_jobline_cmd(struct fio_net_cmd *cmd) static int handle_probe_cmd(struct fio_net_cmd *cmd) { - struct cmd_probe_pdu probe; + struct cmd_client_probe_pdu *pdu = (struct cmd_client_probe_pdu *) cmd->payload; + struct cmd_probe_reply_pdu probe; uint64_t tag = cmd->tag; dprint(FD_NET, "server: sending probe reply\n"); + strcpy(me, (char *) pdu->server); + memset(&probe, 0, sizeof(probe)); gethostname((char *) probe.hostname, sizeof(probe.hostname)); #ifdef CONFIG_BIG_ENDIAN @@ -630,7 +687,17 @@ static int handle_probe_cmd(struct fio_net_cmd *cmd) probe.arch = FIO_ARCH; probe.bpp = sizeof(void *); probe.cpus = __cpu_to_le32(cpus_online()); - probe.flags = 0; + + /* + * If the client supports compression and we do too, then enable it + */ + if (has_zlib && le64_to_cpu(pdu->flags) & FIO_PROBE_FLAG_ZLIB) { + probe.flags = __cpu_to_le64(FIO_PROBE_FLAG_ZLIB); + use_zlib = 1; + } else { + probe.flags = 0; + use_zlib = 0; + } return fio_net_send_cmd(server_fd, FIO_NET_CMD_PROBE, &probe, sizeof(probe), &tag, NULL); } @@ -638,27 +705,20 @@ static int handle_probe_cmd(struct fio_net_cmd *cmd) static int handle_send_eta_cmd(struct fio_net_cmd *cmd) { struct jobs_eta *je; - size_t size; uint64_t tag = cmd->tag; + size_t size; int i; - if (!thread_number) + je = get_jobs_eta(1, &size); + if (!je) return 0; - size = sizeof(*je) + thread_number * sizeof(char) + 1; - je = malloc(size); - memset(je, 0, size); - - if (!calc_thread_status(je, 1)) { - free(je); - return 0; - } - dprint(FD_NET, "server sending status\n"); je->nr_running = cpu_to_le32(je->nr_running); je->nr_ramp = cpu_to_le32(je->nr_ramp); je->nr_pending = cpu_to_le32(je->nr_pending); + je->nr_setting_up = cpu_to_le32(je->nr_setting_up); je->files_open = cpu_to_le32(je->files_open); for (i = 0; i < DDIR_RWDIR_CNT; i++) { @@ -666,12 +726,15 @@ static int handle_send_eta_cmd(struct fio_net_cmd *cmd) je->t_rate[i] = cpu_to_le32(je->t_rate[i]); je->m_iops[i] = cpu_to_le32(je->m_iops[i]); je->t_iops[i] = cpu_to_le32(je->t_iops[i]); + je->rate[i] = cpu_to_le32(je->rate[i]); + je->iops[i] = cpu_to_le32(je->iops[i]); } je->elapsed_sec = cpu_to_le64(je->elapsed_sec); je->eta_sec = cpu_to_le64(je->eta_sec); je->nr_threads = cpu_to_le32(je->nr_threads); je->is_pow2 = cpu_to_le32(je->is_pow2); + je->unit_base = cpu_to_le32(je->unit_base); fio_net_send_cmd(server_fd, FIO_NET_CMD_ETA, je, size, &tag, NULL); free(je); @@ -708,7 +771,32 @@ static int handle_update_job_cmd(struct fio_net_cmd *cmd) return 0; } -static int handle_command(struct fio_net_cmd *cmd) +static int handle_trigger_cmd(struct fio_net_cmd *cmd) +{ + struct cmd_vtrigger_pdu *pdu = (struct cmd_vtrigger_pdu *) cmd->payload; + char *buf = (char *) pdu->cmd; + struct all_io_list *rep; + size_t sz; + + pdu->len = le16_to_cpu(pdu->len); + buf[pdu->len] = '\0'; + + rep = get_all_io_list(IO_LIST_ALL, &sz); + if (!rep) { + struct all_io_list state; + + state.threads = cpu_to_le64((uint64_t) 0); + fio_net_send_cmd(server_fd, FIO_NET_CMD_VTRIGGER, &state, sizeof(state), NULL, NULL); + } else { + fio_net_send_cmd(server_fd, FIO_NET_CMD_VTRIGGER, rep, sz, NULL, NULL); + free(rep); + } + + exec_trigger(buf); + return 0; +} + +static int handle_command(struct flist_head *job_list, struct fio_net_cmd *cmd) { int ret; @@ -723,6 +811,9 @@ static int handle_command(struct fio_net_cmd *cmd) case FIO_NET_CMD_EXIT: exit_backend = 1; return -1; + case FIO_NET_CMD_LOAD_FILE: + ret = handle_load_file_cmd(cmd); + break; case FIO_NET_CMD_JOB: ret = handle_job_cmd(cmd); break; @@ -736,11 +827,40 @@ static int handle_command(struct fio_net_cmd *cmd) ret = handle_send_eta_cmd(cmd); break; case FIO_NET_CMD_RUN: - ret = handle_run_cmd(cmd); + ret = handle_run_cmd(job_list, cmd); break; case FIO_NET_CMD_UPDATE_JOB: ret = handle_update_job_cmd(cmd); break; + case FIO_NET_CMD_VTRIGGER: + ret = handle_trigger_cmd(cmd); + break; + case FIO_NET_CMD_SENDFILE: { + struct cmd_sendfile_reply *in; + struct cmd_reply *rep; + + rep = (struct cmd_reply *) (uintptr_t) cmd->tag; + + in = (struct cmd_sendfile_reply *) cmd->payload; + in->size = le32_to_cpu(in->size); + in->error = le32_to_cpu(in->error); + if (in->error) { + ret = 1; + rep->error = in->error; + } else { + ret = 0; + rep->data = smalloc(in->size); + if (!rep->data) { + ret = 1; + rep->error = ENOMEM; + } else { + rep->size = in->size; + memcpy(rep->data, in->data, in->size); + } + } + fio_mutex_up(&rep->lock); + break; + } default: log_err("fio: unknown opcode: %s\n", fio_server_op(cmd->opcode)); ret = 1; @@ -752,10 +872,10 @@ static int handle_command(struct fio_net_cmd *cmd) static int handle_connection(int sk) { struct fio_net_cmd *cmd = NULL; + FLIST_HEAD(job_list); int ret = 0; reset_fio_state(); - INIT_FLIST_HEAD(&job_list); server_fd = sk; /* read forever */ @@ -779,7 +899,7 @@ static int handle_connection(int sk) log_err("fio: poll: %s\n", strerror(errno)); break; } else if (!ret) { - fio_server_check_jobs(); + fio_server_check_jobs(&job_list); continue; } @@ -791,7 +911,7 @@ static int handle_connection(int sk) } } while (!exit_backend); - fio_server_check_jobs(); + fio_server_check_jobs(&job_list); if (ret < 0) break; @@ -802,7 +922,7 @@ static int handle_connection(int sk) break; } - ret = handle_command(cmd); + ret = handle_command(&job_list, cmd); if (ret) break; @@ -817,20 +937,55 @@ static int handle_connection(int sk) _exit(ret); } +/* get the address on this host bound by the input socket, + * whether it is ipv6 or ipv4 */ + +int get_my_addr_str( int sk ) +{ + int ret; + struct sockaddr * sockaddr_p; + struct sockaddr_in myaddr4 = {0}; + struct sockaddr_in6 myaddr6 = {0}; + char * net_addr; + socklen_t len = use_ipv6 ? sizeof(myaddr6) : sizeof(myaddr4); + + if (use_ipv6) + sockaddr_p = (struct sockaddr * )&myaddr6; + else + sockaddr_p = (struct sockaddr * )&myaddr4; + ret = getsockname(sk, sockaddr_p, &len); + if (ret) { + log_err("fio: getsockaddr: %s\n", strerror(errno)); + return -1; + } + if (use_ipv6) + net_addr = (char * )&myaddr6.sin6_addr; + else + net_addr = (char * )&myaddr4.sin_addr; + if (NULL == inet_ntop(use_ipv6?AF_INET6:AF_INET, net_addr, client_sockaddr_str, INET6_ADDRSTRLEN-1)) { + log_err("inet_ntop: failed to convert addr to string\n"); + return -1; + } + dprint(FD_NET, "fio server bound to addr %s\n", client_sockaddr_str); + return 0; +} + static int accept_loop(int listen_sk) { struct sockaddr_in addr; - socklen_t len = sizeof(addr); + struct sockaddr_in6 addr6; + socklen_t len = use_ipv6 ? sizeof(addr6) : sizeof(addr); struct pollfd pfd; - int ret = 0, sk, flags, exitval = 0; + int ret = 0, sk, exitval = 0; + FLIST_HEAD(conn_list); dprint(FD_NET, "server enter accept loop\n"); - flags = fcntl(listen_sk, F_GETFL); - flags |= O_NONBLOCK; - fcntl(listen_sk, F_SETFL, flags); + fio_set_fd_nonblocking(listen_sk, "server"); while (!exit_backend) { + const char *from; + char buf[64]; pid_t pid; pfd.fd = listen_sk; @@ -848,7 +1003,7 @@ static int accept_loop(int listen_sk) log_err("fio: poll: %s\n", strerror(errno)); break; } else if (!ret) { - fio_server_check_conns(); + fio_server_check_conns(&conn_list); continue; } @@ -856,27 +1011,37 @@ static int accept_loop(int listen_sk) break; } while (!exit_backend); - fio_server_check_conns(); + fio_server_check_conns(&conn_list); if (exit_backend || ret < 0) break; - sk = accept(listen_sk, (struct sockaddr *) &addr, &len); + if (use_ipv6) + sk = accept(listen_sk, (struct sockaddr *) &addr6, &len); + else + sk = accept(listen_sk, (struct sockaddr *) &addr, &len); + if (sk < 0) { log_err("fio: accept: %s\n", strerror(errno)); return -1; } - dprint(FD_NET, "server: connect from %s\n", inet_ntoa(addr.sin_addr)); + if (use_ipv6) + from = inet_ntop(AF_INET6, (struct sockaddr *) &addr6.sin6_addr, buf, sizeof(buf)); + else + from = inet_ntop(AF_INET, (struct sockaddr *) &addr.sin_addr, buf, sizeof(buf)); + + dprint(FD_NET, "server: connect from %s\n", from); pid = fork(); if (pid) { close(sk); - fio_server_add_conn_pid(pid); + fio_server_add_conn_pid(&conn_list, pid); continue; } /* exits */ + get_my_addr_str(sk); /* if error, it's already logged, non-fatal */ handle_connection(sk); } @@ -918,8 +1083,8 @@ static void convert_io_stat(struct io_stat *dst, struct io_stat *src) /* * Encode to IEEE 754 for network transfer */ - dst->mean.u.i = __cpu_to_le64(fio_double_to_uint64(src->mean.u.f)); - dst->S.u.i = __cpu_to_le64(fio_double_to_uint64(src->S.u.f)); + dst->mean.u.i = cpu_to_le64(fio_double_to_uint64(src->mean.u.f)); + dst->S.u.i = cpu_to_le64(fio_double_to_uint64(src->S.u.f)); } static void convert_gs(struct group_run_stats *dst, struct group_run_stats *src) @@ -936,6 +1101,7 @@ static void convert_gs(struct group_run_stats *dst, struct group_run_stats *src) } dst->kb_base = cpu_to_le32(src->kb_base); + dst->unit_base = cpu_to_le32(src->unit_base); dst->groupid = cpu_to_le32(src->groupid); dst->unified_rw_rep = cpu_to_le32(src->unified_rw_rep); } @@ -953,9 +1119,9 @@ void fio_server_send_ts(struct thread_stat *ts, struct group_run_stats *rs) memset(&p, 0, sizeof(p)); - strcpy(p.ts.name, ts->name); - strcpy(p.ts.verror, ts->verror); - strcpy(p.ts.description, ts->description); + strncpy(p.ts.name, ts->name, FIO_JOBNAME_SIZE - 1); + strncpy(p.ts.verror, ts->verror, FIO_VERROR_SIZE - 1); + strncpy(p.ts.description, ts->description, FIO_JOBDESC_SIZE - 1); p.ts.error = cpu_to_le32(ts->error); p.ts.thread_number = cpu_to_le32(ts->thread_number); @@ -977,12 +1143,13 @@ void fio_server_send_ts(struct thread_stat *ts, struct group_run_stats *rs) p.ts.minf = cpu_to_le64(ts->minf); p.ts.majf = cpu_to_le64(ts->majf); p.ts.clat_percentiles = cpu_to_le64(ts->clat_percentiles); + p.ts.percentile_precision = cpu_to_le64(ts->percentile_precision); for (i = 0; i < FIO_IO_U_LIST_MAX_LEN; i++) { fio_fp64_t *src = &ts->percentile_list[i]; fio_fp64_t *dst = &p.ts.percentile_list[i]; - dst->u.i = __cpu_to_le64(fio_double_to_uint64(src->u.f)); + dst->u.i = cpu_to_le64(fio_double_to_uint64(src->u.f)); } for (i = 0; i < FIO_IO_U_MAP_NR; i++) { @@ -1003,6 +1170,7 @@ void fio_server_send_ts(struct thread_stat *ts, struct group_run_stats *rs) for (i = 0; i < DDIR_RWDIR_CNT; i++) { p.ts.total_io_u[i] = cpu_to_le64(ts->total_io_u[i]); p.ts.short_io_u[i] = cpu_to_le64(ts->short_io_u[i]); + p.ts.drop_io_u[i] = cpu_to_le64(ts->drop_io_u[i]); } p.ts.total_submit = cpu_to_le64(ts->total_submit); @@ -1018,6 +1186,16 @@ void fio_server_send_ts(struct thread_stat *ts, struct group_run_stats *rs) p.ts.total_err_count = cpu_to_le64(ts->total_err_count); p.ts.first_error = cpu_to_le32(ts->first_error); p.ts.kb_base = cpu_to_le32(ts->kb_base); + p.ts.unit_base = cpu_to_le32(ts->unit_base); + + p.ts.latency_depth = cpu_to_le32(ts->latency_depth); + p.ts.latency_target = cpu_to_le64(ts->latency_target); + p.ts.latency_window = cpu_to_le64(ts->latency_window); + p.ts.latency_percentile.u.i = cpu_to_le64(fio_double_to_uint64(ts->latency_percentile.u.f)); + + p.ts.nr_block_infos = le64_to_cpu(ts->nr_block_infos); + for (i = 0; i < p.ts.nr_block_infos; i++) + p.ts.block_infos[i] = le32_to_cpu(ts->block_infos[i]); convert_gs(&p.rs, rs); @@ -1039,34 +1217,35 @@ static void convert_agg(struct disk_util_agg *dst, struct disk_util_agg *src) int i; for (i = 0; i < 2; i++) { - dst->ios[i] = cpu_to_le32(src->ios[i]); - dst->merges[i] = cpu_to_le32(src->merges[i]); + dst->ios[i] = cpu_to_le64(src->ios[i]); + dst->merges[i] = cpu_to_le64(src->merges[i]); dst->sectors[i] = cpu_to_le64(src->sectors[i]); - dst->ticks[i] = cpu_to_le32(src->ticks[i]); + dst->ticks[i] = cpu_to_le64(src->ticks[i]); } - dst->io_ticks = cpu_to_le32(src->io_ticks); - dst->time_in_queue = cpu_to_le32(src->time_in_queue); + dst->io_ticks = cpu_to_le64(src->io_ticks); + dst->time_in_queue = cpu_to_le64(src->time_in_queue); dst->slavecount = cpu_to_le32(src->slavecount); - dst->max_util.u.i = __cpu_to_le64(fio_double_to_uint64(src->max_util.u.f)); + dst->max_util.u.i = cpu_to_le64(fio_double_to_uint64(src->max_util.u.f)); } static void convert_dus(struct disk_util_stat *dst, struct disk_util_stat *src) { int i; - strcpy((char *) dst->name, (char *) src->name); + dst->name[FIO_DU_NAME_SZ - 1] = '\0'; + strncpy((char *) dst->name, (char *) src->name, FIO_DU_NAME_SZ - 1); for (i = 0; i < 2; i++) { - dst->ios[i] = cpu_to_le32(src->ios[i]); - dst->merges[i] = cpu_to_le32(src->merges[i]); - dst->sectors[i] = cpu_to_le64(src->sectors[i]); - dst->ticks[i] = cpu_to_le32(src->ticks[i]); + dst->s.ios[i] = cpu_to_le64(src->s.ios[i]); + dst->s.merges[i] = cpu_to_le64(src->s.merges[i]); + dst->s.sectors[i] = cpu_to_le64(src->s.sectors[i]); + dst->s.ticks[i] = cpu_to_le64(src->s.ticks[i]); } - dst->io_ticks = cpu_to_le32(src->io_ticks); - dst->time_in_queue = cpu_to_le32(src->time_in_queue); - dst->msec = cpu_to_le64(src->msec); + dst->s.io_ticks = cpu_to_le64(src->s.io_ticks); + dst->s.time_in_queue = cpu_to_le64(src->s.time_in_queue); + dst->s.msec = cpu_to_le64(src->s.msec); } void fio_server_send_du(void) @@ -1098,7 +1277,7 @@ static int fio_send_cmd_ext_pdu(int sk, uint16_t opcode, const void *buf, struct fio_net_cmd cmd; struct iovec iov[2]; - iov[0].iov_base = &cmd; + iov[0].iov_base = (void *) &cmd; iov[0].iov_len = sizeof(cmd); iov[1].iov_base = (void *) buf; iov[1].iov_len = size; @@ -1110,26 +1289,12 @@ static int fio_send_cmd_ext_pdu(int sk, uint16_t opcode, const void *buf, return fio_sendv_data(sk, iov, 2); } -int fio_send_iolog(struct thread_data *td, struct io_log *log, const char *name) +static int fio_send_iolog_gz(struct cmd_iolog_pdu *pdu, struct io_log *log) { - struct cmd_iolog_pdu pdu; + int ret = 0; +#ifdef CONFIG_ZLIB z_stream stream; void *out_pdu; - int i, ret = 0; - - pdu.thread_number = cpu_to_le32(td->thread_number); - pdu.nr_samples = __cpu_to_le32(log->nr_samples); - pdu.log_type = cpu_to_le32(log->log_type); - strcpy((char *) pdu.name, name); - - for (i = 0; i < log->nr_samples; i++) { - struct io_sample *s = &log->log[i]; - - s->time = cpu_to_le64(s->time); - s->val = cpu_to_le64(s->val); - s->ddir = cpu_to_le32(s->ddir); - s->bs = cpu_to_le32(s->bs); - } /* * Dirty - since the log is potentially huge, compress it into @@ -1147,20 +1312,11 @@ int fio_send_iolog(struct thread_data *td, struct io_log *log, const char *name) goto err; } - /* - * Send header first, it's not compressed. - */ - ret = fio_send_cmd_ext_pdu(server_fd, FIO_NET_CMD_IOLOG, &pdu, - sizeof(pdu), 0, FIO_NET_CMD_F_MORE); - if (ret) - goto err_zlib; - stream.next_in = (void *) log->log; - stream.avail_in = log->nr_samples * sizeof(struct io_sample); + stream.avail_in = log->nr_samples * log_entry_sz(log); do { unsigned int this_len, flags = 0; - int ret; stream.avail_out = FIO_SERVER_MAX_FRAGMENT_PDU; stream.next_out = out_pdu; @@ -1184,9 +1340,56 @@ err_zlib: deflateEnd(&stream); err: free(out_pdu); +#endif return ret; } +int fio_send_iolog(struct thread_data *td, struct io_log *log, const char *name) +{ + struct cmd_iolog_pdu pdu; + int i, ret = 0; + + pdu.nr_samples = cpu_to_le64(log->nr_samples); + pdu.thread_number = cpu_to_le32(td->thread_number); + pdu.log_type = cpu_to_le32(log->log_type); + pdu.compressed = cpu_to_le32(use_zlib); + + strncpy((char *) pdu.name, name, FIO_NET_NAME_MAX); + pdu.name[FIO_NET_NAME_MAX - 1] = '\0'; + + for (i = 0; i < log->nr_samples; i++) { + struct io_sample *s = get_sample(log, i); + + s->time = cpu_to_le64(s->time); + s->val = cpu_to_le64(s->val); + s->__ddir = cpu_to_le32(s->__ddir); + s->bs = cpu_to_le32(s->bs); + + if (log->log_offset) { + struct io_sample_offset *so = (void *) s; + + so->offset = cpu_to_le64(so->offset); + } + } + + /* + * Send header first, it's not compressed. + */ + ret = fio_send_cmd_ext_pdu(server_fd, FIO_NET_CMD_IOLOG, &pdu, + sizeof(pdu), 0, FIO_NET_CMD_F_MORE); + if (ret) + return ret; + + /* + * Now send actual log, compress if we can, otherwise just plain + */ + if (use_zlib) + return fio_send_iolog_gz(&pdu, log); + + return fio_send_cmd_ext_pdu(server_fd, FIO_NET_CMD_IOLOG, log->log, + log->nr_samples * log_entry_sz(log), 0, 0); +} + void fio_server_send_add_job(struct thread_data *td) { struct cmd_add_job_pdu pdu; @@ -1206,10 +1409,78 @@ void fio_server_send_start(struct thread_data *td) fio_net_send_simple_cmd(server_fd, FIO_NET_CMD_SERVER_START, 0, NULL); } +int fio_server_get_verify_state(const char *name, int threadnumber, + void **datap) +{ + struct thread_io_list *s; + struct cmd_sendfile out; + struct cmd_reply *rep; + uint64_t tag; + void *data; + + dprint(FD_NET, "server: request verify state\n"); + + rep = smalloc(sizeof(*rep)); + if (!rep) { + log_err("fio: smalloc pool too small\n"); + return 1; + } + + __fio_mutex_init(&rep->lock, FIO_MUTEX_LOCKED); + rep->data = NULL; + rep->error = 0; + + verify_state_gen_name((char *) out.path, sizeof(out.path), name, me, + threadnumber); + tag = (uint64_t) (uintptr_t) rep; + fio_net_send_cmd(server_fd, FIO_NET_CMD_SENDFILE, &out, sizeof(out), + &tag, NULL); + + /* + * Wait for the backend to receive the reply + */ + if (fio_mutex_down_timeout(&rep->lock, 10)) { + log_err("fio: timed out waiting for reply\n"); + goto fail; + } + + if (rep->error) { + log_err("fio: failure on receiving state file: %s\n", strerror(rep->error)); +fail: + *datap = NULL; + sfree(rep); + fio_net_send_quit(server_fd); + return 1; + } + + /* + * The format is verify_state_hdr, then thread_io_list. Verify + * the header, and the thread_io_list checksum + */ + s = rep->data + sizeof(struct verify_state_hdr); + if (verify_state_hdr(rep->data, s)) + goto fail; + + /* + * Don't need the header from now, copy just the thread_io_list + */ + rep->size -= sizeof(struct verify_state_hdr); + data = malloc(rep->size); + memcpy(data, s, rep->size); + *datap = data; + + sfree(rep->data); + __fio_mutex_remove(&rep->lock); + sfree(rep); + return 0; +} + static int fio_init_server_ip(void) { struct sockaddr *addr; socklen_t socklen; + char buf[80]; + const char *str; int sk, opt; if (use_ipv6) @@ -1237,17 +1508,24 @@ static int fio_init_server_ip(void) #endif if (use_ipv6) { + const void *src = &saddr_in6.sin6_addr; + addr = (struct sockaddr *) &saddr_in6; socklen = sizeof(saddr_in6); saddr_in6.sin6_family = AF_INET6; + str = inet_ntop(AF_INET6, src, buf, sizeof(buf)); } else { + const void *src = &saddr_in.sin_addr; + addr = (struct sockaddr *) &saddr_in; socklen = sizeof(saddr_in); saddr_in.sin_family = AF_INET; + str = inet_ntop(AF_INET, src, buf, sizeof(buf)); } if (bind(sk, addr, socklen) < 0) { log_err("fio: bind: %s\n", strerror(errno)); + log_info("fio: failed with IPv%c %s\n", use_ipv6 ? '6' : '4', str); close(sk); return -1; } @@ -1272,8 +1550,7 @@ static int fio_init_server_sock(void) memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; - strcpy(addr.sun_path, bind_sock); - unlink(bind_sock); + strncpy(addr.sun_path, bind_sock, sizeof(addr.sun_path) - 1); len = sizeof(addr.sun_family) + strlen(bind_sock) + 1; @@ -1302,6 +1579,8 @@ static int fio_init_server_connection(void) if (sk < 0) return sk; + memset(bind_str, 0, sizeof(bind_str)); + if (!bind_sock) { char *p, port[16]; const void *src; @@ -1321,55 +1600,53 @@ static int fio_init_server_connection(void) if (p) strcat(p, port); else - strcpy(bind_str, port); + strncpy(bind_str, port, sizeof(bind_str) - 1); } else - strcpy(bind_str, bind_sock); + strncpy(bind_str, bind_sock, sizeof(bind_str) - 1); log_info("fio: server listening on %s\n", bind_str); if (listen(sk, 0) < 0) { log_err("fio: listen: %s\n", strerror(errno)); + close(sk); return -1; } return sk; } -int fio_server_parse_host(const char *host, int *ipv6, struct in_addr *inp, +int fio_server_parse_host(const char *host, int ipv6, struct in_addr *inp, struct in6_addr *inp6) { int ret = 0; - if (*ipv6) + if (ipv6) ret = inet_pton(AF_INET6, host, inp6); else ret = inet_pton(AF_INET, host, inp); if (ret != 1) { - struct hostent *hent; + struct addrinfo hints, *res; - hent = gethostbyname(host); - if (!hent) { - log_err("fio: failed to resolve <%s>\n", host); - return 0; - } + memset(&hints, 0, sizeof(hints)); + hints.ai_family = ipv6 ? AF_INET6 : AF_INET; + hints.ai_socktype = SOCK_STREAM; - if (*ipv6) { - if (hent->h_addrtype != AF_INET6) { - log_info("fio: falling back to IPv4\n"); - *ipv6 = 0; - } else - memcpy(inp6, hent->h_addr_list[0], 16); - } - if (!*ipv6) { - if (hent->h_addrtype != AF_INET) { - log_err("fio: lookup type mismatch\n"); - return 0; - } - memcpy(inp, hent->h_addr_list[0], 4); + ret = getaddrinfo(host, NULL, &hints, &res); + if (ret) { + log_err("fio: failed to resolve <%s> (%s)\n", host, + gai_strerror(ret)); + return 1; } + + if (ipv6) + memcpy(inp6, &((struct sockaddr_in6 *) res->ai_addr)->sin6_addr, sizeof(*inp6)); + else + memcpy(inp, &((struct sockaddr_in *) res->ai_addr)->sin_addr, sizeof(*inp)); + ret = 1; + freeaddrinfo(res); } return !(ret == 1); @@ -1422,7 +1699,7 @@ int fio_server_parse_string(const char *str, char **ptr, int *is_sock, host++; lport = atoi(host); if (!lport || lport > 65535) { - log_err("fio: bad server port %u\n", port); + log_err("fio: bad server port %u\n", lport); return 1; } /* no hostname given, we are done */ @@ -1431,7 +1708,7 @@ int fio_server_parse_string(const char *str, char **ptr, int *is_sock, } /* - * If no port seen yet, check if there's a last ':' at the end + * If no port seen yet, check if there's a last ',' at the end */ if (!lport) { portp = strchr(host, ','); @@ -1440,7 +1717,7 @@ int fio_server_parse_string(const char *str, char **ptr, int *is_sock, portp++; lport = atoi(portp); if (!lport || lport > 65535) { - log_err("fio: bad server port %u\n", port); + log_err("fio: bad server port %u\n", lport); return 1; } } @@ -1454,7 +1731,7 @@ int fio_server_parse_string(const char *str, char **ptr, int *is_sock, *ptr = strdup(host); - if (fio_server_parse_host(*ptr, ipv6, inp, inp6)) { + if (fio_server_parse_host(*ptr, *ipv6, inp, inp6)) { free(*ptr); *ptr = NULL; return 1; @@ -1504,6 +1781,22 @@ out: return ret; } +static void sig_int(int sig) +{ + if (bind_sock) + unlink(bind_sock); +} + +static void set_sig_handlers(void) +{ + struct sigaction act; + + memset(&act, 0, sizeof(act)); + act.sa_handler = sig_int; + act.sa_flags = SA_RESTART; + sigaction(SIGINT, &act, NULL); +} + static int fio_server(void) { int sk, ret; @@ -1517,6 +1810,8 @@ static int fio_server(void) if (sk < 0) return -1; + set_sig_handlers(); + ret = accept_loop(sk); close(sk); @@ -1602,6 +1897,7 @@ int fio_start_server(char *pidfile) if (check_existing_pidfile(pidfile)) { log_err("fio: pidfile %s exists and server appears alive\n", pidfile); + free(pidfile); return -1; } @@ -1611,9 +1907,9 @@ int fio_start_server(char *pidfile) free(pidfile); return -1; } else if (pid) { - int ret = write_pid(pid, pidfile); - - exit(ret); + ret = write_pid(pid, pidfile); + free(pidfile); + _exit(ret); } setsid();