Merge branch 'master' into gfio
authorJens Axboe <axboe@kernel.dk>
Mon, 4 Mar 2013 11:27:07 +0000 (12:27 +0100)
committerJens Axboe <axboe@kernel.dk>
Mon, 4 Mar 2013 11:27:07 +0000 (12:27 +0100)
Conflicts:
init.c

Signed-off-by: Jens Axboe <axboe@kernel.dk>
1  2 
HOWTO
Makefile
configure
debug.h
engines/net.c
eta.c
filesetup.c
fio.h
init.c
memory.c
server.c

diff --combined HOWTO
index 14417342761f9e2c39abb7f512a4e60a96c453f9,fbe8f7919988db293b921334268a424e887604fd..9ac485b6147ae278f15b474e3b9bac1af932d967
--- 1/HOWTO
--- 2/HOWTO
+++ b/HOWTO
@@@ -9,6 -9,7 +9,7 @@@ Table of content
  6. Normal output
  7. Terse output
  8. Trace file format
+ 9. CPU idleness profiling
  
  1.0 Overview and history
  ------------------------
@@@ -775,7 -776,7 +776,7 @@@ random_generator=str       Fio supports the f
                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
@@@ -1229,6 -1230,12 +1230,6 @@@ exec_postrun=str After the job complete
  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.
  
@@@ -1388,11 -1395,6 +1389,11 @@@ that defines them is selected
                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
diff --combined Makefile
index 8eb2f45d005c4fd56b8189b0698dca40f91d6a56,d55626d2576b834b333bd50a2c9e7d1e05739e3b..4b19a593487e48f4aa9d78c5146a205df2d02a7f
+++ b/Makefile
@@@ -2,7 -2,7 +2,7 @@@ DEBUGFLAGS = -D_FORTIFY_SOURCE=2 -DFIO_
  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)
@@@ -21,19 -21,15 +21,19 @@@ all
  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
@@@ -96,7 -92,7 +96,7 @@@ ifeq ($(UNAME), Android
    LDFLAGS += -rdynamic
  endif
  ifeq ($(UNAME), SunOS)
-   LIBS         += -lpthread -ldl -laio -lrt -lnsl -lsocket
+   LIBS         += -lpthread -ldl
    CPPFLAGS += -D__EXTENSIONS__
  endif
  ifeq ($(UNAME), FreeBSD)
@@@ -127,11 -123,6 +127,11 @@@ ifneq (,$(findstring CYGWIN,$(UNAME))
  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
@@@ -201,53 -192,23 +201,53 @@@ override CFLAGS += -DFIO_VERSION='"$(FI
  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
diff --combined configure
index 71faf9184a9e9fb57a386f79ebd55868facf2403,d1f277d057ea1e0ed8226f80d63bfb18ee5097cd..5d576e104fc863eabf9da37f31158f34c9f0a51b
+++ b/configure
@@@ -87,7 -87,7 +87,7 @@@ compile_object() 
  
  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
  }
@@@ -128,10 -128,8 +128,10 @@@ cpu="
  
  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"
@@@ -163,7 -158,6 +163,7 @@@ if test "$show_help" = "yes" ; the
    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
  
@@@ -199,6 -193,7 +199,7 @@@ SunOS
    if test -z "$cpu" && test "$(isainfo -k)" = "amd64"; then
      cpu="x86_64"
    fi
+   LIBS="-lnsl -lsocket"
    ;;
  CYGWIN*)
    echo "Forcing known good options on Windows"
@@@ -355,7 -350,7 +356,7 @@@ int main(void
  }
  EOF
  if compile_prog "" "" "wordsize"; then
-   wordsize=$($TMPE)
+   wordsize=`$TMPE`
  fi
  echo "Wordsize                      $wordsize"
  
@@@ -451,7 -446,7 +452,7 @@@ sfaa="no
  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)
@@@ -464,7 -459,7 +465,7 @@@ EO
  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
@@@ -957,6 -910,23 +958,23 @@@ if compile_prog "" "" "TCP_NODELAY"; th
  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
diff --combined debug.h
index b55a1e4dc7d76d3c17e660288441ffb255960942,6dd2ad86b87d60f9a27228f4b18f40f9b6754009..6a74131aed7a95394c8cb4b384c80fd5407da5f4
+++ b/debug.h
@@@ -27,7 -27,6 +27,7 @@@ extern unsigned int fio_debug_jobno, *f
  #ifdef FIO_INC_DEBUG
  struct debug_level {
        const char *name;
 +      const char *help;
        unsigned long shift;
        unsigned int jobno;
  };
