Merge branch 'master' into gfio
authorJens Axboe <axboe@kernel.dk>
Tue, 29 Jan 2013 09:09:55 +0000 (10:09 +0100)
committerJens Axboe <axboe@kernel.dk>
Tue, 29 Jan 2013 09:09:55 +0000 (10:09 +0100)
Conflicts:
Makefile
configure
options.c

Signed-off-by: Jens Axboe <axboe@kernel.dk>
1  2 
Makefile
backend.c
configure
filesetup.c
fio.h
options.c
os/os.h
parse.c
stat.c

diff --combined Makefile
index 9d3465c40086719b52ccc161d8ff3455992b5637,52ec4a2dad491bedb7116dffcffc6dd383a52e4f..038eacf14f91420e1450d732e5a57f59f0faaca1
+++ b/Makefile
@@@ -1,9 -1,8 +1,8 @@@
  DEBUGFLAGS = -D_FORTIFY_SOURCE=2 -DFIO_INC_DEBUG
- CPPFLAGS= -D_GNU_SOURCE -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 \
-       $(DEBUGFLAGS)
+ 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)
@@@ -22,19 -21,14 +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
 +              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/hweight.c lib/getrusage.c
  
  ifdef CONFIG_64BIT_LLP64
    CFLAGS += -DBITS_PER_LONG=32
@@@ -45,22 -39,13 +44,13 @@@ endi
  ifdef CONFIG_32BIT
    CFLAGS += -DBITS_PER_LONG=32
  endif
- ifdef CONFIG_BIG_ENDIAN
-   CFLAGS += -DCONFIG_BIG_ENDIAN
- endif
- ifdef CONFIG_LITTLE_ENDIAN
-   CFLAGS += -DCONFIG_LITTLE_ENDIAN
- endif
  ifdef CONFIG_LIBAIO
-   CFLAGS += -DCONFIG_LIBAIO
    SOURCE += engines/libaio.c
  endif
  ifdef CONFIG_RDMA
-   CFLAGS += -DCONFIG_RDMA
    SOURCE += engines/rdma.c
  endif
  ifdef CONFIG_POSIXAIO
-   CFLAGS += -DCONFIG_POSIXAIO
    SOURCE += engines/posixaio.c
  endif
  ifdef CONFIG_LINUX_FALLOCATE
@@@ -70,83 -55,29 +60,29 @@@ ifdef CONFIG_LINUX_EXT4_MOVE_EXTEN
    SOURCE += engines/e4defrag.c
  endif
  ifdef CONFIG_LINUX_SPLICE
-   CFLAGS += -DCONFIG_LINUX_SPLICE
    SOURCE += engines/splice.c
  endif
  ifdef CONFIG_GUASI
-   CFLAGS += -DCONFIG_GUASI
    SOURCE += engines/guasi.c
  endif
  ifdef CONFIG_FUSION_AW
-   CFLAGS += -DCONFIG_FUSION_AW
    SOURCE += engines/fusion-aw.c
  endif
  ifdef CONFIG_SOLARISAIO
-   CFLAGS += -DCONFIG_SOLARISAIO
    SOURCE += engines/solarisaio.c
  endif
+ ifdef CONFIG_WINDOWSAIO
+   SOURCE += engines/windowsaio.c
+ endif
  ifndef CONFIG_STRSEP
-   CFLAGS += -DCONFIG_STRSEP
    SOURCE += lib/strsep.c
  endif
  ifndef CONFIG_GETOPT_LONG_ONLY
-   CFLAGS += -DCONFIG_GETOPT_LONG_ONLY
    SOURCE += lib/getopt_long.c
  endif
  ifndef CONFIG_INET_ATON
-   CFLAGS += -DCONFIG_INET_ATON
    SOURCE += lib/inet_aton.c
  endif
- ifdef CONFIG_CLOCK_GETTIME
-   CFLAGS += -DCONFIG_CLOCK_GETTIME
- endif
- ifdef CONFIG_POSIXAIO_FSYNC
-   CFLAGS += -DCONFIG_POSIXAIO_FSYNC
- endif
- ifdef CONFIG_FADVISE
-   CFLAGS += -DCONFIG_FADVISE
- endif
- ifdef CONFIG_CLOCK_MONOTONIC
-   CFLAGS += -DCONFIG_CLOCK_MONOTONIC
- endif
- ifdef CONFIG_CLOCK_MONOTONIC_PRECISE
-   CFLAGS += -DCONFIG_CLOCK_MONOTONIC_PRECISE
- endif
- ifdef CONFIG_GETTIMEOFDAY
-   CFLAGS += -DCONFIG_GETTIMEOFDAY
- endif
- ifdef CONFIG_SOCKLEN_T
-   CFLAGS += -DCONFIG_SOCKLEN_T
- endif
- ifdef CONFIG_SFAA
-   CFLAGS += -DCONFIG_SFAA
- endif
- ifdef CONFIG_FDATASYNC
-   CFLAGS += -DCONFIG_FDATASYNC
- endif
- ifdef CONFIG_3ARG_AFFINITY
-   CFLAGS += -DCONFIG_3ARG_AFFINITY
- endif
- ifdef CONFIG_2ARG_AFFINITY
-   CFLAGS += -DCONFIG_2ARG_AFFINITY
- endif
- ifdef CONFIG_SYNC_FILE_RANGE
-   CFLAGS += -DCONFIG_SYNC_FILE_RANGE
- endif
- ifdef CONFIG_LIBNUMA
-   CFLAGS += -DCONFIG_LIBNUMA
- endif
- ifdef CONFIG_TLS_THREAD
-   CFLAGS += -DCONFIG_TLS_THREAD
- endif
- ifdef CONFIG_POSIX_FALLOCATE
-   CFLAGS += -DCONFIG_POSIX_FALLOCATE
- endif
- ifdef CONFIG_LINUX_FALLOCATE
-   CFLAGS += -DCONFIG_LINUX_FALLOCATE
- endif
  
  ifeq ($(UNAME), Linux)
    SOURCE += diskutil.c fifo.c blktrace.c cgroup.c trim.c engines/sg.c \
@@@ -186,15 -117,12 +122,15 @@@ ifeq ($(UNAME), Darwin
  endif
  ifneq (,$(findstring CYGWIN,$(UNAME)))
    SOURCE := $(filter-out engines/mmap.c,$(SOURCE))
-   SOURCE += engines/windowsaio.c os/windows/posix.c
+   SOURCE += os/windows/posix.c
    LIBS         += -lpthread -lpsapi -lws2_32
    CFLAGS += -DPSAPI_VERSION=1 -Ios/windows/posix/include -Wno-format
  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
  
  T_SMALLOC_OBJS = t/stest.o
  T_SMALLOC_OBJS += gettime.o mutex.o smalloc.o t/log.o
@@@ -251,63 -179,33 +187,63 @@@ FIO-VERSION-FILE: FORC
  CFLAGS += -DFIO_VERSION='"$(FIO_VERSION)"'
  
  .c.o: .depend FORCE
-       $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) $(CPPFLAGS) $<
+       $(QUIET_CC)$(CC) -o $@ $(CFLAGS) $(CPPFLAGS) -c $<
  
  init.o: FIO-VERSION-FILE
-       $(QUIET_CC)$(CC) -o init.o -c $(CFLAGS) $(CPPFLAGS) -c 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_CC)$(CC) $(LDFLAGS) $(CFLAGS) -o $@ $(T_SMALLOC_OBJS) $(LIBS) $(LDFLAGS)
  
  t/ieee754: $(T_IEEE_OBJS)
        $(QUIET_CC)$(CC) $(LDFLAGS) $(CFLAGS) -o $@ $(T_IEEE_OBJS) $(LIBS) $(LDFLAGS)
  
 +fio: $(FIO_OBJS)
 +      $(QUIET_CC)$(CC) $(LDFLAGS) $(CFLAGS) -o $@ $(FIO_OBJS) $(LIBS) $(LDFLAGS)
 +
 +gfio: $(GFIO_OBJS)
 +      $(QUIET_CC)$(CC) $(LIBS) -o gfio $(GFIO_OBJS) $(LIBS) $(GTK_LDFLAGS)
 +
  t/genzipf: $(T_ZIPF_OBJS)
        $(QUIET_CC)$(CC) $(LDFLAGS) $(CFLAGS) -o $@ $(T_ZIPF_OBJS) $(LIBS) $(LDFLAGS)
  
  t/axmap: $(T_AXMAP_OBJS)
        $(QUIET_CC)$(CC) $(LDFLAGS) $(CFLAGS) -o $@ $(T_AXMAP_OBJS) $(LIBS) $(LDFLAGS)
  
 -fio: $(OBJS)
 -      $(QUIET_CC)$(CC) $(LDFLAGS) $(CFLAGS) -o $@ $(OBJS) $(LIBS) $(LDFLAGS)
 -
  .depend: $(SOURCE)
        $(QUIET_DEP)$(CC) -MM $(CFLAGS) $(CPPFLAGS) $(SOURCE) 1> .depend
  
  $(PROGS): .depend
  
  clean: FORCE
-       -rm -f .depend $(GFIO_OBJS) $(OBJS) $(T_OBJS) $(PROGS) $(T_PROGS) core.* core gfio FIO-VERSION-FILE config-host.mak config-host.ld cscope.out
 -      -rm -f .depend $(OBJS) $(T_OBJS) $(PROGS) $(T_PROGS) core.* core FIO-VERSION-FILE config-host.mak cscope.out
++      -rm -f .depend $(GFIO_OBJS) $(OBJS) $(T_OBJS) $(PROGS) $(T_PROGS) core.* core gfio FIO-VERSION-FILE config-host.mak cscope.out
  
  cscope:
        @cscope -b -R
@@@ -322,5 -220,3 +258,5 @@@ install: $(PROGS) $(SCRIPTS) FORC
  ifneq ($(wildcard .depend),)
  include .depend
  endif
 +
 +
diff --combined backend.c
index 48b00b685ae7bb0310b3df48d2044b08e80b1081,87810e89d696ea7f453469eff59a486620dbdcab..9aa2a28551234220b6b1ba12c3f504eff0e16075
+++ b/backend.c
@@@ -50,6 -50,7 +50,7 @@@
  #include "lib/rand.h"
  #include "memalign.h"
  #include "server.h"
+ #include "lib/getrusage.h"
  
  static pthread_t disk_util_thread;
  static struct fio_mutex *disk_thread_mutex;
@@@ -59,14 -60,14 +60,14 @@@ static struct flist_head *cgroup_list
  static char *cgroup_mnt;
  static int exit_value;
  static volatile int fio_abort;
 +static unsigned int nr_process = 0;
 +static unsigned int nr_thread = 0;
  
  struct io_log *agg_io_log[DDIR_RWDIR_CNT];
  
  int groupid = 0;
  unsigned int thread_number = 0;
  unsigned int stat_number = 0;
 -unsigned int nr_process = 0;
 -unsigned int nr_thread = 0;
  int shm_id = 0;
  int temp_stall_ts;
  unsigned long done_secs = 0;
