6. Normal output
7. Terse output
8. Trace file format
+ 9. CPU idleness profiling
1.0 Overview and history
------------------------
block sizes, not with workloads that use multiple block
sizes. If used with such a workload, fio may read or write
some blocks multiple times.
-
+
nice=int Run the job with the given nice value. See man nice(2).
prio=int Set the io priority value of this job. Linux limits us to
ioscheduler=str Attempt to switch the device hosting the file to the specified
io scheduler before running.
-cpuload=int If the job is a CPU cycle eater, attempt to use the specified
- percentage of CPU cycles.
-
-cpuchunks=int If the job is a CPU cycle eater, split the load into
- cycles of the given time. In microseconds.
-
disk_util=bool Generate disk utilization statistics, if the platform
supports it. Defaults to on.
enabled when polling for a minimum of 0 events (eg when
iodepth_batch_complete=0).
+[cpu] cpuload=int Attempt to use the specified percentage of CPU cycles.
+
+[cpu] cpuchunks=int Split the load into cycles of the given time. In
+ microseconds.
+
[netsplice] hostname=str
[net] hostname=str The host name or IP address to use for TCP or UDP based IO.
If the job is a TCP listener or UDP reader, the hostname is not
CPPFLAGS= -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 $(DEBUGFLAGS)
OPTFLAGS= -O3 -g -ffast-math $(EXTFLAGS)
CFLAGS = -std=gnu99 -Wwrite-strings -Wall $(OPTFLAGS)
-LIBS = -lm $(EXTLIBS)
+LIBS = -lm -lz $(EXTLIBS)
PROGS = fio
SCRIPTS = fio_generate_plots
UNAME := $(shell uname)
include config-host.mak
endif
-SOURCE := gettime.c fio.c ioengines.c init.c stat.c log.c time.c filesetup.c \
+ifdef CONFIG_GFIO
+ PROGS += gfio
+endif
+
+SOURCE := gettime.c ioengines.c init.c stat.c log.c time.c filesetup.c \
eta.c verify.c memory.c io_u.c parse.c mutex.c options.c \
- rbtree.c smalloc.c filehash.c profile.c debug.c lib/rand.c \
+ lib/rbtree.c smalloc.c filehash.c profile.c debug.c lib/rand.c \
lib/num2str.c lib/ieee754.c $(wildcard crc/*.c) engines/cpu.c \
engines/mmap.c engines/sync.c engines/null.c engines/net.c \
memalign.c server.c client.c iolog.c backend.c libfio.c flow.c \
- json.c lib/zipf.c lib/axmap.c lib/lfsr.c gettime-thread.c \
- helpers.c lib/flist_sort.c lib/hweight.c lib/getrusage.c \
- idletime.c
+ cconv.c lib/prio_tree.c json.c lib/zipf.c lib/axmap.c \
+ lib/lfsr.c gettime-thread.c helpers.c lib/flist_sort.c \
+ lib/hweight.c lib/getrusage.c idletime.c
ifdef CONFIG_64BIT_LLP64
CFLAGS += -DBITS_PER_LONG=32
LDFLAGS += -rdynamic
endif
ifeq ($(UNAME), SunOS)
- LIBS += -lpthread -ldl -laio -lrt -lnsl -lsocket
+ LIBS += -lpthread -ldl
CPPFLAGS += -D__EXTENSIONS__
endif
ifeq ($(UNAME), FreeBSD)
endif
OBJS = $(SOURCE:.c=.o)
+
+FIO_OBJS = $(OBJS) fio.o
+GFIO_OBJS = $(OBJS) gfio.o graph.o tickmarks.o ghelpers.o goptions.o gerror.o \
+ gclient.o gcompat.o cairo_text_helpers.o printing.o
+
-include $(OBJS:.o=.d)
T_SMALLOC_OBJS = t/stest.o
init.o: FIO-VERSION-FILE init.c
$(QUIET_CC)$(CC) -o init.o $(CFLAGS) $(CPPFLAGS) -c init.c
+gcompat.o: gcompat.c gcompat.h
+ $(QUIET_CC)$(CC) $(CFLAGS) $(GTK_CFLAGS) $(CPPFLAGS) -c gcompat.c
+
+goptions.o: goptions.c goptions.h
+ $(QUIET_CC)$(CC) $(CFLAGS) $(GTK_CFLAGS) $(CPPFLAGS) -c goptions.c
+
+ghelpers.o: ghelpers.c ghelpers.h
+ $(QUIET_CC)$(CC) $(CFLAGS) $(GTK_CFLAGS) $(CPPFLAGS) -c ghelpers.c
+
+gerror.o: gerror.c gerror.h
+ $(QUIET_CC)$(CC) $(CFLAGS) $(GTK_CFLAGS) $(CPPFLAGS) -c gerror.c
+
+gclient.o: gclient.c gclient.h
+ $(QUIET_CC)$(CC) $(CFLAGS) $(GTK_CFLAGS) $(CPPFLAGS) -c gclient.c
+
+gfio.o: gfio.c ghelpers.c
+ $(QUIET_CC)$(CC) $(CFLAGS) $(GTK_CFLAGS) $(CPPFLAGS) -c gfio.c
+
+graph.o: graph.c graph.h
+ $(QUIET_CC)$(CC) $(CFLAGS) $(GTK_CFLAGS) $(CPPFLAGS) -c graph.c
+
+cairo_text_helpers.o: cairo_text_helpers.c cairo_text_helpers.h
+ $(QUIET_CC)$(CC) $(CFLAGS) $(GTK_CFLAGS) $(CPPFLAGS) -c cairo_text_helpers.c
+
+printing.o: printing.c printing.h
+ $(QUIET_CC)$(CC) $(CFLAGS) $(GTK_CFLAGS) $(CPPFLAGS) -c printing.c
+
t/stest: $(T_SMALLOC_OBJS)
$(QUIET_LINK)$(CC) $(LDFLAGS) $(CFLAGS) -o $@ $(T_SMALLOC_OBJS) $(LIBS) $(LDFLAGS)
t/ieee754: $(T_IEEE_OBJS)
$(QUIET_LINK)$(CC) $(LDFLAGS) $(CFLAGS) -o $@ $(T_IEEE_OBJS) $(LIBS) $(LDFLAGS)
+fio: $(FIO_OBJS)
+ $(QUIET_LINK)$(CC) $(LDFLAGS) $(CFLAGS) -o $@ $(FIO_OBJS) $(LIBS) $(LDFLAGS)
+
+gfio: $(GFIO_OBJS)
+ $(QUIET_LINK)$(CC) $(LIBS) -o gfio $(GFIO_OBJS) $(LIBS) $(GTK_LDFLAGS)
+
t/genzipf: $(T_ZIPF_OBJS)
$(QUIET_LINK)$(CC) $(LDFLAGS) $(CFLAGS) -o $@ $(T_ZIPF_OBJS) $(LIBS) $(LDFLAGS)
t/axmap: $(T_AXMAP_OBJS)
$(QUIET_LINK)$(CC) $(LDFLAGS) $(CFLAGS) -o $@ $(T_AXMAP_OBJS) $(LIBS) $(LDFLAGS)
-fio: $(OBJS)
- $(QUIET_LINK)$(CC) $(LDFLAGS) $(CFLAGS) -o $@ $(OBJS) $(LIBS) $(LDFLAGS)
-
clean: FORCE
- -rm -f .depend $(OBJS) $(T_OBJS) $(PROGS) $(T_PROGS) core.* core FIO-VERSION-FILE config-host.mak cscope.out *.d
+ -rm -f .depend $(GFIO_OBJS) $(OBJS) $(T_OBJS) $(PROGS) $(T_PROGS) core.* core gfio FIO-VERSION-FILE config-host.mak cscope.out *.d
cscope:
@cscope -b -R
compile_prog() {
local_cflags="$1"
- local_ldflags="$2"
+ local_ldflags="$2 $LIBS"
echo "Compiling test case $3" >> config.log
do_cc $CFLAGS $local_cflags -o $TMPE $TMPC $LDFLAGS $local_ldflags
}
cc="${CC-${cross_prefix}gcc}"
+# default options
show_help="no"
exit_val=0
+gfio="no"
# parse options
for opt do
;;
--build-32bit-win=*) build_32bit_win="$optarg"
;;
- --help)
- show_help="yes"
+ --enable-gfio)
+ gfio="yes"
;;
+ --help)
+ show_help="yes"
+ ;;
*)
echo "Bad option $opt"
show_help="yes"
echo "--cc= Specify compiler to use"
echo "--extra-cflags= Specify extra CFLAGS to pass to compiler"
echo "--build-32bit-win= Specify yes for a 32-bit build on Windows"
+ echo "--enable-gfio Enable building of gtk gfio"
exit $exit_val
fi
if test -z "$cpu" && test "$(isainfo -k)" = "amd64"; then
cpu="x86_64"
fi
+ LIBS="-lnsl -lsocket"
;;
CYGWIN*)
echo "Forcing known good options on Windows"
}
EOF
if compile_prog "" "" "wordsize"; then
- wordsize=$($TMPE)
+ wordsize=`$TMPE`
fi
echo "Wordsize $wordsize"
cat > $TMPC << EOF
static int sfaa(int *ptr)
{
- return __sync_fetch_and_and(ptr, 0);
+ return __sync_fetch_and_add(ptr, 0);
}
int main(int argc, char **argv)
if compile_prog "" "" "__sync_fetch_and_add()" ; then
sfaa="yes"
fi
- echo "__sync_fetch_and add $sfaa"
+ echo "__sync_fetch_and_add $sfaa"
##########################################
# libverbs probe
echo "__thread $tls_thread"
##########################################
+# Whether or not __thread is supported for TLS
+if test "$gfio" = "yes" ; then
+ cat > $TMPC << EOF
+#include <glib.h>
+#include <cairo.h>
+#include <gtk/gtk.h>
+int main(void)
+{
+ gdk_threads_enter();
+ gdk_threads_leave();
+
+ printf("%d", GTK_CHECK_VERSION(2, 18, 0));
+}
+EOF
+GTK_CFLAGS=$(pkg-config --cflags gtk+-2.0 gthread-2.0)
+if test "$?" != "0" ; then
+ echo "configure: gtk and gthread not found"
+ exit 1
+fi
+GTK_LIBS=$(pkg-config --libs gtk+-2.0 gthread-2.0)
+if test "$?" != "0" ; then
+ echo "configure: gtk and gthread not found"
+ exit 1
+fi
+if compile_prog "$GTK_CFLAGS" "$GTK_LIBS" "gfio" ; then
+ r=$($TMPE)
+ if test "$r" != "0" ; then
+ gfio="yes"
+ LIBS="$LIBS $GTK_LIBS"
+ CFLAGS="$CFLAGS $GTK_CFLAGS"
+ else
+ echo "GTK found, but need version 2.18 or higher"
+ gfio="no"
+ fi
+else
+ echo "Please install gtk and gdk libraries"
+ gfio="no"
+fi
+fi
+
+echo "gfio $gfio"
+
# Check whether we have getrusage(RUSAGE_THREAD)
rusage_thread="no"
cat > $TMPC << EOF
fi
echo "TCP_NODELAY $tcp_nodelay"
+ ##########################################
+ # Check whether we have RLIMIT_MEMLOCK
+ rlimit_memlock="no"
+ cat > $TMPC << EOF
+ #include <sys/time.h>
+ #include <sys/resource.h>
+ int main(int argc, char **argv)
+ {
+ struct rlimit rl;
+ return getrlimit(RLIMIT_MEMLOCK, &rl);
+ }
+ EOF
+ if compile_prog "" "" "RLIMIT_MEMLOCK"; then
+ rlimit_memlock="yes"
+ fi
+ echo "RLIMIT_MEMLOCK $rlimit_memlock"
+
#############################################################################
echo "# Automatically generated by configure - do not modify" > $config_host_mak
if test "$rusage_thread" = "yes" ; then
output_sym "CONFIG_RUSAGE_THREAD"
fi
+if test "$gfio" = "yes" ; then
+ echo "CONFIG_GFIO=y" >> $config_host_mak
+fi
if test "$sched_idle" = "yes" ; then
output_sym "CONFIG_SCHED_IDLE"
fi
if test "$tcp_nodelay" = "yes" ; then
output_sym "CONFIG_TCP_NODELAY"
fi
+ if test "$rlimit_memlock" = "yes" ; then
+ output_sym "CONFIG_RLIMIT_MEMLOCK"
+ fi
echo "LIBS+=$LIBS" >> $config_host_mak
+echo "CFLAGS+=$CFLAGS" >> $config_host_mak
echo "CC=$cc" >> $config_host_mak
echo "EXTFLAGS=$EXTFLAGS $CFLAGS" >> $config_host_mak
#ifdef FIO_INC_DEBUG
struct debug_level {
const char *name;
+ const char *help;
unsigned long shift;
unsigned int jobno;
};
extern unsigned long fio_debug;
- void __dprint(int type, const char *str, ...);
+ void __dprint(int type, const char *str, ...) __attribute__((format (printf, 2, 3)));
#define dprint(type, str, args...) \
do { \
static struct fio_option options[] = {
{
.name = "hostname",
+ .lname = "net engine hostname",
.type = FIO_OPT_STR_STORE,
.cb = str_hostname_cb,
.help = "Hostname for net IO engine",
+ .category = FIO_OPT_C_IO,
},
{
.name = "port",
+ .lname = "net engine port",
.type = FIO_OPT_INT,
.off1 = offsetof(struct netio_options, port),
.minval = 1,
.maxval = 65535,
.help = "Port to use for TCP or UDP net connections",
+ .category = FIO_OPT_C_IO,
},
{
.name = "protocol",
+ .lname = "net engine protocol",
.alias = "proto",
.type = FIO_OPT_STR,
.off1 = offsetof(struct netio_options, proto),
.help = "Network protocol to use",
.def = "tcp",
+ .category = FIO_OPT_C_IO,
.posval = {
{ .ival = "tcp",
.oval = FIO_TYPE_TCP,
#endif
{
.name = "listen",
+ .lname = "net engine listen",
.type = FIO_OPT_STR_SET,
.off1 = offsetof(struct netio_options, listen),
.help = "Listen for incoming TCP connections",
+ .category = FIO_OPT_C_IO,
},
{
.name = "pingpong",
{
struct netio_data *nd = td->io_ops->data;
struct netio_options *o = td->eo;
- int type, domain, optval;
+ int type, domain;
if (o->proto == FIO_TYPE_TCP) {
domain = AF_INET;
#ifdef CONFIG_TCP_NODELAY
if (o->nodelay && o->proto == FIO_TYPE_TCP) {
- optval = 1;
+ int optval = 1;
+
if (setsockopt(f->fd, IPPROTO_TCP, TCP_NODELAY, (void *) &optval, sizeof(int)) < 0) {
log_err("fio: cannot set TCP_NODELAY option on socket (%s), disable with 'nodelay=0'\n", strerror(errno));
return 1;
struct netio_data *nd = td->io_ops->data;
struct netio_options *o = td->eo;
socklen_t socklen = sizeof(nd->addr);
- int state, optval;
+ int state;
if (o->proto == FIO_TYPE_UDP) {
f->fd = nd->listenfd;
#ifdef CONFIG_TCP_NODELAY
if (o->nodelay && o->proto == FIO_TYPE_TCP) {
- optval = 1;
+ int optval = 1;
+
if (setsockopt(f->fd, IPPROTO_TCP, TCP_NODELAY, (void *) &optval, sizeof(int)) < 0) {
log_err("fio: cannot set TCP_NODELAY option on socket (%s), disable with 'nodelay=0'\n", strerror(errno));
return 1;
/*
* Convert seconds to a printable string.
*/
-static void eta_to_str(char *str, unsigned long eta_sec)
+void eta_to_str(char *str, unsigned long eta_sec)
{
unsigned int d, h, m, s;
int disp_hour = 0;
|| td->runstate == TD_PRE_READING) {
je->nr_running++;
if (td_read(td)) {
- je->t_rate += td->o.rate[DDIR_READ];
- je->t_iops += td->o.rate_iops[DDIR_READ];
- je->m_rate += td->o.ratemin[DDIR_READ];
- je->m_iops += td->o.rate_iops_min[DDIR_READ];
+ je->t_rate[0] += td->o.rate[DDIR_READ];
+ je->t_iops[0] += td->o.rate_iops[DDIR_READ];
+ je->m_rate[0] += td->o.ratemin[DDIR_READ];
+ je->m_iops[0] += td->o.rate_iops_min[DDIR_READ];
}
if (td_write(td)) {
- je->t_rate += td->o.rate[DDIR_WRITE];
- je->t_iops += td->o.rate_iops[DDIR_WRITE];
- je->m_rate += td->o.ratemin[DDIR_WRITE];
- je->m_iops += td->o.rate_iops_min[DDIR_WRITE];
+ je->t_rate[1] += td->o.rate[DDIR_WRITE];
+ je->t_iops[1] += td->o.rate_iops[DDIR_WRITE];
+ je->m_rate[1] += td->o.ratemin[DDIR_WRITE];
+ je->m_iops[1] += td->o.rate_iops_min[DDIR_WRITE];
}
if (td_trim(td)) {
- je->t_rate += td->o.rate[DDIR_TRIM];
- je->t_iops += td->o.rate_iops[DDIR_TRIM];
- je->m_rate += td->o.ratemin[DDIR_TRIM];
- je->m_iops += td->o.rate_iops_min[DDIR_TRIM];
+ je->t_rate[2] += td->o.rate[DDIR_TRIM];
+ je->t_iops[2] += td->o.rate_iops[DDIR_TRIM];
+ je->m_rate[2] += td->o.ratemin[DDIR_TRIM];
+ je->m_iops[2] += td->o.rate_iops_min[DDIR_TRIM];
}
je->files_open += td->nr_open_files;
je->nr_threads = thread_number;
memcpy(je->run_str, run_str, thread_number * sizeof(char));
-
return 1;
}
void display_thread_status(struct jobs_eta *je)
{
+ static struct timeval disp_eta_new_line;
+ static int eta_new_line_init, eta_new_line_pending;
static int linelen_last;
static int eta_good;
char output[REAL_MAX_JOBS + 512], *p = output;
eta_to_str(eta_str, je->eta_sec);
}
+ if (eta_new_line_pending) {
+ eta_new_line_pending = 0;
+ p += sprintf(p, "\n");
+ }
+
p += sprintf(p, "Jobs: %d (f=%d)", je->nr_running, je->files_open);
- if (je->m_rate || je->t_rate) {
+ if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
char *tr, *mr;
- mr = num2str(je->m_rate, 4, 0, je->is_pow2);
- tr = num2str(je->t_rate, 4, 0, je->is_pow2);
+ mr = num2str(je->m_rate[0] + je->m_rate[1], 4, 0, je->is_pow2);
+ tr = num2str(je->t_rate[0] + je->t_rate[1], 4, 0, je->is_pow2);
p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
free(tr);
free(mr);
- } else if (je->m_iops || je->t_iops)
- p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
+ } else if (je->m_iops[0] || je->m_iops[1] || je->t_iops[0] || je->t_iops[1]) {
+ p += sprintf(p, ", CR=%d/%d IOPS",
+ je->t_iops[0] + je->t_iops[1],
+ je->m_iops[0] + je->m_iops[1]);
+ }
if (je->eta_sec != INT_MAX && je->nr_running) {
char perc_str[32];
char *iops_str[DDIR_RWDIR_CNT];
p += sprintf(p, "\r");
printf("%s", output);
+
+ if (!eta_new_line_init) {
+ fio_gettime(&disp_eta_new_line, NULL);
+ eta_new_line_init = 1;
+ } else if (eta_new_line &&
+ mtime_since_now(&disp_eta_new_line) > eta_new_line * 1000) {
+ fio_gettime(&disp_eta_new_line, NULL);
+ eta_new_line_pending = 1;
+ }
+
fflush(stdout);
}
break;
case FIO_FALLOCATE_POSIX:
dprint(FD_FILE, "posix_fallocate file %s size %llu\n",
- f->file_name, f->real_file_size);
+ f->file_name,
+ (unsigned long long) f->real_file_size);
r = posix_fallocate(f->fd, 0, f->real_file_size);
if (r > 0) {
case FIO_FALLOCATE_KEEP_SIZE:
dprint(FD_FILE,
"fallocate(FALLOC_FL_KEEP_SIZE) "
- "file %s size %llu\n",
- f->file_name, f->real_file_size);
+ "file %s size %llu\n", f->file_name,
+ (unsigned long long) f->real_file_size);
r = fallocate(f->fd, FALLOC_FL_KEEP_SIZE, 0,
f->real_file_size);
- if (r != 0) {
+ if (r != 0)
td_verror(td, errno, "fallocate");
- }
+
break;
#endif /* CONFIG_LINUX_FALLOCATE */
default:
*/
if (!td->o.fill_device) {
dprint(FD_FILE, "truncate file %s, size %llu\n", f->file_name,
- f->real_file_size);
+ (unsigned long long) f->real_file_size);
if (ftruncate(f->fd, f->real_file_size) == -1) {
td_verror(td, errno, "ftruncate");
goto err;
seed = td->rand_seeds[4];
if (td->o.random_distribution == FIO_RAND_DIST_ZIPF)
- zipf_init(&f->zipf, nranges, td->o.zipf_theta, seed);
+ zipf_init(&f->zipf, nranges, td->o.zipf_theta.u.f, seed);
else
- pareto_init(&f->zipf, nranges, td->o.pareto_h, seed);
+ pareto_init(&f->zipf, nranges, td->o.pareto_h.u.f, seed);
return 1;
}
struct thread_data;
#include "compiler/compiler.h"
+#include "thread_options.h"
#include "flist.h"
#include "fifo.h"
-#include "rbtree.h"
+#include "lib/rbtree.h"
#include "arch/arch.h"
#include "os/os.h"
#include "mutex.h"
#include "gettime.h"
#include "lib/getopt.h"
#include "lib/rand.h"
+#include "client.h"
#include "server.h"
#include "stat.h"
#include "flow.h"
#define MPOL_LOCAL MPOL_MAX
#endif
-/*
- * What type of allocation to use for io buffers
- */
-enum fio_memtype {
- MEM_MALLOC = 0, /* ordinary malloc */
- MEM_SHM, /* use shared memory segments */
- MEM_SHMHUGE, /* use shared memory segments with huge pages */
- MEM_MMAP, /* use anonynomous mmap */
- MEM_MMAPHUGE, /* memory mapped huge file */
-};
-
/*
* offset generator types
*/
RW_SEQ_IDENT,
};
-/*
- * What type of errors to continue on when continue_on_error is used
- */
-enum error_type_bit {
- ERROR_TYPE_READ_BIT = 0,
- ERROR_TYPE_WRITE_BIT = 1,
- ERROR_TYPE_VERIFY_BIT = 2,
- ERROR_TYPE_CNT = 3,
-};
-
-enum error_type {
- ERROR_TYPE_NONE = 0,
- ERROR_TYPE_READ = 1 << ERROR_TYPE_READ_BIT,
- ERROR_TYPE_WRITE = 1 << ERROR_TYPE_WRITE_BIT,
- ERROR_TYPE_VERIFY = 1 << ERROR_TYPE_VERIFY_BIT,
- ERROR_TYPE_ANY = 0xffff,
-};
-
-struct bssplit {
- unsigned int bs;
- unsigned char perc;
-};
-
-struct thread_options {
- int pad;
- char *description;
- char *name;
- char *directory;
- char *filename;
- char *opendir;
- char *ioengine;
- enum td_ddir td_ddir;
- unsigned int rw_seq;
- unsigned int kb_base;
- unsigned int ddir_seq_nr;
- long ddir_seq_add;
- unsigned int iodepth;
- unsigned int iodepth_low;
- unsigned int iodepth_batch;
- unsigned int iodepth_batch_complete;
-
- unsigned long long size;
- unsigned int size_percent;
- unsigned int fill_device;
- unsigned long long file_size_low;
- unsigned long long file_size_high;
- unsigned long long start_offset;
-
- unsigned int bs[DDIR_RWDIR_CNT];
- unsigned int ba[DDIR_RWDIR_CNT];
- unsigned int min_bs[DDIR_RWDIR_CNT];
- unsigned int max_bs[DDIR_RWDIR_CNT];
- struct bssplit *bssplit[DDIR_RWDIR_CNT];
- unsigned int bssplit_nr[DDIR_RWDIR_CNT];
-
- int *ignore_error[ERROR_TYPE_CNT];
- unsigned int ignore_error_nr[ERROR_TYPE_CNT];
- unsigned int error_dump;
-
- unsigned int nr_files;
- unsigned int open_files;
- enum file_lock_mode file_lock_mode;
- unsigned int lockfile_batch;
-
- unsigned int odirect;
- unsigned int invalidate_cache;
- unsigned int create_serialize;
- unsigned int create_fsync;
- unsigned int create_on_open;
- unsigned int create_only;
- unsigned int end_fsync;
- unsigned int pre_read;
- unsigned int sync_io;
- unsigned int verify;
- unsigned int do_verify;
- unsigned int verifysort;
- unsigned int verifysort_nr;
- unsigned int verify_interval;
- unsigned int verify_offset;
- char verify_pattern[MAX_PATTERN_SIZE];
- unsigned int verify_pattern_bytes;
- unsigned int verify_fatal;
- unsigned int verify_dump;
- unsigned int verify_async;
- unsigned long long verify_backlog;
- unsigned int verify_batch;
- unsigned int experimental_verify;
- unsigned int use_thread;
- unsigned int unlink;
- unsigned int do_disk_util;
- unsigned int override_sync;
- unsigned int rand_repeatable;
- unsigned int use_os_rand;
- unsigned int write_lat_log;
- unsigned int write_bw_log;
- unsigned int write_iops_log;
- unsigned int log_avg_msec;
- unsigned int norandommap;
- unsigned int softrandommap;
- unsigned int bs_unaligned;
- unsigned int fsync_on_close;
-
- unsigned int random_distribution;
- double zipf_theta;
- double pareto_h;
-
- unsigned int random_generator;
-
- unsigned int hugepage_size;
- unsigned int rw_min_bs;
- unsigned int thinktime;
- unsigned int thinktime_spin;
- unsigned int thinktime_blocks;
- unsigned int fsync_blocks;
- unsigned int fdatasync_blocks;
- unsigned int barrier_blocks;
- unsigned long long start_delay;
- unsigned long long timeout;
- unsigned long long ramp_time;
- unsigned int overwrite;
- unsigned int bw_avg_time;
- unsigned int iops_avg_time;
- unsigned int loops;
- unsigned long long zone_range;
- unsigned long long zone_size;
- unsigned long long zone_skip;
- enum fio_memtype mem_type;
- unsigned int mem_align;
-
- unsigned int max_latency;
-
- unsigned int stonewall;
- unsigned int new_group;
- unsigned int numjobs;
- os_cpu_mask_t cpumask;
- unsigned int cpumask_set;
- os_cpu_mask_t verify_cpumask;
- unsigned int verify_cpumask_set;
-#ifdef CONFIG_LIBNUMA
- struct bitmask *numa_cpunodesmask;
- unsigned int numa_cpumask_set;
- unsigned short numa_mem_mode;
- unsigned int numa_mem_prefer_node;
- struct bitmask *numa_memnodesmask;
- unsigned int numa_memmask_set;
-#endif
- unsigned int iolog;
- unsigned int rwmixcycle;
- unsigned int rwmix[2];
- unsigned int nice;
- unsigned int file_service_type;
- unsigned int group_reporting;
- unsigned int fadvise_hint;
- enum fio_fallocate_mode fallocate_mode;
- unsigned int zero_buffers;
- unsigned int refill_buffers;
- unsigned int scramble_buffers;
- unsigned int compress_percentage;
- unsigned int compress_chunk;
- unsigned int time_based;
- unsigned int disable_lat;
- unsigned int disable_clat;
- unsigned int disable_slat;
- unsigned int disable_bw;
- unsigned int unified_rw_rep;
- unsigned int gtod_reduce;
- unsigned int gtod_cpu;
- unsigned int gtod_offload;
- enum fio_cs clocksource;
- unsigned int no_stall;
- unsigned int trim_percentage;
- unsigned int trim_batch;
- unsigned int trim_zero;
- unsigned long long trim_backlog;
- unsigned int clat_percentiles;
- unsigned int percentile_precision; /* digits after decimal for percentiles */
- fio_fp64_t percentile_list[FIO_IO_U_LIST_MAX_LEN];
-
- char *read_iolog_file;
- char *write_iolog_file;
- char *bw_log_file;
- char *lat_log_file;
- char *iops_log_file;
- char *replay_redirect;
-
- /*
- * Pre-run and post-run shell
- */
- char *exec_prerun;
- char *exec_postrun;
-
- unsigned int rate[DDIR_RWDIR_CNT];
- unsigned int ratemin[DDIR_RWDIR_CNT];
- unsigned int ratecycle;
- unsigned int rate_iops[DDIR_RWDIR_CNT];
- unsigned int rate_iops_min[DDIR_RWDIR_CNT];
-
- char *ioscheduler;
-
- /*
- * CPU "io" cycle burner
- */
- unsigned int cpuload;
- unsigned int cpucycle;
-
- /*
- * I/O Error handling
- */
- enum error_type continue_on_error;
-
- /*
- * Benchmark profile type
- */
- char *profile;
-
- /*
- * blkio cgroup support
- */
- char *cgroup;
- unsigned int cgroup_weight;
- unsigned int cgroup_nodelete;
-
- unsigned int uid;
- unsigned int gid;
-
- int flow_id;
- int flow;
- int flow_watermark;
- unsigned int flow_sleep;
-
- unsigned long long offset_increment;
-
- unsigned int sync_file_range;
-};
-
enum {
TD_F_VER_BACKLOG = 1,
TD_F_TRIM_BACKLOG = 2,
void *eo;
char verror[FIO_VERROR_SIZE];
pthread_t thread;
- int thread_number;
- int groupid;
+ unsigned int thread_number;
+ unsigned int groupid;
struct thread_stat ts;
+ int client_type;
+
struct io_log *slat_log;
struct io_log *clat_log;
struct io_log *lat_log;
size_t orig_buffer_size;
volatile int terminate;
volatile int runstate;
- unsigned int ioprio;
- unsigned int ioprio_set;
unsigned int last_was_sync;
enum fio_ddir last_ddir;
- char *mmapfile;
int mmapfd;
void *iolog_buf;
*/
struct prof_io_ops prof_io_ops;
void *prof_data;
+
+ void *pinned_mem;
};
/*
extern int exitall_on_terminate;
extern unsigned int thread_number;
extern unsigned int stat_number;
-extern unsigned int nr_process, nr_thread;
extern int shm_id;
extern int groupid;
extern int output_format;
extern int temp_stall_ts;
-extern unsigned long long mlock_size;
extern uintptr_t page_mask, page_size;
extern int read_only;
extern int eta_print;
+ extern int eta_new_line;
extern unsigned long done_secs;
extern char *job_section;
extern int fio_gtod_offload;
/*
* Init/option functions
*/
+extern int __must_check fio_init_options(void);
extern int __must_check parse_options(int, char **);
-extern int parse_jobs_ini(char *, int, int);
-extern int parse_cmd_line(int, char **);
+extern int parse_jobs_ini(char *, int, int, int);
+extern int parse_cmd_line(int, char **, int);
extern int fio_backend(void);
extern void reset_fio_state(void);
extern void clear_io_state(struct thread_data *);
extern void fio_options_mem_dupe(struct thread_data *);
extern void options_mem_dupe(void *data, struct fio_option *options);
extern void td_fill_rand_seeds(struct thread_data *);
-extern void add_job_opts(const char **);
+extern void add_job_opts(const char **, int);
extern char *num2str(unsigned long, int, int, int);
extern int ioengine_load(struct thread_data *);
+extern unsigned long page_mask;
+extern unsigned long page_size;
+extern int initialize_fio(char *envp[]);
+
#define FIO_GETOPT_JOB 0x89000000
#define FIO_GETOPT_IOENGINE 0x98000000
#define FIO_NR_OPTIONS (FIO_MAX_OPTS + 128)
*/
extern void print_thread_status(void);
extern void print_status_init(int);
+extern char *fio_uint_to_kmg(unsigned int val);
/*
* Thread life cycle. Once a thread has a runstate beyond TD_INITIALIZED, it
/*
* Memory helpers
*/
-extern int __must_check fio_pin_memory(void);
-extern void fio_unpin_memory(void);
+extern int __must_check fio_pin_memory(struct thread_data *);
+extern void fio_unpin_memory(struct thread_data *);
extern int __must_check allocate_io_mem(struct thread_data *);
extern void free_io_mem(struct thread_data *);
+extern void free_threads_shm(void);
/*
* Reset stats after ramp time completes
extern const char *fio_get_arch_string(int);
extern const char *fio_get_os_string(int);
+#define ARRAY_SIZE(x) (sizeof((x)) / (sizeof((x)[0])))
+
enum {
FIO_OUTPUT_TERSE = 0,
FIO_OUTPUT_JSON,
int exitall_on_terminate = 0;
int output_format = FIO_OUTPUT_NORMAL;
int eta_print = FIO_ETA_AUTO;
+ int eta_new_line = 0;
+ unsigned long long mlock_size = 0;
FILE *f_out = NULL;
FILE *f_err = NULL;
char **job_sections = NULL;
.has_arg = required_argument,
.val = 'e' | FIO_CLIENT_FLAG,
},
+ {
+ .name = (char *) "eta-newline",
+ .has_arg = required_argument,
+ .val = 'E' | FIO_CLIENT_FLAG,
+ },
{
.name = (char *) "debug",
.has_arg = required_argument,
},
};
-static void free_shm(void)
+void free_threads_shm(void)
{
struct shmid_ds sbuf;
void *tp = threads;
threads = NULL;
+ shmdt(tp);
+ shmctl(shm_id, IPC_RMID, &sbuf);
+ shm_id = -1;
+ }
+}
+
+void free_shm(void)
+{
+ if (threads) {
file_hash_exit();
flow_exit();
fio_debug_jobp = NULL;
- shmdt(tp);
- shmctl(shm_id, IPC_RMID, &sbuf);
+ free_threads_shm();
}
scleanup();
/*
* This function leaks the buffer
*/
-static char *to_kmg(unsigned int val)
+char *fio_uint_to_kmg(unsigned int val)
{
char *buf = malloc(32);
char post[] = { 0, 'K', 'M', 'G', 'P', 'E', 0 };
* to make sure we don't have conflicts, and initializes various
* members of td.
*/
-static int add_job(struct thread_data *td, const char *jobname, int job_add_num)
+static int add_job(struct thread_data *td, const char *jobname, int job_add_num,
+ int recursed, int client_type)
{
- const char *ddir_str[] = { NULL, "read", "write", "rw", NULL,
- "randread", "randwrite", "randrw",
- "trim", NULL, NULL, NULL, "randtrim" };
unsigned int i;
char fname[PATH_MAX];
int numjobs, file_alloced;
return 0;
}
+ td->client_type = client_type;
+
if (profile_td_init(td))
goto err;
if (ioengine_load(td))
goto err;
- if (td->o.use_thread)
- nr_thread++;
- else
- nr_process++;
-
if (td->o.odirect)
td->io_ops->flags |= FIO_RAWIO;
if (setup_rate(td))
goto err;
- if (td->o.write_lat_log) {
- setup_log(&td->lat_log, td->o.log_avg_msec);
- setup_log(&td->slat_log, td->o.log_avg_msec);
- setup_log(&td->clat_log, td->o.log_avg_msec);
+ if (td->o.lat_log_file) {
+ setup_log(&td->lat_log, td->o.log_avg_msec, IO_LOG_TYPE_LAT);
+ setup_log(&td->slat_log, td->o.log_avg_msec, IO_LOG_TYPE_SLAT);
+ setup_log(&td->clat_log, td->o.log_avg_msec, IO_LOG_TYPE_CLAT);
}
- if (td->o.write_bw_log)
- setup_log(&td->bw_log, td->o.log_avg_msec);
- if (td->o.write_iops_log)
- setup_log(&td->iops_log, td->o.log_avg_msec);
+ if (td->o.bw_log_file)
+ setup_log(&td->bw_log, td->o.log_avg_msec, IO_LOG_TYPE_BW);
+ if (td->o.iops_log_file)
+ setup_log(&td->iops_log, td->o.log_avg_msec, IO_LOG_TYPE_IOPS);
if (!td->o.name)
td->o.name = strdup(jobname);
if (output_format == FIO_OUTPUT_NORMAL) {
if (!job_add_num) {
- if (!strcmp(td->io_ops->name, "cpuio")) {
- log_info("%s: ioengine=cpu, cpuload=%u,"
- " cpucycle=%u\n", td->o.name,
- td->o.cpuload,
- td->o.cpucycle);
- } else {
+ if (is_backend && !recursed)
+ fio_server_send_add_job(td);
+
+ if (!(td->io_ops->flags & FIO_NOIO)) {
char *c1, *c2, *c3, *c4, *c5, *c6;
- c1 = to_kmg(td->o.min_bs[DDIR_READ]);
- c2 = to_kmg(td->o.max_bs[DDIR_READ]);
- c3 = to_kmg(td->o.min_bs[DDIR_WRITE]);
- c4 = to_kmg(td->o.max_bs[DDIR_WRITE]);
- c5 = to_kmg(td->o.min_bs[DDIR_TRIM]);
- c6 = to_kmg(td->o.max_bs[DDIR_TRIM]);
+ c1 = fio_uint_to_kmg(td->o.min_bs[DDIR_READ]);
+ c2 = fio_uint_to_kmg(td->o.max_bs[DDIR_READ]);
+ c3 = fio_uint_to_kmg(td->o.min_bs[DDIR_WRITE]);
+ c4 = fio_uint_to_kmg(td->o.max_bs[DDIR_WRITE]);
+ c5 = fio_uint_to_kmg(td->o.min_bs[DDIR_TRIM]);
+ c6 = fio_uint_to_kmg(td->o.max_bs[DDIR_TRIM]);
log_info("%s: (g=%d): rw=%s, bs=%s-%s/%s-%s/%s-%s,"
" ioengine=%s, iodepth=%u\n",
td->o.name, td->groupid,
- ddir_str[td->o.td_ddir],
+ ddir_str(td->o.td_ddir),
c1, c2, c3, c4, c5, c6,
td->io_ops->name,
td->o.iodepth);
job_add_num = numjobs - 1;
- if (add_job(td_new, jobname, job_add_num))
+ if (add_job(td_new, jobname, job_add_num, 1, client_type))
goto err;
}
/*
* Parse as if 'o' was a command line
*/
-void add_job_opts(const char **o)
+void add_job_opts(const char **o, int client_type)
{
struct thread_data *td, *td_parent;
int i, in_global = 1;
if (!strncmp(o[i], "name", 4)) {
in_global = 0;
if (td)
- add_job(td, jobname, 0);
+ add_job(td, jobname, 0, 0, client_type);
td = NULL;
sprintf(jobname, "%s", o[i] + 5);
}
}
if (td)
- add_job(td, jobname, 0);
+ add_job(td, jobname, 0, 0, client_type);
}
static int skip_this_section(const char *name)
/*
* This is our [ini] type file parser.
*/
-int parse_jobs_ini(char *file, int is_buf, int stonewall_flag)
+int parse_jobs_ini(char *file, int is_buf, int stonewall_flag, int type)
{
unsigned int global;
struct thread_data *td;
for (i = 0; i < num_opts; i++)
log_info("--%s ", opts[i]);
- ret = add_job(td, name, 0);
+ ret = add_job(td, name, 0, 0, type);
} else {
log_err("fio: job %s dropped\n", name);
put_job(td);
printf(" --showcmd\t\tTurn a job file into command line options\n");
printf(" --eta=when\t\tWhen ETA estimate should be printed\n");
printf(" \t\tMay be \"always\", \"never\" or \"auto\"\n");
+ printf(" --eta-newline=time\tForce a new line for every 'time'");
+ printf(" period passed\n");
printf(" --readonly\t\tTurn on safety read-only checks, preventing"
" writes\n");
printf(" --section=name\tOnly run specified section in job file\n");
#ifdef FIO_INC_DEBUG
struct debug_level debug_levels[] = {
- { .name = "process", .shift = FD_PROCESS, },
- { .name = "file", .shift = FD_FILE, },
- { .name = "io", .shift = FD_IO, },
- { .name = "mem", .shift = FD_MEM, },
- { .name = "blktrace", .shift = FD_BLKTRACE },
- { .name = "verify", .shift = FD_VERIFY },
- { .name = "random", .shift = FD_RANDOM },
- { .name = "parse", .shift = FD_PARSE },
- { .name = "diskutil", .shift = FD_DISKUTIL },
- { .name = "job", .shift = FD_JOB },
- { .name = "mutex", .shift = FD_MUTEX },
- { .name = "profile", .shift = FD_PROFILE },
- { .name = "time", .shift = FD_TIME },
- { .name = "net", .shift = FD_NET },
+ { .name = "process",
+ .help = "Process creation/exit logging",
+ .shift = FD_PROCESS,
+ },
+ { .name = "file",
+ .help = "File related action logging",
+ .shift = FD_FILE,
+ },
+ { .name = "io",
+ .help = "IO and IO engine action logging (offsets, queue, completions, etc)",
+ .shift = FD_IO,
+ },
+ { .name = "mem",
+ .help = "Memory allocation/freeing logging",
+ .shift = FD_MEM,
+ },
+ { .name = "blktrace",
+ .help = "blktrace action logging",
+ .shift = FD_BLKTRACE,
+ },
+ { .name = "verify",
+ .help = "IO verification action logging",
+ .shift = FD_VERIFY,
+ },
+ { .name = "random",
+ .help = "Random generation logging",
+ .shift = FD_RANDOM,
+ },
+ { .name = "parse",
+ .help = "Parser logging",
+ .shift = FD_PARSE,
+ },
+ { .name = "diskutil",
+ .help = "Disk utility logging actions",
+ .shift = FD_DISKUTIL,
+ },
+ { .name = "job",
+ .help = "Logging related to creating/destroying jobs",
+ .shift = FD_JOB,
+ },
+ { .name = "mutex",
+ .help = "Mutex logging",
+ .shift = FD_MUTEX
+ },
+ { .name = "profile",
+ .help = "Logging related to profiles",
+ .shift = FD_PROFILE,
+ },
+ { .name = "time",
+ .help = "Logging related to time keeping functions",
+ .shift = FD_TIME,
+ },
+ { .name = "net",
+ .help = "Network logging",
+ .shift = FD_NET,
+ },
{ .name = NULL, },
};
fio_client_add_cmd_option(client, opt);
}
-int parse_cmd_line(int argc, char *argv[])
+int parse_cmd_line(int argc, char *argv[], int client_type)
{
struct thread_data *td = NULL;
int c, ini_idx = 0, lidx, ret = 0, do_exit = 0, exit_val = 0;
else if (!strcmp("never", optarg))
eta_print = FIO_ETA_NEVER;
break;
+ case 'E': {
+ long long t = 0;
+
+ if (str_to_decimal(optarg, &t, 0, NULL)) {
+ log_err("fio: failed parsing eta time %s\n", optarg);
+ exit_val = 1;
+ do_exit++;
+ }
+ eta_new_line = t;
+ break;
+ }
case 'd':
if (set_debug(optarg))
do_exit++;
char *val = optarg;
if (!strncmp(opt, "name", 4) && td) {
- ret = add_job(td, td->o.name ?: "fio", 0);
+ ret = add_job(td, td->o.name ?: "fio", 0, 0, client_type);
if (ret)
return 0;
td = NULL;
exit_val = 1;
break;
}
- if (fio_client_add(optarg, &cur_client)) {
+ if (fio_client_add(&fio_client_ops, optarg, &cur_client)) {
log_err("fio: failed adding client %s\n", optarg);
do_exit++;
exit_val = 1;
if (td) {
if (!ret)
- ret = add_job(td, td->o.name ?: "fio", 0);
+ ret = add_job(td, td->o.name ?: "fio", 0, 0, client_type);
}
while (!ret && optind < argc) {
return ini_idx;
}
-int parse_options(int argc, char *argv[])
+int fio_init_options(void)
{
- int job_files, i;
-
f_out = stdout;
f_err = stderr;
if (fill_def_thread())
return 1;
- job_files = parse_cmd_line(argc, argv);
+ return 0;
+}
+
+extern int fio_check_options(struct thread_options *);
+
+int parse_options(int argc, char *argv[])
+{
+ const int type = FIO_CLIENT_TYPE_CLI;
+ int job_files, i;
+
+ if (fio_init_options())
+ return 1;
+ if (fio_test_cconv(&def_thread.o))
+ log_err("fio: failed internal cconv test\n");
+
+ job_files = parse_cmd_line(argc, argv, type);
if (job_files > 0) {
for (i = 0; i < job_files; i++) {
return 1;
free(ini_file[i]);
} else if (!is_backend) {
- if (parse_jobs_ini(ini_file[i], 0, i))
+ if (parse_jobs_ini(ini_file[i], 0, i, type))
return 1;
free(ini_file[i]);
}
return 0;
}
+
+void options_default_fill(struct thread_options *o)
+{
+ memcpy(o, &def_thread.o, sizeof(*o));
+}
#include "fio.h"
-static void *pinned_mem;
-
-void fio_unpin_memory(void)
+void fio_unpin_memory(struct thread_data *td)
{
- if (pinned_mem) {
- dprint(FD_MEM, "unpinning %llu bytes\n", mlock_size);
- if (munlock(pinned_mem, mlock_size) < 0)
+ if (td->pinned_mem) {
+ dprint(FD_MEM, "unpinning %llu bytes\n", td->o.lockmem);
+ if (munlock(td->pinned_mem, td->o.lockmem) < 0)
perror("munlock");
- munmap(pinned_mem, mlock_size);
- pinned_mem = NULL;
+ munmap(td->pinned_mem, td->o.lockmem);
+ td->pinned_mem = NULL;
}
}
-int fio_pin_memory(void)
+int fio_pin_memory(struct thread_data *td)
{
unsigned long long phys_mem;
- if (!mlock_size)
+ if (!td->o.lockmem)
return 0;
- dprint(FD_MEM, "pinning %llu bytes\n", mlock_size);
+ dprint(FD_MEM, "pinning %llu bytes\n", td->o.lockmem);
/*
* Don't allow mlock of more than real_mem-128MB
*/
phys_mem = os_phys_mem();
if (phys_mem) {
- if ((mlock_size + 128 * 1024 * 1024) > phys_mem) {
- mlock_size = phys_mem - 128 * 1024 * 1024;
+ if ((td->o.lockmem + 128 * 1024 * 1024) > phys_mem) {
+ td->o.lockmem = phys_mem - 128 * 1024 * 1024;
log_info("fio: limiting mlocked memory to %lluMB\n",
- mlock_size >> 20);
+ td->o.lockmem >> 20);
}
}
- pinned_mem = mmap(NULL, mlock_size, PROT_READ | PROT_WRITE,
+ td->pinned_mem = mmap(NULL, td->o.lockmem, PROT_READ | PROT_WRITE,
MAP_PRIVATE | OS_MAP_ANON, -1, 0);
- if (pinned_mem == MAP_FAILED) {
+ if (td->pinned_mem == MAP_FAILED) {
perror("malloc locked mem");
- pinned_mem = NULL;
+ td->pinned_mem = NULL;
return 1;
}
- if (mlock(pinned_mem, mlock_size) < 0) {
+ if (mlock(td->pinned_mem, td->o.lockmem) < 0) {
perror("mlock");
- munmap(pinned_mem, mlock_size);
- pinned_mem = NULL;
+ munmap(td->pinned_mem, td->o.lockmem);
+ td->pinned_mem = NULL;
return 1;
}
unsigned long mask = td->o.hugepage_size - 1;
/* TODO: make sure the file is a real hugetlbfs file */
- if (!td->mmapfile)
+ if (!td->o.mmapfile)
flags |= MAP_HUGETLB;
total_mem = (total_mem + mask) & ~mask;
}
- if (td->mmapfile) {
- td->mmapfd = open(td->mmapfile, O_RDWR|O_CREAT, 0644);
+ if (td->o.mmapfile) {
+ td->mmapfd = open(td->o.mmapfile, O_RDWR|O_CREAT, 0644);
if (td->mmapfd < 0) {
td_verror(td, errno, "open mmap file");
td->orig_buffer = mmap(NULL, total_mem, PROT_READ | PROT_WRITE, flags,
td->mmapfd, 0);
- dprint(FD_MEM, "mmap %u/%d %p\n", total_mem, td->mmapfd,
- td->orig_buffer);
+ dprint(FD_MEM, "mmap %llu/%d %p\n", (unsigned long long) total_mem,
+ td->mmapfd, td->orig_buffer);
if (td->orig_buffer == MAP_FAILED) {
td_verror(td, errno, "mmap");
td->orig_buffer = NULL;
if (td->mmapfd) {
close(td->mmapfd);
- unlink(td->mmapfile);
+ unlink(td->o.mmapfile);
}
return 1;
static void free_mem_mmap(struct thread_data *td, size_t total_mem)
{
- dprint(FD_MEM, "munmap %u %p\n", total_mem, td->orig_buffer);
+ dprint(FD_MEM, "munmap %llu %p\n", (unsigned long long) total_mem,
+ td->orig_buffer);
munmap(td->orig_buffer, td->orig_buffer_size);
- if (td->mmapfile) {
+ if (td->o.mmapfile) {
close(td->mmapfd);
- unlink(td->mmapfile);
- free(td->mmapfile);
+ unlink(td->o.mmapfile);
+ free(td->o.mmapfile);
}
}
static int alloc_mem_malloc(struct thread_data *td, size_t total_mem)
{
td->orig_buffer = malloc(total_mem);
- dprint(FD_MEM, "malloc %u %p\n", total_mem, td->orig_buffer);
+ dprint(FD_MEM, "malloc %llu %p\n", (unsigned long long) total_mem,
+ td->orig_buffer);
return td->orig_buffer == NULL;
}
total_mem += td->o.mem_align - page_size;
}
- dprint(FD_MEM, "Alloc %lu for buffers\n", (size_t) total_mem);
+ dprint(FD_MEM, "Alloc %llu for buffers\n", (unsigned long long) total_mem);
if (td->o.mem_type == MEM_MALLOC)
ret = alloc_mem_malloc(td, total_mem);
#include <netdb.h>
#include <syslog.h>
#include <signal.h>
+#include <zlib.h>
#include "fio.h"
#include "server.h"
#include "crc/crc16.h"
#include "lib/ieee754.h"
-int fio_net_port = 8765;
+int fio_net_port = FIO_NET_PORT;
int exit_backend = 0;
static char *bind_sock;
static struct sockaddr_in saddr_in;
static struct sockaddr_in6 saddr_in6;
-static int first_cmd_check;
static int use_ipv6;
+struct fio_fork_item {
+ struct flist_head list;
+ int exitval;
+ int signal;
+ int exited;
+ pid_t pid;
+};
+
+/* Created on fork on new connection */
+static FLIST_HEAD(conn_list);
+
+/* Created on job fork from connection */
+static FLIST_HEAD(job_list);
+
static const char *fio_server_ops[FIO_NET_CMD_NR] = {
"",
"QUIT",
"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)
+static void add_reply(uint64_t tag, struct flist_head *list)
+{
+ struct fio_net_cmd_reply *reply = (struct fio_net_cmd_reply *) tag;
+
+ flist_add_tail(&reply->list, list);
+}
+
+static uint64_t alloc_reply(uint64_t tag, uint16_t opcode)
+{
+ struct fio_net_cmd_reply *reply;
+
+ reply = calloc(1, sizeof(*reply));
+ INIT_FLIST_HEAD(&reply->list);
+ gettimeofday(&reply->tv, NULL);
+ reply->saved_tag = tag;
+ reply->opcode = opcode;
+
+ return (uintptr_t) reply;
+}
+
+static void free_reply(uint64_t tag)
+{
+ struct fio_net_cmd_reply *reply = (struct fio_net_cmd_reply *) tag;
+
+ free(reply);
+}
+
+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,
- uint64_t tag)
+ uint64_t *tagptr, struct flist_head *list)
{
struct fio_net_cmd *cmd = NULL;
size_t this_len, cur_len = 0;
+ uint64_t tag;
int ret;
+ if (list) {
+ assert(tagptr);
+ tag = *tagptr = alloc_reply(*tagptr, opcode);
+ } else
+ tag = tagptr ? *tagptr : 0;
+
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)
buf += this_len;
} while (!ret && size);
+ if (list) {
+ if (ret)
+ free_reply(tag);
+ else
+ add_reply(tag, list);
+ }
+
if (cmd)
free(cmd);
int fio_net_send_simple_cmd(int sk, uint16_t opcode, uint64_t tag,
struct flist_head *list)
{
- struct fio_net_int_cmd *cmd;
int ret;
- if (!list)
- return fio_net_send_simple_stack_cmd(sk, opcode, tag);
-
- cmd = malloc(sizeof(*cmd));
-
- fio_init_net_cmd(&cmd->cmd, opcode, NULL, 0, (uintptr_t) cmd);
- fio_net_cmd_crc(&cmd->cmd);
+ if (list)
+ tag = alloc_reply(tag, opcode);
- INIT_FLIST_HEAD(&cmd->list);
- fio_gettime(&cmd->tv, NULL);
- cmd->saved_tag = tag;
-
- ret = fio_send_data(sk, &cmd->cmd, sizeof(cmd->cmd));
+ ret = fio_net_send_simple_stack_cmd(sk, opcode, tag);
if (ret) {
- free(cmd);
+ if (list)
+ free_reply(tag);
+
return ret;
}
- flist_add_tail(&cmd->list, list);
+ if (list)
+ add_reply(tag, list);
+
return 0;
}
-static int fio_server_send_quit_cmd(void)
+int fio_net_send_quit(int sk)
{
dprint(FD_NET, "server: sending quit\n");
- return fio_net_send_simple_cmd(server_fd, FIO_NET_CMD_QUIT, 0, NULL);
+
+ return fio_net_send_simple_cmd(sk, FIO_NET_CMD_QUIT, 0, NULL);
}
-static int handle_job_cmd(struct fio_net_cmd *cmd)
+static int fio_net_send_ack(int sk, struct fio_net_cmd *cmd, int error,
+ int signal)
{
- char *buf = (char *) cmd->payload;
- struct cmd_start_pdu spdu;
struct cmd_end_pdu epdu;
- int ret;
+ uint64_t tag = 0;
- if (parse_jobs_ini(buf, 1, 0)) {
- fio_server_send_quit_cmd();
- return -1;
+ if (cmd)
+ tag = cmd->tag;
+
+ epdu.error = __cpu_to_le32(error);
+ epdu.signal = __cpu_to_le32(signal);
+ return fio_net_send_cmd(sk, FIO_NET_CMD_STOP, &epdu, sizeof(epdu), &tag, NULL);
+}
+
+int fio_net_send_stop(int sk, int error, int signal)
+{
+ dprint(FD_NET, "server: sending stop (%d, %d)\n", error, signal);
+ return fio_net_send_ack(sk, NULL, error, signal);
+}
+
+static void fio_server_add_fork_item(pid_t pid, struct flist_head *list)
+{
+ struct fio_fork_item *ffi;
+
+ ffi = malloc(sizeof(*ffi));
+ ffi->exitval = 0;
+ ffi->signal = 0;
+ ffi->exited = 0;
+ ffi->pid = pid;
+ flist_add_tail(&ffi->list, list);
+}
+
+static void fio_server_add_conn_pid(pid_t pid)
+{
+ dprint(FD_NET, "server: forked off connection job (pid=%u)\n", pid);
+ fio_server_add_fork_item(pid, &conn_list);
+}
+
+static void fio_server_add_job_pid(pid_t pid)
+{
+ dprint(FD_NET, "server: forked off job job (pid=%u)\n", pid);
+ fio_server_add_fork_item(pid, &job_list);
+}
+
+static void fio_server_check_fork_item(struct fio_fork_item *ffi)
+{
+ int ret, status;
+
+ ret = waitpid(ffi->pid, &status, WNOHANG);
+ if (ret < 0) {
+ if (errno == ECHILD) {
+ log_err("fio: connection pid %u disappeared\n", ffi->pid);
+ ffi->exited = 1;
+ } else
+ log_err("fio: waitpid: %s\n", strerror(errno));
+ } else if (ret == ffi->pid) {
+ if (WIFSIGNALED(status)) {
+ ffi->signal = WTERMSIG(status);
+ ffi->exited = 1;
+ }
+ if (WIFEXITED(status)) {
+ if (WEXITSTATUS(status))
+ ffi->exitval = WEXITSTATUS(status);
+ ffi->exited = 1;
+ }
}
+}
- spdu.jobs = cpu_to_le32(thread_number);
- spdu.stat_outputs = cpu_to_le32(stat_number);
- fio_net_send_cmd(server_fd, FIO_NET_CMD_START, &spdu, sizeof(spdu), 0);
+static void fio_server_fork_item_done(struct fio_fork_item *ffi)
+{
+ dprint(FD_NET, "pid %u exited, sig=%u, exitval=%d\n", ffi->pid, ffi->signal, ffi->exitval);
+
+ /*
+ * Fold STOP and QUIT...
+ */
+ fio_net_send_stop(server_fd, ffi->exitval, ffi->signal);
+ fio_net_send_quit(server_fd);
+ flist_del(&ffi->list);
+ free(ffi);
+}
+
+static void fio_server_check_fork_items(struct flist_head *list)
+{
+ struct flist_head *entry, *tmp;
+ struct fio_fork_item *ffi;
+
+ flist_for_each_safe(entry, tmp, list) {
+ ffi = flist_entry(entry, struct fio_fork_item, list);
+
+ fio_server_check_fork_item(ffi);
+
+ if (ffi->exited)
+ fio_server_fork_item_done(ffi);
+ }
+}
+
+static void fio_server_check_jobs(void)
+{
+ fio_server_check_fork_items(&job_list);
+}
+
+static void fio_server_check_conns(void)
+{
+ fio_server_check_fork_items(&conn_list);
+}
+
+static int handle_run_cmd(struct fio_net_cmd *cmd)
+{
+ pid_t pid;
+ int ret;
+
+ set_genesis_time();
+
+ pid = fork();
+ if (pid) {
+ fio_server_add_job_pid(pid);
+ return 0;
+ }
ret = fio_backend();
+ free_threads_shm();
+ _exit(ret);
+}
- epdu.error = ret;
- fio_net_send_cmd(server_fd, FIO_NET_CMD_STOP, &epdu, sizeof(epdu), 0);
+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;
- fio_server_send_quit_cmd();
- reset_fio_state();
- return ret;
+ 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_net_send_quit(server_fd);
+ return -1;
+ }
+
+ spdu.jobs = cpu_to_le32(thread_number);
+ spdu.stat_outputs = cpu_to_le32(stat_number);
+ fio_net_send_cmd(server_fd, FIO_NET_CMD_START, &spdu, sizeof(spdu), NULL, NULL);
+ return 0;
}
static int handle_jobline_cmd(struct fio_net_cmd *cmd)
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)) {
- fio_server_send_quit_cmd();
+ if (parse_cmd_line(clp->lines, argv, clp->client_type)) {
+ fio_net_send_quit(server_fd);
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);
+ spdu.stat_outputs = cpu_to_le32(stat_number);
+ fio_net_send_cmd(server_fd, FIO_NET_CMD_START, &spdu, sizeof(spdu), NULL, NULL);
+ return 0;
}
static int handle_probe_cmd(struct fio_net_cmd *cmd)
{
struct cmd_probe_pdu probe;
+ uint64_t tag = cmd->tag;
dprint(FD_NET, "server: sending probe reply\n");
probe.os = FIO_OS;
probe.arch = FIO_ARCH;
-
probe.bpp = sizeof(void *);
+ probe.cpus = __cpu_to_le32(cpus_online());
+ probe.flags = 0;
- return fio_net_send_cmd(server_fd, FIO_NET_CMD_PROBE, &probe, sizeof(probe), cmd->tag);
+ return fio_net_send_cmd(server_fd, FIO_NET_CMD_PROBE, &probe, sizeof(probe), &tag, NULL);
}
static int handle_send_eta_cmd(struct fio_net_cmd *cmd)
{
struct jobs_eta *je;
size_t size;
+ uint64_t tag = cmd->tag;
int i;
if (!thread_number)
je->nr_ramp = cpu_to_le32(je->nr_ramp);
je->nr_pending = cpu_to_le32(je->nr_pending);
je->files_open = cpu_to_le32(je->files_open);
- je->m_rate = cpu_to_le32(je->m_rate);
- je->t_rate = cpu_to_le32(je->t_rate);
- je->m_iops = cpu_to_le32(je->m_iops);
- je->t_iops = cpu_to_le32(je->t_iops);
for (i = 0; i < DDIR_RWDIR_CNT; i++) {
- je->rate[i] = cpu_to_le32(je->rate[i]);
- je->iops[i] = cpu_to_le32(je->iops[i]);
+ je->m_rate[i] = cpu_to_le32(je->m_rate[i]);
+ je->t_rate[i] = cpu_to_le32(je->t_rate[i]);
+ je->m_iops[i] = cpu_to_le32(je->m_iops[i]);
+ je->t_iops[i] = cpu_to_le32(je->t_iops[i]);
}
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);
je->is_pow2 = cpu_to_le32(je->is_pow2);
- fio_net_send_cmd(server_fd, FIO_NET_CMD_ETA, je, size, cmd->tag);
+ fio_net_send_cmd(server_fd, FIO_NET_CMD_ETA, je, size, &tag, NULL);
free(je);
return 0;
}
+static int send_update_job_reply(int fd, uint64_t __tag, int error)
+{
+ uint64_t tag = __tag;
+ uint32_t pdu_error;
+
+ pdu_error = __cpu_to_le32(error);
+ return fio_net_send_cmd(fd, FIO_NET_CMD_UPDATE_JOB, &pdu_error, sizeof(pdu_error), &tag, NULL);
+}
+
+static int handle_update_job_cmd(struct fio_net_cmd *cmd)
+{
+ struct cmd_add_job_pdu *pdu = (struct cmd_add_job_pdu *) cmd->payload;
+ struct thread_data *td;
+ uint32_t tnumber;
+
+ tnumber = le32_to_cpu(pdu->thread_number);
+
+ dprint(FD_NET, "server: updating options for job %u\n", tnumber);
+
+ if (!tnumber || tnumber > thread_number) {
+ send_update_job_reply(server_fd, cmd->tag, ENODEV);
+ return 0;
+ }
+
+ td = &threads[tnumber - 1];
+ convert_thread_options_to_cpu(&td->o, &pdu->top);
+ send_update_job_reply(server_fd, cmd->tag, 0);
+ return 0;
+}
+
static int handle_command(struct fio_net_cmd *cmd)
{
int ret;
- dprint(FD_NET, "server: got op [%s], pdu=%u, tag=%lx\n",
- fio_server_op(cmd->opcode), cmd->pdu_len, cmd->tag);
+ dprint(FD_NET, "server: got op [%s], pdu=%u, tag=%llx\n",
+ fio_server_op(cmd->opcode), cmd->pdu_len,
+ (unsigned long long) cmd->tag);
switch (cmd->opcode) {
case FIO_NET_CMD_QUIT:
case FIO_NET_CMD_SEND_ETA:
ret = handle_send_eta_cmd(cmd);
break;
+ case FIO_NET_CMD_RUN:
+ ret = handle_run_cmd(cmd);
+ break;
+ case FIO_NET_CMD_UPDATE_JOB:
+ ret = handle_update_job_cmd(cmd);
+ break;
default:
- log_err("fio: unknown opcode: %s\n",fio_server_op(cmd->opcode));
+ log_err("fio: unknown opcode: %s\n", fio_server_op(cmd->opcode));
ret = 1;
}
return ret;
}
-static int handle_connection(int sk, int block)
+static int handle_connection(int sk)
{
struct fio_net_cmd *cmd = NULL;
int ret = 0;
+ reset_fio_state();
+ INIT_FLIST_HEAD(&job_list);
+ server_fd = sk;
+
/* read forever */
while (!exit_backend) {
struct pollfd pfd = {
ret = 0;
do {
- ret = poll(&pfd, 1, 100);
+ int timeout = 1000;
+
+ if (!flist_empty(&job_list))
+ timeout = 100;
+
+ ret = poll(&pfd, 1, timeout);
if (ret < 0) {
if (errno == EINTR)
break;
log_err("fio: poll: %s\n", strerror(errno));
break;
} else if (!ret) {
- if (!block)
- return 0;
+ fio_server_check_jobs();
continue;
}
}
} while (!exit_backend);
+ fio_server_check_jobs();
+
if (ret < 0)
break;
if (cmd)
free(cmd);
- return ret;
-}
-
-void fio_server_idle_loop(void)
-{
- if (!first_cmd_check)
- fio_net_send_simple_cmd(server_fd, FIO_NET_CMD_RUN, 0, NULL);
- if (server_fd != -1)
- handle_connection(server_fd, 0);
+ close(sk);
+ _exit(ret);
}
static int accept_loop(int listen_sk)
struct sockaddr_in addr;
socklen_t len = sizeof(addr);
struct pollfd pfd;
- int ret, sk, flags, exitval = 0;
+ int ret = 0, sk, flags, exitval = 0;
dprint(FD_NET, "server enter accept loop\n");
flags = fcntl(listen_sk, F_GETFL);
flags |= O_NONBLOCK;
fcntl(listen_sk, F_SETFL, flags);
-again:
- pfd.fd = listen_sk;
- pfd.events = POLLIN;
- do {
- ret = poll(&pfd, 1, 100);
- if (ret < 0) {
- if (errno == EINTR)
- break;
- log_err("fio: poll: %s\n", strerror(errno));
- goto out;
- } else if (!ret)
- continue;
- if (pfd.revents & POLLIN)
- break;
- } while (!exit_backend);
+ while (!exit_backend) {
+ pid_t pid;
- if (exit_backend)
- goto out;
+ pfd.fd = listen_sk;
+ pfd.events = POLLIN;
+ do {
+ int timeout = 1000;
- sk = accept(listen_sk, (struct sockaddr *) &addr, &len);
- if (sk < 0) {
- log_err("fio: accept: %s\n", strerror(errno));
- return -1;
- }
+ if (!flist_empty(&conn_list))
+ timeout = 100;
- dprint(FD_NET, "server: connect from %s\n", inet_ntoa(addr.sin_addr));
+ ret = poll(&pfd, 1, timeout);
+ if (ret < 0) {
+ if (errno == EINTR)
+ break;
+ log_err("fio: poll: %s\n", strerror(errno));
+ break;
+ } else if (!ret) {
+ fio_server_check_conns();
+ continue;
+ }
- server_fd = sk;
+ if (pfd.revents & POLLIN)
+ break;
+ } while (!exit_backend);
- exitval = handle_connection(sk, 1);
+ fio_server_check_conns();
- server_fd = -1;
- close(sk);
+ if (exit_backend || ret < 0)
+ break;
- if (!exit_backend)
- goto again;
+ sk = accept(listen_sk, (struct sockaddr *) &addr, &len);
+ if (sk < 0) {
+ log_err("fio: accept: %s\n", strerror(errno));
+ return -1;
+ }
+
+ dprint(FD_NET, "server: connect from %s\n", inet_ntoa(addr.sin_addr));
+
+ pid = fork();
+ if (pid) {
+ close(sk);
+ fio_server_add_conn_pid(pid);
+ continue;
+ }
+
+ /* exits */
+ handle_connection(sk);
+ }
-out:
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, NULL, NULL);
+ 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.unified_rw_rep = cpu_to_le32(ts->unified_rw_rep);
- 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);
p.ts.unified_rw_rep = cpu_to_le32(ts->unified_rw_rep);
for (i = 0; i < DDIR_RWDIR_CNT; i++) {
convert_gs(&p.rs, rs);
- fio_net_send_cmd(server_fd, FIO_NET_CMD_TS, &p, sizeof(p), 0);
+ fio_net_send_cmd(server_fd, FIO_NET_CMD_TS, &p, sizeof(p), NULL, NULL);
}
void fio_server_send_gs(struct group_run_stats *rs)
dprint(FD_NET, "server sending group run stats\n");
convert_gs(&gs, rs);
- fio_net_send_cmd(server_fd, FIO_NET_CMD_GS, &gs, sizeof(gs), 0);
+ fio_net_send_cmd(server_fd, FIO_NET_CMD_GS, &gs, sizeof(gs), NULL, NULL);
}
static void convert_agg(struct disk_util_agg *dst, struct disk_util_agg *src)
convert_dus(&pdu.dus, &du->dus);
convert_agg(&pdu.agg, &du->agg);
- fio_net_send_cmd(server_fd, FIO_NET_CMD_DU, &pdu, sizeof(pdu), 0);
+ fio_net_send_cmd(server_fd, FIO_NET_CMD_DU, &pdu, sizeof(pdu), NULL, NULL);
+ }
+}
+
+/*
+ * 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 fio_net_cmd cmd;
+ struct iovec iov[2];
+
+ iov[0].iov_base = &cmd;
+ iov[0].iov_len = sizeof(cmd);
+ iov[1].iov_base = (void *) buf;
+ iov[1].iov_len = size;
+
+ __fio_init_net_cmd(&cmd, opcode, size, tag);
+ cmd.flags = __cpu_to_le32(flags);
+ fio_net_cmd_crc_pdu(&cmd, buf);
+
+ return fio_sendv_data(sk, 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);
}
+
+ /*
+ * 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;
+ }
+
+ /*
+ * 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;
+ 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);
- dprint(FD_NET, "server log\n");
+ fio_net_send_cmd(server_fd, FIO_NET_CMD_ADD_JOB, &pdu, sizeof(pdu), NULL, NULL);
+}
- va_start(args, format);
- len = vsnprintf(buffer, sizeof(buffer), format, args);
- va_end(args);
- len = min(len, sizeof(buffer) - 1);
+void fio_server_send_start(struct thread_data *td)
+{
+ assert(server_fd != -1);
- return fio_server_text_output(buffer, len);
+ fio_net_send_simple_cmd(server_fd, FIO_NET_CMD_SERVER_START, 0, NULL);
}
static int fio_init_server_ip(void)
return sk;
}
+int fio_server_parse_host(const char *host, int *ipv6, struct in_addr *inp,
+ struct in6_addr *inp6)
+
+{
+ int ret = 0;
+
+ if (*ipv6)
+ ret = inet_pton(AF_INET6, host, inp6);
+ else
+ ret = inet_pton(AF_INET, host, inp);
+
+ if (ret != 1) {
+ struct hostent *hent;
+
+ hent = gethostbyname(host);
+ if (!hent) {
+ log_err("fio: failed to resolve <%s>\n", host);
+ return 0;
+ }
+
+ if (*ipv6) {
+ if (hent->h_addrtype != AF_INET6) {
+ log_info("fio: falling back to IPv4\n");
+ *ipv6 = 0;
+ } else
+ memcpy(inp6, hent->h_addr_list[0], 16);
+ }
+ if (!*ipv6) {
+ if (hent->h_addrtype != AF_INET) {
+ log_err("fio: lookup type mismatch\n");
+ return 0;
+ }
+ memcpy(inp, hent->h_addr_list[0], 4);
+ }
+ ret = 1;
+ }
+
+ return !(ret == 1);
+}
+
/*
* Parse a host/ip/port string. Reads from 'str'.
*
{
const char *host = str;
char *portp;
- int ret, lport = 0;
+ int lport = 0;
*ptr = NULL;
*is_sock = 0;
*ptr = strdup(host);
- if (*ipv6)
- ret = inet_pton(AF_INET6, host, inp6);
- else
- ret = inet_pton(AF_INET, host, inp);
-
- if (ret != 1) {
- struct hostent *hent;
-
- hent = gethostbyname(host);
- if (!hent) {
- log_err("fio: failed to resolve <%s>\n", host);
- free(*ptr);
- *ptr = NULL;
- return 1;
- }
-
- if (*ipv6) {
- if (hent->h_addrtype != AF_INET6) {
- log_info("fio: falling back to IPv4\n");
- *ipv6 = 0;
- } else
- memcpy(inp6, hent->h_addr_list[0], 16);
- }
- if (!*ipv6) {
- if (hent->h_addrtype != AF_INET) {
- log_err("fio: lookup type mismatch\n");
- free(*ptr);
- *ptr = NULL;
- return 1;
- }
- memcpy(inp, hent->h_addr_list[0], 4);
- }
+ if (fio_server_parse_host(*ptr, ipv6, inp, inp6)) {
+ free(*ptr);
+ *ptr = NULL;
+ return 1;
}
if (*port == 0)
#if defined(WIN32)
WSADATA wsd;
- WSAStartup(MAKEWORD(2,2), &wsd);
+ WSAStartup(MAKEWORD(2, 2), &wsd);
#endif
if (!pidfile)