server: transmit status as structures, not text
authorJens Axboe <axboe@kernel.dk>
Mon, 3 Oct 2011 12:20:01 +0000 (14:20 +0200)
committerJens Axboe <axboe@kernel.dk>
Mon, 3 Oct 2011 12:20:01 +0000 (14:20 +0200)
Signed-off-by: Jens Axboe <axboe@kernel.dk>
client.c
fio.h
server.c
server.h
stat.c
stat.h [new file with mode: 0644]

index 05bba38d60d54d1a8f9facb7433da41ab07d0a7c..cb2293123acb9c27c93ef5f31d30e56a0c782fc6 100644 (file)
--- 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 1d3a9d470878a03e34c3a54e2ca2980537213259..fdc2c7e04b178175bd1882c1534430b6f347cee0 100644 (file)
--- 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 <guasi.h>
@@ -45,14 +46,6 @@ struct thread_data;
 #include <sys/asynch.h>
 #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;
index d1de0e48ad4aa44065a357db1c7f5693416d856a..72761c562fa938a6fe70867e60765dbd329c5698 100644 (file)
--- 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];
index 5e691e01bb143eadb39ccb0187f539232a073dfa..10c9006e4339c1ec57a6f12eed5da26493ddc680 100644 (file)
--- a/server.h
+++ b/server.h
@@ -5,6 +5,8 @@
 #include <string.h>
 #include <endian.h>
 
+#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 564edf342014bd07d4f61f99c09aa57c63f6f731..bcf8fa83ff5f97a73f8b6ee10161e387688a8140 100644 (file)
--- 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 (file)
index 0000000..507a76a
--- /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