@@@ -1045,12 -1046,10 +1046,12 @@@ static void *thread_main(void *data
  {
        unsigned long long elapsed;
        struct thread_data *td = data;
 +      struct thread_options *o = &td->o;
        pthread_condattr_t attr;
        int clear_state;
 +      int ret;
  
 -      if (!td->o.use_thread) {
 +      if (!o->use_thread) {
                setsid();
                td->pid = getpid();
        } else
  
        dprint(FD_PROCESS, "jobs pid=%d started\n", (int) td->pid);
  
 +      if (is_backend)
 +              fio_server_send_start(td);
 +
        INIT_FLIST_HEAD(&td->io_u_freelist);
        INIT_FLIST_HEAD(&td->io_u_busylist);
        INIT_FLIST_HEAD(&td->io_u_requeues);
         * eating a file descriptor
         */
        fio_mutex_remove(td->mutex);
 +      td->mutex = NULL;
  
        /*
         * A new gid requires privilege, so we need to do this before setting
         * the uid.
         */
 -      if (td->o.gid != -1U && setgid(td->o.gid)) {
 +      if (o->gid != -1U && setgid(o->gid)) {
                td_verror(td, errno, "setgid");
                goto err;
        }
 -      if (td->o.uid != -1U && setuid(td->o.uid)) {
 +      if (o->uid != -1U && setuid(o->uid)) {
                td_verror(td, errno, "setuid");
                goto err;
        }
         * If we have a gettimeofday() thread, make sure we exclude that
         * thread from this job
         */
 -      if (td->o.gtod_cpu)
 -              fio_cpu_clear(&td->o.cpumask, td->o.gtod_cpu);
 +      if (o->gtod_cpu)
 +              fio_cpu_clear(&o->cpumask, o->gtod_cpu);
  
        /*
         * Set affinity first, in case it has an impact on the memory
         * allocations.
         */
 -      if (td->o.cpumask_set && fio_setaffinity(td->pid, td->o.cpumask) == -1) {
 -              td_verror(td, errno, "cpu_set_affinity");
 -              goto err;
 +      if (o->cpumask_set) {
 +              ret = fio_setaffinity(td->pid, o->cpumask);
 +              if (ret == -1) {
 +                      td_verror(td, errno, "cpu_set_affinity");
 +                      goto err;
 +              }
        }
  
  #ifdef CONFIG_LIBNUMA
        if (init_io_u(td))
                goto err;
  
 -      if (td->o.verify_async && verify_async_init(td))
 +      if (o->verify_async && verify_async_init(td))
                goto err;
  
 -      if (td->ioprio_set) {
 -              if (ioprio_set(IOPRIO_WHO_PROCESS, 0, td->ioprio) == -1) {
 +      if (o->ioprio) {
 +              ret = ioprio_set(IOPRIO_WHO_PROCESS, 0, o->ioprio_class, o->ioprio);
 +              if (ret == -1) {
                        td_verror(td, errno, "ioprio_set");
                        goto err;
                }
                goto err;
  
        errno = 0;
 -      if (nice(td->o.nice) == -1 && errno != 0) {
 +      if (nice(o->nice) == -1 && errno != 0) {
                td_verror(td, errno, "nice");
                goto err;
        }
  
 -      if (td->o.ioscheduler && switch_ioscheduler(td))
 +      if (o->ioscheduler && switch_ioscheduler(td))
                goto err;
  
 -      if (!td->o.create_serialize && setup_files(td))
 +      if (!o->create_serialize && setup_files(td))
                goto err;
  
        if (td_io_init(td))
        if (init_random_map(td))
                goto err;
  
 -      if (td->o.exec_prerun) {
 -              if (exec_string(td->o.exec_prerun))
 -                      goto err;
 -      }
 +      if (o->exec_prerun && exec_string(o->exec_prerun))
 +              goto err;
  
 -      if (td->o.pre_read) {
 +      if (o->pre_read) {
                if (pre_read_files(td) < 0)
                        goto err;
        }
  
 +      fio_verify_init(td);
 +
        fio_gettime(&td->epoch, NULL);
-       getrusage(RUSAGE_SELF, &td->ru_start);
+       fio_getrusage(&td->ru_start);
        clear_state = 0;
        while (keep_running(td)) {
                uint64_t verify_bytes;
        td->ts.io_bytes[DDIR_WRITE] = td->io_bytes[DDIR_WRITE];
        td->ts.io_bytes[DDIR_TRIM] = td->io_bytes[DDIR_TRIM];
  
 +      fio_unpin_memory(td);
 +
        fio_mutex_down(writeout_mutex);
        if (td->bw_log) {
                if (td->o.bw_log_file) {
@@@ -1351,8 -1339,8 +1351,8 @@@ err
        close_ioengine(td);
        cgroup_shutdown(td, &cgroup_mnt);
  
 -      if (td->o.cpumask_set) {
 -              int ret = fio_cpuset_exit(&td->o.cpumask);
 +      if (o->cpumask_set) {
 +              int ret = fio_cpuset_exit(&o->cpumask);
  
                td_verror(td, ret, "fio_cpuset_exit");
        }
@@@ -1506,19 -1494,14 +1506,19 @@@ static void run_threads(void
        unsigned long spent;
        unsigned int i, todo, nr_running, m_rate, t_rate, nr_started;
  
 -      if (fio_pin_memory())
 -              return;
 -
        if (fio_gtod_offload && fio_start_gtod_thread())
                return;
  
        set_sig_handlers();
  
 +      nr_thread = nr_process = 0;
 +      for_each_td(td, i) {
 +              if (td->o.use_thread)
 +                      nr_thread++;
 +              else
 +                      nr_process++;
 +      }
 +
        if (output_format == FIO_OUTPUT_NORMAL) {
                log_info("Starting ");
                if (nr_thread)
  
                reap_threads(&nr_running, &t_rate, &m_rate);
  
 -              if (todo) {
 -                      if (is_backend)
 -                              fio_server_idle_loop();
 -                      else
 -                              usleep(100000);
 -              }
 +              if (todo)
 +                      usleep(100000);
        }
  
        while (nr_running) {
                reap_threads(&nr_running, &t_rate, &m_rate);
 -
 -              if (is_backend)
 -                      fio_server_idle_loop();
 -              else
 -                      usleep(10000);
 +              usleep(10000);
        }
  
        update_io_ticks();
 -      fio_unpin_memory();
  }
  
  void wait_for_disk_thread_exit(void)
@@@ -1805,9 -1797,9 +1805,9 @@@ int fio_backend(void
                return 0;
  
        if (write_bw_log) {
 -              setup_log(&agg_io_log[DDIR_READ], 0);
 -              setup_log(&agg_io_log[DDIR_WRITE], 0);
 -              setup_log(&agg_io_log[DDIR_TRIM], 0);
 +              setup_log(&agg_io_log[DDIR_READ], 0, IO_LOG_TYPE_BW);
 +              setup_log(&agg_io_log[DDIR_WRITE], 0, IO_LOG_TYPE_BW);
 +              setup_log(&agg_io_log[DDIR_TRIM], 0, IO_LOG_TYPE_BW);
        }
  
        startup_mutex = fio_mutex_init(FIO_MUTEX_LOCKED);
diff --combined configure
index af2c1d97f6667aa20a4de571b09abc022e3fe3df,583e3ab286b0cc9088d221ff3fc49630699e5bd5..ff87acc58b54d2ed460a29658b41656ac0c5db8e
+++ b/configure
@@@ -24,7 -24,14 +24,14 @@@ trap "rm -f $TMPC $TMPO $TMPE" EXIT IN
  rm -rf config.log
  
  config_host_mak="config-host.mak"
- config_host_ld="config-host.ld"
+ config_host_h="config-host.h"
+ rm -rf $config_host_mak
+ rm -rf $config_host_h
+ # Default CFLAGS
+ CFLAGS="-D_GNU_SOURCE"
+ EXTFLAGS="-include config-host.h"
  
  # Print a helpful header at the top of config.log
  echo "# FIO configure log $(date)" >> config.log
@@@ -33,6 -40,14 +40,14 @@@ printf " '%s'" "$0" "$@" >> config.lo
  echo >> config.log
  echo "#" >> config.log
  
+ # Print configure header at the top of $config_host_h
+ echo "/*" > $config_host_h
+ echo " * Automatically generated by configure - do not modify" >> $config_host_h
+ printf " * Configured with:" >> $config_host_h
+ printf " * '%s'" "$0" "$@" >> $config_host_h
+ echo "" >> $config_host_h
+ echo " */" >> $config_host_h
  do_cc() {
      # Run the compiler, capturing its output to the log.
      echo $cc "$@" >> config.log
@@@ -97,29 -112,30 +112,37 @@@ EO
    compile_object
  }
  
+ output_sym() {
+   echo "$1=y" >> $config_host_mak
+   echo "#define $1" >> $config_host_h
+ }
  targetos=""
  cpu=""
  
  cc="${CC-${cross_prefix}gcc}"
  
- gfio="no"
 +# default options
  show_help="no"
  exit_val=0
++gfio="no"
  
  # parse options
  for opt do
    optarg=`expr "x$opt" : 'x[^=]*=\(.*\)'`
    case "$opt" in
 -  --cc=*) CC="$optarg"
 -  ;;
 -  --extra-cflags=*) CFLAGS="$CFLAGS $optarg"
 +  --cc=*)
 +    CC="$optarg"
 +    ;;
++  --extra-cflags=*)
++  CFLAGS="$CFLAGS $optarg"
+   ;;
 +  --enable-gfio)
 +    gfio="yes"
 +    ;;
    --help)
 -  show_help="yes"
 -  ;;
 +    show_help="yes"
 +    ;;
    *)
    echo "Bad option $opt"
    show_help="yes"
  done
  
  if test "$show_help" = "yes" ; then
 -  echo "--cc=                  Specify compiler to use"
 +    echo "--cc=                  Specify compiler to use"
+   echo "--extra-cflags=        Specify extra CFLAGS to pass to compiler"
 -  exit $exit_val
 +    echo "--enable-gfio          Enable building of gtk gfio"
 +    exit $exit_val
  fi
  
  if check_define __linux__ ; then
@@@ -169,17 -185,23 +193,23 @@@ CYGWIN*
    if test -z "$CC" ; then
      CC="x86_64-w64-mingw32-gcc"
    fi
-   echo "CC=$CC"                    >  $config_host_mak
-   echo "CONFIG_LITTLE_ENDIAN=y"    >> $config_host_mak
-   echo "CONFIG_64BIT_LLP64=y"      >> $config_host_mak
-   echo "CONFIG_CLOCK_GETTIME=y"    >> $config_host_mak
-   echo "CONFIG_CLOCK_MONOTONIC=y"  >> $config_host_mak
-   echo "CONFIG_GETTIMEOFDAY=y"     >> $config_host_mak
-   echo "CONFIG_FADVISE=y"          >> $config_host_mak
-   echo "CONFIG_SOCKLEN_T=y"        >> $config_host_mak
-   echo "CONFIG_POSIX_FALLOCATE=y"  >> $config_host_mak
-   echo "CONFIG_FADVISE=y"          >> $config_host_mak
-   echo "CONFIG_SFAA=y"             >> $config_host_mak
+   output_sym "CONFIG_LITTLE_ENDIAN"
+   output_sym "CONFIG_64BIT_LLP64"
+   output_sym "CONFIG_CLOCK_GETTIME"
+   output_sym "CONFIG_CLOCK_MONOTONIC"
+   output_sym "CONFIG_GETTIMEOFDAY"
+   output_sym "CONFIG_FADVISE"
+   output_sym "CONFIG_SOCKLEN_T"
+   output_sym "CONFIG_POSIX_FALLOCATE"
+   output_sym "CONFIG_FADVISE"
+   output_sym "CONFIG_SFAA"
+   output_sym "CONFIG_RUSAGE_THREAD"
+   output_sym "CONFIG_WINDOWSAIO"
+   output_sym "CONFIG_FDATASYNC"
+   output_sym "CONFIG_GETTIMEOFDAY"
+   output_sym "CONFIG_CLOCK_GETTIME"
+   echo "CC=$CC" >> $config_host_mak
+   echo "EXTFLAGS=$CFLAGS -include config-host.h -D_GNU_SOURCE" >> $config_host_mak
    exit 0
  esac
  
@@@ -249,7 -271,7 +279,7 @@@ case "$cpu" i
    ;;
  esac
  
- if test -z $CC; then
+ if test -z "$CC" ; then
    if test "$targetos" = "FreeBSD"; then
      if has clang; then
        CC=clang
@@@ -499,7 -521,6 +529,6 @@@ echo "POSIX fallocate               $po
  linux_2arg_affinity="no"
  linux_3arg_affinity="no"
  cat > $TMPC << EOF
- #define _GNU_SOURCE
  #include <sched.h>
  int main(int argc, char **argv)
  {
@@@ -511,7 -532,6 +540,6 @@@ if compile_prog "" "" "sched_setaffinit
    linux_3arg_affinity="yes"
  else
    cat > $TMPC << EOF
- #define _GNU_SOURCE
  #include <sched.h>
  int main(int argc, char **argv)
  {
@@@ -620,7 -640,6 +648,6 @@@ sync_file_range="no
  cat > $TMPC << EOF
  #include <stdio.h>
  #include <unistd.h>
- #define _GNU_SOURCE
  #include <fcntl.h>
  #include <linux/fs.h>
  int main(int argc, char **argv)
@@@ -661,7 -680,6 +688,6 @@@ echo "EXT4 move extent              $ex
  # splice probe
  linux_splice="no"
  cat > $TMPC << EOF
- #define _GNU_SOURCE
  #include <stdio.h>
  #include <fcntl.h>
  int main(int argc, char **argv)
  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();
 +  gtk_main();
 +  gdk_threads_leave();
 +  return 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
 +  gfio="yes"
 +  LIBS="$LIBS $GTK_LIBS"
 +  CFLAGS="$CFLAGS $GTK_CFLAGS"
 +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
+ #include <sys/time.h>
+ #include <sys/resource.h>
+ int main(int argc, char **argv)
+ {
+   struct rusage ru;
+   getrusage(RUSAGE_THREAD, &ru);
+   return 0;
+ }
+ EOF
+ if compile_prog "" "" "RUSAGE_THREAD"; then
+   rusage_thread="yes"
+ fi
+ echo "RUSAGE_THREAD                 $rusage_thread"
  #############################################################################
  
  echo "# Automatically generated by configure - do not modify" > $config_host_mak
@@@ -853,102 -852,102 +896,106 @@@ printf " '%s'" "$0" "$@" >> $config_hos
  echo >> $config_host_mak
  
  if test "$wordsize" = "64" ; then
-   echo "CONFIG_64BIT=y" >> $config_host_mak
+   output_sym "CONFIG_64BIT"
  elif test "$wordsize" = "32" ; then
-   echo "CONFIG_32BIT=y" >> $config_host_mak
+   output_sym "CONFIG_32BIT"
  else
    echo "Unknown wordsize!"
    exit 1
  fi
  if test "$bigendian" = "yes" ; then
-   echo "CONFIG_BIG_ENDIAN=y" >> $config_host_mak
+   output_sym "CONFIG_BIG_ENDIAN"
  else
-   echo "CONFIG_LITTLE_ENDIAN=y" >> $config_host_mak
+   output_sym "CONFIG_LITTLE_ENDIAN"
  fi
  if test "$libaio" = "yes" ; then
-   echo "CONFIG_LIBAIO=y" >> $config_host_mak
+   output_sym "CONFIG_LIBAIO"
  fi
  if test "$posix_aio" = "yes" ; then
-   echo "CONFIG_POSIXAIO=y" >> $config_host_mak
+   output_sym "CONFIG_POSIXAIO"
  fi
  if test "$posix_aio_fsync" = "yes" ; then
-   echo "CONFIG_POSIXAIO_FSYNC=y" >> $config_host_mak
+   output_sym "CONFIG_POSIXAIO_FSYNC"
  fi
  if test "$linux_fallocate" = "yes" ; then
-   echo "CONFIG_LINUX_FALLOCATE=y" >> $config_host_mak
+   output_sym "CONFIG_LINUX_FALLOCATE"
  fi
  if test "$posix_fallocate" = "yes" ; then
-   echo "CONFIG_POSIX_FALLOCATE=y" >> $config_host_mak
+   output_sym "CONFIG_POSIX_FALLOCATE"
  fi
  if test "$fdatasync" = "yes" ; then
-   echo "CONFIG_FDATASYNC=y" >> $config_host_mak
+   output_sym "CONFIG_FDATASYNC"
  fi
  if test "$sync_file_range" = "yes" ; then
-   echo "CONFIG_SYNC_FILE_RANGE=y" >> $config_host_mak
+   output_sym "CONFIG_SYNC_FILE_RANGE"
  fi
  if test "$sfaa" = "yes" ; then
-   echo "CONFIG_SFAA=y" >> $config_host_mak
+   output_sym "CONFIG_SFAA"
  fi
  if test "$libverbs" = "yes" -o "rdmacm" = "yes" ; then
-   echo "CONFIG_RDMA=y" >> $config_host_mak
+   output_sym "CONFIG_RDMA"
  fi
  if test "$clock_gettime" = "yes" ; then
-   echo "CONFIG_CLOCK_GETTIME=y" >> $config_host_mak
+   output_sym "CONFIG_CLOCK_GETTIME"
  fi
  if test "$clock_monotonic" = "yes" ; then
-   echo "CONFIG_CLOCK_MONOTONIC=y" >> $config_host_mak
+   output_sym "CONFIG_CLOCK_MONOTONIC"
  fi
  if test "$clock_monotonic_precise" = "yes" ; then
-   echo "CONFIG_CLOCK_MONOTONIC_PRECISE=y" >> $config_host_mak
+   output_sym "CONFIG_CLOCK_MONOTONIC_PRECISE"
  fi
  if test "$gettimeofday" = "yes" ; then
-   echo "CONFIG_GETTIMEOFDAY=y" >> $config_host_mak
+   output_sym "CONFIG_GETTIMEOFDAY"
  fi
  if test "$posix_fadvise" = "yes" ; then
-   echo "CONFIG_POSIX_FADVISE=y" >> $config_host_mak
+   output_sym "CONFIG_POSIX_FADVISE"
  fi
  if test "$linux_3arg_affinity" = "yes" ; then
-   echo "CONFIG_3ARG_AFFINITY=y" >> $config_host_mak
+   output_sym "CONFIG_3ARG_AFFINITY"
  elif test "$linux_2arg_affinity" = "yes" ; then
-   echo "CONFIG_2ARG_AFFINITY=y" >> $config_host_mak
+   output_sym "CONFIG_2ARG_AFFINITY"
  fi
  if test "$strsep" = "yes" ; then
-   echo "CONFIG_STRSEP=y" >> $config_host_mak
+   output_sym "CONFIG_STRSEP"
  fi
  if test "$getopt_long_only" = "yes" ; then
-   echo "CONFIG_GETOPT_LONG_ONLY=y" >> $config_host_mak
+   output_sym "CONFIG_GETOPT_LONG_ONLY"
  fi
  if test "$inet_aton" = "yes" ; then
-   echo "CONFIG_INET_ATON=y" >> $config_host_mak
+   output_sym "CONFIG_INET_ATON"
  fi
  if test "$socklen_t" = "yes" ; then
-   echo "CONFIG_SOCKLEN_T=y" >> $config_host_mak
+   output_sym "CONFIG_SOCKLEN_T"
  fi
  if test "$ext4_me" = "yes" ; then
-   echo "CONFIG_LINUX_EXT4_MOVE_EXTENT=y" >> $config_host_mak
+   output_sym "CONFIG_LINUX_EXT4_MOVE_EXTENT"
  fi
  if test "$linux_splice" = "yes" ; then
-   echo "CONFIG_LINUX_SPLICE=y" >> $config_host_mak
+   output_sym "CONFIG_LINUX_SPLICE"
  fi
  if test "$guasi" = "yes" ; then
-   echo "CONFIG_GUASI=y" >> $config_host_mak
+   output_sym "CONFIG_GUASI"
  fi
  if test "$fusion_aw" = "yes" ; then
-   echo "CONFIG_FUSION_AW=y" >> $config_host_mak
+   output_sym "CONFIG_FUSION_AW"
  fi
  if test "$libnuma" = "yes" ; then
-   echo "CONFIG_LIBNUMA=y" >> $config_host_mak
+   output_sym "CONFIG_LIBNUMA"
  fi
  if test "$solaris_aio" = "yes" ; then
-   echo "CONFIG_SOLARISAIO=y" >> $config_host_mak
+   output_sym "CONFIG_SOLARISAIO"
  fi
  if test "$tls_thread" = "yes" ; then
-   echo "CONFIG_TLS_THREAD=y" >> $config_host_mak
+   output_sym "CONFIG_TLS_THREAD"
+ fi
+ 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
  
  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 filesetup.c
index c2170aae2ad44401d04231d1fdceefc8b81dabb2,6f0a876dd1b5dfc6f5b7ebe6e02324284cf136ab..8262ed3deddd71440724aad527d3480ce0c00573
@@@ -15,7 -15,7 +15,7 @@@
  #include "hash.h"
  #include "lib/axmap.h"
  
- #ifdef FIO_HAVE_LINUX_FALLOCATE
+ #ifdef CONFIG_LINUX_FALLOCATE
  #include <linux/falloc.h>
  #endif
  
@@@ -72,7 -72,7 +72,7 @@@ static int extend_file(struct thread_da
                return 1;
        }
  
- #ifdef FIO_HAVE_FALLOCATE
+ #ifdef CONFIG_POSIX_FALLOCATE
        if (!td->o.fill_device) {
                switch (td->o.fallocate_mode) {
                case FIO_FALLOCATE_NONE:
@@@ -87,7 -87,7 +87,7 @@@
                                                strerror(r));
                        }
                        break;
- #ifdef FIO_HAVE_LINUX_FALLOCATE
+ #ifdef CONFIG_LINUX_FALLOCATE
                case FIO_FALLOCATE_KEEP_SIZE:
                        dprint(FD_FILE,
                                "fallocate(FALLOC_FL_KEEP_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 /* FIO_HAVE_LINUX_FALLOCATE */
+ #endif /* CONFIG_LINUX_FALLOCATE */
                default:
                        log_err("fio: unknown fallocate mode: %d\n",
                                td->o.fallocate_mode);
                        assert(0);
                }
        }
- #endif /* FIO_HAVE_FALLOCATE */
+ #endif /* CONFIG_POSIX_FALLOCATE */
  
        if (!new_layout)
                goto done;
@@@ -919,9 -919,9 +919,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 db08aff8bdfcc07cbe51981b0380f67f1e064925,9e202996997bec070bfa01ffcc413210735ea5f3..f0babafede7119fbda8200199e7d1f36b1b4c5a0
--- 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"
  #include "gettime.h"
  #include "lib/getopt.h"
  #include "lib/rand.h"
 +#include "client.h"
  #include "server.h"
  #include "stat.h"
  #include "flow.h"
  
- #ifdef FIO_HAVE_SOLARISAIO
+ #ifdef CONFIG_SOLARISAIO
  #include <sys/asynch.h>
  #endif
  
  #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,240 +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 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 overwrite_plist;
 -      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 -338,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,10 -584,12 +342,10 @@@ 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;
@@@ -411,8 -655,6 +411,6 @@@ static inline int should_fsync(struct t
  {
        if (td->last_was_sync)
                return 0;
-       if (td->o.odirect)
-               return 0;
        if (td_write(td) || td_rw(td) || td->o.override_sync)
                return 1;
  
  /*
   * 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 *);
@@@ -440,14 -681,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
@@@ -486,11 -722,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
@@@ -599,8 -834,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 options.c
index 14616408dcd0da3953f764289b0196309220af70,1009df34936706ef433714cad12995fce8be37fc..9d49ff1702977a06d846ff82bf6800e3ddbc2c56
+++ b/options.c
@@@ -37,7 -37,7 +37,7 @@@ static int converthexchartoint(char a
  {
        int base;
  
 -      switch(a) {
 +      switch (a) {
        case '0'...'9':
                base = '0';
                break;
@@@ -50,7 -50,7 +50,7 @@@
        default:
                base = 0;
        }
 -      return (a - base);
 +      return a - base;
  }
  
  static int bs_cmp(const void *p1, const void *p2)
@@@ -61,7 -61,7 +61,7 @@@
        return bsp1->perc < bsp2->perc;
  }
  
 -static int bssplit_ddir(struct thread_data *td, int ddir, char *str)
 +static int bssplit_ddir(struct thread_options *o, int ddir, char *str)
  {
        struct bssplit *bssplit;
        unsigned int i, perc, perc_missing;
@@@ -69,7 -69,7 +69,7 @@@
        long long val;
        char *fname;
  
 -      td->o.bssplit_nr[ddir] = 4;
 +      o->bssplit_nr[ddir] = 4;
        bssplit = malloc(4 * sizeof(struct bssplit));
  
        i = 0;
@@@ -84,9 -84,9 +84,9 @@@
                /*
                 * grow struct buffer, if needed
                 */
 -              if (i == td->o.bssplit_nr[ddir]) {
 -                      td->o.bssplit_nr[ddir] <<= 1;
 -                      bssplit = realloc(bssplit, td->o.bssplit_nr[ddir]
 +              if (i == o->bssplit_nr[ddir]) {
 +                      o->bssplit_nr[ddir] <<= 1;
 +                      bssplit = realloc(bssplit, o->bssplit_nr[ddir]
                                                  * sizeof(struct bssplit));
                }
  
                } else
                        perc = -1;
  
 -              if (str_to_decimal(fname, &val, 1, td)) {
 +              if (str_to_decimal(fname, &val, 1, o)) {
                        log_err("fio: bssplit conversion failed\n");
 -                      free(td->o.bssplit);
 +                      free(o->bssplit);
                        return 1;
                }
  
                i++;
        }
  
 -      td->o.bssplit_nr[ddir] = i;
 +      o->bssplit_nr[ddir] = i;
  
        /*
         * Now check if the percentages add up, and how much is missing
         */
        perc = perc_missing = 0;
 -      for (i = 0; i < td->o.bssplit_nr[ddir]; i++) {
 +      for (i = 0; i < o->bssplit_nr[ddir]; i++) {
                struct bssplit *bsp = &bssplit[i];
  
                if (bsp->perc == (unsigned char) -1)
         * them.
         */
        if (perc_missing) {
 -              for (i = 0; i < td->o.bssplit_nr[ddir]; i++) {
 +              for (i = 0; i < o->bssplit_nr[ddir]; i++) {
                        struct bssplit *bsp = &bssplit[i];
  
                        if (bsp->perc == (unsigned char) -1)
                }
        }
  
 -      td->o.min_bs[ddir] = min_bs;
 -      td->o.max_bs[ddir] = max_bs;
 +      o->min_bs[ddir] = min_bs;
 +      o->max_bs[ddir] = max_bs;
  
        /*
         * now sort based on percentages, for ease of lookup
         */
 -      qsort(bssplit, td->o.bssplit_nr[ddir], sizeof(struct bssplit), bs_cmp);
 -      td->o.bssplit[ddir] = bssplit;
 +      qsort(bssplit, o->bssplit_nr[ddir], sizeof(struct bssplit), bs_cmp);
 +      o->bssplit[ddir] = bssplit;
        return 0;
 -
  }
  
  static int str_bssplit_cb(void *data, const char *input)
        if (odir) {
                ddir = strchr(odir + 1, ',');
                if (ddir) {
 -                      ret = bssplit_ddir(td, DDIR_TRIM, ddir + 1);
 +                      ret = bssplit_ddir(&td->o, DDIR_TRIM, ddir + 1);
                        if (!ret)
                                *ddir = '\0';
                } else {
                        char *op;
  
                        op = strdup(odir + 1);
 -                      ret = bssplit_ddir(td, DDIR_TRIM, op);
 +                      ret = bssplit_ddir(&td->o, DDIR_TRIM, op);
  
                        free(op);
                }
 -              if (!ret) 
 -                      ret = bssplit_ddir(td, DDIR_WRITE, odir + 1);
 +              if (!ret)
 +                      ret = bssplit_ddir(&td->o, DDIR_WRITE, odir + 1);
                if (!ret) {
                        *odir = '\0';
 -                      ret = bssplit_ddir(td, DDIR_READ, str);
 +                      ret = bssplit_ddir(&td->o, DDIR_READ, str);
                }
        } else {
                char *op;
  
                op = strdup(str);
 -              ret = bssplit_ddir(td, DDIR_WRITE, op);
 +              ret = bssplit_ddir(&td->o, DDIR_WRITE, op);
                free(op);
  
                if (!ret) {
                        op = strdup(str);
 -                      ret = bssplit_ddir(td, DDIR_TRIM, op);
 +                      ret = bssplit_ddir(&td->o, DDIR_TRIM, op);
                        free(op);
                }
 -              ret = bssplit_ddir(td, DDIR_READ, str);
 +              ret = bssplit_ddir(&td->o, DDIR_READ, str);
        }
  
        free(p);
  
  static int str2error(char *str)
  {
 -      const char * err[] = {"EPERM", "ENOENT", "ESRCH", "EINTR", "EIO",
 +      const char *err[] = { "EPERM", "ENOENT", "ESRCH", "EINTR", "EIO",
                            "ENXIO", "E2BIG", "ENOEXEC", "EBADF",
                            "ECHILD", "EAGAIN", "ENOMEM", "EACCES",
                            "EFAULT", "ENOTBLK", "EBUSY", "EEXIST",
                            "EXDEV", "ENODEV", "ENOTDIR", "EISDIR",
                            "EINVAL", "ENFILE", "EMFILE", "ENOTTY",
                            "ETXTBSY","EFBIG", "ENOSPC", "ESPIPE",
 -                          "EROFS","EMLINK", "EPIPE", "EDOM", "ERANGE"};
 +                          "EROFS","EMLINK", "EPIPE", "EDOM", "ERANGE" };
        int i = 0, num = sizeof(err) / sizeof(void *);
  
 -      whilei < num) {
 +      while (i < num) {
                if (!strcmp(err[i], str))
                        return i + 1;
                i++;
@@@ -311,27 -312,26 +311,27 @@@ static int str_ignore_error_cb(void *da
  static int str_rw_cb(void *data, const char *str)
  {
        struct thread_data *td = data;
 +      struct thread_options *o = &td->o;
        char *nr = get_opt_postfix(str);
  
 -      td->o.ddir_seq_nr = 1;
 -      td->o.ddir_seq_add = 0;
 +      o->ddir_seq_nr = 1;
 +      o->ddir_seq_add = 0;
  
        if (!nr)
                return 0;
  
        if (td_random(td))
 -              td->o.ddir_seq_nr = atoi(nr);
 +              o->ddir_seq_nr = atoi(nr);
        else {
                long long val;
  
 -              if (str_to_decimal(nr, &val, 1, td)) {
 +              if (str_to_decimal(nr, &val, 1, o)) {
                        log_err("fio: rw postfix parsing failed\n");
                        free(nr);
                        return 1;
                }
  
 -              td->o.ddir_seq_add = val;
 +              o->ddir_seq_add = val;
        }
  
        free(nr);
@@@ -343,7 -343,19 +343,7 @@@ static int str_mem_cb(void *data, cons
        struct thread_data *td = data;
  
        if (td->o.mem_type == MEM_MMAPHUGE || td->o.mem_type == MEM_MMAP)
 -              td->mmapfile = get_opt_postfix(mem);
 -
 -      return 0;
 -}
 -
 -static int str_verify_cb(void *data, const char *mem)
 -{
 -      struct thread_data *td = data;
 -
 -      if (td->o.verify == VERIFY_CRC32C_INTEL ||
 -          td->o.verify == VERIFY_CRC32C) {
 -              crc32c_intel_probe();
 -      }
 +              td->o.mmapfile = get_opt_postfix(mem);
  
        return 0;
  }
@@@ -358,6 -370,12 +358,6 @@@ static int fio_clock_source_cb(void *da
        return 0;
  }
  
 -static int str_lockmem_cb(void fio_unused *data, unsigned long long *val)
 -{
 -      mlock_size = *val;
 -      return 0;
 -}
 -
  static int str_rwmix_read_cb(void *data, unsigned long long *val)
  {
        struct thread_data *td = data;
@@@ -376,6 -394,40 +376,6 @@@ static int str_rwmix_write_cb(void *dat
        return 0;
  }
  
 -#ifdef FIO_HAVE_IOPRIO
 -static int str_prioclass_cb(void *data, unsigned long long *val)
 -{
 -      struct thread_data *td = data;
 -      unsigned short mask;
 -
 -      /*
 -       * mask off old class bits, str_prio_cb() may have set a default class
 -       */
 -      mask = (1 << IOPRIO_CLASS_SHIFT) - 1;
 -      td->ioprio &= mask;
 -
 -      td->ioprio |= *val << IOPRIO_CLASS_SHIFT;
 -      td->ioprio_set = 1;
 -      return 0;
 -}
 -
 -static int str_prio_cb(void *data, unsigned long long *val)
 -{
 -      struct thread_data *td = data;
 -
 -      td->ioprio |= *val;
 -
 -      /*
 -       * If no class is set, assume BE
 -       */
 -      if ((td->ioprio >> IOPRIO_CLASS_SHIFT) == 0)
 -              td->ioprio |= IOPRIO_CLASS_BE << IOPRIO_CLASS_SHIFT;
 -
 -      td->ioprio_set = 1;
 -      return 0;
 -}
 -#endif
 -
  static int str_exitall_cb(void)
  {
        exitall_on_terminate = 1;
@@@ -632,6 -684,16 +632,6 @@@ out
  }
  #endif
  
 -#ifdef FIO_HAVE_TRIM
 -static int str_verify_trim_cb(void *data, unsigned long long *val)
 -{
 -      struct thread_data *td = data;
 -
 -      td->o.trim_percentage = *val;
 -      return 0;
 -}
 -#endif
 -
  static int str_fst_cb(void *data, const char *str)
  {
        struct thread_data *td = data;
@@@ -689,14 -751,53 +689,14 @@@ static int str_random_distribution_cb(v
                        log_err("fio: zipf theta must different than 1.0\n");
                        return 1;
                }
 -              td->o.zipf_theta = val;
 +              td->o.zipf_theta.u.f = val;
        } else {
                if (val <= 0.00 || val >= 1.00) {
                        log_err("fio: pareto input out of range (0 < input < 1.0)\n");
                        return 1;
                }
 -              td->o.pareto_h = val;
 -      }
 -
 -      return 0;
 -}
 -
 -static int check_dir(struct thread_data *td, char *fname)
 -{
 -#if 0
 -      char file[PATH_MAX], *dir;
 -      int elen = 0;
 -
 -      if (td->o.directory) {
 -              strcpy(file, td->o.directory);
 -              strcat(file, "/");
 -              elen = strlen(file);
 -      }
 -
 -      sprintf(file + elen, "%s", fname);
 -      dir = dirname(file);
 -
 -      {
 -      struct stat sb;
 -      /*
 -       * We can't do this on FIO_DISKLESSIO engines. The engine isn't loaded
 -       * yet, so we can't do this check right here...
 -       */
 -      if (lstat(dir, &sb) < 0) {
 -              int ret = errno;
 -
 -              log_err("fio: %s is not a directory\n", dir);
 -              td_verror(td, ret, "lstat");
 -              return 1;
 -      }
 -
 -      if (!S_ISDIR(sb.st_mode)) {
 -              log_err("fio: %s is not a directory\n", dir);
 -              return 1;
 -      }
 +              td->o.pareto_h.u.f = val;
        }
 -#endif
  
        return 0;
  }
@@@ -763,6 -864,10 +763,6 @@@ static int str_filename_cb(void *data, 
        while ((fname = get_next_file_name(&str)) != NULL) {
                if (!strlen(fname))
                        break;
 -              if (check_dir(td, fname)) {
 -                      free(p);
 -                      return 1;
 -              }
                add_file(td, fname);
                td->o.nr_files++;
        }
@@@ -801,12 -906,25 +801,12 @@@ static int str_opendir_cb(void *data, c
        return add_dir_files(td, td->o.opendir);
  }
  
 -static int str_verify_offset_cb(void *data, unsigned long long *off)
 -{
 -      struct thread_data *td = data;
 -
 -      if (*off && *off < sizeof(struct verify_header)) {
 -              log_err("fio: verify_offset too small\n");
 -              return 1;
 -      }
 -
 -      td->o.verify_offset = *off;
 -      return 0;
 -}
 -
  static int str_verify_pattern_cb(void *data, const char *input)
  {
        struct thread_data *td = data;
        long off;
        int i = 0, j = 0, len, k, base = 10;
 -      char* loc1, * loc2;
 +      char *loc1, *loc2;
  
        loc1 = strstr(input, "0x");
        loc2 = strstr(input, "0X");
@@@ -882,6 -1000,39 +882,6 @@@ static int str_lockfile_cb(void *data, 
        return 0;
  }
  
 -static int str_write_bw_log_cb(void *data, const char *str)
 -{
 -      struct thread_data *td = data;
 -
 -      if (str)
 -              td->o.bw_log_file = strdup(str);
 -
 -      td->o.write_bw_log = 1;
 -      return 0;
 -}
 -
 -static int str_write_lat_log_cb(void *data, const char *str)
 -{
 -      struct thread_data *td = data;
 -
 -      if (str)
 -              td->o.lat_log_file = strdup(str);
 -
 -      td->o.write_lat_log = 1;
 -      return 0;
 -}
 -
 -static int str_write_iops_log_cb(void *data, const char *str)
 -{
 -      struct thread_data *td = data;
 -
 -      if (str)
 -              td->o.iops_log_file = strdup(str);
 -
 -      td->o.write_iops_log = 1;
 -      return 0;
 -}
 -
  static int str_gtod_reduce_cb(void *data, int *il)
  {
        struct thread_data *td = data;
@@@ -963,216 -1114,54 +963,216 @@@ static int kb_base_verify(struct fio_op
        return 0;
  }
  
 +/*
 + * Option grouping
 + */
 +static struct opt_group fio_opt_groups[] = {
 +      {
 +              .name   = "General",
 +              .mask   = FIO_OPT_C_GENERAL,
 +      },
 +      {
 +              .name   = "I/O",
 +              .mask   = FIO_OPT_C_IO,
 +      },
 +      {
 +              .name   = "File",
 +              .mask   = FIO_OPT_C_FILE,
 +      },
 +      {
 +              .name   = "Statistics",
 +              .mask   = FIO_OPT_C_STAT,
 +      },
 +      {
 +              .name   = "Logging",
 +              .mask   = FIO_OPT_C_LOG,
 +      },
 +      {
 +              .name   = "Profiles",
 +              .mask   = FIO_OPT_C_PROFILE,
 +      },
 +      {
 +              .name   = NULL,
 +      },
 +};
 +
 +static struct opt_group *__opt_group_from_mask(struct opt_group *ogs, unsigned int *mask,
 +                                             unsigned int inv_mask)
 +{
 +      struct opt_group *og;
 +      int i;
 +
 +      if (*mask == inv_mask || !*mask)
 +              return NULL;
 +
 +      for (i = 0; ogs[i].name; i++) {
 +              og = &ogs[i];
 +
 +              if (*mask & og->mask) {
 +                      *mask &= ~(og->mask);
 +                      return og;
 +              }
 +      }
 +
 +      return NULL;
 +}
 +
 +struct opt_group *opt_group_from_mask(unsigned int *mask)
 +{
 +      return __opt_group_from_mask(fio_opt_groups, mask, FIO_OPT_C_INVALID);
 +}
 +
 +static struct opt_group fio_opt_cat_groups[] = {
 +      {
 +              .name   = "Rate",
 +              .mask   = FIO_OPT_G_RATE,
 +      },
 +      {
 +              .name   = "Zone",
 +              .mask   = FIO_OPT_G_ZONE,
 +      },
 +      {
 +              .name   = "Read/write mix",
 +              .mask   = FIO_OPT_G_RWMIX,
 +      },
 +      {
 +              .name   = "Verify",
 +              .mask   = FIO_OPT_G_VERIFY,
 +      },
 +      {
 +              .name   = "Trim",
 +              .mask   = FIO_OPT_G_TRIM,
 +      },
 +      {
 +              .name   = "I/O Logging",
 +              .mask   = FIO_OPT_G_IOLOG,
 +      },
 +      {
 +              .name   = "I/O Depth",
 +              .mask   = FIO_OPT_G_IO_DEPTH,
 +      },
 +      {
 +              .name   = "I/O Flow",
 +              .mask   = FIO_OPT_G_IO_FLOW,
 +      },
 +      {
 +              .name   = "Description",
 +              .mask   = FIO_OPT_G_DESC,
 +      },
 +      {
 +              .name   = "Filename",
 +              .mask   = FIO_OPT_G_FILENAME,
 +      },
 +      {
 +              .name   = "General I/O",
 +              .mask   = FIO_OPT_G_IO_BASIC,
 +      },
 +      {
 +              .name   = "Cgroups",
 +              .mask   = FIO_OPT_G_CGROUP,
 +      },
 +      {
 +              .name   = "Runtime",
 +              .mask   = FIO_OPT_G_RUNTIME,
 +      },
 +      {
 +              .name   = "Process",
 +              .mask   = FIO_OPT_G_PROCESS,
 +      },
 +      {
 +              .name   = "Job credentials / priority",
 +              .mask   = FIO_OPT_G_CRED,
 +      },
 +      {
 +              .name   = "Clock settings",
 +              .mask   = FIO_OPT_G_CLOCK,
 +      },
 +      {
 +              .name   = "I/O Type",
 +              .mask   = FIO_OPT_G_IO_TYPE,
 +      },
 +      {
 +              .name   = "I/O Thinktime",
 +              .mask   = FIO_OPT_G_THINKTIME,
 +      },
 +      {
 +              .name   = "Randomizations",
 +              .mask   = FIO_OPT_G_RANDOM,
 +      },
 +      {
 +              .name   = "I/O buffers",
 +              .mask   = FIO_OPT_G_IO_BUF,
 +      },
 +      {
 +              .name   = "Tiobench profile",
 +              .mask   = FIO_OPT_G_TIOBENCH,
 +      },
 +
 +      {
 +              .name   = NULL,
 +      }
 +};
 +
 +struct opt_group *opt_group_cat_from_mask(unsigned int *mask)
 +{
 +      return __opt_group_from_mask(fio_opt_cat_groups, mask, FIO_OPT_G_INVALID);
 +}
 +
  /*
   * Map of job/command line options
   */
 -static struct fio_option options[FIO_MAX_OPTS] = {
 +struct fio_option fio_options[FIO_MAX_OPTS] = {
        {
                .name   = "description",
 +              .lname  = "Description of job",
                .type   = FIO_OPT_STR_STORE,
                .off1   = td_var_offset(description),
                .help   = "Text job description",
 +              .category = FIO_OPT_C_GENERAL,
 +              .group  = FIO_OPT_G_DESC,
        },
        {
                .name   = "name",
 +              .lname  = "Job name",
                .type   = FIO_OPT_STR_STORE,
                .off1   = td_var_offset(name),
                .help   = "Name of this job",
 -      },
 -      {
 -              .name   = "directory",
 -              .type   = FIO_OPT_STR_STORE,
 -              .off1   = td_var_offset(directory),
 -              .cb     = str_directory_cb,
 -              .help   = "Directory to store files in",
 +              .category = FIO_OPT_C_GENERAL,
 +              .group  = FIO_OPT_G_DESC,
        },
        {
                .name   = "filename",
 +              .lname  = "Filename(s)",
                .type   = FIO_OPT_STR_STORE,
                .off1   = td_var_offset(filename),
                .cb     = str_filename_cb,
                .prio   = -1, /* must come after "directory" */
                .help   = "File(s) to use for the workload",
 +              .category = FIO_OPT_C_FILE,
 +              .group  = FIO_OPT_G_FILENAME,
        },
        {
 -              .name   = "kb_base",
 -              .type   = FIO_OPT_INT,
 -              .off1   = td_var_offset(kb_base),
 -              .verify = kb_base_verify,
 -              .prio   = 1,
 -              .def    = "1024",
 -              .help   = "How many bytes per KB for reporting (1000 or 1024)",
 +              .name   = "directory",
 +              .lname  = "Directory",
 +              .type   = FIO_OPT_STR_STORE,
 +              .off1   = td_var_offset(directory),
 +              .cb     = str_directory_cb,
 +              .help   = "Directory to store files in",
 +              .category = FIO_OPT_C_FILE,
 +              .group  = FIO_OPT_G_FILENAME,
        },
        {
                .name   = "lockfile",
 +              .lname  = "Lockfile",
                .type   = FIO_OPT_STR,
                .cb     = str_lockfile_cb,
                .off1   = td_var_offset(file_lock_mode),
                .help   = "Lock file when doing IO to it",
                .parent = "filename",
 +              .hide   = 0,
                .def    = "none",
 +              .category = FIO_OPT_C_FILE,
 +              .group  = FIO_OPT_G_FILENAME,
                .posval = {
                          { .ival = "none",
                            .oval = FILE_LOCK_NONE,
        },
        {
                .name   = "opendir",
 +              .lname  = "Open directory",
                .type   = FIO_OPT_STR_STORE,
                .off1   = td_var_offset(opendir),
                .cb     = str_opendir_cb,
                .help   = "Recursively add files from this directory and down",
 +              .category = FIO_OPT_C_FILE,
 +              .group  = FIO_OPT_G_FILENAME,
        },
        {
                .name   = "rw",
 +              .lname  = "Read/write",
                .alias  = "readwrite",
                .type   = FIO_OPT_STR,
                .cb     = str_rw_cb,
                .help   = "IO direction",
                .def    = "read",
                .verify = rw_verify,
 +              .category = FIO_OPT_C_IO,
 +              .group  = FIO_OPT_G_IO_BASIC,
                .posval = {
                          { .ival = "read",
                            .oval = TD_DDIR_READ,
        },
        {
                .name   = "rw_sequencer",
 +              .lname  = "RW Sequencer",
                .type   = FIO_OPT_STR,
                .off1   = td_var_offset(rw_seq),
                .help   = "IO offset generator modifier",
                .def    = "sequential",
 +              .category = FIO_OPT_C_IO,
 +              .group  = FIO_OPT_G_IO_BASIC,
                .posval = {
                          { .ival = "sequential",
                            .oval = RW_SEQ_SEQ,
  
        {
                .name   = "ioengine",
 +              .lname  = "IO Engine",
                .type   = FIO_OPT_STR_STORE,
                .off1   = td_var_offset(ioengine),
                .help   = "IO engine to use",
                .def    = FIO_PREFERRED_ENGINE,
 +              .category = FIO_OPT_C_IO,
 +              .group  = FIO_OPT_G_IO_BASIC,
                .posval = {
                          { .ival = "sync",
                            .help = "Use read/write",
                            .help = "POSIX asynchronous IO",
                          },
  #endif
- #ifdef FIO_HAVE_SOLARISAIO
+ #ifdef CONFIG_SOLARISAIO
                          { .ival = "solarisaio",
                            .help = "Solaris native asynchronous IO",
                          },
  #endif
- #ifdef FIO_HAVE_WINDOWSAIO
+ #ifdef CONFIG_WINDOWSAIO
                          { .ival = "windowsaio",
                            .help = "Windows native asynchronous IO"
                          },
                            .help = "Fusion-io atomic write engine",
                          },
  #endif
- #ifdef FIO_HAVE_E4_ENG
+ #ifdef CONFIG_LINUX_EXT4_MOVE_EXTENT
                          { .ival = "e4defrag",
                            .help = "ext4 defrag engine",
                          },
  #endif
- #ifdef FIO_HAVE_FALLOC_ENG
+ #ifdef CONFIG_LINUX_FALLOCATE
                          { .ival = "falloc",
                            .help = "fallocate() file based engine",
                          },
        },
        {
                .name   = "iodepth",
 +              .lname  = "IO Depth",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(iodepth),
                .help   = "Number of IO buffers to keep in flight",
                .minval = 1,
 +              .interval = 1,
                .def    = "1",
 +              .category = FIO_OPT_C_IO,
 +              .group  = FIO_OPT_G_IO_BASIC,
        },
        {
                .name   = "iodepth_batch",
 +              .lname  = "IO Depth batch",
                .alias  = "iodepth_batch_submit",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(iodepth_batch),
                .help   = "Number of IO buffers to submit in one go",
                .parent = "iodepth",
 +              .hide   = 1,
                .minval = 1,
 +              .interval = 1,
                .def    = "1",
 +              .category = FIO_OPT_C_IO,
 +              .group  = FIO_OPT_G_IO_BASIC,
        },
        {
                .name   = "iodepth_batch_complete",
 +              .lname  = "IO Depth batch complete",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(iodepth_batch_complete),
                .help   = "Number of IO buffers to retrieve in one go",
                .parent = "iodepth",
 +              .hide   = 1,
                .minval = 0,
 +              .interval = 1,
                .def    = "1",
 +              .category = FIO_OPT_C_IO,
 +              .group  = FIO_OPT_G_IO_BASIC,
        },
        {
                .name   = "iodepth_low",
 +              .lname  = "IO Depth batch low",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(iodepth_low),
                .help   = "Low water mark for queuing depth",
                .parent = "iodepth",
 +              .hide   = 1,
 +              .interval = 1,
 +              .category = FIO_OPT_C_IO,
 +              .group  = FIO_OPT_G_IO_BASIC,
        },
        {
                .name   = "size",
 +              .lname  = "Size",
                .type   = FIO_OPT_STR_VAL,
                .cb     = str_size_cb,
                .help   = "Total size of device or files",
 +              .interval = 1024 * 1024,
 +              .category = FIO_OPT_C_IO,
 +              .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "fill_device",
 +              .lname  = "Fill device",
                .alias  = "fill_fs",
                .type   = FIO_OPT_BOOL,
                .off1   = td_var_offset(fill_device),
                .help   = "Write until an ENOSPC error occurs",
                .def    = "0",
 +              .category = FIO_OPT_C_FILE,
 +              .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "filesize",
 +              .lname  = "File size",
                .type   = FIO_OPT_STR_VAL,
                .off1   = td_var_offset(file_size_low),
                .off2   = td_var_offset(file_size_high),
                .minval = 1,
                .help   = "Size of individual files",
 +              .interval = 1024 * 1024,
 +              .category = FIO_OPT_C_FILE,
 +              .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "offset",
 +              .lname  = "IO offset",
                .alias  = "fileoffset",
                .type   = FIO_OPT_STR_VAL,
                .off1   = td_var_offset(start_offset),
                .help   = "Start IO from this offset",
                .def    = "0",
 +              .interval = 1024 * 1024,
 +              .category = FIO_OPT_C_IO,
 +              .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "offset_increment",
 +              .lname  = "IO offset increment",
                .type   = FIO_OPT_STR_VAL,
                .off1   = td_var_offset(offset_increment),
                .help   = "What is the increment from one offset to the next",
                .parent = "offset",
 +              .hide   = 1,
                .def    = "0",
 +              .interval = 1024 * 1024,
 +              .category = FIO_OPT_C_IO,
 +              .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "bs",
 +              .lname  = "Block size",
                .alias  = "blocksize",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(bs[DDIR_READ]),
                .help   = "Block size unit",
                .def    = "4k",
                .parent = "rw",
 +              .hide   = 1,
 +              .interval = 512,
 +              .category = FIO_OPT_C_IO,
 +              .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "ba",
 +              .lname  = "Block size align",
                .alias  = "blockalign",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(ba[DDIR_READ]),
                .minval = 1,
                .help   = "IO block offset alignment",
                .parent = "rw",
 +              .hide   = 1,
 +              .interval = 512,
 +              .category = FIO_OPT_C_IO,
 +              .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "bsrange",
 +              .lname  = "Block size range",
                .alias  = "blocksize_range",
                .type   = FIO_OPT_RANGE,
                .off1   = td_var_offset(min_bs[DDIR_READ]),
                .minval = 1,
                .help   = "Set block size range (in more detail than bs)",
                .parent = "rw",
 +              .hide   = 1,
 +              .interval = 4096,
 +              .category = FIO_OPT_C_IO,
 +              .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "bssplit",
 +              .lname  = "Block size split",
                .type   = FIO_OPT_STR,
                .cb     = str_bssplit_cb,
                .help   = "Set a specific mix of block sizes",
                .parent = "rw",
 +              .hide   = 1,
 +              .category = FIO_OPT_C_IO,
 +              .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "bs_unaligned",
 +              .lname  = "Block size unaligned",
                .alias  = "blocksize_unaligned",
                .type   = FIO_OPT_STR_SET,
                .off1   = td_var_offset(bs_unaligned),
                .help   = "Don't sector align IO buffer sizes",
                .parent = "rw",
 +              .hide   = 1,
 +              .category = FIO_OPT_C_IO,
 +              .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "randrepeat",
 +              .lname  = "Random repeatable",
                .type   = FIO_OPT_BOOL,
                .off1   = td_var_offset(rand_repeatable),
                .help   = "Use repeatable random IO pattern",
                .def    = "1",
                .parent = "rw",
 +              .hide   = 1,
 +              .category = FIO_OPT_C_IO,
 +              .group  = FIO_OPT_G_RANDOM,
        },
        {
                .name   = "use_os_rand",
 +              .lname  = "Use OS random",
                .type   = FIO_OPT_BOOL,
                .off1   = td_var_offset(use_os_rand),
                .help   = "Set to use OS random generator",
                .def    = "0",
                .parent = "rw",
 +              .hide   = 1,
 +              .category = FIO_OPT_C_IO,
 +              .group  = FIO_OPT_G_RANDOM,
        },
        {
                .name   = "norandommap",
 +              .lname  = "No randommap",
                .type   = FIO_OPT_STR_SET,
                .off1   = td_var_offset(norandommap),
                .help   = "Accept potential duplicate random blocks",
                .parent = "rw",
 +              .hide   = 1,
 +              .hide_on_set = 1,
 +              .category = FIO_OPT_C_IO,
 +              .group  = FIO_OPT_G_RANDOM,
        },
        {
                .name   = "softrandommap",
 +              .lname  = "Soft randommap",
                .type   = FIO_OPT_BOOL,
                .off1   = td_var_offset(softrandommap),
                .help   = "Set norandommap if randommap allocation fails",
                .parent = "norandommap",
 +              .hide   = 1,
                .def    = "0",
 +              .category = FIO_OPT_C_IO,
 +              .group  = FIO_OPT_G_RANDOM,
        },
        {
                .name   = "random_generator",
                            .help = "Variable length LFSR",
                          },
                },
 +              .category = FIO_OPT_C_IO,
 +              .group  = FIO_OPT_G_RANDOM,
        },
        {
                .name   = "random_distribution",
                            .help = "Pareto distribution",
                          },
                },
 +              .category = FIO_OPT_C_IO,
 +              .group  = FIO_OPT_G_RANDOM,
        },
        {
                .name   = "nrfiles",
 +              .lname  = "Number of files",
                .alias  = "nr_files",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(nr_files),
                .help   = "Split job workload between this number of files",
                .def    = "1",
 +              .interval = 1,
 +              .category = FIO_OPT_C_FILE,
 +              .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "openfiles",
 +              .lname  = "Number of open files",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(open_files),
                .help   = "Number of files to keep open at the same time",
 +              .category = FIO_OPT_C_FILE,
 +              .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "file_service_type",
 +              .lname  = "File service type",
                .type   = FIO_OPT_STR,
                .cb     = str_fst_cb,
                .off1   = td_var_offset(file_service_type),
                .help   = "How to select which file to service next",
                .def    = "roundrobin",
 +              .category = FIO_OPT_C_FILE,
 +              .group  = FIO_OPT_G_INVALID,
                .posval = {
                          { .ival = "random",
                            .oval = FIO_FSERVICE_RANDOM,
                          },
                },
                .parent = "nrfiles",
 +              .hide   = 1,
        },
- #ifdef FIO_HAVE_FALLOCATE
+ #ifdef CONFIG_POSIX_FALLOCATE
        {
                .name   = "fallocate",
 +              .lname  = "Fallocate",
                .type   = FIO_OPT_STR,
                .off1   = td_var_offset(fallocate_mode),
                .help   = "Whether pre-allocation is performed when laying out files",
                .def    = "posix",
 +              .category = FIO_OPT_C_FILE,
 +              .group  = FIO_OPT_G_INVALID,
                .posval = {
                          { .ival = "none",
                            .oval = FIO_FALLOCATE_NONE,
                            .oval = FIO_FALLOCATE_POSIX,
                            .help = "Use posix_fallocate()",
                          },
- #ifdef FIO_HAVE_LINUX_FALLOCATE
+ #ifdef CONFIG_LINUX_FALLOCATE
                          { .ival = "keep",
                            .oval = FIO_FALLOCATE_KEEP_SIZE,
                            .help = "Use fallocate(..., FALLOC_FL_KEEP_SIZE, ...)",
                          },
                },
        },
- #endif        /* FIO_HAVE_FALLOCATE */
+ #endif        /* CONFIG_POSIX_FALLOCATE */
        {
                .name   = "fadvise_hint",
 +              .lname  = "Fadvise hint",
                .type   = FIO_OPT_BOOL,
                .off1   = td_var_offset(fadvise_hint),
                .help   = "Use fadvise() to advise the kernel on IO pattern",
                .def    = "1",
 +              .category = FIO_OPT_C_FILE,
 +              .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "fsync",
 +              .lname  = "Fsync",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(fsync_blocks),
                .help   = "Issue fsync for writes every given number of blocks",
                .def    = "0",
 +              .interval = 1,
 +              .category = FIO_OPT_C_FILE,
 +              .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "fdatasync",
 +              .lname  = "Fdatasync",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(fdatasync_blocks),
                .help   = "Issue fdatasync for writes every given number of blocks",
                .def    = "0",
 +              .interval = 1,
 +              .category = FIO_OPT_C_FILE,
 +              .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "write_barrier",
 +              .lname  = "Write barrier",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(barrier_blocks),
                .help   = "Make every Nth write a barrier write",
                .def    = "0",
 +              .interval = 1,
 +              .category = FIO_OPT_C_IO,
 +              .group  = FIO_OPT_G_INVALID,
        },
  #ifdef CONFIG_SYNC_FILE_RANGE
        {
                .name   = "sync_file_range",
 +              .lname  = "Sync file range",
                .posval = {
                          { .ival = "wait_before",
                            .oval = SYNC_FILE_RANGE_WAIT_BEFORE,
                .cb     = str_sfr_cb,
                .off1   = td_var_offset(sync_file_range),
                .help   = "Use sync_file_range()",
 +              .category = FIO_OPT_C_FILE,
 +              .group  = FIO_OPT_G_INVALID,
        },
  #endif
        {
                .name   = "direct",
 +              .lname  = "Direct I/O",
                .type   = FIO_OPT_BOOL,
                .off1   = td_var_offset(odirect),
                .help   = "Use O_DIRECT IO (negates buffered)",
                .def    = "0",
 +              .inverse = "buffered",
 +              .category = FIO_OPT_C_IO,
 +              .group  = FIO_OPT_G_IO_TYPE,
        },
        {
                .name   = "buffered",
 +              .lname  = "Buffered I/O",
                .type   = FIO_OPT_BOOL,
                .off1   = td_var_offset(odirect),
                .neg    = 1,
                .help   = "Use buffered IO (negates direct)",
                .def    = "1",
 +              .inverse = "direct",
 +              .category = FIO_OPT_C_IO,
 +              .group  = FIO_OPT_G_IO_TYPE,
        },
        {
                .name   = "overwrite",
 +              .lname  = "Overwrite",
                .type   = FIO_OPT_BOOL,
                .off1   = td_var_offset(overwrite),
                .help   = "When writing, set whether to overwrite current data",
                .def    = "0",
 +              .category = FIO_OPT_C_FILE,
 +              .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "loops",
 +              .lname  = "Loops",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(loops),
                .help   = "Number of times to run the job",
                .def    = "1",
 +              .interval = 1,
 +              .category = FIO_OPT_C_GENERAL,
 +              .group  = FIO_OPT_G_RUNTIME,
        },
        {
                .name   = "numjobs",
 +              .lname  = "Number of jobs",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(numjobs),
                .help   = "Duplicate this job this many times",
                .def    = "1",
 +              .interval = 1,
 +              .category = FIO_OPT_C_GENERAL,
 +              .group  = FIO_OPT_G_RUNTIME,
        },
        {
                .name   = "startdelay",
 +              .lname  = "Start delay",
                .type   = FIO_OPT_STR_VAL_TIME,
                .off1   = td_var_offset(start_delay),
                .help   = "Only start job when this period has passed",
                .def    = "0",
 +              .category = FIO_OPT_C_GENERAL,
 +              .group  = FIO_OPT_G_RUNTIME,
        },
        {
                .name   = "runtime",
 +              .lname  = "Runtime",
                .alias  = "timeout",
                .type   = FIO_OPT_STR_VAL_TIME,
                .off1   = td_var_offset(timeout),
                .help   = "Stop workload when this amount of time has passed",
                .def    = "0",
 +              .category = FIO_OPT_C_GENERAL,
 +              .group  = FIO_OPT_G_RUNTIME,
        },
        {
                .name   = "time_based",
 +              .lname  = "Time based",
                .type   = FIO_OPT_STR_SET,
                .off1   = td_var_offset(time_based),
                .help   = "Keep running until runtime/timeout is met",
 +              .category = FIO_OPT_C_GENERAL,
 +              .group  = FIO_OPT_G_RUNTIME,
        },
        {
                .name   = "ramp_time",
 +              .lname  = "Ramp time",
                .type   = FIO_OPT_STR_VAL_TIME,
                .off1   = td_var_offset(ramp_time),
                .help   = "Ramp up time before measuring performance",
 +              .category = FIO_OPT_C_GENERAL,
 +              .group  = FIO_OPT_G_RUNTIME,
        },
        {
                .name   = "clocksource",
 +              .lname  = "Clock source",
                .type   = FIO_OPT_STR,
                .cb     = fio_clock_source_cb,
                .off1   = td_var_offset(clocksource),
                .help   = "What type of timing source to use",
 +              .category = FIO_OPT_C_GENERAL,
 +              .group  = FIO_OPT_G_CLOCK,
                .posval = {
  #ifdef CONFIG_GETTIMEOFDAY
                          { .ival = "gettimeofday",
        {
                .name   = "mem",
                .alias  = "iomem",
 +              .lname  = "I/O Memory",
                .type   = FIO_OPT_STR,
                .cb     = str_mem_cb,
                .off1   = td_var_offset(mem_type),
                .help   = "Backing type for IO buffers",
                .def    = "malloc",
 +              .category = FIO_OPT_C_IO,
 +              .group  = FIO_OPT_G_INVALID,
                .posval = {
                          { .ival = "malloc",
                            .oval = MEM_MALLOC,
        {
                .name   = "iomem_align",
                .alias  = "mem_align",
 +              .lname  = "I/O memory alignment",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(mem_align),
                .minval = 0,
                .help   = "IO memory buffer offset alignment",
                .def    = "0",
                .parent = "iomem",
 +              .hide   = 1,
 +              .category = FIO_OPT_C_IO,
 +              .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "verify",
 +              .lname  = "Verify",
                .type   = FIO_OPT_STR,
                .off1   = td_var_offset(verify),
                .help   = "Verify data written",
 -              .cb     = str_verify_cb,
                .def    = "0",
 +              .category = FIO_OPT_C_IO,
 +              .group  = FIO_OPT_G_VERIFY,
                .posval = {
                          { .ival = "0",
                            .oval = VERIFY_NONE,
        },
        {
                .name   = "do_verify",
 +              .lname  = "Perform verify step",
                .type   = FIO_OPT_BOOL,
                .off1   = td_var_offset(do_verify),
                .help   = "Run verification stage after write",
                .def    = "1",
                .parent = "verify",
 +              .hide   = 1,
 +              .category = FIO_OPT_C_IO,
 +              .group  = FIO_OPT_G_VERIFY,
        },
        {
                .name   = "verifysort",
 +              .lname  = "Verify sort",
                .type   = FIO_OPT_BOOL,
                .off1   = td_var_offset(verifysort),
                .help   = "Sort written verify blocks for read back",
                .def    = "1",
                .parent = "verify",
 +              .hide   = 1,
 +              .category = FIO_OPT_C_IO,
 +              .group  = FIO_OPT_G_VERIFY,
        },
        {
                .name   = "verifysort_nr",
                .maxval = 131072,
                .def    = "1024",
                .parent = "verify",
 +              .category = FIO_OPT_C_IO,
 +              .group  = FIO_OPT_G_VERIFY,
        },
        {
                .name   = "verify_interval",
 +              .lname  = "Verify interval",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(verify_interval),
                .minval = 2 * sizeof(struct verify_header),
                .help   = "Store verify buffer header every N bytes",
                .parent = "verify",
 +              .hide   = 1,
 +              .interval = 2 * sizeof(struct verify_header),
 +              .category = FIO_OPT_C_IO,
 +              .group  = FIO_OPT_G_VERIFY,
        },
        {
                .name   = "verify_offset",
 +              .lname  = "Verify offset",
                .type   = FIO_OPT_INT,
                .help   = "Offset verify header location by N bytes",
 -              .def    = "0",
 -              .cb     = str_verify_offset_cb,
 +              .off1   = td_var_offset(verify_offset),
 +              .minval = sizeof(struct verify_header),
                .parent = "verify",
 +              .hide   = 1,
 +              .category = FIO_OPT_C_IO,
 +              .group  = FIO_OPT_G_VERIFY,
        },
        {
                .name   = "verify_pattern",
 +              .lname  = "Verify pattern",
                .type   = FIO_OPT_STR,
                .cb     = str_verify_pattern_cb,
                .help   = "Fill pattern for IO buffers",
                .parent = "verify",
 +              .hide   = 1,
 +              .category = FIO_OPT_C_IO,
 +              .group  = FIO_OPT_G_VERIFY,
        },
        {
                .name   = "verify_fatal",
 +              .lname  = "Verify fatal",
                .type   = FIO_OPT_BOOL,
                .off1   = td_var_offset(verify_fatal),
                .def    = "0",
                .help   = "Exit on a single verify failure, don't continue",
                .parent = "verify",
 +              .hide   = 1,
 +              .category = FIO_OPT_C_IO,
 +              .group  = FIO_OPT_G_VERIFY,
        },
        {
                .name   = "verify_dump",
 +              .lname  = "Verify dump",
                .type   = FIO_OPT_BOOL,
                .off1   = td_var_offset(verify_dump),
                .def    = "0",
                .help   = "Dump contents of good and bad blocks on failure",
                .parent = "verify",
 +              .hide   = 1,
 +              .category = FIO_OPT_C_IO,
 +              .group  = FIO_OPT_G_VERIFY,
        },
        {
                .name   = "verify_async",
 +              .lname  = "Verify asynchronously",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(verify_async),
                .def    = "0",
                .help   = "Number of async verifier threads to use",
                .parent = "verify",
 +              .hide   = 1,
 +              .category = FIO_OPT_C_IO,
 +              .group  = FIO_OPT_G_VERIFY,
        },
        {
                .name   = "verify_backlog",
 +              .lname  = "Verify backlog",
                .type   = FIO_OPT_STR_VAL,
                .off1   = td_var_offset(verify_backlog),
                .help   = "Verify after this number of blocks are written",
                .parent = "verify",
 +              .hide   = 1,
 +              .category = FIO_OPT_C_IO,
 +              .group  = FIO_OPT_G_VERIFY,
        },
        {
                .name   = "verify_backlog_batch",
 +              .lname  = "Verify backlog batch",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(verify_batch),
                .help   = "Verify this number of IO blocks",
                .parent = "verify",
 +              .hide   = 1,
 +              .category = FIO_OPT_C_IO,
 +              .group  = FIO_OPT_G_VERIFY,
        },
  #ifdef FIO_HAVE_CPU_AFFINITY
        {
                .name   = "verify_async_cpus",
 +              .lname  = "Async verify CPUs",
                .type   = FIO_OPT_STR,
                .cb     = str_verify_cpus_allowed_cb,
                .help   = "Set CPUs allowed for async verify threads",
                .parent = "verify_async",
 +              .hide   = 1,
 +              .category = FIO_OPT_C_IO,
 +              .group  = FIO_OPT_G_VERIFY,
        },
  #endif
        {
                .name   = "experimental_verify",
                .off1   = td_var_offset(experimental_verify),
                .type   = FIO_OPT_BOOL,
+               .help   = "Enable experimental verification",
 +              .category = FIO_OPT_C_IO,
 +              .group  = FIO_OPT_G_VERIFY,
        },
  #ifdef FIO_HAVE_TRIM
        {
                .name   = "trim_percentage",
 +              .lname  = "Trim percentage",
                .type   = FIO_OPT_INT,
 -              .cb     = str_verify_trim_cb,
 +              .off1   = td_var_offset(trim_percentage),
 +              .minval = 0,
                .maxval = 100,
                .help   = "Number of verify blocks to discard/trim",
                .parent = "verify",
                .def    = "0",
 +              .interval = 1,
 +              .hide   = 1,
 +              .category = FIO_OPT_C_IO,
 +              .group  = FIO_OPT_G_TRIM,
        },
        {
                .name   = "trim_verify_zero",
 -              .type   = FIO_OPT_INT,
 +              .lname  = "Verify trim zero",
 +              .type   = FIO_OPT_BOOL,
                .help   = "Verify that trim/discarded blocks are returned as zeroes",
                .off1   = td_var_offset(trim_zero),
                .parent = "trim_percentage",
 +              .hide   = 1,
                .def    = "1",
 +              .category = FIO_OPT_C_IO,
 +              .group  = FIO_OPT_G_TRIM,
        },
        {
                .name   = "trim_backlog",
 +              .lname  = "Trim backlog",
                .type   = FIO_OPT_STR_VAL,
                .off1   = td_var_offset(trim_backlog),
                .help   = "Trim after this number of blocks are written",
                .parent = "trim_percentage",
 +              .hide   = 1,
 +              .interval = 1,
 +              .category = FIO_OPT_C_IO,
 +              .group  = FIO_OPT_G_TRIM,
        },
        {
                .name   = "trim_backlog_batch",
 +              .lname  = "Trim backlog batch",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(trim_batch),
                .help   = "Trim this number of IO blocks",
                .parent = "trim_percentage",
 +              .hide   = 1,
 +              .interval = 1,
 +              .category = FIO_OPT_C_IO,
 +              .group  = FIO_OPT_G_TRIM,
        },
  #endif
        {
                .name   = "write_iolog",
 +              .lname  = "Write I/O log",
                .type   = FIO_OPT_STR_STORE,
                .off1   = td_var_offset(write_iolog_file),
                .help   = "Store IO pattern to file",
 +              .category = FIO_OPT_C_IO,
 +              .group  = FIO_OPT_G_IOLOG,
        },
        {
                .name   = "read_iolog",
 +              .lname  = "Read I/O log",
                .type   = FIO_OPT_STR_STORE,
                .off1   = td_var_offset(read_iolog_file),
                .help   = "Playback IO pattern from file",
 +              .category = FIO_OPT_C_IO,
 +              .group  = FIO_OPT_G_IOLOG,
        },
        {
                .name   = "replay_no_stall",
 -              .type   = FIO_OPT_INT,
 +              .lname  = "Don't stall on replay",
 +              .type   = FIO_OPT_BOOL,
                .off1   = td_var_offset(no_stall),
                .def    = "0",
                .parent = "read_iolog",
 +              .hide   = 1,
                .help   = "Playback IO pattern file as fast as possible without stalls",
 +              .category = FIO_OPT_C_IO,
 +              .group  = FIO_OPT_G_IOLOG,
        },
        {
                .name   = "replay_redirect",
 +              .lname  = "Redirect device for replay",
                .type   = FIO_OPT_STR_STORE,
                .off1   = td_var_offset(replay_redirect),
                .parent = "read_iolog",
 +              .hide   = 1,
                .help   = "Replay all I/O onto this device, regardless of trace device",
 +              .category = FIO_OPT_C_IO,
 +              .group  = FIO_OPT_G_IOLOG,
        },
        {
                .name   = "exec_prerun",
 +              .lname  = "Pre-execute runnable",
                .type   = FIO_OPT_STR_STORE,
                .off1   = td_var_offset(exec_prerun),
                .help   = "Execute this file prior to running job",
 +              .category = FIO_OPT_C_GENERAL,
 +              .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "exec_postrun",
 +              .lname  = "Post-execute runnable",
                .type   = FIO_OPT_STR_STORE,
                .off1   = td_var_offset(exec_postrun),
                .help   = "Execute this file after running job",
 +              .category = FIO_OPT_C_GENERAL,
 +              .group  = FIO_OPT_G_INVALID,
        },
  #ifdef FIO_HAVE_IOSCHED_SWITCH
        {
                .name   = "ioscheduler",
 +              .lname  = "I/O scheduler",
                .type   = FIO_OPT_STR_STORE,
                .off1   = td_var_offset(ioscheduler),
                .help   = "Use this IO scheduler on the backing device",
 +              .category = FIO_OPT_C_FILE,
 +              .group  = FIO_OPT_G_INVALID,
        },
  #endif
        {
                .name   = "zonesize",
 +              .lname  = "Zone size",
                .type   = FIO_OPT_STR_VAL,
                .off1   = td_var_offset(zone_size),
                .help   = "Amount of data to read per zone",
                .def    = "0",
 +              .interval = 1024 * 1024,
 +              .category = FIO_OPT_C_IO,
 +              .group  = FIO_OPT_G_ZONE,
        },
        {
                .name   = "zonerange",
 +              .lname  = "Zone range",
                .type   = FIO_OPT_STR_VAL,
                .off1   = td_var_offset(zone_range),
                .help   = "Give size of an IO zone",
                .def    = "0",
 +              .interval = 1024 * 1024,
 +              .category = FIO_OPT_C_IO,
 +              .group  = FIO_OPT_G_ZONE,
        },
        {
                .name   = "zoneskip",
 +              .lname  = "Zone skip",
                .type   = FIO_OPT_STR_VAL,
                .off1   = td_var_offset(zone_skip),
                .help   = "Space between IO zones",
                .def    = "0",
 +              .interval = 1024 * 1024,
 +              .category = FIO_OPT_C_IO,
 +              .group  = FIO_OPT_G_ZONE,
        },
        {
                .name   = "lockmem",
 +              .lname  = "Lock memory",
                .type   = FIO_OPT_STR_VAL,
 -              .cb     = str_lockmem_cb,
 +              .off1   = td_var_offset(lockmem),
                .help   = "Lock down this amount of memory",
                .def    = "0",
 +              .interval = 1024 * 1024,
 +              .category = FIO_OPT_C_GENERAL,
 +              .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "rwmixread",
 +              .lname  = "Read/write mix read",
                .type   = FIO_OPT_INT,
                .cb     = str_rwmix_read_cb,
                .maxval = 100,
                .help   = "Percentage of mixed workload that is reads",
                .def    = "50",
 +              .interval = 5,
 +              .inverse = "rwmixwrite",
 +              .category = FIO_OPT_C_IO,
 +              .group  = FIO_OPT_G_RWMIX,
        },
        {
                .name   = "rwmixwrite",
 +              .lname  = "Read/write mix write",
                .type   = FIO_OPT_INT,
                .cb     = str_rwmix_write_cb,
                .maxval = 100,
                .help   = "Percentage of mixed workload that is writes",
                .def    = "50",
 +              .interval = 5,
 +              .inverse = "rwmixread",
 +              .category = FIO_OPT_C_IO,
 +              .group  = FIO_OPT_G_RWMIX,
        },
        {
                .name   = "rwmixcycle",
 +              .lname  = "Read/write mix cycle",
                .type   = FIO_OPT_DEPRECATED,
 +              .category = FIO_OPT_C_IO,
 +              .group  = FIO_OPT_G_RWMIX,
        },
        {
                .name   = "nice",
 +              .lname  = "Nice",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(nice),
                .help   = "Set job CPU nice value",
                .minval = -19,
                .maxval = 20,
                .def    = "0",
 +              .interval = 1,
 +              .category = FIO_OPT_C_GENERAL,
 +              .group  = FIO_OPT_G_CRED,
        },
  #ifdef FIO_HAVE_IOPRIO
        {
                .name   = "prio",
 +              .lname  = "I/O nice priority",
                .type   = FIO_OPT_INT,
 -              .cb     = str_prio_cb,
 +              .off1   = td_var_offset(ioprio),
                .help   = "Set job IO priority value",
                .minval = 0,
                .maxval = 7,
 +              .interval = 1,
 +              .category = FIO_OPT_C_GENERAL,
 +              .group  = FIO_OPT_G_CRED,
        },
        {
                .name   = "prioclass",
 +              .lname  = "I/O nice priority class",
                .type   = FIO_OPT_INT,
 -              .cb     = str_prioclass_cb,
 +              .off1   = td_var_offset(ioprio_class),
                .help   = "Set job IO priority class",
                .minval = 0,
                .maxval = 3,
 +              .interval = 1,
 +              .category = FIO_OPT_C_GENERAL,
 +              .group  = FIO_OPT_G_CRED,
        },
  #endif
        {
                .name   = "thinktime",
 +              .lname  = "Thinktime",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(thinktime),
                .help   = "Idle time between IO buffers (usec)",
                .def    = "0",
 +              .category = FIO_OPT_C_IO,
 +              .group  = FIO_OPT_G_THINKTIME,
        },
        {
                .name   = "thinktime_spin",
 +              .lname  = "Thinktime spin",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(thinktime_spin),
                .help   = "Start think time by spinning this amount (usec)",
                .def    = "0",
                .parent = "thinktime",
 +              .hide   = 1,
 +              .category = FIO_OPT_C_IO,
 +              .group  = FIO_OPT_G_THINKTIME,
        },
        {
                .name   = "thinktime_blocks",
 +              .lname  = "Thinktime blocks",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(thinktime_blocks),
                .help   = "IO buffer period between 'thinktime'",
                .def    = "1",
                .parent = "thinktime",
 +              .hide   = 1,
 +              .category = FIO_OPT_C_IO,
 +              .group  = FIO_OPT_G_THINKTIME,
        },
        {
                .name   = "rate",
 +              .lname  = "I/O rate",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(rate[DDIR_READ]),
                .off2   = td_var_offset(rate[DDIR_WRITE]),
                .off3   = td_var_offset(rate[DDIR_TRIM]),
                .help   = "Set bandwidth rate",
 +              .category = FIO_OPT_C_IO,
 +              .group  = FIO_OPT_G_RATE,
        },
        {
                .name   = "ratemin",
 +              .lname  = "I/O min rate",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(ratemin[DDIR_READ]),
                .off2   = td_var_offset(ratemin[DDIR_WRITE]),
                .off3   = td_var_offset(ratemin[DDIR_TRIM]),
                .help   = "Job must meet this rate or it will be shutdown",
                .parent = "rate",
 +              .hide   = 1,
 +              .category = FIO_OPT_C_IO,
 +              .group  = FIO_OPT_G_RATE,
        },
        {
                .name   = "rate_iops",
 +              .lname  = "I/O rate IOPS",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(rate_iops[DDIR_READ]),
                .off2   = td_var_offset(rate_iops[DDIR_WRITE]),
                .off3   = td_var_offset(rate_iops[DDIR_TRIM]),
                .help   = "Limit IO used to this number of IO operations/sec",
 +              .hide   = 1,
 +              .category = FIO_OPT_C_IO,
 +              .group  = FIO_OPT_G_RATE,
        },
        {
                .name   = "rate_iops_min",
 +              .lname  = "I/O min rate IOPS",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(rate_iops_min[DDIR_READ]),
                .off2   = td_var_offset(rate_iops_min[DDIR_WRITE]),
                .off3   = td_var_offset(rate_iops_min[DDIR_TRIM]),
                .help   = "Job must meet this rate or it will be shut down",
                .parent = "rate_iops",
 +              .hide   = 1,
 +              .category = FIO_OPT_C_IO,
 +              .group  = FIO_OPT_G_RATE,
        },
        {
                .name   = "ratecycle",
 +              .lname  = "I/O rate cycle",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(ratecycle),
                .help   = "Window average for rate limits (msec)",
                .def    = "1000",
                .parent = "rate",
 +              .hide   = 1,
 +              .category = FIO_OPT_C_IO,
 +              .group  = FIO_OPT_G_RATE,
        },
        {
                .name   = "max_latency",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(max_latency),
                .help   = "Maximum tolerated IO latency (usec)",
 +              .category = FIO_OPT_C_IO,
 +              .group = FIO_OPT_G_RATE,
        },
        {
                .name   = "invalidate",
 +              .lname  = "Cache invalidate",
                .type   = FIO_OPT_BOOL,
                .off1   = td_var_offset(invalidate_cache),
                .help   = "Invalidate buffer/page cache prior to running job",
                .def    = "1",
 +              .category = FIO_OPT_C_IO,
 +              .group  = FIO_OPT_G_IO_TYPE,
        },
        {
                .name   = "sync",
 +              .lname  = "Synchronous I/O",
                .type   = FIO_OPT_BOOL,
                .off1   = td_var_offset(sync_io),
                .help   = "Use O_SYNC for buffered writes",
                .def    = "0",
                .parent = "buffered",
 -      },
 -      {
 -              .name   = "bwavgtime",
 -              .type   = FIO_OPT_INT,
 -              .off1   = td_var_offset(bw_avg_time),
 -              .help   = "Time window over which to calculate bandwidth"
 -                        " (msec)",
 -              .def    = "500",
 -              .parent = "write_bw_log",
 -      },
 -      {
 -              .name   = "iopsavgtime",
 -              .type   = FIO_OPT_INT,
 -              .off1   = td_var_offset(iops_avg_time),
 -              .help   = "Time window over which to calculate IOPS (msec)",
 -              .def    = "500",
 -              .parent = "write_iops_log",
 +              .hide   = 1,
 +              .category = FIO_OPT_C_IO,
 +              .group  = FIO_OPT_G_IO_TYPE,
        },
        {
                .name   = "create_serialize",
 +              .lname  = "Create serialize",
                .type   = FIO_OPT_BOOL,
                .off1   = td_var_offset(create_serialize),
                .help   = "Serialize creating of job files",
                .def    = "1",
 +              .category = FIO_OPT_C_FILE,
 +              .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "create_fsync",
 +              .lname  = "Create fsync",
                .type   = FIO_OPT_BOOL,
                .off1   = td_var_offset(create_fsync),
                .help   = "fsync file after creation",
                .def    = "1",
 +              .category = FIO_OPT_C_FILE,
 +              .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "create_on_open",
 +              .lname  = "Create on open",
                .type   = FIO_OPT_BOOL,
                .off1   = td_var_offset(create_on_open),
                .help   = "Create files when they are opened for IO",
                .def    = "0",
 +              .category = FIO_OPT_C_FILE,
 +              .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "create_only",
                .type   = FIO_OPT_BOOL,
                .off1   = td_var_offset(create_only),
                .help   = "Only perform file creation phase",
 +              .category = FIO_OPT_C_FILE,
                .def    = "0",
        },
        {
                .name   = "pre_read",
 +              .lname  = "Pre-read files",
                .type   = FIO_OPT_BOOL,
                .off1   = td_var_offset(pre_read),
                .help   = "Pre-read files before starting official testing",
                .def    = "0",
 -      },
 -      {
 -              .name   = "cpuload",
 -              .type   = FIO_OPT_INT,
 -              .off1   = td_var_offset(cpuload),
 -              .help   = "Use this percentage of CPU",
 -      },
 -      {
 -              .name   = "cpuchunks",
 -              .type   = FIO_OPT_INT,
 -              .off1   = td_var_offset(cpucycle),
 -              .help   = "Length of the CPU burn cycles (usecs)",
 -              .def    = "50000",
 -              .parent = "cpuload",
 +              .category = FIO_OPT_C_FILE,
 +              .group  = FIO_OPT_G_INVALID,
        },
  #ifdef FIO_HAVE_CPU_AFFINITY
        {
                .name   = "cpumask",
 +              .lname  = "CPU mask",
                .type   = FIO_OPT_INT,
                .cb     = str_cpumask_cb,
                .help   = "CPU affinity mask",
 +              .category = FIO_OPT_C_GENERAL,
 +              .group  = FIO_OPT_G_CRED,
        },
        {
                .name   = "cpus_allowed",
 +              .lname  = "CPUs allowed",
                .type   = FIO_OPT_STR,
                .cb     = str_cpus_allowed_cb,
                .help   = "Set CPUs allowed",
 +              .category = FIO_OPT_C_GENERAL,
 +              .group  = FIO_OPT_G_CRED,
        },
  #endif
  #ifdef CONFIG_LIBNUMA
  #endif
        {
                .name   = "end_fsync",
 +              .lname  = "End fsync",
                .type   = FIO_OPT_BOOL,
                .off1   = td_var_offset(end_fsync),
                .help   = "Include fsync at the end of job",
                .def    = "0",
 +              .category = FIO_OPT_C_FILE,
 +              .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "fsync_on_close",
 +              .lname  = "Fsync on close",
                .type   = FIO_OPT_BOOL,
                .off1   = td_var_offset(fsync_on_close),
                .help   = "fsync files on close",
                .def    = "0",
 +              .category = FIO_OPT_C_FILE,
 +              .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "unlink",
 +              .lname  = "Unlink file",
                .type   = FIO_OPT_BOOL,
                .off1   = td_var_offset(unlink),
                .help   = "Unlink created files after job has completed",
                .def    = "0",
 +              .category = FIO_OPT_C_FILE,
 +              .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "exitall",
 +              .lname  = "Exit-all on terminate",
                .type   = FIO_OPT_STR_SET,
                .cb     = str_exitall_cb,
                .help   = "Terminate all jobs when one exits",
 +              .category = FIO_OPT_C_GENERAL,
 +              .group  = FIO_OPT_G_PROCESS,
        },
        {
                .name   = "stonewall",
 +              .lname  = "Wait for previous",
                .alias  = "wait_for_previous",
                .type   = FIO_OPT_STR_SET,
                .off1   = td_var_offset(stonewall),
                .help   = "Insert a hard barrier between this job and previous",
 +              .category = FIO_OPT_C_GENERAL,
 +              .group  = FIO_OPT_G_PROCESS,
        },
        {
                .name   = "new_group",
 +              .lname  = "New group",
                .type   = FIO_OPT_STR_SET,
                .off1   = td_var_offset(new_group),
                .help   = "Mark the start of a new group (for reporting)",
 +              .category = FIO_OPT_C_GENERAL,
 +              .group  = FIO_OPT_G_PROCESS,
        },
        {
                .name   = "thread",
 +              .lname  = "Thread",
                .type   = FIO_OPT_STR_SET,
                .off1   = td_var_offset(use_thread),
 -              .help   = "Use threads instead of forks",
 +              .help   = "Use threads instead of processes",
 +              .category = FIO_OPT_C_GENERAL,
 +              .group  = FIO_OPT_G_PROCESS,
        },
        {
                .name   = "write_bw_log",
 -              .type   = FIO_OPT_STR,
 -              .off1   = td_var_offset(write_bw_log),
 -              .cb     = str_write_bw_log_cb,
 +              .lname  = "Write bandwidth log",
 +              .type   = FIO_OPT_STR_STORE,
 +              .off1   = td_var_offset(bw_log_file),
                .help   = "Write log of bandwidth during run",
 +              .category = FIO_OPT_C_LOG,
 +              .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "write_lat_log",
 -              .type   = FIO_OPT_STR,
 -              .off1   = td_var_offset(write_lat_log),
 -              .cb     = str_write_lat_log_cb,
 +              .lname  = "Write latency log",
 +              .type   = FIO_OPT_STR_STORE,
 +              .off1   = td_var_offset(lat_log_file),
                .help   = "Write log of latency during run",
 +              .category = FIO_OPT_C_LOG,
 +              .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "write_iops_log",
 +              .lname  = "Write IOPS log",
                .type   = FIO_OPT_STR,
 -              .off1   = td_var_offset(write_iops_log),
 -              .cb     = str_write_iops_log_cb,
 +              .off1   = td_var_offset(iops_log_file),
                .help   = "Write log of IOPS during run",
 +              .category = FIO_OPT_C_LOG,
 +              .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "log_avg_msec",
 +              .lname  = "Log averaging (msec)",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(log_avg_msec),
                .help   = "Average bw/iops/lat logs over this period of time",
                .def    = "0",
 +              .category = FIO_OPT_C_LOG,
 +              .group  = FIO_OPT_G_INVALID,
        },
        {
 -              .name   = "hugepage-size",
 +              .name   = "bwavgtime",
 +              .lname  = "Bandwidth average time",
                .type   = FIO_OPT_INT,
 -              .off1   = td_var_offset(hugepage_size),
 -              .help   = "When using hugepages, specify size of each page",
 -              .def    = __fio_stringify(FIO_HUGE_PAGE),
 +              .off1   = td_var_offset(bw_avg_time),
 +              .help   = "Time window over which to calculate bandwidth"
 +                        " (msec)",
 +              .def    = "500",
 +              .parent = "write_bw_log",
 +              .hide   = 1,
 +              .interval = 100,
 +              .category = FIO_OPT_C_LOG,
 +              .group  = FIO_OPT_G_INVALID,
 +      },
 +      {
 +              .name   = "iopsavgtime",
 +              .lname  = "IOPS average time",
 +              .type   = FIO_OPT_INT,
 +              .off1   = td_var_offset(iops_avg_time),
 +              .help   = "Time window over which to calculate IOPS (msec)",
 +              .def    = "500",
 +              .parent = "write_iops_log",
 +              .hide   = 1,
 +              .interval = 100,
 +              .category = FIO_OPT_C_LOG,
 +              .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "group_reporting",
 -              .type   = FIO_OPT_STR_SET,
 +              .lname  = "Group reporting",
 +              .type   = FIO_OPT_BOOL,
                .off1   = td_var_offset(group_reporting),
                .help   = "Do reporting on a per-group basis",
 +              .def    = "1",
 +              .category = FIO_OPT_C_STAT,
 +              .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "zero_buffers",
 +              .lname  = "Zero I/O buffers",
                .type   = FIO_OPT_STR_SET,
                .off1   = td_var_offset(zero_buffers),
                .help   = "Init IO buffers to all zeroes",
 +              .category = FIO_OPT_C_IO,
 +              .group  = FIO_OPT_G_IO_BUF,
        },
        {
                .name   = "refill_buffers",
 +              .lname  = "Refill I/O buffers",
                .type   = FIO_OPT_STR_SET,
                .off1   = td_var_offset(refill_buffers),
                .help   = "Refill IO buffers on every IO submit",
 +              .category = FIO_OPT_C_IO,
 +              .group  = FIO_OPT_G_IO_BUF,
        },
        {
                .name   = "scramble_buffers",
 +              .lname  = "Scramble I/O buffers",
                .type   = FIO_OPT_BOOL,
                .off1   = td_var_offset(scramble_buffers),
                .help   = "Slightly scramble buffers on every IO submit",
                .def    = "1",
 +              .category = FIO_OPT_C_IO,
 +              .group  = FIO_OPT_G_IO_BUF,
        },
        {
                .name   = "buffer_compress_percentage",
 +              .lname  = "Buffer compression percentage",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(compress_percentage),
                .maxval = 100,
                .minval = 1,
                .help   = "How compressible the buffer is (approximately)",
 +              .interval = 5,
 +              .category = FIO_OPT_C_IO,
 +              .group  = FIO_OPT_G_IO_BUF,
        },
        {
                .name   = "buffer_compress_chunk",
 +              .lname  = "Buffer compression chunk size",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(compress_chunk),
                .parent = "buffer_compress_percentage",
 +              .hide   = 1,
                .help   = "Size of compressible region in buffer",
 +              .interval = 256,
 +              .category = FIO_OPT_C_IO,
 +              .group  = FIO_OPT_G_IO_BUF,
        },
        {
                .name   = "clat_percentiles",
 +              .lname  = "Completion latency percentiles",
                .type   = FIO_OPT_BOOL,
                .off1   = td_var_offset(clat_percentiles),
                .help   = "Enable the reporting of completion latency percentiles",
                .def    = "1",
 +              .category = FIO_OPT_C_STAT,
 +              .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "percentile_list",
 +              .lname  = "Completion latency percentile list",
                .type   = FIO_OPT_FLOAT_LIST,
                .off1   = td_var_offset(percentile_list),
                .off2   = td_var_offset(overwrite_plist),
                .maxlen = FIO_IO_U_LIST_MAX_LEN,
                .minfp  = 0.0,
                .maxfp  = 100.0,
 +              .category = FIO_OPT_C_STAT,
 +              .group  = FIO_OPT_G_INVALID,
        },
  
  #ifdef FIO_HAVE_DISK_UTIL
        {
                .name   = "disk_util",
 +              .lname  = "Disk utilization",
                .type   = FIO_OPT_BOOL,
                .off1   = td_var_offset(do_disk_util),
                .help   = "Log disk utilization statistics",
                .def    = "1",
 +              .category = FIO_OPT_C_STAT,
 +              .group  = FIO_OPT_G_INVALID,
        },
  #endif
        {
                .name   = "gtod_reduce",
 +              .lname  = "Reduce gettimeofday() calls",
                .type   = FIO_OPT_BOOL,
                .help   = "Greatly reduce number of gettimeofday() calls",
                .cb     = str_gtod_reduce_cb,
                .def    = "0",
 +              .hide_on_set = 1,
 +              .category = FIO_OPT_C_STAT,
 +              .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "disable_lat",
 +              .lname  = "Disable all latency stats",
                .type   = FIO_OPT_BOOL,
                .off1   = td_var_offset(disable_lat),
                .help   = "Disable latency numbers",
                .parent = "gtod_reduce",
 +              .hide   = 1,
                .def    = "0",
 +              .category = FIO_OPT_C_STAT,
 +              .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "disable_clat",
 +              .lname  = "Disable completion latency stats",
                .type   = FIO_OPT_BOOL,
                .off1   = td_var_offset(disable_clat),
                .help   = "Disable completion latency numbers",
                .parent = "gtod_reduce",
 +              .hide   = 1,
                .def    = "0",
 +              .category = FIO_OPT_C_STAT,
 +              .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "disable_slat",
 +              .lname  = "Disable submission latency stats",
                .type   = FIO_OPT_BOOL,
                .off1   = td_var_offset(disable_slat),
                .help   = "Disable submission latency numbers",
                .parent = "gtod_reduce",
 +              .hide   = 1,
                .def    = "0",
 +              .category = FIO_OPT_C_STAT,
 +              .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "disable_bw_measurement",
 +              .lname  = "Disable bandwidth stats",
                .type   = FIO_OPT_BOOL,
                .off1   = td_var_offset(disable_bw),
                .help   = "Disable bandwidth logging",
                .parent = "gtod_reduce",
 +              .hide   = 1,
                .def    = "0",
 +              .category = FIO_OPT_C_STAT,
 +              .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "gtod_cpu",
 +              .lname  = "Dedicated gettimeofday() CPU",
                .type   = FIO_OPT_INT,
                .cb     = str_gtod_cpu_cb,
                .help   = "Set up dedicated gettimeofday() thread on this CPU",
                .verify = gtod_cpu_verify,
 +              .category = FIO_OPT_C_GENERAL,
 +              .group  = FIO_OPT_G_CLOCK,
        },
        {
                .name   = "continue_on_error",
 +              .lname  = "Continue on error",
                .type   = FIO_OPT_STR,
                .off1   = td_var_offset(continue_on_error),
                .help   = "Continue on non-fatal errors during IO",
                .def    = "none",
 +              .category = FIO_OPT_C_GENERAL,
 +              .group  = FIO_OPT_G_ERR,
                .posval = {
                          { .ival = "none",
                            .oval = ERROR_TYPE_NONE,
                .cb     = str_ignore_error_cb,
                .help   = "Set a specific list of errors to ignore",
                .parent = "rw",
 +              .category = FIO_OPT_C_GENERAL,
 +              .group  = FIO_OPT_G_ERR,
        },
        {
                .name   = "error_dump",
                .off1   = td_var_offset(error_dump),
                .def    = "0",
                .help   = "Dump info on each error",
 +              .category = FIO_OPT_C_GENERAL,
 +              .group  = FIO_OPT_G_ERR,
        },
 -
        {
                .name   = "profile",
 +              .lname  = "Profile",
                .type   = FIO_OPT_STR_STORE,
                .off1   = td_var_offset(profile),
                .help   = "Select a specific builtin performance test",
 +              .category = FIO_OPT_C_PROFILE,
 +              .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "cgroup",
 +              .lname  = "Cgroup",
                .type   = FIO_OPT_STR_STORE,
                .off1   = td_var_offset(cgroup),
                .help   = "Add job to cgroup of this name",
 +              .category = FIO_OPT_C_GENERAL,
 +              .group  = FIO_OPT_G_CGROUP,
 +      },
 +      {
 +              .name   = "cgroup_nodelete",
 +              .lname  = "Cgroup no-delete",
 +              .type   = FIO_OPT_BOOL,
 +              .off1   = td_var_offset(cgroup_nodelete),
 +              .help   = "Do not delete cgroups after job completion",
 +              .def    = "0",
 +              .parent = "cgroup",
 +              .category = FIO_OPT_C_GENERAL,
 +              .group  = FIO_OPT_G_CGROUP,
        },
        {
                .name   = "cgroup_weight",
 +              .lname  = "Cgroup weight",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(cgroup_weight),
                .help   = "Use given weight for cgroup",
                .minval = 100,
                .maxval = 1000,
 -      },
 -      {
 -              .name   = "cgroup_nodelete",
 -              .type   = FIO_OPT_BOOL,
 -              .off1   = td_var_offset(cgroup_nodelete),
 -              .help   = "Do not delete cgroups after job completion",
 -              .def    = "0",
 +              .parent = "cgroup",
 +              .category = FIO_OPT_C_GENERAL,
 +              .group  = FIO_OPT_G_CGROUP,
        },
        {
                .name   = "uid",
 +              .lname  = "User ID",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(uid),
                .help   = "Run job with this user ID",
 +              .category = FIO_OPT_C_GENERAL,
 +              .group  = FIO_OPT_G_CRED,
        },
        {
                .name   = "gid",
 +              .lname  = "Group ID",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(gid),
                .help   = "Run job with this group ID",
 +              .category = FIO_OPT_C_GENERAL,
 +              .group  = FIO_OPT_G_CRED,
 +      },
 +      {
 +              .name   = "kb_base",
 +              .lname  = "KB Base",
 +              .type   = FIO_OPT_INT,
 +              .off1   = td_var_offset(kb_base),
 +              .verify = kb_base_verify,
 +              .prio   = 1,
 +              .def    = "1024",
 +              .help   = "How many bytes per KB for reporting (1000 or 1024)",
 +              .category = FIO_OPT_C_GENERAL,
 +              .group  = FIO_OPT_G_INVALID,
 +      },
 +      {
 +              .name   = "hugepage-size",
 +              .lname  = "Hugepage size",
 +              .type   = FIO_OPT_INT,
 +              .off1   = td_var_offset(hugepage_size),
 +              .help   = "When using hugepages, specify size of each page",
 +              .def    = __fio_stringify(FIO_HUGE_PAGE),
 +              .interval = 1024 * 1024,
 +              .category = FIO_OPT_C_GENERAL,
 +              .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "flow_id",
 +              .lname  = "I/O flow ID",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(flow_id),
                .help   = "The flow index ID to use",
                .def    = "0",
 +              .category = FIO_OPT_C_IO,
 +              .group  = FIO_OPT_G_IO_FLOW,
        },
        {
                .name   = "flow",
 +              .lname  = "I/O flow weight",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(flow),
                .help   = "Weight for flow control of this job",
                .parent = "flow_id",
 +              .hide   = 1,
                .def    = "0",
 +              .category = FIO_OPT_C_IO,
 +              .group  = FIO_OPT_G_IO_FLOW,
        },
        {
                .name   = "flow_watermark",
 +              .lname  = "I/O flow watermark",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(flow_watermark),
                .help   = "High watermark for flow control. This option"
                        " should be set to the same value for all threads"
                        " with non-zero flow.",
                .parent = "flow_id",
 +              .hide   = 1,
                .def    = "1024",
 +              .category = FIO_OPT_C_IO,
 +              .group  = FIO_OPT_G_IO_FLOW,
        },
        {
                .name   = "flow_sleep",
 +              .lname  = "I/O flow sleep",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(flow_sleep),
                .help   = "How many microseconds to sleep after being held"
                        " back by the flow control mechanism",
                .parent = "flow_id",
 +              .hide   = 1,
                .def    = "0",
 +              .category = FIO_OPT_C_IO,
 +              .group  = FIO_OPT_G_IO_FLOW,
        },
        {
                .name = NULL,
@@@ -3195,13 -2687,13 +3196,13 @@@ void fio_options_dup_and_init(struct op
  {
        unsigned int i;
  
 -      options_init(options);
 +      options_init(fio_options);
  
        i = 0;
        while (long_options[i].name)
                i++;
  
 -      options_to_lopts(options, long_options, i, FIO_GETOPT_JOB);
 +      options_to_lopts(fio_options, long_options, i, FIO_GETOPT_JOB);
  }
  
  struct fio_keyword {
@@@ -3284,12 -2776,14 +3285,12 @@@ static char *bc_calc(char *str
  
        sprintf(buf, "echo '%s' | %s", tmp, BC_APP);
        f = popen(buf, "r");
 -      if (!f) {
 +      if (!f)
                return NULL;
 -      }
  
        ret = fread(&buf[tmp - str], 1, 128 - (tmp - str), f);
 -      if (ret <= 0) {
 +      if (ret <= 0)
                return NULL;
 -      }
  
        pclose(f);
        buf[(tmp - str) + ret - 1] = '\0';
@@@ -3422,13 -2916,13 +3423,13 @@@ int fio_options_parse(struct thread_dat
        int i, ret, unknown;
        char **opts_copy;
  
 -      sort_options(opts, options, num_opts);
 +      sort_options(opts, fio_options, num_opts);
        opts_copy = dup_and_sub_options(opts, num_opts);
  
        for (ret = 0, i = 0, unknown = 0; i < num_opts; i++) {
                struct fio_option *o;
 -              int newret = parse_option(opts_copy[i], opts[i], options, &o,
 -                                        td);
 +              int newret = parse_option(opts_copy[i], opts[i], fio_options,
 +                                              &o, td);
  
                if (opts_copy[i]) {
                        if (newret && !o) {
  
  int fio_cmd_option_parse(struct thread_data *td, const char *opt, char *val)
  {
 -      return parse_cmd_option(opt, val, options, td);
 +      return parse_cmd_option(opt, val, fio_options, td);
  }
  
  int fio_cmd_ioengine_option_parse(struct thread_data *td, const char *opt,
  
  void fio_fill_default_options(struct thread_data *td)
  {
 -      fill_default_options(td, options);
 +      fill_default_options(td, fio_options);
  }
  
  int fio_show_option_help(const char *opt)
  {
 -      return show_cmd_help(options, opt);
 +      return show_cmd_help(fio_options, opt);
  }
  
  void options_mem_dupe(void *data, struct fio_option *options)
   */
  void fio_options_mem_dupe(struct thread_data *td)
  {
 -      options_mem_dupe(&td->o, options);
 +      options_mem_dupe(&td->o, fio_options);
  
        if (td->eo && td->io_ops) {
                void *oldeo = td->eo;
  
  unsigned int fio_get_kb_base(void *data)
  {
 -      struct thread_data *td = data;
 +      struct thread_options *o = data;
        unsigned int kb_base = 0;
  
 -      if (td)
 -              kb_base = td->o.kb_base;
 +      if (o)
 +              kb_base = o->kb_base;
        if (!kb_base)
                kb_base = 1024;
  
@@@ -3542,13 -3036,13 +3543,13 @@@ int add_option(struct fio_option *o
        struct fio_option *__o;
        int opt_index = 0;
  
 -      __o = options;
 +      __o = fio_options;
        while (__o->name) {
                opt_index++;
                __o++;
        }
  
 -      memcpy(&options[opt_index], o, sizeof(*o));
 +      memcpy(&fio_options[opt_index], o, sizeof(*o));
        return 0;
  }
  
@@@ -3556,7 -3050,7 +3557,7 @@@ void invalidate_profile_options(const c
  {
        struct fio_option *o;
  
 -      o = options;
 +      o = fio_options;
        while (o->name) {
                if (o->prof_name && !strcmp(o->prof_name, prof_name)) {
                        o->type = FIO_OPT_INVALID;
@@@ -3571,7 -3065,7 +3572,7 @@@ void add_opt_posval(const char *optname
        struct fio_option *o;
        unsigned int i;
  
 -      o = find_option(options, optname);
 +      o = find_option(fio_options, optname);
        if (!o)
                return;
  
@@@ -3590,7 -3084,7 +3591,7 @@@ void del_opt_posval(const char *optname
        struct fio_option *o;
        unsigned int i;
  
 -      o = find_option(options, optname);
 +      o = find_option(fio_options, optname);
        if (!o)
                return;
  
  
  void fio_options_free(struct thread_data *td)
  {
 -      options_free(options, td);
 +      options_free(fio_options, td);
        if (td->eo && td->io_ops && td->io_ops->options) {
                options_free(td->io_ops->options, td->eo);
                free(td->eo);
                td->eo = NULL;
        }
  }
 +
 +struct fio_option *fio_option_find(const char *name)
 +{
 +      return find_option(fio_options, name);
 +}
 +
diff --combined os/os.h
index 28bb6319f216e095c04324163cd3bf90b65a2077,ef9f91f523b6f7c15ebe7871b717c96c9ef61a8c..1e924d3360b423201cab1cfce1288864d8559c5e
+++ b/os/os.h
@@@ -8,8 -8,6 +8,8 @@@
  #include <unistd.h>
  #include <stdlib.h>
  
 +#include "../arch/arch.h"
 +
  enum {
        os_linux = 1,
        os_aix,
@@@ -58,7 -56,7 +58,7 @@@ typedef struct aiocb os_aiocb_t
  #include <scsi/sg.h>
  #endif
  
- #ifdef CONFIG_STRSEP
+ #ifndef CONFIG_STRSEP
  #include "../lib/strsep.h"
  #endif
  
@@@ -81,7 -79,7 +81,7 @@@ typedef unsigned long os_cpu_mask_t
  #endif
  
  #ifndef FIO_HAVE_IOPRIO
 -#define ioprio_set(which, who, prio)  (0)
 +#define ioprio_set(which, who, prioclass, prio)       (0)
  #endif
  
  #ifndef FIO_HAVE_ODIRECT
diff --combined parse.c
index f3497440aade02ca875dc7c0f9e07469c4030595,d15d22ba3f7ed03a68e77614a02b44616d64daa5..a0b18e26e85af4081fc32b982250e5e500b1d874
+++ b/parse.c
@@@ -16,7 -16,8 +16,7 @@@
  #include "options.h"
  #include "minmax.h"
  
 -static struct fio_option *fio_options;
 -extern unsigned int fio_get_kb_base(void *);
 +static struct fio_option *__fio_options;
  
  static int vp_cmp(const void *p1, const void *p2)
  {
@@@ -47,7 -48,7 +47,7 @@@ static void posval_sort(struct fio_opti
  static void show_option_range(struct fio_option *o,
                                int (*logger)(const char *format, ...))
  {
 -      if (o->type == FIO_OPT_FLOAT_LIST){
 +      if (o->type == FIO_OPT_FLOAT_LIST) {
                if (isnan(o->minfp) && isnan(o->maxfp))
                        return;
  
@@@ -153,32 -154,32 +153,32 @@@ static unsigned long long __get_mult_by
        for (i = 0; i < strlen(c); i++)
                c[i] = tolower(c[i]);
  
 -      if (!strcmp("pib", c)) {
 +      if (!strncmp("pib", c, 3)) {
                pow = 5;
                mult = 1000;
 -      } else if (!strcmp("tib", c)) {
 +      } else if (!strncmp("tib", c, 3)) {
                pow = 4;
                mult = 1000;
 -      } else if (!strcmp("gib", c)) {
 +      } else if (!strncmp("gib", c, 3)) {
                pow = 3;
                mult = 1000;
 -      } else if (!strcmp("mib", c)) {
 +      } else if (!strncmp("mib", c, 3)) {
                pow = 2;
                mult = 1000;
 -      } else if (!strcmp("kib", c)) {
 +      } else if (!strncmp("kib", c, 3)) {
                pow = 1;
                mult = 1000;
 -      } else if (!strcmp("p", c) || !strcmp("pb", c))
 +      } else if (!strncmp("p", c, 1) || !strncmp("pb", c, 2))
                pow = 5;
 -      else if (!strcmp("t", c) || !strcmp("tb", c))
 +      else if (!strncmp("t", c, 1) || !strncmp("tb", c, 2))
                pow = 4;
 -      else if (!strcmp("g", c) || !strcmp("gb", c))
 +      else if (!strncmp("g", c, 1) || !strncmp("gb", c, 2))
                pow = 3;
 -      else if (!strcmp("m", c) || !strcmp("mb", c))
 +      else if (!strncmp("m", c, 1) || !strncmp("mb", c, 2))
                pow = 2;
 -      else if (!strcmp("k", c) || !strcmp("kb", c))
 +      else if (!strncmp("k", c, 1) || !strncmp("kb", c, 2))
                pow = 1;
 -      else if (!strcmp("%", c)) {
 +      else if (!strncmp("%", c, 1)) {
                *percent = 1;
                free(c);
                return ret;
@@@ -260,7 -261,7 +260,7 @@@ int str_to_decimal(const char *str, lon
        return 0;
  }
  
 -static int check_str_bytes(const char *p, long long *val, void *data)
 +int check_str_bytes(const char *p, long long *val, void *data)
  {
        return str_to_decimal(p, val, 1, data);
  }
@@@ -361,7 -362,7 +361,7 @@@ static int __handle_option(struct fio_o
                           int first, int more, int curr)
  {
        int il, *ilp;
 -      doubleflp;
 +      double *flp;
        long long ull, *ullp;
        long ul1, ul2;
        double uf;
                                        o->maxlen);
                        return 1;
                }
 -              if (!str_to_float(ptr, &uf)){
 +              if (!str_to_float(ptr, &uf)) {
                        log_err("not a floating point value: %s\n", ptr);
                        return 1;
                }
@@@ -830,14 -831,14 +830,14 @@@ static int opt_cmp(const void *p1, cons
  
        if (*(char **)p1) {
                s = strdup(*((char **) p1));
 -              o = get_option(s, fio_options, &foo);
 +              o = get_option(s, __fio_options, &foo);
                if (o)
                        prio1 = o->prio;
                free(s);
        }
        if (*(char **)p2) {
                s = strdup(*((char **) p2));
 -              o = get_option(s, fio_options, &foo);
 +              o = get_option(s, __fio_options, &foo);
                if (o)
                        prio2 = o->prio;
                free(s);
  
  void sort_options(char **opts, struct fio_option *options, int num_opts)
  {
 -      fio_options = options;
 +      __fio_options = options;
        qsort(opts, num_opts, sizeof(char *), opt_cmp);
 -      fio_options = NULL;
 +      __fio_options = NULL;
  }
  
  int parse_cmd_option(const char *opt, const char *val,
@@@ -893,8 -894,9 +893,8 @@@ int parse_option(char *opt, const char 
                return 1;
        }
  
 -      if (!handle_option(*o, post, data)) {
 +      if (!handle_option(*o, post, data))
                return 0;
 -      }
  
        log_err("fio: failed parsing %s\n", input);
        return 1;
@@@ -1094,6 -1096,9 +1094,9 @@@ void option_init(struct fio_option *o
                        o->maxval = UINT_MAX;
        }
        if (o->type == FIO_OPT_FLOAT_LIST) {
+ #ifndef NAN
+ #define NAN __builtin_nanf("")
+ #endif
                o->minfp = NAN;
                o->maxfp = NAN;
        }
                      (o->roff1 || o->roff2 || o->roff3 || o->roff4))) {
                log_err("Option %s: both cb and offset given\n", o->name);
        }
 +      if (!o->category) {
 +              log_info("Options %s: no category defined. Setting to misc\n", o->name);
 +              o->category = FIO_OPT_C_GENERAL;
 +      }
  }
  
  /*
@@@ -1126,11 -1127,8 +1129,11 @@@ void options_init(struct fio_option *op
  
        dprint(FD_PARSE, "init options\n");
  
 -      for (o = &options[0]; o->name; o++)
 +      for (o = &options[0]; o->name; o++) {
                option_init(o);
 +              if (o->inverse)
 +                      o->inv_opt = find_option(options, o->inverse);
 +      }
  }
  
  void options_free(struct fio_option *options, void *data)
diff --combined stat.c
index 2e7824295600385430d724db60c4dfd574870907,8e1034b513fd162dbc539eacdcf2640cfa4ff88d..7cf66f0075217c238cd7fc93846e39fd476e3c11
--- 1/stat.c
--- 2/stat.c
+++ b/stat.c
  #include "diskutil.h"
  #include "lib/ieee754.h"
  #include "json.h"
+ #include "lib/getrusage.h"
  
  void update_rusage_stat(struct thread_data *td)
  {
        struct thread_stat *ts = &td->ts;
  
- #ifdef RUSAGE_THREAD
-       getrusage(RUSAGE_THREAD, &td->ru_end);
- #else
-       getrusage(RUSAGE_SELF, &td->ru_end);
- #endif
+       fio_getrusage(&td->ru_end);
        ts->usr_time += mtime_since(&td->ru_start.ru_utime,
                                        &td->ru_end.ru_utime);
        ts->sys_time += mtime_since(&td->ru_start.ru_stime,
@@@ -68,12 -64,12 +64,12 @@@ static unsigned int plat_val_to_idx(uns
  
        /*
         * Discard the error bits and apply the mask to find the
 -         * index for the buckets in the group
 +       * index for the buckets in the group
         */
        offset = (FIO_IO_U_PLAT_VAL - 1) & (val >> error_bits);
  
        /* Make sure the index does not exceed (array size - 1) */
 -      idx = (base + offset) < (FIO_IO_U_PLAT_NR - 1)?
 +      idx = (base + offset) < (FIO_IO_U_PLAT_NR - 1) ?
                (base + offset) : (FIO_IO_U_PLAT_NR - 1);
  
        return idx;
@@@ -91,11 -87,11 +87,11 @@@ static unsigned int plat_idx_to_val(uns
  
        /* MSB <= (FIO_IO_U_PLAT_BITS-1), cannot be rounded off. Use
         * all bits of the sample as index */
 -      if (idx < (FIO_IO_U_PLAT_VAL << 1) )
 +      if (idx < (FIO_IO_U_PLAT_VAL << 1))
                return idx;
  
        /* Find the group and compute the minimum value of that group */
 -      error_bits = (idx >> FIO_IO_U_PLAT_BITS) -1;
 +      error_bits = (idx >> FIO_IO_U_PLAT_BITS) - 1;
        base = 1 << (error_bits + FIO_IO_U_PLAT_BITS);
  
        /* Find its bucket number of the group */
@@@ -119,9 -115,11 +115,9 @@@ static int double_cmp(const void *a, co
        return cmp;
  }
  
 -static unsigned int calc_clat_percentiles(unsigned int *io_u_plat,
 -                                        unsigned long nr, fio_fp64_t *plist,
 -                                        unsigned int **output,
 -                                        unsigned int *maxv,
 -                                        unsigned int *minv)
 +unsigned int calc_clat_percentiles(unsigned int *io_u_plat, unsigned long nr,
 +                                 fio_fp64_t *plist, unsigned int **output,
 +                                 unsigned int *maxv, unsigned int *minv)
  {
        unsigned long sum = 0;
        unsigned int len, i, j = 0;
         * isn't a worry. Also note that this does not work for NaN values.
         */
        if (len > 1)
 -              qsort((void*)plist, len, sizeof(plist[0]), double_cmp);
 +              qsort((void *)plist, len, sizeof(plist[0]), double_cmp);
  
        /*
         * Calculate bucket values, note down max and min values
@@@ -237,8 -235,8 +233,8 @@@ out
                free(ovals);
  }
  
 -static int calc_lat(struct io_stat *is, unsigned long *min, unsigned long *max,
 -                  double *mean, double *dev)
 +int calc_lat(struct io_stat *is, unsigned long *min, unsigned long *max,
 +           double *mean, double *dev)
  {
        double n = is->samples;
  
@@@ -290,7 -288,12 +286,7 @@@ void show_group_stats(struct group_run_
        }
  }
  
 -#define ts_total_io_u(ts)     \
 -      ((ts)->total_io_u[DDIR_READ] + (ts)->total_io_u[DDIR_WRITE] +\
 -              (ts)->total_io_u[DDIR_TRIM])
 -
 -static void stat_calc_dist(unsigned int *map, unsigned long total,
 -                         double *io_u_dist)
 +void stat_calc_dist(unsigned int *map, unsigned long total, double *io_u_dist)
  {
        int i;
  
  static void stat_calc_lat(struct thread_stat *ts, double *dst,
                          unsigned int *src, int nr)
  {
 -      unsigned long total = ts_total_io_u(ts);
 +      unsigned long total = ddir_rw_sum(ts->total_io_u);
        int i;
  
        /*
        }
  }
  
 -static void stat_calc_lat_u(struct thread_stat *ts, double *io_u_lat)
 +void stat_calc_lat_u(struct thread_stat *ts, double *io_u_lat)
  {
        stat_calc_lat(ts, io_u_lat, ts->io_u_lat_u, FIO_IO_U_LAT_U_NR);
  }
  
 -static void stat_calc_lat_m(struct thread_stat *ts, double *io_u_lat)
 +void stat_calc_lat_m(struct thread_stat *ts, double *io_u_lat)
  {
        stat_calc_lat(ts, io_u_lat, ts->io_u_lat_m, FIO_IO_U_LAT_M_NR);
  }
  
 -static int usec_to_msec(unsigned long *min, unsigned long *max, double *mean,
 -                      double *dev)
 +static void display_lat(const char *name, unsigned long min, unsigned long max,
 +                      double mean, double dev)
  {
 -      if (*min > 1000 && *max > 1000 && *mean > 1000.0 && *dev > 1000.0) {
 -              *min /= 1000;
 -              *max /= 1000;
 -              *mean /= 1000.0;
 -              *dev /= 1000.0;
 -              return 0;
 -      }
 +      const char *base = "(usec)";
 +      char *minp, *maxp;
  
 -      return 1;
 +      if (!usec_to_msec(&min, &max, &mean, &dev))
 +              base = "(msec)";
 +
 +      minp = num2str(min, 6, 1, 0);
 +      maxp = num2str(max, 6, 1, 0);
 +
 +      log_info("    %s %s: min=%s, max=%s, avg=%5.02f,"
 +               " stdev=%5.02f\n", name, base, minp, maxp, mean, dev);
 +
 +      free(minp);
 +      free(maxp);
  }
  
  static void show_ddir_status(struct group_run_stats *rs, struct thread_stat *ts,
        free(bw_p);
        free(iops_p);
  
 -      if (calc_lat(&ts->slat_stat[ddir], &min, &max, &mean, &dev)) {
 -              const char *base = "(usec)";
 -              char *minp, *maxp;
 -
 -              if (!usec_to_msec(&min, &max, &mean, &dev))
 -                      base = "(msec)";
 -
 -              minp = num2str(min, 6, 1, 0);
 -              maxp = num2str(max, 6, 1, 0);
 -
 -              log_info("    slat %s: min=%s, max=%s, avg=%5.02f,"
 -                       " stdev=%5.02f\n", base, minp, maxp, mean, dev);
 -
 -              free(minp);
 -              free(maxp);
 -      }
 -      if (calc_lat(&ts->clat_stat[ddir], &min, &max, &mean, &dev)) {
 -              const char *base = "(usec)";
 -              char *minp, *maxp;
 -
 -              if (!usec_to_msec(&min, &max, &mean, &dev))
 -                      base = "(msec)";
 -
 -              minp = num2str(min, 6, 1, 0);
 -              maxp = num2str(max, 6, 1, 0);
 -
 -              log_info("    clat %s: min=%s, max=%s, avg=%5.02f,"
 -                       " stdev=%5.02f\n", base, minp, maxp, mean, dev);
 -
 -              free(minp);
 -              free(maxp);
 -      }
 -      if (calc_lat(&ts->lat_stat[ddir], &min, &max, &mean, &dev)) {
 -              const char *base = "(usec)";
 -              char *minp, *maxp;
 -
 -              if (!usec_to_msec(&min, &max, &mean, &dev))
 -                      base = "(msec)";
 -
 -              minp = num2str(min, 6, 1, 0);
 -              maxp = num2str(max, 6, 1, 0);
 -
 -              log_info("     lat %s: min=%s, max=%s, avg=%5.02f,"
 -                       " stdev=%5.02f\n", base, minp, maxp, mean, dev);
 +      if (calc_lat(&ts->slat_stat[ddir], &min, &max, &mean, &dev))
 +              display_lat("slat", min, max, mean, dev);
 +      if (calc_lat(&ts->clat_stat[ddir], &min, &max, &mean, &dev))
 +              display_lat("clat", min, max, mean, dev);
 +      if (calc_lat(&ts->lat_stat[ddir], &min, &max, &mean, &dev))
 +              display_lat(" lat", min, max, mean, dev);
  
 -              free(minp);
 -              free(maxp);
 -      }
        if (ts->clat_percentiles) {
                show_clat_percentiles(ts->io_u_plat[ddir],
                                        ts->clat_stat[ddir].samples,
@@@ -473,14 -512,8 +469,14 @@@ static void show_lat_m(double *io_u_lat
        show_lat(io_u_lat_m, FIO_IO_U_LAT_M_NR, ranges, "msec");
  }
  
 -static void show_latencies(double *io_u_lat_u, double *io_u_lat_m)
 +static void show_latencies(struct thread_stat *ts)
  {
 +      double io_u_lat_u[FIO_IO_U_LAT_U_NR];
 +      double io_u_lat_m[FIO_IO_U_LAT_M_NR];
 +
 +      stat_calc_lat_u(ts, io_u_lat_u);
 +      stat_calc_lat_m(ts, io_u_lat_m);
 +
        show_lat_u(io_u_lat_u);
        show_lat_m(io_u_lat_m);
  }
@@@ -490,6 -523,8 +486,6 @@@ void show_thread_status(struct thread_s
        double usr_cpu, sys_cpu;
        unsigned long runtime;
        double io_u_dist[FIO_IO_U_MAP_NR];
 -      double io_u_lat_u[FIO_IO_U_LAT_U_NR];
 -      double io_u_lat_m[FIO_IO_U_LAT_M_NR];
        time_t time_p;
        char time_buf[64];
  
        if (ts->io_bytes[DDIR_TRIM])
                show_ddir_status(rs, ts, DDIR_TRIM);
  
 -      stat_calc_lat_u(ts, io_u_lat_u);
 -      stat_calc_lat_m(ts, io_u_lat_m);
 -      show_latencies(io_u_lat_u, io_u_lat_m);
 +      show_latencies(ts);
  
        runtime = ts->total_run_time;
        if (runtime) {
        log_info("  cpu          : usr=%3.2f%%, sys=%3.2f%%, ctx=%lu, majf=%lu,"
                 " minf=%lu\n", usr_cpu, sys_cpu, ts->ctx, ts->majf, ts->minf);
  
 -      stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist);
 +      stat_calc_dist(ts->io_u_map, ddir_rw_sum(ts->total_io_u), io_u_dist);
        log_info("  IO depths    : 1=%3.1f%%, 2=%3.1f%%, 4=%3.1f%%, 8=%3.1f%%,"
                 " 16=%3.1f%%, 32=%3.1f%%, >=64=%3.1f%%\n", io_u_dist[0],
                                        io_u_dist[1], io_u_dist[2],
@@@ -746,7 -783,7 +742,7 @@@ static void add_ddir_status_json(struc
  }
  
  static void show_thread_status_terse_v2(struct thread_stat *ts,
 -                                      struct group_run_stats *rs)
 +                                      struct group_run_stats *rs)
  {
        double io_u_dist[FIO_IO_U_MAP_NR];
        double io_u_lat_u[FIO_IO_U_LAT_U_NR];
                                                                ts->minf);
  
        /* Calc % distribution of IO depths, usecond, msecond latency */
 -      stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist);
 +      stat_calc_dist(ts->io_u_map, ddir_rw_sum(ts->total_io_u), io_u_dist);
        stat_calc_lat_u(ts, io_u_lat_u);
        stat_calc_lat_m(ts, io_u_lat_m);
  
@@@ -840,7 -877,7 +836,7 @@@ static void show_thread_status_terse_v3
                                                                ts->minf);
  
        /* Calc % distribution of IO depths, usecond, msecond latency */
 -      stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist);
 +      stat_calc_dist(ts->io_u_map, ddir_rw_sum(ts->total_io_u), io_u_dist);
        stat_calc_lat_u(ts, io_u_lat_u);
        stat_calc_lat_m(ts, io_u_lat_m);
  
@@@ -907,7 -944,7 +903,7 @@@ static struct json_object *show_thread_
  
  
        /* Calc % distribution of IO depths, usecond, msecond latency */
 -      stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist);
 +      stat_calc_dist(ts->io_u_map, ddir_rw_sum(ts->total_io_u), io_u_dist);
        stat_calc_lat_u(ts, io_u_lat_u);
        stat_calc_lat_m(ts, io_u_lat_m);
  
@@@ -1165,11 -1202,6 +1161,11 @@@ void show_run_stats(void
                        else
                                memset(ts->description, 0, FIO_JOBNAME_SIZE);
  
 +                      /*
 +                       * If multiple entries in this group, this is
 +                       * the first member.
 +                       */
 +                      ts->thread_number = td->thread_number;
                        ts->groupid = td->groupid;
  
                        /*