From: Jens Axboe Date: Mon, 3 Oct 2011 12:20:01 +0000 (+0200) Subject: server: transmit status as structures, not text X-Git-Tag: fio-1.99~55 X-Git-Url: https://git.kernel.dk/?p=fio.git;a=commitdiff_plain;h=a64e88dad0c0e4a510ae8ab54cde1a20b99c59d1 server: transmit status as structures, not text Signed-off-by: Jens Axboe --- diff --git a/client.c b/client.c index 05bba38d..cb229312 100644 --- a/client.c +++ b/client.c @@ -202,31 +202,144 @@ int fio_clients_send_ini(const char *filename) return !nr_clients; } +static void convert_io_stat(struct io_stat *dst, struct io_stat *src) +{ + dst->max_val = le64_to_cpu(src->max_val); + dst->min_val = le64_to_cpu(src->min_val); + dst->samples = le64_to_cpu(src->samples); + /* FIXME */ + dst->mean = le64_to_cpu(src->mean); + dst->S = le64_to_cpu(src->S); +} + +static void convert_ts(struct thread_stat *dst, struct thread_stat *src) +{ + int i, j; + + dst->error = le32_to_cpu(src->error); + dst->groupid = le32_to_cpu(src->groupid); + dst->pid = le32_to_cpu(src->pid); + dst->members = le32_to_cpu(src->members); + + for (i = 0; i < 2; i++) { + convert_io_stat(&dst->clat_stat[i], &src->clat_stat[i]); + 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]); + } + + dst->usr_time = le64_to_cpu(src->usr_time); + dst->sys_time = le64_to_cpu(src->sys_time); + 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->percentile_list = NULL; + + for (i = 0; i < FIO_IO_U_MAP_NR; i++) { + dst->io_u_map[i] = le32_to_cpu(src->io_u_map[i]); + dst->io_u_submit[i] = le32_to_cpu(src->io_u_submit[i]); + dst->io_u_complete[i] = le32_to_cpu(src->io_u_complete[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]); + dst->io_u_lat_m[i] = le32_to_cpu(src->io_u_lat_m[i]); + } + + for (i = 0; i < 2; i++) + for (j = 0; j < FIO_IO_U_PLAT_NR; j++) + dst->io_u_plat[i][j] = le32_to_cpu(src->io_u_plat[i][j]); + + for (i = 0; i < 3; i++) { + dst->total_io_u[i] = le64_to_cpu(src->total_io_u[i]); + dst->short_io_u[i] = le64_to_cpu(src->total_io_u[i]); + } + + dst->total_submit = le64_to_cpu(src->total_submit); + dst->total_complete = le64_to_cpu(src->total_complete); + + for (i = 0; i < 2; i++) { + dst->io_bytes[i] = le64_to_cpu(src->io_bytes[i]); + dst->runtime[i] = le64_to_cpu(src->runtime[i]); + } + + dst->total_run_time = le64_to_cpu(src->total_run_time); + dst->continue_on_error = le16_to_cpu(src->continue_on_error); + dst->total_err_count = le64_to_cpu(src->total_err_count); + dst->first_error = le64_to_cpu(src->first_error); + dst->kb_base = le64_to_cpu(src->kb_base); +} + +static void convert_gs(struct group_run_stats *dst, struct group_run_stats *src) +{ + int i; + + for (i = 0; i < 2; i++) { + dst->max_run[i] = le64_to_cpu(src->max_run[i]); + 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->agg[i] = le64_to_cpu(src->agg[i]); + } + + dst->kb_base = le32_to_cpu(src->kb_base); + dst->groupid = le32_to_cpu(src->groupid); +} + +static void handle_ts(struct fio_net_cmd *cmd) +{ + struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload; + + convert_ts(&p->ts, &p->ts); + convert_gs(&p->rs, &p->rs); + + show_thread_status(&p->ts, &p->rs); +} + +static void handle_gs(struct fio_net_cmd *cmd) +{ + struct group_run_stats *gs = (struct group_run_stats *) cmd->payload; + + convert_gs(gs, gs); + show_group_stats(gs); +} + static int handle_client(struct fio_client *client) { struct fio_net_cmd *cmd; - while ((cmd = fio_net_cmd_read(client->fd)) != NULL) { + while ((cmd = fio_net_recv_cmd(client->fd)) != NULL) { dprint(FD_NET, "%s: got cmd op %d\n", client->hostname, cmd->opcode); - if (cmd->opcode == FIO_NET_CMD_ACK) { + switch (cmd->opcode) { + case FIO_NET_CMD_ACK: free(cmd); - continue; - } - if (cmd->opcode == FIO_NET_CMD_QUIT) { + break; + case FIO_NET_CMD_QUIT: remove_client(client); free(cmd); break; - } - if (cmd->opcode != FIO_NET_CMD_TEXT) { - printf("non text: %d\n", cmd->opcode); + case FIO_NET_CMD_TEXT: + fwrite(cmd->payload, cmd->pdu_len, 1, stdout); + fflush(stdout); free(cmd); - continue; + break; + case FIO_NET_CMD_TS: + handle_ts(cmd); + free(cmd); + break; + case FIO_NET_CMD_GS: + handle_gs(cmd); + free(cmd); + break; + default: + log_err("fio: unknown client op: %d\n", cmd->opcode); + free(cmd); + break; } - fwrite(cmd->payload, cmd->pdu_len, 1, stdout); - fflush(stdout); - free(cmd); } return 0; diff --git a/fio.h b/fio.h index 1d3a9d47..fdc2c7e0 100644 --- a/fio.h +++ b/fio.h @@ -36,6 +36,7 @@ struct thread_data; #include "lib/getopt.h" #include "lib/rand.h" #include "server.h" +#include "stat.h" #ifdef FIO_HAVE_GUASI #include @@ -45,14 +46,6 @@ struct thread_data; #include #endif -struct group_run_stats { - uint64_t max_run[2], min_run[2]; - uint64_t max_bw[2], min_bw[2]; - uint64_t io_kb[2]; - uint64_t agg[2]; - uint32_t kb_base; -}; - /* * What type of allocation to use for io buffers */ @@ -72,164 +65,6 @@ enum { RW_SEQ_IDENT, }; -/* - * How many depth levels to log - */ -#define FIO_IO_U_MAP_NR 7 -#define FIO_IO_U_LAT_U_NR 10 -#define FIO_IO_U_LAT_M_NR 12 - -/* - * Aggregate clat samples to report percentile(s) of them. - * - * EXECUTIVE SUMMARY - * - * FIO_IO_U_PLAT_BITS determines the maximum statistical error on the - * value of resulting percentiles. The error will be approximately - * 1/2^(FIO_IO_U_PLAT_BITS+1) of the value. - * - * FIO_IO_U_PLAT_GROUP_NR and FIO_IO_U_PLAT_BITS determine the maximum - * range being tracked for latency samples. The maximum value tracked - * accurately will be 2^(GROUP_NR + PLAT_BITS -1) microseconds. - * - * FIO_IO_U_PLAT_GROUP_NR and FIO_IO_U_PLAT_BITS determine the memory - * requirement of storing those aggregate counts. The memory used will - * be (FIO_IO_U_PLAT_GROUP_NR * 2^FIO_IO_U_PLAT_BITS) * sizeof(int) - * bytes. - * - * FIO_IO_U_PLAT_NR is the total number of buckets. - * - * DETAILS - * - * Suppose the clat varies from 0 to 999 (usec), the straightforward - * method is to keep an array of (999 + 1) buckets, in which a counter - * keeps the count of samples which fall in the bucket, e.g., - * {[0],[1],...,[999]}. However this consumes a huge amount of space, - * and can be avoided if an approximation is acceptable. - * - * One such method is to let the range of the bucket to be greater - * than one. This method has low accuracy when the value is small. For - * example, let the buckets be {[0,99],[100,199],...,[900,999]}, and - * the represented value of each bucket be the mean of the range. Then - * a value 0 has an round-off error of 49.5. To improve on this, we - * use buckets with non-uniform ranges, while bounding the error of - * each bucket within a ratio of the sample value. A simple example - * would be when error_bound = 0.005, buckets are { - * {[0],[1],...,[99]}, {[100,101],[102,103],...,[198,199]},.., - * {[900,909],[910,919]...} }. The total range is partitioned into - * groups with different ranges, then buckets with uniform ranges. An - * upper bound of the error is (range_of_bucket/2)/value_of_bucket - * - * For better efficiency, we implement this using base two. We group - * samples by their Most Significant Bit (MSB), extract the next M bit - * of them as an index within the group, and discard the rest of the - * bits. - * - * E.g., assume a sample 'x' whose MSB is bit n (starting from bit 0), - * and use M bit for indexing - * - * | n | M bits | bit (n-M-1) ... bit 0 | - * - * Because x is at least 2^n, and bit 0 to bit (n-M-1) is at most - * (2^(n-M) - 1), discarding bit 0 to (n-M-1) makes the round-off - * error - * - * 2^(n-M)-1 2^(n-M) 1 - * e <= --------- <= ------- = --- - * 2^n 2^n 2^M - * - * Furthermore, we use "mean" of the range to represent the bucket, - * the error e can be lowered by half to 1 / 2^(M+1). By using M bits - * as the index, each group must contains 2^M buckets. - * - * E.g. Let M (FIO_IO_U_PLAT_BITS) be 6 - * Error bound is 1/2^(6+1) = 0.0078125 (< 1%) - * - * Group MSB #discarded range of #buckets - * error_bits value - * ---------------------------------------------------------------- - * 0* 0~5 0 [0,63] 64 - * 1* 6 0 [64,127] 64 - * 2 7 1 [128,255] 64 - * 3 8 2 [256,511] 64 - * 4 9 3 [512,1023] 64 - * ... ... ... [...,...] ... - * 18 23 17 [8838608,+inf]** 64 - * - * * Special cases: when n < (M-1) or when n == (M-1), in both cases, - * the value cannot be rounded off. Use all bits of the sample as - * index. - * - * ** If a sample's MSB is greater than 23, it will be counted as 23. - */ - -#define FIO_IO_U_PLAT_BITS 6 -#define FIO_IO_U_PLAT_VAL (1 << FIO_IO_U_PLAT_BITS) -#define FIO_IO_U_PLAT_GROUP_NR 19 -#define FIO_IO_U_PLAT_NR (FIO_IO_U_PLAT_GROUP_NR * FIO_IO_U_PLAT_VAL) -#define FIO_IO_U_LIST_MAX_LEN 20 /* The size of the default and user-specified - list of percentiles */ - -#define MAX_PATTERN_SIZE 512 -#define FIO_JOBNAME_SIZE 128 -#define FIO_VERROR_SIZE 128 - -struct thread_stat { - char name[FIO_JOBNAME_SIZE]; - char verror[FIO_VERROR_SIZE]; - int32_t error; - int32_t groupid; - uint32_t pid; - char description[FIO_JOBNAME_SIZE]; - uint32_t members; - - /* - * bandwidth and latency stats - */ - struct io_stat clat_stat[2]; /* completion latency */ - struct io_stat slat_stat[2]; /* submission latency */ - struct io_stat lat_stat[2]; /* total latency */ - struct io_stat bw_stat[2]; /* bandwidth stats */ - - /* - * fio system usage accounting - */ - uint64_t usr_time; - uint64_t sys_time; - uint64_t ctx; - uint64_t minf, majf; - - /* - * IO depth and latency stats - */ - uint64_t clat_percentiles; - double *percentile_list; - - uint32_t io_u_map[FIO_IO_U_MAP_NR]; - uint32_t io_u_submit[FIO_IO_U_MAP_NR]; - uint32_t io_u_complete[FIO_IO_U_MAP_NR]; - uint32_t io_u_lat_u[FIO_IO_U_LAT_U_NR]; - uint32_t io_u_lat_m[FIO_IO_U_LAT_M_NR]; - uint32_t io_u_plat[2][FIO_IO_U_PLAT_NR]; - uint64_t total_io_u[3]; - uint64_t short_io_u[3]; - uint64_t total_submit; - uint64_t total_complete; - - uint64_t io_bytes[2]; - uint64_t runtime[2]; - uint64_t total_run_time; - - /* - * IO Error related stats - */ - uint16_t continue_on_error; - uint64_t total_err_count; - int32_t first_error; - - uint32_t kb_base; -}; - struct bssplit { unsigned int bs; unsigned char perc; diff --git a/server.c b/server.c index d1de0e48..72761c56 100644 --- a/server.c +++ b/server.c @@ -22,10 +22,6 @@ int fio_net_port = 8765; int exit_backend = 0; -static char *job_buf; -static unsigned int job_cur_len; -static unsigned int job_max_len; - static int server_fd = -1; int fio_send_data(int sk, const void *p, unsigned int len) @@ -112,41 +108,72 @@ static int verify_convert_cmd(struct fio_net_cmd *cmd) return 0; } -struct fio_net_cmd *fio_net_cmd_read(int sk) +/* + * Read (and defragment, if necessary) incoming commands + */ +struct fio_net_cmd *fio_net_recv_cmd(int sk) { - struct fio_net_cmd cmd, *ret = NULL; + struct fio_net_cmd cmd, *cmdret = NULL; + size_t cmd_size = 0, pdu_offset = 0; uint16_t crc; + int ret, first = 1; + void *pdu = NULL; - if (fio_recv_data(sk, &cmd, sizeof(cmd))) - return NULL; + do { + ret = fio_recv_data(sk, &cmd, sizeof(cmd)); + if (ret) + break; - /* We have a command, verify it and swap if need be */ - if (verify_convert_cmd(&cmd)) - return NULL; + /* We have a command, verify it and swap if need be */ + ret = verify_convert_cmd(&cmd); + if (ret) + break; - /* Command checks out, alloc real command and fill in */ - ret = malloc(sizeof(cmd) + cmd.pdu_len); - memcpy(ret, &cmd, sizeof(cmd)); + if (first) + cmd_size = sizeof(cmd) + cmd.pdu_len; + else + cmd_size += cmd.pdu_len; - if (!ret->pdu_len) - return ret; + cmdret = realloc(cmdret, cmd_size); + pdu = (void *) cmdret->payload; - /* There's payload, get it */ - if (fio_recv_data(sk, (void *) ret + sizeof(*ret), ret->pdu_len)) { - free(ret); - return NULL; - } + if (first) + memcpy(cmdret, &cmd, sizeof(cmd)); + else + assert(cmdret->opcode == cmd.opcode); + + if (!cmd.pdu_len) + break; + + /* There's payload, get it */ + pdu = (void *) cmdret->payload + pdu_offset; + ret = fio_recv_data(sk, pdu, cmd.pdu_len); + if (ret) + break; + + /* Verify payload crc */ + crc = crc16(pdu, cmd.pdu_len); + if (crc != cmd.pdu_crc16) { + log_err("fio: server bad crc on payload "); + log_err("(got %x, wanted %x)\n", cmd.pdu_crc16, crc); + ret = 1; + break; + } + + pdu_offset += cmd.pdu_len; + cmdret->pdu_len += cmd.pdu_len; + first = 0; + } while (cmd.flags & FIO_NET_CMD_F_MORE); - /* Verify payload crc */ - crc = crc16(ret->payload, ret->pdu_len); - if (crc != ret->pdu_crc16) { - log_err("fio: server bad crc on payload (got %x, wanted %x)\n", - ret->pdu_crc16, crc); - free(ret); - return NULL; + if (ret) { + free(cmdret); + cmdret = NULL; } - return ret; + if (cmdret) + cmdret->flags &= ~FIO_NET_CMD_F_MORE; + + return cmdret; } void fio_net_cmd_crc(struct fio_net_cmd *cmd) @@ -160,7 +187,7 @@ void fio_net_cmd_crc(struct fio_net_cmd *cmd) cmd->pdu_crc16 = cpu_to_le16(crc16(cmd->payload, pdu_len)); } -int fio_net_send_cmd(int fd, uint16_t opcode, const char *buf, off_t size) +int fio_net_send_cmd(int fd, uint16_t opcode, const void *buf, off_t size) { struct fio_net_cmd *cmd; size_t this_len; @@ -229,30 +256,13 @@ static int send_quit_command(void) static int handle_cur_job(struct fio_net_cmd *cmd) { - unsigned int left = job_max_len - job_cur_len; - int ret = 0; - - if (left < cmd->pdu_len) { - job_buf = realloc(job_buf, job_max_len + 2 * cmd->pdu_len); - job_max_len += 2 * cmd->pdu_len; - } - - memcpy(job_buf + job_cur_len, cmd->payload, cmd->pdu_len); - job_cur_len += cmd->pdu_len; - - /* - * More data coming for this job - */ - if (cmd->flags & FIO_NET_CMD_F_MORE) - return 0; + void *buf = cmd->payload; + int ret; - parse_jobs_ini(job_buf, 1, 0); + parse_jobs_ini(buf, 1, 0); ret = exec_run(); send_quit_command(); reset_fio_state(); - free(job_buf); - job_buf = NULL; - job_cur_len = job_max_len = 0; return ret; } @@ -290,7 +300,7 @@ static int handle_connection(int sk) /* read forever */ while (!exit_backend) { - cmd = fio_net_cmd_read(sk); + cmd = fio_net_recv_cmd(sk); if (!cmd) { ret = 1; break; @@ -426,6 +436,113 @@ int fio_server_text_output(const char *buf, unsigned int len) return 0; } +static void convert_io_stat(struct io_stat *dst, struct io_stat *src) +{ + dst->max_val = cpu_to_le64(src->max_val); + dst->min_val = cpu_to_le64(src->min_val); + dst->samples = cpu_to_le64(src->samples); + /* FIXME */ + dst->mean = cpu_to_le64(src->mean); + dst->S = cpu_to_le64(src->S); +} + +static void convert_gs(struct group_run_stats *dst, struct group_run_stats *src) +{ + int i; + + for (i = 0; i < 2; i++) { + dst->max_run[i] = cpu_to_le64(src->max_run[i]); + dst->min_run[i] = cpu_to_le64(src->min_run[i]); + dst->max_bw[i] = cpu_to_le64(src->max_bw[i]); + dst->min_bw[i] = cpu_to_le64(src->min_bw[i]); + dst->io_kb[i] = cpu_to_le64(src->io_kb[i]); + dst->agg[i] = cpu_to_le64(src->agg[i]); + } + + dst->kb_base = cpu_to_le32(src->kb_base); + dst->groupid = cpu_to_le32(src->groupid); +} + +/* + * Send a CMD_TS, which packs struct thread_stat and group_run_stats + * into a single payload. + */ +void fio_server_send_ts(struct thread_stat *ts, struct group_run_stats *rs) +{ + struct cmd_ts_pdu p; + int i, j; + + strcpy(p.ts.name, ts->name); + strcpy(p.ts.verror, ts->verror); + strcpy(p.ts.description, ts->description); + + p.ts.error = cpu_to_le32(ts->error); + p.ts.groupid = cpu_to_le32(ts->groupid); + p.ts.pid = cpu_to_le32(ts->pid); + p.ts.members = cpu_to_le32(ts->members); + + for (i = 0; i < 2; i++) { + convert_io_stat(&p.ts.clat_stat[i], &ts->clat_stat[i]); + convert_io_stat(&p.ts.slat_stat[i], &ts->slat_stat[i]); + convert_io_stat(&p.ts.lat_stat[i], &ts->lat_stat[i]); + convert_io_stat(&p.ts.bw_stat[i], &ts->bw_stat[i]); + } + + p.ts.usr_time = cpu_to_le64(ts->usr_time); + p.ts.sys_time = cpu_to_le64(ts->sys_time); + p.ts.ctx = cpu_to_le64(ts->ctx); + 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_list = NULL; + + for (i = 0; i < FIO_IO_U_MAP_NR; i++) { + p.ts.io_u_map[i] = cpu_to_le32(ts->io_u_map[i]); + p.ts.io_u_submit[i] = cpu_to_le32(ts->io_u_submit[i]); + p.ts.io_u_complete[i] = cpu_to_le32(ts->io_u_complete[i]); + } + + for (i = 0; i < FIO_IO_U_LAT_U_NR; i++) { + p.ts.io_u_lat_u[i] = cpu_to_le32(ts->io_u_lat_u[i]); + p.ts.io_u_lat_m[i] = cpu_to_le32(ts->io_u_lat_m[i]); + } + + for (i = 0; i < 2; i++) + for (j = 0; j < FIO_IO_U_PLAT_NR; j++) + p.ts.io_u_plat[i][j] = cpu_to_le32(ts->io_u_plat[i][j]); + + for (i = 0; i < 3; 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->total_io_u[i]); + } + + p.ts.total_submit = cpu_to_le64(ts->total_submit); + p.ts.total_complete = cpu_to_le64(ts->total_complete); + + for (i = 0; i < 2; i++) { + p.ts.io_bytes[i] = cpu_to_le64(ts->io_bytes[i]); + p.ts.runtime[i] = cpu_to_le64(ts->runtime[i]); + } + + p.ts.total_run_time = cpu_to_le64(ts->total_run_time); + p.ts.continue_on_error = cpu_to_le16(ts->continue_on_error); + p.ts.total_err_count = cpu_to_le64(ts->total_err_count); + p.ts.first_error = cpu_to_le64(ts->first_error); + p.ts.kb_base = cpu_to_le64(ts->kb_base); + + convert_gs(&p.rs, rs); + + fio_net_send_cmd(server_fd, FIO_NET_CMD_TS, &p, sizeof(p)); +} + +void fio_server_send_gs(struct group_run_stats *rs) +{ + struct group_run_stats gs; + + convert_gs(&gs, rs); + fio_net_send_cmd(server_fd, FIO_NET_CMD_GS, &gs, sizeof(gs)); +} + int fio_server_log(const char *format, ...) { char buffer[1024]; diff --git a/server.h b/server.h index 5e691e01..10c9006e 100644 --- a/server.h +++ b/server.h @@ -5,6 +5,8 @@ #include #include +#include "stat.h" + /* * On-wire encoding is little endian */ @@ -14,6 +16,10 @@ struct fio_net_cmd { uint32_t flags; /* modifier flags */ uint64_t serial; /* serial number */ uint32_t pdu_len; /* length of post-cmd layload */ + /* + * These must be immediately before the payload, anything before + * these fields are checksummed. + */ uint16_t cmd_crc16; /* cmd checksum */ uint16_t pdu_crc16; /* payload checksum */ uint8_t payload[0]; /* payload */ @@ -31,6 +37,8 @@ enum { FIO_NET_CMD_ACK = 4, FIO_NET_CMD_NAK = 5, FIO_NET_CMD_TEXT = 6, + FIO_NET_CMD_TS = 7, + FIO_NET_CMD_GS = 8, FIO_NET_CMD_F_MORE = 1UL << 0, @@ -39,10 +47,20 @@ enum { 2 * sizeof(uint16_t), }; +struct cmd_ts_pdu { + struct thread_stat ts; + struct group_run_stats rs; +}; + extern int fio_start_server(int); extern int fio_server_text_output(const char *, unsigned int len); extern int fio_server_log(const char *format, ...); -extern int fio_net_send_cmd(int, uint16_t, const char *, off_t); +extern int fio_net_send_cmd(int, uint16_t, const void *, off_t); + +struct thread_stat; +struct group_run_stats; +extern void fio_server_send_ts(struct thread_stat *, struct group_run_stats *); +extern void fio_server_send_gs(struct group_run_stats *); extern int fio_clients_connect(void); extern int fio_clients_send_ini(const char *); @@ -52,7 +70,7 @@ extern void fio_client_add(const char *); extern int fio_recv_data(int sk, void *p, unsigned int len); extern int fio_send_data(int sk, const void *p, unsigned int len); extern void fio_net_cmd_crc(struct fio_net_cmd *); -extern struct fio_net_cmd *fio_net_cmd_read(int sk); +extern struct fio_net_cmd *fio_net_recv_cmd(int sk); extern int exit_backend; extern int fio_net_port; diff --git a/stat.c b/stat.c index 564edf34..bcf8fa83 100644 --- a/stat.c +++ b/stat.c @@ -191,13 +191,13 @@ static int calc_lat(struct io_stat *is, unsigned long *min, unsigned long *max, return 1; } -static void show_group_stats(struct group_run_stats *rs, int id) +void show_group_stats(struct group_run_stats *rs) { char *p1, *p2, *p3, *p4; const char *ddir_str[] = { " READ", " WRITE" }; int i; - log_info("\nRun status group %d (all jobs):\n", id); + log_info("\nRun status group %d (all jobs):\n", rs->groupid); for (i = 0; i <= DDIR_WRITE; i++) { const int i2p = is_power_of_2(rs->kb_base); @@ -433,8 +433,7 @@ static void show_latencies(double *io_u_lat_u, double *io_u_lat_m) log_info("\n"); } -static void show_thread_status(struct thread_stat *ts, - struct group_run_stats *rs) +void show_thread_status(struct thread_stat *ts, struct group_run_stats *rs) { double usr_cpu, sys_cpu; unsigned long runtime; @@ -728,8 +727,12 @@ void show_run_stats(void) * These are per-group shared already */ strncpy(ts->name, td->o.name, FIO_JOBNAME_SIZE); - strncpy(ts->description, td->o.description, - FIO_JOBNAME_SIZE); + if (td->o.description) + strncpy(ts->description, td->o.description, + FIO_JOBNAME_SIZE); + else + memset(ts->description, 0, FIO_JOBNAME_SIZE); + ts->groupid = td->groupid; /* @@ -858,15 +861,24 @@ void show_run_stats(void) ts = &threadstats[i]; rs = &runstats[ts->groupid]; - if (terse_output) + if (is_backend) + fio_server_send_ts(ts, rs); + else if (terse_output) show_thread_status_terse(ts, rs); else show_thread_status(ts, rs); } if (!terse_output) { - for (i = 0; i < groupid + 1; i++) - show_group_stats(&runstats[i], i); + for (i = 0; i < groupid + 1; i++) { + rs = &runstats[i]; + + rs->groupid = i; + if (is_backend) + fio_server_send_gs(rs); + else + show_group_stats(rs); + } show_disk_util(); } diff --git a/stat.h b/stat.h new file mode 100644 index 00000000..507a76a9 --- /dev/null +++ b/stat.h @@ -0,0 +1,175 @@ +#ifndef FIO_STAT_H +#define FIO_STAT_H + +struct group_run_stats { + uint64_t max_run[2], min_run[2]; + uint64_t max_bw[2], min_bw[2]; + uint64_t io_kb[2]; + uint64_t agg[2]; + uint32_t kb_base; + uint32_t groupid; +}; + +/* + * How many depth levels to log + */ +#define FIO_IO_U_MAP_NR 7 +#define FIO_IO_U_LAT_U_NR 10 +#define FIO_IO_U_LAT_M_NR 12 + +/* + * Aggregate clat samples to report percentile(s) of them. + * + * EXECUTIVE SUMMARY + * + * FIO_IO_U_PLAT_BITS determines the maximum statistical error on the + * value of resulting percentiles. The error will be approximately + * 1/2^(FIO_IO_U_PLAT_BITS+1) of the value. + * + * FIO_IO_U_PLAT_GROUP_NR and FIO_IO_U_PLAT_BITS determine the maximum + * range being tracked for latency samples. The maximum value tracked + * accurately will be 2^(GROUP_NR + PLAT_BITS -1) microseconds. + * + * FIO_IO_U_PLAT_GROUP_NR and FIO_IO_U_PLAT_BITS determine the memory + * requirement of storing those aggregate counts. The memory used will + * be (FIO_IO_U_PLAT_GROUP_NR * 2^FIO_IO_U_PLAT_BITS) * sizeof(int) + * bytes. + * + * FIO_IO_U_PLAT_NR is the total number of buckets. + * + * DETAILS + * + * Suppose the clat varies from 0 to 999 (usec), the straightforward + * method is to keep an array of (999 + 1) buckets, in which a counter + * keeps the count of samples which fall in the bucket, e.g., + * {[0],[1],...,[999]}. However this consumes a huge amount of space, + * and can be avoided if an approximation is acceptable. + * + * One such method is to let the range of the bucket to be greater + * than one. This method has low accuracy when the value is small. For + * example, let the buckets be {[0,99],[100,199],...,[900,999]}, and + * the represented value of each bucket be the mean of the range. Then + * a value 0 has an round-off error of 49.5. To improve on this, we + * use buckets with non-uniform ranges, while bounding the error of + * each bucket within a ratio of the sample value. A simple example + * would be when error_bound = 0.005, buckets are { + * {[0],[1],...,[99]}, {[100,101],[102,103],...,[198,199]},.., + * {[900,909],[910,919]...} }. The total range is partitioned into + * groups with different ranges, then buckets with uniform ranges. An + * upper bound of the error is (range_of_bucket/2)/value_of_bucket + * + * For better efficiency, we implement this using base two. We group + * samples by their Most Significant Bit (MSB), extract the next M bit + * of them as an index within the group, and discard the rest of the + * bits. + * + * E.g., assume a sample 'x' whose MSB is bit n (starting from bit 0), + * and use M bit for indexing + * + * | n | M bits | bit (n-M-1) ... bit 0 | + * + * Because x is at least 2^n, and bit 0 to bit (n-M-1) is at most + * (2^(n-M) - 1), discarding bit 0 to (n-M-1) makes the round-off + * error + * + * 2^(n-M)-1 2^(n-M) 1 + * e <= --------- <= ------- = --- + * 2^n 2^n 2^M + * + * Furthermore, we use "mean" of the range to represent the bucket, + * the error e can be lowered by half to 1 / 2^(M+1). By using M bits + * as the index, each group must contains 2^M buckets. + * + * E.g. Let M (FIO_IO_U_PLAT_BITS) be 6 + * Error bound is 1/2^(6+1) = 0.0078125 (< 1%) + * + * Group MSB #discarded range of #buckets + * error_bits value + * ---------------------------------------------------------------- + * 0* 0~5 0 [0,63] 64 + * 1* 6 0 [64,127] 64 + * 2 7 1 [128,255] 64 + * 3 8 2 [256,511] 64 + * 4 9 3 [512,1023] 64 + * ... ... ... [...,...] ... + * 18 23 17 [8838608,+inf]** 64 + * + * * Special cases: when n < (M-1) or when n == (M-1), in both cases, + * the value cannot be rounded off. Use all bits of the sample as + * index. + * + * ** If a sample's MSB is greater than 23, it will be counted as 23. + */ + +#define FIO_IO_U_PLAT_BITS 6 +#define FIO_IO_U_PLAT_VAL (1 << FIO_IO_U_PLAT_BITS) +#define FIO_IO_U_PLAT_GROUP_NR 19 +#define FIO_IO_U_PLAT_NR (FIO_IO_U_PLAT_GROUP_NR * FIO_IO_U_PLAT_VAL) +#define FIO_IO_U_LIST_MAX_LEN 20 /* The size of the default and user-specified + list of percentiles */ + +#define MAX_PATTERN_SIZE 512 +#define FIO_JOBNAME_SIZE 128 +#define FIO_VERROR_SIZE 128 + +struct thread_stat { + char name[FIO_JOBNAME_SIZE]; + char verror[FIO_VERROR_SIZE]; + int32_t error; + int32_t groupid; + uint32_t pid; + char description[FIO_JOBNAME_SIZE]; + uint32_t members; + + /* + * bandwidth and latency stats + */ + struct io_stat clat_stat[2]; /* completion latency */ + struct io_stat slat_stat[2]; /* submission latency */ + struct io_stat lat_stat[2]; /* total latency */ + struct io_stat bw_stat[2]; /* bandwidth stats */ + + /* + * fio system usage accounting + */ + uint64_t usr_time; + uint64_t sys_time; + uint64_t ctx; + uint64_t minf, majf; + + /* + * IO depth and latency stats + */ + uint64_t clat_percentiles; + double *percentile_list; + + uint32_t io_u_map[FIO_IO_U_MAP_NR]; + uint32_t io_u_submit[FIO_IO_U_MAP_NR]; + uint32_t io_u_complete[FIO_IO_U_MAP_NR]; + uint32_t io_u_lat_u[FIO_IO_U_LAT_U_NR]; + uint32_t io_u_lat_m[FIO_IO_U_LAT_M_NR]; + uint32_t io_u_plat[2][FIO_IO_U_PLAT_NR]; + uint64_t total_io_u[3]; + uint64_t short_io_u[3]; + uint64_t total_submit; + uint64_t total_complete; + + uint64_t io_bytes[2]; + uint64_t runtime[2]; + uint64_t total_run_time; + + /* + * IO Error related stats + */ + uint16_t continue_on_error; + uint64_t total_err_count; + int32_t first_error; + + uint32_t kb_base; +}; + +extern void show_thread_status(struct thread_stat *ts, struct group_run_stats *rs); +extern void show_group_stats(struct group_run_stats *rs); + + +#endif