X-Git-Url: https://git.kernel.dk/?p=fio.git;a=blobdiff_plain;f=client.c;h=82e12855ca5f904943238a47ddcd0ceef5cda730;hp=3cb7c1c50a92f27706da48f5d4ce64ccb7ca6b59;hb=5610bcd8f4bd106a551fcba9c2f88c89ac709794;hpb=f5041ed9d2890116ecba4bd3fe2121b272e01776 diff --git a/client.c b/client.c index 3cb7c1c5..82e12855 100644 --- a/client.c +++ b/client.c @@ -33,6 +33,8 @@ static void handle_text(struct fio_client *client, struct fio_net_cmd *cmd); static void handle_stop(struct fio_client *client, struct fio_net_cmd *cmd); static void handle_start(struct fio_client *client, struct fio_net_cmd *cmd); +static void convert_text(struct fio_net_cmd *cmd); + struct client_ops fio_client_ops = { .text = handle_text, .disk_util = handle_du, @@ -46,7 +48,7 @@ struct client_ops fio_client_ops = { .client_type = FIO_CLIENT_TYPE_CLI, }; -static struct timeval eta_tv; +static struct timespec eta_ts; static FLIST_HEAD(client_list); static FLIST_HEAD(eta_list); @@ -59,14 +61,19 @@ int sum_stat_clients; static int sum_stat_nr; static struct json_object *root = NULL; +static struct json_object *job_opt_object = NULL; static struct json_array *clients_array = NULL; static struct json_array *du_array = NULL; +static int error_clients; + #define FIO_CLIENT_HASH_BITS 7 #define FIO_CLIENT_HASH_SZ (1 << FIO_CLIENT_HASH_BITS) #define FIO_CLIENT_HASH_MASK (FIO_CLIENT_HASH_SZ - 1) static struct flist_head client_hash[FIO_CLIENT_HASH_SZ]; +static struct cmd_iolog_pdu *convert_iolog(struct fio_net_cmd *, bool *); + static void fio_client_add_hash(struct fio_client *client) { int bucket = hash_long(client->fd, FIO_CLIENT_HASH_BITS); @@ -115,10 +122,23 @@ static int read_data(int fd, void *data, size_t size) static void fio_client_json_init(void) { - if (output_format != FIO_OUTPUT_JSON) + char time_buf[32]; + time_t time_p; + + if (!(output_format & FIO_OUTPUT_JSON)) return; + + time(&time_p); + os_ctime_r((const time_t *) &time_p, time_buf, sizeof(time_buf)); + time_buf[strlen(time_buf) - 1] = '\0'; + root = json_create_object(); json_object_add_value_string(root, "fio version", fio_version_string); + json_object_add_value_int(root, "timestamp", time_p); + json_object_add_value_string(root, "time", time_buf); + + job_opt_object = json_create_object(); + json_object_add_value_object(root, "global options", job_opt_object); clients_array = json_create_array(); json_object_add_value_array(root, "client_stats", clients_array); du_array = json_create_array(); @@ -127,9 +147,11 @@ static void fio_client_json_init(void) static void fio_client_json_fini(void) { - if (output_format != FIO_OUTPUT_JSON) + if (!(output_format & FIO_OUTPUT_JSON)) return; - json_print_object(root); + + log_info("\n"); + json_print_object(root, NULL); log_info("\n"); json_free_object(root); root = NULL; @@ -172,19 +194,55 @@ void fio_put_client(struct fio_client *client) } if (client->files) free(client->files); + if (client->opt_lists) + free(client->opt_lists); if (!client->did_stat) sum_stat_clients--; + if (client->error) + error_clients++; + free(client); } +static int fio_client_dec_jobs_eta(struct client_eta *eta, client_eta_op eta_fn) +{ + if (!--eta->pending) { + eta_fn(&eta->eta); + free(eta); + return 0; + } + + return 1; +} + +static void fio_drain_client_text(struct fio_client *client) +{ + do { + struct fio_net_cmd *cmd; + + cmd = fio_net_recv_cmd(client->fd, false); + if (!cmd) + break; + + if (cmd->opcode == FIO_NET_CMD_TEXT) { + convert_text(cmd); + client->ops->text(client, cmd); + } + + free(cmd); + } while (1); +} + static void remove_client(struct fio_client *client) { assert(client->refs); dprint(FD_NET, "client: removed <%s>\n", client->hostname); + fio_drain_client_text(client); + if (!flist_empty(&client->list)) flist_del_init(&client->list); @@ -260,7 +318,7 @@ struct fio_client *fio_client_add_explicit(struct client_ops *ops, client->hostname = strdup(hostname); if (type == Fio_client_socket) - client->is_sock = 1; + client->is_sock = true; else { int ipv6; @@ -289,7 +347,7 @@ err: return NULL; } -int fio_client_add_ini_file(void *cookie, const char *ini_file, int remote) +int fio_client_add_ini_file(void *cookie, const char *ini_file, bool remote) { struct fio_client *client = cookie; struct client_file *cf; @@ -380,6 +438,7 @@ static const char *server_name(struct fio_client *client, char *buf, static void probe_client(struct fio_client *client) { struct cmd_client_probe_pdu pdu; + const char *sname; uint64_t tag; char buf[64]; @@ -391,7 +450,9 @@ static void probe_client(struct fio_client *client) pdu.flags = 0; #endif - strcpy((char *) pdu.server, server_name(client, buf, sizeof(buf))); + sname = server_name(client, buf, sizeof(buf)); + memset(pdu.server, 0, sizeof(pdu.server)); + strncpy((char *) pdu.server, sname, sizeof(pdu.server) - 1); fio_net_send_cmd(client->fd, FIO_NET_CMD_PROBE, &pdu, sizeof(pdu), &tag, &client->cmd_list); } @@ -496,7 +557,7 @@ int fio_client_terminate(struct fio_client *client) return fio_net_send_quit(client->fd); } -void fio_clients_terminate(void) +static void fio_clients_terminate(void) { struct flist_head *entry; struct fio_client *client; @@ -515,11 +576,6 @@ static void sig_int(int sig) fio_clients_terminate(); } -static void sig_show_status(int sig) -{ - show_running_run_stats(); -} - static void client_signal_handler(void) { struct sigaction act; @@ -672,7 +728,7 @@ static int __fio_client_send_remote_ini(struct fio_client *client, strcpy((char *) pdu->file, filename); pdu->client_type = cpu_to_le16((uint16_t) client->type); - client->sent_job = 1; + client->sent_job = true; ret = fio_net_send_cmd(client->fd, FIO_NET_CMD_LOAD_FILE, pdu, p_size,NULL, NULL); free(pdu); return ret; @@ -725,7 +781,7 @@ static int __fio_client_send_local_ini(struct fio_client *client, pdu->buf_len = __cpu_to_le32(sb.st_size); pdu->client_type = cpu_to_le32(client->type); - client->sent_job = 1; + client->sent_job = true; ret = fio_net_send_cmd(client->fd, FIO_NET_CMD_JOB, pdu, p_size, NULL, NULL); free(pdu); close(fd); @@ -733,7 +789,7 @@ static int __fio_client_send_local_ini(struct fio_client *client, } int fio_client_send_ini(struct fio_client *client, const char *filename, - int remote) + bool remote) { int ret; @@ -743,7 +799,7 @@ int fio_client_send_ini(struct fio_client *client, const char *filename, ret = __fio_client_send_remote_ini(client, filename); if (!ret) - client->sent_job = 1; + client->sent_job = true; return ret; } @@ -760,6 +816,8 @@ int fio_clients_send_ini(const char *filename) struct flist_head *entry, *tmp; flist_for_each_safe(entry, tmp, &client_list) { + bool failed = false; + client = flist_entry(entry, struct fio_client, list); if (client->nr_files) { @@ -771,12 +829,13 @@ int fio_clients_send_ini(const char *filename) cf = &client->files[i]; if (fio_client_send_cf(client, cf)) { + failed = true; remove_client(client); break; } } } - if (client->sent_job) + if (client->sent_job || failed) continue; if (!filename || fio_client_send_ini(client, filename, 0)) remove_client(client); @@ -826,6 +885,7 @@ static void convert_ts(struct thread_stat *dst, struct thread_stat *src) convert_io_stat(&dst->slat_stat[i], &src->slat_stat[i]); convert_io_stat(&dst->lat_stat[i], &src->lat_stat[i]); convert_io_stat(&dst->bw_stat[i], &src->bw_stat[i]); + convert_io_stat(&dst->iops_stat[i], &src->iops_stat[i]); } dst->usr_time = le64_to_cpu(src->usr_time); @@ -833,7 +893,8 @@ static void convert_ts(struct thread_stat *dst, struct thread_stat *src) dst->ctx = le64_to_cpu(src->ctx); dst->minf = le64_to_cpu(src->minf); dst->majf = le64_to_cpu(src->majf); - dst->clat_percentiles = le64_to_cpu(src->clat_percentiles); + dst->clat_percentiles = le32_to_cpu(src->clat_percentiles); + dst->lat_percentiles = le32_to_cpu(src->lat_percentiles); dst->percentile_precision = le64_to_cpu(src->percentile_precision); for (i = 0; i < FIO_IO_U_LIST_MAX_LEN; i++) { @@ -849,10 +910,12 @@ static void convert_ts(struct thread_stat *dst, struct thread_stat *src) dst->io_u_complete[i] = le32_to_cpu(src->io_u_complete[i]); } - for (i = 0; i < FIO_IO_U_LAT_U_NR; i++) { + for (i = 0; i < FIO_IO_U_LAT_N_NR; i++) + dst->io_u_lat_n[i] = le32_to_cpu(src->io_u_lat_n[i]); + for (i = 0; i < FIO_IO_U_LAT_U_NR; i++) dst->io_u_lat_u[i] = le32_to_cpu(src->io_u_lat_u[i]); + for (i = 0; i < FIO_IO_U_LAT_M_NR; i++) dst->io_u_lat_m[i] = le32_to_cpu(src->io_u_lat_m[i]); - } for (i = 0; i < DDIR_RWDIR_CNT; i++) for (j = 0; j < FIO_IO_U_PLAT_NR; j++) @@ -879,10 +942,31 @@ static void convert_ts(struct thread_stat *dst, struct thread_stat *src) dst->kb_base = le32_to_cpu(src->kb_base); dst->unit_base = le32_to_cpu(src->unit_base); + dst->sig_figs = le32_to_cpu(src->sig_figs); + dst->latency_depth = le32_to_cpu(src->latency_depth); dst->latency_target = le64_to_cpu(src->latency_target); dst->latency_window = le64_to_cpu(src->latency_window); dst->latency_percentile.u.f = fio_uint64_to_double(le64_to_cpu(src->latency_percentile.u.i)); + + dst->nr_block_infos = le64_to_cpu(src->nr_block_infos); + for (i = 0; i < dst->nr_block_infos; i++) + dst->block_infos[i] = le32_to_cpu(src->block_infos[i]); + + dst->ss_dur = le64_to_cpu(src->ss_dur); + dst->ss_state = le32_to_cpu(src->ss_state); + dst->ss_head = le32_to_cpu(src->ss_head); + dst->ss_limit.u.f = fio_uint64_to_double(le64_to_cpu(src->ss_limit.u.i)); + dst->ss_slope.u.f = fio_uint64_to_double(le64_to_cpu(src->ss_slope.u.i)); + dst->ss_deviation.u.f = fio_uint64_to_double(le64_to_cpu(src->ss_deviation.u.i)); + dst->ss_criterion.u.f = fio_uint64_to_double(le64_to_cpu(src->ss_criterion.u.i)); + + if (dst->ss_state & FIO_SS_DATA) { + for (i = 0; i < dst->ss_dur; i++ ) { + dst->ss_iops_data[i] = le64_to_cpu(src->ss_iops_data[i]); + dst->ss_bw_data[i] = le64_to_cpu(src->ss_bw_data[i]); + } + } } static void convert_gs(struct group_run_stats *dst, struct group_run_stats *src) @@ -894,12 +978,13 @@ static void convert_gs(struct group_run_stats *dst, struct group_run_stats *src) dst->min_run[i] = le64_to_cpu(src->min_run[i]); dst->max_bw[i] = le64_to_cpu(src->max_bw[i]); dst->min_bw[i] = le64_to_cpu(src->min_bw[i]); - dst->io_kb[i] = le64_to_cpu(src->io_kb[i]); + dst->iobytes[i] = le64_to_cpu(src->iobytes[i]); dst->agg[i] = le64_to_cpu(src->agg[i]); } dst->kb_base = le32_to_cpu(src->kb_base); dst->unit_base = le32_to_cpu(src->unit_base); + dst->sig_figs = le32_to_cpu(src->sig_figs); dst->groupid = le32_to_cpu(src->groupid); dst->unified_rw_rep = le32_to_cpu(src->unified_rw_rep); } @@ -916,10 +1001,14 @@ static void json_object_add_client_info(struct json_object *obj, static void handle_ts(struct fio_client *client, struct fio_net_cmd *cmd) { struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload; + struct flist_head *opt_list = NULL; struct json_object *tsobj; - tsobj = show_thread_status(&p->ts, &p->rs); - client->did_stat = 1; + if (client->opt_lists && p->ts.thread_number <= client->jobs) + opt_list = &client->opt_lists[p->ts.thread_number - 1]; + + tsobj = show_thread_status(&p->ts, &p->rs, opt_list, NULL); + client->did_stat = true; if (tsobj) { json_object_add_client_info(tsobj, client); json_array_add_value_object(clients_array, tsobj); @@ -928,7 +1017,7 @@ static void handle_ts(struct fio_client *client, struct fio_net_cmd *cmd) if (sum_stat_clients <= 1) return; - sum_thread_stats(&client_ts, &p->ts, sum_stat_nr); + sum_thread_stats(&client_ts, &p->ts, sum_stat_nr == 1); sum_group_stats(&client_gs, &p->rs); client_ts.members++; @@ -938,7 +1027,7 @@ static void handle_ts(struct fio_client *client, struct fio_net_cmd *cmd) if (++sum_stat_nr == sum_stat_clients) { strcpy(client_ts.name, "All clients"); - tsobj = show_thread_status(&client_ts, &client_gs); + tsobj = show_thread_status(&client_ts, &client_gs, NULL, NULL); if (tsobj) { json_object_add_client_info(tsobj, client); json_array_add_value_object(clients_array, tsobj); @@ -950,7 +1039,41 @@ static void handle_gs(struct fio_client *client, struct fio_net_cmd *cmd) { struct group_run_stats *gs = (struct group_run_stats *) cmd->payload; - show_group_stats(gs); + if (output_format & FIO_OUTPUT_NORMAL) + show_group_stats(gs, NULL); +} + +static void handle_job_opt(struct fio_client *client, struct fio_net_cmd *cmd) +{ + struct cmd_job_option *pdu = (struct cmd_job_option *) cmd->payload; + struct print_option *p; + + if (!job_opt_object) + return; + + pdu->global = le16_to_cpu(pdu->global); + pdu->truncated = le16_to_cpu(pdu->truncated); + pdu->groupid = le32_to_cpu(pdu->groupid); + + p = malloc(sizeof(*p)); + p->name = strdup((char *) pdu->name); + if (pdu->value[0] != '\0') + p->value = strdup((char *) pdu->value); + else + p->value = NULL; + + if (pdu->global) { + const char *pos = ""; + + if (p->value) + pos = p->value; + + json_object_add_value_string(job_opt_object, p->name, pos); + } else if (client->opt_lists) { + struct flist_head *opt_list = &client->opt_lists[pdu->groupid]; + + flist_add_tail(&p->list, opt_list); + } } static void handle_text(struct fio_client *client, struct fio_net_cmd *cmd) @@ -1007,17 +1130,20 @@ static void handle_du(struct fio_client *client, struct fio_net_cmd *cmd) struct cmd_du_pdu *du = (struct cmd_du_pdu *) cmd->payload; if (!client->disk_stats_shown) { - client->disk_stats_shown = 1; + client->disk_stats_shown = true; log_info("\nDisk stats (read/write):\n"); } - if (output_format == FIO_OUTPUT_JSON) { + if (output_format & FIO_OUTPUT_JSON) { struct json_object *duobj; json_array_add_disk_util(&du->dus, &du->agg, du_array); duobj = json_array_last_value_object(du_array); json_object_add_client_info(duobj, client); - } else - print_disk_util(&du->dus, &du->agg, output_format == FIO_OUTPUT_TERSE); + } + if (output_format & FIO_OUTPUT_TERSE) + print_disk_util(&du->dus, &du->agg, 1, NULL); + if (output_format & FIO_OUTPUT_NORMAL) + print_disk_util(&du->dus, &du->agg, 0, NULL); } static void convert_jobs_eta(struct jobs_eta *je) @@ -1031,11 +1157,11 @@ static void convert_jobs_eta(struct jobs_eta *je) je->files_open = le32_to_cpu(je->files_open); for (i = 0; i < DDIR_RWDIR_CNT; i++) { - je->m_rate[i] = le32_to_cpu(je->m_rate[i]); - je->t_rate[i] = le32_to_cpu(je->t_rate[i]); + je->m_rate[i] = le64_to_cpu(je->m_rate[i]); + je->t_rate[i] = le64_to_cpu(je->t_rate[i]); je->m_iops[i] = le32_to_cpu(je->m_iops[i]); je->t_iops[i] = le32_to_cpu(je->t_iops[i]); - je->rate[i] = le32_to_cpu(je->rate[i]); + je->rate[i] = le64_to_cpu(je->rate[i]); je->iops[i] = le32_to_cpu(je->iops[i]); } @@ -1044,6 +1170,7 @@ static void convert_jobs_eta(struct jobs_eta *je) je->nr_threads = le32_to_cpu(je->nr_threads); je->is_pow2 = le32_to_cpu(je->is_pow2); je->unit_base = le32_to_cpu(je->unit_base); + je->sig_figs = le32_to_cpu(je->sig_figs); } void fio_client_sum_jobs_eta(struct jobs_eta *dst, struct jobs_eta *je) @@ -1079,15 +1206,7 @@ void fio_client_sum_jobs_eta(struct jobs_eta *dst, struct jobs_eta *je) strcpy((char *) dst->run_str, (char *) je->run_str); } -void fio_client_dec_jobs_eta(struct client_eta *eta, client_eta_op eta_fn) -{ - if (!--eta->pending) { - eta_fn(&eta->eta); - free(eta); - } -} - -static void remove_reply_cmd(struct fio_client *client, struct fio_net_cmd *cmd) +static bool remove_reply_cmd(struct fio_client *client, struct fio_net_cmd *cmd) { struct fio_net_cmd_reply *reply = NULL; struct flist_head *entry; @@ -1103,12 +1222,13 @@ static void remove_reply_cmd(struct fio_client *client, struct fio_net_cmd *cmd) if (!reply) { log_err("fio: client: unable to find matching tag (%llx)\n", (unsigned long long) cmd->tag); - return; + return false; } flist_del(&reply->list); cmd->tag = reply->saved_tag; free(reply); + return true; } int fio_client_wait_for_reply(struct fio_client *client, uint64_t tag) @@ -1146,6 +1266,7 @@ static void handle_eta(struct fio_client *client, struct fio_net_cmd *cmd) client->eta_in_flight = NULL; flist_del_init(&client->eta_list); + client->eta_timeouts = 0; if (client->ops->jobs_eta) client->ops->jobs_eta(client, je); @@ -1154,6 +1275,126 @@ static void handle_eta(struct fio_client *client, struct fio_net_cmd *cmd) fio_client_dec_jobs_eta(eta, client->ops->eta); } +static void client_flush_hist_samples(FILE *f, int hist_coarseness, void *samples, + uint64_t sample_size) +{ + struct io_sample *s; + int log_offset; + uint64_t i, j, nr_samples; + struct io_u_plat_entry *entry; + unsigned int *io_u_plat; + + int stride = 1 << hist_coarseness; + + if (!sample_size) + return; + + s = __get_sample(samples, 0, 0); + log_offset = (s->__ddir & LOG_OFFSET_SAMPLE_BIT) != 0; + + nr_samples = sample_size / __log_entry_sz(log_offset); + + for (i = 0; i < nr_samples; i++) { + + s = (struct io_sample *)((char *)__get_sample(samples, log_offset, i) + + i * sizeof(struct io_u_plat_entry)); + + entry = s->data.plat_entry; + io_u_plat = entry->io_u_plat; + + fprintf(f, "%lu, %u, %u, ", (unsigned long) s->time, + io_sample_ddir(s), s->bs); + for (j = 0; j < FIO_IO_U_PLAT_NR - stride; j += stride) { + fprintf(f, "%lu, ", hist_sum(j, stride, io_u_plat, NULL)); + } + fprintf(f, "%lu\n", (unsigned long) + hist_sum(FIO_IO_U_PLAT_NR - stride, stride, io_u_plat, NULL)); + + } +} + +static int fio_client_handle_iolog(struct fio_client *client, + struct fio_net_cmd *cmd) +{ + struct cmd_iolog_pdu *pdu = NULL; + bool store_direct; + char *log_pathname = NULL; + int ret = 0; + + pdu = convert_iolog(cmd, &store_direct); + if (!pdu) { + log_err("fio: failed converting IO log\n"); + ret = 1; + goto out; + } + + /* allocate buffer big enough for next sprintf() call */ + log_pathname = malloc(10 + strlen((char *)pdu->name) + + strlen(client->hostname)); + if (!log_pathname) { + log_err("fio: memory allocation of unique pathname failed\n"); + ret = -1; + goto out; + } + /* generate a unique pathname for the log file using hostname */ + sprintf(log_pathname, "%s.%s", pdu->name, client->hostname); + + if (store_direct) { + ssize_t ret; + size_t sz; + int fd; + + fd = open((const char *) log_pathname, + O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (fd < 0) { + log_err("fio: open log %s: %s\n", + log_pathname, strerror(errno)); + ret = 1; + goto out; + } + + sz = cmd->pdu_len - sizeof(*pdu); + ret = write(fd, pdu->samples, sz); + close(fd); + + if (ret != sz) { + log_err("fio: short write on compressed log\n"); + ret = 1; + goto out; + } + + ret = 0; + } else { + FILE *f; + f = fopen((const char *) log_pathname, "w"); + if (!f) { + log_err("fio: fopen log %s : %s\n", + log_pathname, strerror(errno)); + ret = 1; + goto out; + } + + if (pdu->log_type == IO_LOG_TYPE_HIST) { + client_flush_hist_samples(f, pdu->log_hist_coarseness, pdu->samples, + pdu->nr_samples * sizeof(struct io_sample)); + } else { + flush_samples(f, pdu->samples, + pdu->nr_samples * sizeof(struct io_sample)); + } + fclose(f); + ret = 0; + } + +out: + if (pdu && pdu != (void *) cmd->payload) + free(pdu); + + if (log_pathname) + free(log_pathname); + + return ret; +} + static void handle_probe(struct fio_client *client, struct fio_net_cmd *cmd) { struct cmd_probe_reply_pdu *probe = (struct cmd_probe_reply_pdu *) cmd->payload; @@ -1187,6 +1428,17 @@ static void handle_start(struct fio_client *client, struct fio_net_cmd *cmd) client->jobs = le32_to_cpu(pdu->jobs); client->nr_stat = le32_to_cpu(pdu->stat_outputs); + if (client->jobs) { + int i; + + if (client->opt_lists) + free(client->opt_lists); + + client->opt_lists = malloc(client->jobs * sizeof(struct flist_head)); + for (i = 0; i < client->jobs; i++) + INIT_FLIST_HEAD(&client->opt_lists[i]); + } + sum_stat_clients += client->nr_stat; } @@ -1221,7 +1473,7 @@ static struct cmd_iolog_pdu *convert_iolog_gz(struct fio_net_cmd *cmd, z_stream stream; uint32_t nr_samples; size_t total; - void *p; + char *p; stream.zalloc = Z_NULL; stream.zfree = Z_NULL; @@ -1237,16 +1489,20 @@ static struct cmd_iolog_pdu *convert_iolog_gz(struct fio_net_cmd *cmd, */ nr_samples = le64_to_cpu(pdu->nr_samples); - total = nr_samples * __log_entry_sz(le32_to_cpu(pdu->log_offset)); + if (pdu->log_type == IO_LOG_TYPE_HIST) + total = nr_samples * (__log_entry_sz(le32_to_cpu(pdu->log_offset)) + + sizeof(struct io_u_plat_entry)); + else + total = nr_samples * __log_entry_sz(le32_to_cpu(pdu->log_offset)); ret = malloc(total + sizeof(*pdu)); ret->nr_samples = nr_samples; memcpy(ret, pdu, sizeof(*pdu)); - p = (void *) ret + sizeof(*pdu); + p = (char *) ret + sizeof(*pdu); stream.avail_in = cmd->pdu_len - sizeof(*pdu); - stream.next_in = (void *) pdu + sizeof(*pdu); + stream.next_in = (void *)((char *) pdu + sizeof(*pdu)); while (stream.avail_in) { unsigned int this_chunk = 65536; unsigned int this_len; @@ -1256,7 +1512,7 @@ static struct cmd_iolog_pdu *convert_iolog_gz(struct fio_net_cmd *cmd, this_chunk = total; stream.avail_out = this_chunk; - stream.next_out = p; + stream.next_out = (void *)p; err = inflate(&stream, Z_NO_FLUSH); /* may be Z_OK, or Z_STREAM_END */ if (err < 0) { @@ -1283,18 +1539,23 @@ err: * This has been compressed on the server side, since it can be big. * Uncompress here. */ -static struct cmd_iolog_pdu *convert_iolog(struct fio_net_cmd *cmd) +static struct cmd_iolog_pdu *convert_iolog(struct fio_net_cmd *cmd, + bool *store_direct) { struct cmd_iolog_pdu *pdu = (struct cmd_iolog_pdu *) cmd->payload; struct cmd_iolog_pdu *ret; uint64_t i; + int compressed; void *samples; + *store_direct = false; + /* * Convert if compressed and we support it. If it's not * compressed, we need not do anything. */ - if (le32_to_cpu(pdu->compressed)) { + compressed = le32_to_cpu(pdu->compressed); + if (compressed == XMIT_COMPRESSED) { #ifndef CONFIG_ZLIB log_err("fio: server sent compressed data by mistake\n"); return NULL; @@ -1304,6 +1565,9 @@ static struct cmd_iolog_pdu *convert_iolog(struct fio_net_cmd *cmd) log_err("fio: failed decompressing log\n"); return NULL; } + } else if (compressed == STORE_COMPRESSED) { + *store_direct = true; + ret = pdu; } else ret = pdu; @@ -1312,14 +1576,21 @@ static struct cmd_iolog_pdu *convert_iolog(struct fio_net_cmd *cmd) ret->log_type = le32_to_cpu(ret->log_type); ret->compressed = le32_to_cpu(ret->compressed); ret->log_offset = le32_to_cpu(ret->log_offset); + ret->log_hist_coarseness = le32_to_cpu(ret->log_hist_coarseness); + + if (*store_direct) + return ret; samples = &ret->samples[0]; for (i = 0; i < ret->nr_samples; i++) { struct io_sample *s; s = __get_sample(samples, ret->log_offset, i); + if (ret->log_type == IO_LOG_TYPE_HIST) + s = (struct io_sample *)((char *)s + sizeof(struct io_u_plat_entry) * i); + s->time = le64_to_cpu(s->time); - s->val = le64_to_cpu(s->val); + s->data.val = le64_to_cpu(s->data.val); s->__ddir = le32_to_cpu(s->__ddir); s->bs = le32_to_cpu(s->bs); @@ -1328,6 +1599,12 @@ static struct cmd_iolog_pdu *convert_iolog(struct fio_net_cmd *cmd) so->offset = le64_to_cpu(so->offset); } + + if (ret->log_type == IO_LOG_TYPE_HIST) { + s->data.plat_entry = (struct io_u_plat_entry *)(((char *)s) + sizeof(*s)); + s->data.plat_entry->list.next = NULL; + s->data.plat_entry->list.prev = NULL; + } } return ret; @@ -1340,8 +1617,8 @@ static void sendfile_reply(int fd, struct cmd_sendfile_reply *rep, fio_net_send_cmd(fd, FIO_NET_CMD_SENDFILE, rep, size, &tag, NULL); } -static int send_file(struct fio_client *client, struct cmd_sendfile *pdu, - uint64_t tag) +static int fio_send_file(struct fio_client *client, struct cmd_sendfile *pdu, + uint64_t tag) { struct cmd_sendfile_reply *rep; struct stat sb; @@ -1378,16 +1655,19 @@ int fio_handle_client(struct fio_client *client) { struct client_ops *ops = client->ops; struct fio_net_cmd *cmd; + int size; dprint(FD_NET, "client: handle %s\n", client->hostname); - cmd = fio_net_recv_cmd(client->fd); + cmd = fio_net_recv_cmd(client->fd, true); if (!cmd) return 0; dprint(FD_NET, "client: got cmd op %s from %s (pdu=%u)\n", fio_server_op(cmd->opcode), client->hostname, cmd->pdu_len); + client->last_cmd = cmd->opcode; + switch (cmd->opcode) { case FIO_NET_CMD_QUIT: if (ops->quit) @@ -1410,6 +1690,15 @@ int fio_handle_client(struct fio_client *client) case FIO_NET_CMD_TS: { struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload; + dprint(FD_NET, "client: ts->ss_state = %u\n", (unsigned int) le32_to_cpu(p->ts.ss_state)); + if (le32_to_cpu(p->ts.ss_state) & FIO_SS_DATA) { + dprint(FD_NET, "client: received steadystate ring buffers\n"); + + size = le64_to_cpu(p->ts.ss_dur); + p->ts.ss_iops_data = (uint64_t *) ((struct cmd_ts_pdu *)cmd->payload + 1); + p->ts.ss_bw_data = p->ts.ss_iops_data + size; + } + convert_ts(&p->ts, &p->ts); convert_gs(&p->rs, &p->rs); @@ -1427,7 +1716,8 @@ int fio_handle_client(struct fio_client *client) case FIO_NET_CMD_ETA: { struct jobs_eta *je = (struct jobs_eta *) cmd->payload; - remove_reply_cmd(client, cmd); + if (!remove_reply_cmd(client, cmd)) + break; convert_jobs_eta(je); handle_eta(client, cmd); break; @@ -1469,12 +1759,7 @@ int fio_handle_client(struct fio_client *client) break; } case FIO_NET_CMD_IOLOG: - if (ops->iolog) { - struct cmd_iolog_pdu *pdu; - - pdu = convert_iolog(cmd); - ops->iolog(client, pdu); - } + fio_client_handle_iolog(client, cmd); break; case FIO_NET_CMD_UPDATE_JOB: ops->update_job(client, cmd); @@ -1482,17 +1767,27 @@ int fio_handle_client(struct fio_client *client) break; case FIO_NET_CMD_VTRIGGER: { struct all_io_list *pdu = (struct all_io_list *) cmd->payload; - char buf[64]; + char buf[128]; + int off = 0; + + if (aux_path) { + strcpy(buf, aux_path); + off = strlen(buf); + } - __verify_save_state(pdu, server_name(client, buf, sizeof(buf))); + __verify_save_state(pdu, server_name(client, &buf[off], sizeof(buf) - off)); exec_trigger(trigger_cmd); break; } case FIO_NET_CMD_SENDFILE: { struct cmd_sendfile *pdu = (struct cmd_sendfile *) cmd->payload; - send_file(client, pdu, cmd->tag); + fio_send_file(client, pdu, cmd->tag); break; } + case FIO_NET_CMD_JOB_OPT: { + handle_job_opt(client, cmd); + break; + } default: log_err("fio: unknown client op: %s\n", fio_server_op(cmd->opcode)); break; @@ -1539,6 +1834,9 @@ static void request_client_etas(struct client_ops *ops) struct client_eta *eta; int skipped = 0; + if (eta_print == FIO_ETA_NEVER) + return; + dprint(FD_NET, "client: request eta (%d)\n", nr_clients); eta = calloc(1, sizeof(*eta) + __THREAD_RUNSTR_SZ(REAL_MAX_JOBS)); @@ -1561,29 +1859,66 @@ static void request_client_etas(struct client_ops *ops) (uintptr_t) eta, &client->cmd_list); } - while (skipped--) - fio_client_dec_jobs_eta(eta, ops->eta); + while (skipped--) { + if (!fio_client_dec_jobs_eta(eta, ops->eta)) + break; + } dprint(FD_NET, "client: requested eta tag %p\n", eta); } +/* + * A single SEND_ETA timeout isn't fatal. Attempt to recover. + */ +static int handle_cmd_timeout(struct fio_client *client, + struct fio_net_cmd_reply *reply) +{ + uint16_t reply_opcode = reply->opcode; + + flist_del(&reply->list); + free(reply); + + if (reply_opcode != FIO_NET_CMD_SEND_ETA) + return 1; + + log_info("client <%s>: timeout on SEND_ETA\n", client->hostname); + + flist_del_init(&client->eta_list); + if (client->eta_in_flight) { + fio_client_dec_jobs_eta(client->eta_in_flight, client->ops->eta); + client->eta_in_flight = NULL; + } + + /* + * If we fail 5 in a row, give up... + */ + if (client->eta_timeouts++ > 5) + return 1; + + return 0; +} + static int client_check_cmd_timeout(struct fio_client *client, - struct timeval *now) + struct timespec *now) { struct fio_net_cmd_reply *reply; struct flist_head *entry, *tmp; int ret = 0; flist_for_each_safe(entry, tmp, &client->cmd_list) { + unsigned int op; + reply = flist_entry(entry, struct fio_net_cmd_reply, list); - if (mtime_since(&reply->tv, now) < FIO_NET_CLIENT_TIMEOUT) + if (mtime_since(&reply->ts, now) < FIO_NET_CLIENT_TIMEOUT) + continue; + + op = reply->opcode; + if (!handle_cmd_timeout(client, reply)) continue; log_err("fio: client %s, timeout on cmd %s\n", client->hostname, - fio_server_op(reply->opcode)); - flist_del(&reply->list); - free(reply); + fio_server_op(op)); ret = 1; } @@ -1594,10 +1929,10 @@ static int fio_check_clients_timed_out(void) { struct fio_client *client; struct flist_head *entry, *tmp; - struct timeval tv; + struct timespec ts; int ret = 0; - fio_gettime(&tv, NULL); + fio_gettime(&ts, NULL); flist_for_each_safe(entry, tmp, &client_list) { client = flist_entry(entry, struct fio_client, list); @@ -1605,7 +1940,7 @@ static int fio_check_clients_timed_out(void) if (flist_empty(&client->cmd_list)) continue; - if (!client_check_cmd_timeout(client, &tv)) + if (!client_check_cmd_timeout(client, &ts)) continue; if (client->ops->timed_out) @@ -1613,6 +1948,10 @@ static int fio_check_clients_timed_out(void) else log_err("fio: client %s timed out\n", client->hostname); + if (client->last_cmd != FIO_NET_CMD_VTRIGGER) + client->error = ETIMEDOUT; + else + log_info("fio: ignoring timeout due to vtrigger\n"); remove_client(client); ret = 1; } @@ -1625,7 +1964,7 @@ int fio_handle_clients(struct client_ops *ops) struct pollfd *pfds; int i, ret = 0, retval = 0; - fio_gettime(&eta_tv, NULL); + fio_gettime(&eta_ts, NULL); pfds = malloc(nr_clients * sizeof(struct pollfd)); @@ -1657,13 +1996,13 @@ int fio_handle_clients(struct client_ops *ops) assert(i == nr_clients); do { - struct timeval tv; + struct timespec ts; int timeout; - fio_gettime(&tv, NULL); - if (mtime_since(&eta_tv, &tv) >= 900) { + fio_gettime(&ts, NULL); + if (mtime_since(&eta_ts, &ts) >= 900) { request_client_etas(ops); - memcpy(&eta_tv, &tv, sizeof(tv)); + memcpy(&eta_ts, &ts, sizeof(ts)); if (fio_check_clients_timed_out()) break; @@ -1706,5 +2045,5 @@ int fio_handle_clients(struct client_ops *ops) fio_client_json_fini(); free(pfds); - return retval; + return retval || error_clients; }