@@@ -35,7 -34,7 +35,7 @@@ extern struct debug_level debug_levels[
  
  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 {                                            \
diff --combined engines/net.c
index 12f49a2a67ca2f1d7241b42efdc45543b77fd868,624ff1589ead802714c0bc15171d7730464e4586..a70e56e6d5095b01455a6f2ca234448dbe7635f0
@@@ -58,31 -58,25 +58,31 @@@ static int str_hostname_cb(void *data, 
  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",
@@@ -466,7 -458,7 +466,7 @@@ static int fio_netio_connect(struct thr
  {
        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;
@@@ -530,7 -523,7 +531,7 @@@ static int fio_netio_accept(struct thre
        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;
diff --combined eta.c
index a724fe63936bc64caa0719871c9ff90cfb522c49,f90d428197656f362fcdf199865a65f06dbebbec..531b876280b2176d3784d6d50073d2d47797e146
--- 1/eta.c
--- 2/eta.c
+++ b/eta.c
@@@ -94,7 -94,7 +94,7 @@@ static void check_str_update(struct thr
  /*
   * 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;
@@@ -326,22 -326,22 +326,22 @@@ int calc_thread_status(struct jobs_eta 
                    || 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);
  }
  
diff --combined filesetup.c
index 4e828542e97535b6606d966cd9c08711f88f4639,220ceb90a1f9f343983d3c483eba65590b2104c3..422d706a0331b93730631465ef1ab355f0c661a7
@@@ -79,7 -79,8 +79,8 @@@ static int extend_file(struct thread_da
                        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;
@@@ -919,9 -920,9 +920,9 @@@ static int __init_rand_distribution(str
                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;
  }
diff --combined fio.h
index 2d752135cf030d2c6bd64f7cc04a66277fa86576,d7eb6dea65a68c3b312025ea1c342ecb5e3da249..5805fcbfe8b42d0350add583b9188e92efbd636b
--- 1/fio.h
--- 2/fio.h
+++ b/fio.h
  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"
@@@ -37,7 -36,6 +37,7 @@@
  #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
   */
@@@ -64,6 -73,241 +64,6 @@@ enum 
        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,
@@@ -95,12 -339,10 +95,12 @@@ struct thread_data 
        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;
  };
  
  /*
@@@ -342,13 -585,16 +342,14 @@@ enum 
  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;
@@@ -419,10 -665,9 +420,10 @@@ static inline int should_fsync(struct t
  /*
   * 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 *);
@@@ -437,14 -682,10 +438,14 @@@ extern void fio_options_dup_and_init(st
  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
@@@ -483,11 -723,10 +484,11 @@@ extern void fio_terminate_threads(int)
  /*
   * 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
@@@ -596,8 -835,6 +597,8 @@@ static inline void td_io_u_free_notify(
  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,
diff --combined init.c
index 8ec1eb978e90910620452c34015ec1a42923fa55,32da42c5d8e4ba2ab86dbf4e2888cb7af05eb5ff..347d1e9174e0bac33c2469c04219b22bcd285275
--- 1/init.c
--- 2/init.c
+++ b/init.c
@@@ -41,6 -41,8 +41,8 @@@ struct thread_data *threads = NULL
  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;
@@@ -138,6 -140,11 +140,11 @@@ static struct option l_opts[FIO_NR_OPTI
                .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();
@@@ -600,7 -599,7 +607,7 @@@ static int fixup_options(struct thread_
  /*
   * 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 };
@@@ -798,9 -797,11 +805,9 @@@ static int setup_random_seeds(struct th
   * 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;
        }
  
@@@ -980,7 -986,7 +987,7 @@@ 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)
@@@ -1049,7 -1055,7 +1056,7 @@@ static int is_empty_or_comment(char *li
  /*
   * 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);
@@@ -1264,6 -1270,8 +1271,8 @@@ static void usage(const char *name
        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, },
  };
  
@@@ -1445,7 -1411,7 +1454,7 @@@ void parse_cmd_client(void *client, cha
        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));
 +}
diff --combined memory.c
index e969221f36f86a8a1c74b502152a97e75f81c219,ee5f895d2b0b0f88e6bd50a2a2a50149c45591fd..f1cd2d770f4cbe3597ecbc5e236b355e930fc49a
+++ b/memory.c
  
  #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;
        }
  
@@@ -125,13 -127,13 +125,13 @@@ static int alloc_mem_mmap(struct thread
                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;
  }
@@@ -214,7 -218,7 +216,7 @@@ int allocate_io_mem(struct thread_data 
                        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);
diff --combined server.c
index 8e5ca50f45faf5e7480c85d1e84f45f7b014afb9,d4676ddab40cf733007a5685700edda0b27ee67d..b7e9922425c0443798d54a52973fc6a90552c918
+++ b/server.c
  #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;
  
@@@ -32,22 -31,9 +32,22 @@@ static char *fio_server_arg
  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 {
@@@ -193,7 -143,7 +193,7 @@@ static int verify_convert_cmd(struct fi
                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;
        }
@@@ -269,21 -219,12 +269,21 @@@ struct fio_net_cmd *fio_net_recv_cmd(in
                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);
  
@@@ -402,174 -298,61 +402,174 @@@ static int fio_net_send_simple_stack_cm
  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)
@@@ -956,11 -669,11 +957,11 @@@ void fio_server_send_ts(struct thread_s
        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)
@@@ -1084,125 -797,24 +1085,125 @@@ void fio_server_send_du(void
                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)
@@@ -1334,46 -946,6 +1335,46 @@@ static int fio_init_server_connection(v
        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'.
   *
@@@ -1392,7 -964,7 +1393,7 @@@ int fio_server_parse_string(const char 
  {
        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)
@@@ -1592,7 -1192,7 +1593,7 @@@ int fio_start_server(char *pidfile
  
  #if defined(WIN32)
        WSADATA wsd;
 -      WSAStartup(MAKEWORD(2,2), &wsd);
 +      WSAStartup(MAKEWORD(2, 2), &wsd);
  #endif
  
        if (!pidfile)