+ fio_net_queue_cmd(FIO_NET_CMD_GS, &gs, sizeof(gs), NULL, SK_F_COPY);
+}
+
+void fio_server_send_job_options(struct flist_head *opt_list,
+ unsigned int groupid)
+{
+ struct cmd_job_option pdu;
+ struct flist_head *entry;
+
+ if (flist_empty(opt_list))
+ return;
+
+ flist_for_each(entry, opt_list) {
+ struct print_option *p;
+ size_t len;
+
+ p = flist_entry(entry, struct print_option, list);
+ memset(&pdu, 0, sizeof(pdu));
+
+ if (groupid == -1U) {
+ pdu.global = __cpu_to_le16(1);
+ pdu.groupid = 0;
+ } else {
+ pdu.global = 0;
+ pdu.groupid = cpu_to_le32(groupid);
+ }
+ len = strlen(p->name);
+ if (len >= sizeof(pdu.name)) {
+ len = sizeof(pdu.name) - 1;
+ pdu.truncated = __cpu_to_le16(1);
+ }
+ memcpy(pdu.name, p->name, len);
+ if (p->value) {
+ len = strlen(p->value);
+ if (len >= sizeof(pdu.value)) {
+ len = sizeof(pdu.value) - 1;
+ pdu.truncated = __cpu_to_le16(1);
+ }
+ memcpy(pdu.value, p->value, len);
+ }
+ fio_net_queue_cmd(FIO_NET_CMD_JOB_OPT, &pdu, sizeof(pdu), NULL, SK_F_COPY);
+ }
+}
+
+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_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_le64(src->ticks[i]);
+ }
+
+ 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));
+}
+
+static void convert_dus(struct disk_util_stat *dst, struct disk_util_stat *src)
+{
+ int i;
+
+ 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->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->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)
+{
+ struct disk_util *du;
+ struct flist_head *entry;
+ struct cmd_du_pdu pdu;
+
+ dprint(FD_NET, "server: sending disk_util %d\n", !flist_empty(&disk_list));
+
+ memset(&pdu, 0, sizeof(pdu));
+
+ flist_for_each(entry, &disk_list) {
+ du = flist_entry(entry, struct disk_util, list);
+
+ convert_dus(&pdu.dus, &du->dus);
+ convert_agg(&pdu.agg, &du->agg);
+
+ fio_net_queue_cmd(FIO_NET_CMD_DU, &pdu, sizeof(pdu), NULL, SK_F_COPY);
+ }
+}
+
+static int fio_append_iolog_gz(struct sk_entry *first, struct io_log *log)
+{
+ int ret = 0;
+#ifdef CONFIG_ZLIB
+ struct sk_entry *entry;
+ z_stream stream;
+ void *out_pdu;
+
+ /*
+ * Dirty - since the log is potentially huge, compress it into
+ * FIO_SERVER_MAX_FRAGMENT_PDU chunks and let the receiving
+ * side defragment it.
+ */
+ out_pdu = malloc(FIO_SERVER_MAX_FRAGMENT_PDU);
+
+ stream.zalloc = Z_NULL;
+ stream.zfree = Z_NULL;
+ stream.opaque = Z_NULL;
+
+ if (deflateInit(&stream, Z_DEFAULT_COMPRESSION) != Z_OK) {
+ ret = 1;
+ goto err;
+ }
+
+ stream.next_in = (void *) log->log;
+ stream.avail_in = log->nr_samples * log_entry_sz(log);
+
+ do {
+ unsigned int this_len;
+
+ stream.avail_out = FIO_SERVER_MAX_FRAGMENT_PDU;
+ stream.next_out = out_pdu;
+ ret = deflate(&stream, Z_FINISH);
+ /* may be Z_OK, or Z_STREAM_END */
+ if (ret < 0)
+ goto err_zlib;
+
+ this_len = FIO_SERVER_MAX_FRAGMENT_PDU - stream.avail_out;
+
+ entry = fio_net_prep_cmd(FIO_NET_CMD_IOLOG, out_pdu, this_len,
+ NULL, SK_F_VEC | SK_F_INLINE | SK_F_FREE);
+ out_pdu = NULL;
+ flist_add_tail(&entry->list, &first->next);
+ } while (stream.avail_in);
+
+err_zlib:
+ deflateEnd(&stream);
+err:
+ free(out_pdu);
+#endif
+ return ret;
+}
+
+static int fio_append_gz_chunks(struct sk_entry *first, struct io_log *log)
+{
+ struct sk_entry *entry;
+ struct flist_head *node;
+
+ pthread_mutex_lock(&log->chunk_lock);
+ flist_for_each(node, &log->chunk_list) {
+ struct iolog_compress *c;
+
+ c = flist_entry(node, struct iolog_compress, list);
+ entry = fio_net_prep_cmd(FIO_NET_CMD_IOLOG, c->buf, c->len,
+ NULL, SK_F_VEC | SK_F_INLINE);
+ flist_add_tail(&entry->list, &first->next);
+ }
+ pthread_mutex_unlock(&log->chunk_lock);
+
+ return 0;
+}
+
+static int fio_append_text_log(struct sk_entry *first, struct io_log *log)
+{
+ struct sk_entry *entry;
+ size_t size = log->nr_samples * log_entry_sz(log);
+
+ entry = fio_net_prep_cmd(FIO_NET_CMD_IOLOG, log->log, size,
+ NULL, SK_F_VEC | SK_F_INLINE);
+ flist_add_tail(&entry->list, &first->next);
+ return 0;
+}
+
+int fio_send_iolog(struct thread_data *td, struct io_log *log, const char *name)
+{
+ struct cmd_iolog_pdu pdu;
+ struct sk_entry *first;
+ 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);
+
+ if (!flist_empty(&log->chunk_list))
+ pdu.compressed = __cpu_to_le32(STORE_COMPRESSED);
+ else if (use_zlib)
+ pdu.compressed = __cpu_to_le32(XMIT_COMPRESSED);
+ else
+ pdu.compressed = 0;
+
+ strncpy((char *) pdu.name, name, FIO_NET_NAME_MAX);
+ pdu.name[FIO_NET_NAME_MAX - 1] = '\0';
+
+ /*
+ * We can't do this for a pre-compressed log, but for that case,
+ * log->nr_samples is zero anyway.
+ */
+ 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);
+ }
+ }
+
+ /*
+ * Assemble header entry first
+ */
+ first = fio_net_prep_cmd(FIO_NET_CMD_IOLOG, &pdu, sizeof(pdu), NULL, SK_F_VEC | SK_F_INLINE | SK_F_COPY);
+
+ /*
+ * Now append actual log entries. If log compression was enabled on
+ * the job, just send out the compressed chunks directly. If we
+ * have a plain log, compress if we can, then send. Otherwise, send
+ * the plain text output.
+ */
+ if (!flist_empty(&log->chunk_list))
+ ret = fio_append_gz_chunks(first, log);
+ else if (use_zlib)
+ ret = fio_append_iolog_gz(first, log);
+ else
+ ret = fio_append_text_log(first, log);
+
+ fio_net_queue_entry(first);
+ return ret;
+}
+
+void fio_server_send_add_job(struct thread_data *td)
+{
+ struct cmd_add_job_pdu pdu;
+
+ memset(&pdu, 0, sizeof(pdu));
+ pdu.thread_number = cpu_to_le32(td->thread_number);
+ pdu.groupid = cpu_to_le32(td->groupid);
+ convert_thread_options_to_net(&pdu.top, &td->o);
+
+ fio_net_queue_cmd(FIO_NET_CMD_ADD_JOB, &pdu, sizeof(pdu), NULL,
+ SK_F_COPY);
+}
+
+void fio_server_send_start(struct thread_data *td)
+{
+ struct sk_out *sk_out = pthread_getspecific(sk_out_key);
+
+ assert(sk_out->sk != -1);
+
+ fio_net_queue_cmd(FIO_NET_CMD_SERVER_START, NULL, 0, 0, SK_F_SIMPLE);