#include <netdb.h>
#include <syslog.h>
#include <signal.h>
+#include <zlib.h>
#include "fio.h"
#include "server.h"
"START",
"STOP",
"DISK_UTIL",
- "RUN",
+ "SERVER_START",
"ADD_JOB",
+ "CMD_RUN"
+ "CMD_IOLOG",
};
const char *fio_server_op(unsigned int op)
return buf;
}
-int fio_send_data(int sk, const void *p, unsigned int len)
+static ssize_t iov_total_len(const struct iovec *iov, int count)
{
- assert(len <= sizeof(struct fio_net_cmd) + FIO_SERVER_MAX_PDU);
+ ssize_t ret = 0;
- do {
- int ret = send(sk, p, len, 0);
+ while (count--) {
+ ret += iov->iov_len;
+ iov++;
+ }
+ return ret;
+}
+
+static int fio_sendv_data(int sk, struct iovec *iov, int count)
+{
+ ssize_t total_len = iov_total_len(iov, count);
+ ssize_t ret;
+
+ do {
+ ret = writev(sk, iov, count);
if (ret > 0) {
- len -= ret;
- if (!len)
+ total_len -= ret;
+ if (!total_len)
break;
- p += ret;
- continue;
+
+ while (ret) {
+ if (ret >= iov->iov_len) {
+ ret -= iov->iov_len;
+ iov++;
+ continue;
+ }
+ iov->iov_base += ret;
+ iov->iov_len -= ret;
+ ret = 0;
+ }
} else if (!ret)
break;
else if (errno == EAGAIN || errno == EINTR)
break;
} while (!exit_backend);
- if (!len)
+ if (!total_len)
return 0;
+ if (errno)
+ return -errno;
+
return 1;
}
+int fio_send_data(int sk, const void *p, unsigned int len)
+{
+ struct iovec iov = { .iov_base = (void *) p, .iov_len = len };
+
+ assert(len <= sizeof(struct fio_net_cmd) + FIO_SERVER_MAX_FRAGMENT_PDU);
+
+ return fio_sendv_data(sk, &iov, 1);
+}
+
int fio_recv_data(int sk, void *p, unsigned int len)
{
do {
return 1;
}
- if (cmd->pdu_len > FIO_SERVER_MAX_PDU) {
+ if (cmd->pdu_len > FIO_SERVER_MAX_FRAGMENT_PDU) {
log_err("fio: command payload too large: %u\n", cmd->pdu_len);
return 1;
}
cmdret = NULL;
} else if (cmdret) {
/* zero-terminate text input */
- if (cmdret->pdu_len && (cmdret->opcode == FIO_NET_CMD_TEXT ||
- cmdret->opcode == FIO_NET_CMD_JOB)) {
- char *buf = (char *) cmdret->payload;
-
- buf[cmdret->pdu_len ] = '\0';
+ if (cmdret->pdu_len) {
+ if (cmdret->opcode == FIO_NET_CMD_TEXT) {
+ struct cmd_text_pdu *pdu = (struct cmd_text_pdu *) cmdret->payload;
+ char *buf = (char *) pdu->buf;
+
+ buf[pdu->buf_len ] = '\0';
+ } else if (cmdret->opcode == FIO_NET_CMD_JOB) {
+ struct cmd_job_pdu *pdu = (struct cmd_job_pdu *) cmdret->payload;
+ char *buf = (char *) pdu->buf;
+ int len = le32_to_cpu(pdu->buf_len);
+
+ buf[len] = '\0';
+ }
}
+
/* frag flag is internal */
cmdret->flags &= ~FIO_NET_CMD_F_MORE;
}
return cmdret;
}
-void fio_net_cmd_crc(struct fio_net_cmd *cmd)
+void fio_net_cmd_crc_pdu(struct fio_net_cmd *cmd, const void *pdu)
{
uint32_t pdu_len;
cmd->cmd_crc16 = __cpu_to_le16(fio_crc16(cmd, FIO_NET_CMD_CRC_SZ));
pdu_len = le32_to_cpu(cmd->pdu_len);
- if (pdu_len)
- cmd->pdu_crc16 = __cpu_to_le16(fio_crc16(cmd->payload, pdu_len));
+ cmd->pdu_crc16 = __cpu_to_le16(fio_crc16(pdu, pdu_len));
+}
+
+void fio_net_cmd_crc(struct fio_net_cmd *cmd)
+{
+ fio_net_cmd_crc_pdu(cmd, cmd->payload);
}
int fio_net_send_cmd(int fd, uint16_t opcode, const void *buf, off_t size,
do {
this_len = size;
- if (this_len > FIO_SERVER_MAX_PDU)
- this_len = FIO_SERVER_MAX_PDU;
+ if (this_len > FIO_SERVER_MAX_FRAGMENT_PDU)
+ this_len = FIO_SERVER_MAX_FRAGMENT_PDU;
if (!cmd || cur_len < sizeof(*cmd) + this_len) {
if (cmd)
return fio_net_send_simple_cmd(server_fd, FIO_NET_CMD_QUIT, 0, NULL);
}
-static int handle_job_cmd(struct fio_net_cmd *cmd)
+static int handle_run_cmd(struct fio_net_cmd *cmd)
{
- char *buf = (char *) cmd->payload;
- struct cmd_start_pdu spdu;
struct cmd_end_pdu epdu;
int ret;
- if (parse_jobs_ini(buf, 1, 0)) {
- fio_server_send_quit_cmd();
- return -1;
- }
-
- spdu.jobs = cpu_to_le32(thread_number);
- fio_net_send_cmd(server_fd, FIO_NET_CMD_START, &spdu, sizeof(spdu), 0);
-
ret = fio_backend();
epdu.error = ret;
fio_server_send_quit_cmd();
reset_fio_state();
+ first_cmd_check = 0;
return ret;
}
+static int handle_job_cmd(struct fio_net_cmd *cmd)
+{
+ struct cmd_job_pdu *pdu = (struct cmd_job_pdu *) cmd->payload;
+ void *buf = pdu->buf;
+ struct cmd_start_pdu spdu;
+
+ pdu->buf_len = le32_to_cpu(pdu->buf_len);
+ pdu->client_type = le32_to_cpu(pdu->client_type);
+
+ if (parse_jobs_ini(buf, 1, 0, pdu->client_type)) {
+ fio_server_send_quit_cmd();
+ return -1;
+ }
+
+ spdu.jobs = cpu_to_le32(thread_number);
+ fio_net_send_cmd(server_fd, FIO_NET_CMD_START, &spdu, sizeof(spdu), 0);
+ return 0;
+}
+
static int handle_jobline_cmd(struct fio_net_cmd *cmd)
{
void *pdu = cmd->payload;
struct cmd_single_line_pdu *cslp;
struct cmd_line_pdu *clp;
unsigned long offset;
+ struct cmd_start_pdu spdu;
char **argv;
- int ret, i;
+ int i;
clp = pdu;
clp->lines = le16_to_cpu(clp->lines);
+ clp->client_type = le16_to_cpu(clp->client_type);
argv = malloc(clp->lines * sizeof(char *));
offset = sizeof(*clp);
dprint(FD_NET, "server: %d: %s\n", i, argv[i]);
}
- if (parse_cmd_line(clp->lines, argv)) {
+ if (parse_cmd_line(clp->lines, argv, clp->client_type)) {
fio_server_send_quit_cmd();
free(argv);
return -1;
free(argv);
- fio_net_send_simple_cmd(server_fd, FIO_NET_CMD_START, 0, NULL);
-
- ret = fio_backend();
- fio_server_send_quit_cmd();
- reset_fio_state();
- return ret;
+ spdu.jobs = cpu_to_le32(thread_number);
+ fio_net_send_cmd(server_fd, FIO_NET_CMD_START, &spdu, sizeof(spdu), 0);
+ return 0;
}
static int handle_probe_cmd(struct fio_net_cmd *cmd)
je->iops[i] = cpu_to_le32(je->iops[i]);
}
- je->elapsed_sec = cpu_to_le32(je->nr_running);
+ je->elapsed_sec = cpu_to_le64(je->elapsed_sec);
je->eta_sec = cpu_to_le64(je->eta_sec);
+ je->nr_threads = cpu_to_le32(je->nr_threads);
fio_net_send_cmd(server_fd, FIO_NET_CMD_ETA, je, size, cmd->tag);
free(je);
case FIO_NET_CMD_SEND_ETA:
ret = handle_send_eta_cmd(cmd);
break;
+ case FIO_NET_CMD_RUN:
+ ret = handle_run_cmd(cmd);
+ break;
default:
log_err("fio: unknown opcode: %s\n",fio_server_op(cmd->opcode));
ret = 1;
void fio_server_idle_loop(void)
{
if (!first_cmd_check) {
- fio_net_send_simple_cmd(server_fd, FIO_NET_CMD_RUN, 0, NULL);
+ fio_net_send_simple_cmd(server_fd, FIO_NET_CMD_SERVER_START, 0, NULL);
first_cmd_check = 1;
}
if (server_fd != -1)
return exitval;
}
-int fio_server_text_output(const char *buf, size_t len)
+int fio_server_text_output(int level, const char *buf, size_t len)
{
- if (server_fd != -1)
- return fio_net_send_cmd(server_fd, FIO_NET_CMD_TEXT, buf, len, 0);
+ struct cmd_text_pdu *pdu;
+ unsigned int tlen;
+ struct timeval tv;
+
+ if (server_fd == -1)
+ return log_local_buf(buf, len);
+
+ tlen = sizeof(*pdu) + len;
+ pdu = malloc(tlen);
+
+ pdu->level = __cpu_to_le32(level);
+ pdu->buf_len = __cpu_to_le32(len);
- return log_local_buf(buf, len);
+ gettimeofday(&tv, NULL);
+ pdu->log_sec = __cpu_to_le64(tv.tv_sec);
+ pdu->log_usec = __cpu_to_le64(tv.tv_usec);
+
+ memcpy(pdu->buf, buf, len);
+
+ fio_net_send_cmd(server_fd, FIO_NET_CMD_TEXT, pdu, tlen, 0);
+ free(pdu);
+ return len;
}
static void convert_io_stat(struct io_stat *dst, struct io_stat *src)
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);
+ p.ts.error = cpu_to_le32(ts->error);
+ p.ts.thread_number = cpu_to_le32(ts->thread_number);
+ 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]);
}
}
-void fio_server_send_add_job(struct thread_options *o, const char *ioengine)
+/*
+ * Send a command with a separate PDU, not inlined in the command
+ */
+static int fio_send_cmd_ext_pdu(int sk, uint16_t opcode, const void *buf,
+ off_t size, uint64_t tag, uint32_t flags)
{
- struct cmd_add_job_pdu pdu;
- int i;
+ struct fio_net_cmd cmd;
+ struct iovec iov[2];
- strcpy((char *) pdu.jobname, o->name);
- strcpy((char *) pdu.ioengine, ioengine);
+ iov[0].iov_base = &cmd;
+ iov[0].iov_len = sizeof(cmd);
+ iov[1].iov_base = (void *) buf;
+ iov[1].iov_len = size;
- pdu.iodepth = cpu_to_le32(o->iodepth);
- pdu.rw = cpu_to_le32(o->td_ddir);
+ __fio_init_net_cmd(&cmd, opcode, size, tag);
+ cmd.flags = __cpu_to_le32(flags);
+ fio_net_cmd_crc_pdu(&cmd, buf);
- for (i = 0; i < 2; i++) {
- pdu.min_bs[i] = cpu_to_le32(o->min_bs[i]);
- pdu.max_bs[i] = cpu_to_le32(o->max_bs[i]);
+ return fio_sendv_data(server_fd, iov, 2);
+}
+
+int fio_send_iolog(struct thread_data *td, struct io_log *log, const char *name)
+{
+ struct cmd_iolog_pdu pdu;
+ z_stream stream;
+ void *out_pdu;
+ int i, ret = 0;
+
+ pdu.thread_number = cpu_to_le32(td->thread_number);
+ pdu.nr_samples = __cpu_to_le32(log->nr_samples);
+ pdu.log_type = cpu_to_le32(log->log_type);
+ strcpy((char *) pdu.name, name);
+
+ for (i = 0; i < log->nr_samples; i++) {
+ struct io_sample *s = &log->log[i];
+
+ s->time = cpu_to_le64(s->time);
+ s->val = cpu_to_le64(s->val);
+ s->ddir = cpu_to_le32(s->ddir);
+ s->bs = cpu_to_le32(s->bs);
}
- pdu.numjobs = cpu_to_le32(o->numjobs);
- pdu.group_reporting = cpu_to_le32(o->group_reporting);
+ /*
+ * 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);
- fio_net_send_cmd(server_fd, FIO_NET_CMD_ADD_JOB, &pdu, sizeof(pdu), 0);
+ stream.zalloc = Z_NULL;
+ stream.zfree = Z_NULL;
+ stream.opaque = Z_NULL;
+
+ if (deflateInit(&stream, Z_DEFAULT_COMPRESSION) != Z_OK) {
+ ret = 1;
+ goto err;
+ }
+
+ /*
+ * Send header first, it's not compressed.
+ */
+ ret = fio_send_cmd_ext_pdu(server_fd, FIO_NET_CMD_IOLOG, &pdu,
+ sizeof(pdu), 0, FIO_NET_CMD_F_MORE);
+ if (ret)
+ goto err_zlib;
+
+ stream.next_in = (void *) log->log;
+ stream.avail_in = log->nr_samples * sizeof(struct io_sample);
+
+ do {
+ unsigned int this_len, flags = 0;
+ int ret;
+
+ 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;
+
+ if (stream.avail_in)
+ flags = FIO_NET_CMD_F_MORE;
+
+ ret = fio_send_cmd_ext_pdu(server_fd, FIO_NET_CMD_IOLOG,
+ out_pdu, this_len, 0, flags);
+ if (ret)
+ goto err_zlib;
+ } while (stream.avail_in);
+
+err_zlib:
+ deflateEnd(&stream);
+err:
+ free(out_pdu);
+ return ret;
}
-int fio_server_log(const char *format, ...)
+void fio_server_send_add_job(struct thread_data *td)
{
- char buffer[1024];
- va_list args;
- size_t len;
-
- dprint(FD_NET, "server log\n");
+ struct cmd_add_job_pdu pdu;
- va_start(args, format);
- len = vsnprintf(buffer, sizeof(buffer), format, args);
- va_end(args);
+ 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);
- return fio_server_text_output(buffer, len);
+ fio_net_send_cmd(server_fd, FIO_NET_CMD_ADD_JOB, &pdu, sizeof(pdu), 0);
}
static int fio_init_server_ip(void)
int ret;
#if defined(WIN32)
- WSADATA wsd;
- WSAStartup(MAKEWORD(2,2), &wsd);
+ WSADATA wsd;
+ WSAStartup(MAKEWORD(2,2), &wsd);
#endif
if (!pidfile)