allow --client parameter to be pathname containing client host IPs/names
[fio.git] / init.c
diff --git a/init.c b/init.c
index b26dc9fe30681eeff07f2e5c5882ebac8e1f586d..5e6d54f3e89a97c4a415e028b438e9419143e44d 100644 (file)
--- a/init.c
+++ b/init.c
 #include "profile.h"
 #include "server.h"
 #include "idletime.h"
+#include "filelock.h"
 
 #include "lib/getopt.h"
 #include "lib/strcasestr.h"
 
+#include "crc/test.h"
+
 const char fio_version_string[] = FIO_VERSION;
 
 #define FIO_RANDSEED           (0xb1899bedUL)
@@ -35,20 +38,21 @@ const char fio_version_string[] = FIO_VERSION;
 static char **ini_file;
 static int max_jobs = FIO_MAX_JOBS;
 static int dump_cmdline;
-static int def_timeout;
+static long long def_timeout;
 static int parse_only;
 
 static struct thread_data def_thread;
 struct thread_data *threads = NULL;
+static char **job_sections;
+static int nr_job_sections;
 
 int exitall_on_terminate = 0;
 int output_format = FIO_OUTPUT_NORMAL;
+int append_terse_output = 0;
 int eta_print = FIO_ETA_AUTO;
 int eta_new_line = 0;
 FILE *f_out = NULL;
 FILE *f_err = NULL;
-char **job_sections = NULL;
-int nr_job_sections = 0;
 char *exec_profile = NULL;
 int warnings_fatal = 0;
 int terse_version = 3;
@@ -60,7 +64,10 @@ int write_bw_log = 0;
 int read_only = 0;
 int status_interval = 0;
 
-static int write_lat_log;
+char *trigger_file = NULL;
+long long trigger_timeout = 0;
+char *trigger_cmd = NULL;
+char *trigger_remote_cmd = NULL;
 
 static int prev_group_jobs;
 
@@ -108,6 +115,11 @@ static struct option l_opts[FIO_NR_OPTIONS] = {
                .has_arg        = optional_argument,
                .val            = 'F' | FIO_CLIENT_FLAG,
        },
+       {
+               .name           = (char *) "append-terse",
+               .has_arg        = optional_argument,
+               .val            = 'f',
+       },
        {
                .name           = (char *) "version",
                .has_arg        = no_argument,
@@ -163,6 +175,13 @@ static struct option l_opts[FIO_NR_OPTIONS] = {
                .has_arg        = required_argument,
                .val            = 'x' | FIO_CLIENT_FLAG,
        },
+#ifdef CONFIG_ZLIB
+       {
+               .name           = (char *) "inflate-log",
+               .has_arg        = required_argument,
+               .val            = 'X' | FIO_CLIENT_FLAG,
+       },
+#endif
        {
                .name           = (char *) "alloc-size",
                .has_arg        = required_argument,
@@ -202,6 +221,11 @@ static struct option l_opts[FIO_NR_OPTIONS] = {
                .has_arg        = required_argument,
                .val            = 'C',
        },
+       {
+               .name           = (char *) "remote-config",
+               .has_arg        = required_argument,
+               .val            = 'R',
+       },
        {
                .name           = (char *) "cpuclock-test",
                .has_arg        = no_argument,
@@ -222,6 +246,26 @@ static struct option l_opts[FIO_NR_OPTIONS] = {
                .has_arg        = required_argument,
                .val            = 'L',
        },
+       {
+               .name           = (char *) "trigger-file",
+               .has_arg        = required_argument,
+               .val            = 'W',
+       },
+       {
+               .name           = (char *) "trigger-timeout",
+               .has_arg        = required_argument,
+               .val            = 'B',
+       },
+       {
+               .name           = (char *) "trigger",
+               .has_arg        = required_argument,
+               .val            = 'H',
+       },
+       {
+               .name           = (char *) "trigger-remote",
+               .has_arg        = required_argument,
+               .val            = 'J',
+       },
        {
                .name           = NULL,
        },
@@ -229,19 +273,23 @@ static struct option l_opts[FIO_NR_OPTIONS] = {
 
 void free_threads_shm(void)
 {
-       struct shmid_ds sbuf;
-
        if (threads) {
                void *tp = threads;
+#ifndef CONFIG_NO_SHM
+               struct shmid_ds sbuf;
 
                threads = NULL;
                shmdt(tp);
                shmctl(shm_id, IPC_RMID, &sbuf);
                shm_id = -1;
+#else
+               threads = NULL;
+               free(tp);
+#endif
        }
 }
 
-void free_shm(void)
+static void free_shm(void)
 {
        if (threads) {
                file_hash_exit();
@@ -250,6 +298,13 @@ void free_shm(void)
                free_threads_shm();
        }
 
+       free(trigger_file);
+       free(trigger_cmd);
+       free(trigger_remote_cmd);
+       trigger_file = trigger_cmd = trigger_remote_cmd = NULL;
+
+       options_free(fio_options, &def_thread);
+       fio_filelock_exit();
        scleanup();
 }
 
@@ -276,6 +331,7 @@ static int setup_thread_area(void)
                size += file_hash_size;
                size += sizeof(unsigned int);
 
+#ifndef CONFIG_NO_SHM
                shm_id = shmget(0, size, IPC_CREAT | 0600);
                if (shm_id != -1)
                        break;
@@ -283,10 +339,16 @@ static int setup_thread_area(void)
                        perror("shmget");
                        break;
                }
+#else
+               threads = malloc(size);
+               if (threads)
+                       break;
+#endif
 
                max_jobs >>= 1;
        } while (max_jobs);
 
+#ifndef CONFIG_NO_SHM
        if (shm_id == -1)
                return 1;
 
@@ -295,6 +357,7 @@ static int setup_thread_area(void)
                perror("shmat");
                return 1;
        }
+#endif
 
        memset(threads, 0, max_jobs * sizeof(struct thread_data));
        hash = (void *) threads + max_jobs * sizeof(struct thread_data);
@@ -307,16 +370,26 @@ static int setup_thread_area(void)
        return 0;
 }
 
+static void set_cmd_options(struct thread_data *td)
+{
+       struct thread_options *o = &td->o;
+
+       if (!o->timeout)
+               o->timeout = def_timeout;
+}
+
 /*
  * Return a free job structure.
  */
 static struct thread_data *get_new_job(int global, struct thread_data *parent,
-                                      int preserve_eo)
+                                      int preserve_eo, const char *jobname)
 {
        struct thread_data *td;
 
-       if (global)
+       if (global) {
+               set_cmd_options(&def_thread);
                return &def_thread;
+       }
        if (setup_thread_area()) {
                log_err("error: failed to setup shm segment\n");
                return NULL;
@@ -342,10 +415,15 @@ static struct thread_data *get_new_job(int global, struct thread_data *parent,
        profile_add_hooks(td);
 
        td->thread_number = thread_number;
+       td->subjob_number = 0;
+
+       if (jobname)
+               td->o.name = strdup(jobname);
 
-       if (!parent || !parent->o.group_reporting)
+       if (!parent->o.group_reporting)
                stat_number++;
 
+       set_cmd_options(td);
        return td;
 }
 
@@ -364,6 +442,9 @@ static void put_job(struct thread_data *td)
        if (td->io_ops)
                free_ioengine(td);
 
+       if (td->o.name)
+               free(td->o.name);
+
        memset(&threads[td->thread_number - 1], 0, sizeof(*td));
        thread_number--;
 }
@@ -411,6 +492,21 @@ static int fixed_block_size(struct thread_options *o)
                o->min_bs[DDIR_READ] == o->min_bs[DDIR_TRIM];
 }
 
+
+static unsigned long long get_rand_start_delay(struct thread_data *td)
+{
+       unsigned long long delayrange;
+       unsigned long r;
+
+       delayrange = td->o.start_delay_high - td->o.start_delay;
+
+       r = __rand(&td->delay_state);
+       delayrange = (unsigned long long) ((double) delayrange * (r / (FRAND_MAX + 1.0)));
+
+       delayrange += td->o.start_delay;
+       return delayrange;
+}
+
 /*
  * Lazy way of fixing up options that depend on each other. We could also
  * define option callback handlers, but this is easier.
@@ -469,7 +565,6 @@ static int fixup_options(struct thread_data *td)
        if (!o->max_bs[DDIR_TRIM])
                o->max_bs[DDIR_TRIM] = o->bs[DDIR_TRIM];
 
-
        o->rw_min_bs = min(o->min_bs[DDIR_READ], o->min_bs[DDIR_WRITE]);
        o->rw_min_bs = min(o->min_bs[DDIR_TRIM], o->rw_min_bs);
 
@@ -495,11 +590,13 @@ static int fixup_options(struct thread_data *td)
        if (!o->file_size_high)
                o->file_size_high = o->file_size_low;
 
+       if (o->start_delay_high)
+               o->start_delay = get_rand_start_delay(td);
+
        if (o->norandommap && o->verify != VERIFY_NONE
            && !fixed_block_size(o))  {
                log_err("fio: norandommap given for variable block sizes, "
-                       "verify disabled\n");
-               o->verify = VERIFY_NONE;
+                       "verify limited\n");
                ret = warnings_fatal;
        }
        if (o->bs_unaligned && (o->odirect || td->io_ops->flags & FIO_RAWIO))
@@ -567,6 +664,15 @@ static int fixup_options(struct thread_data *td)
                if (o->max_bs[DDIR_WRITE] != o->min_bs[DDIR_WRITE] &&
                    !o->verify_interval)
                        o->verify_interval = o->min_bs[DDIR_WRITE];
+
+               /*
+                * Verify interval must be smaller or equal to the
+                * write size.
+                */
+               if (o->verify_interval > o->min_bs[DDIR_WRITE])
+                       o->verify_interval = o->min_bs[DDIR_WRITE];
+               else if (td_read(td) && o->verify_interval > o->min_bs[DDIR_READ])
+                       o->verify_interval = o->min_bs[DDIR_READ];
        }
 
        if (o->pre_read) {
@@ -646,6 +752,28 @@ static int fixup_options(struct thread_data *td)
        if (td->o.rand_seed)
                td->o.rand_repeatable = 0;
 
+       if ((td->io_ops->flags & FIO_NOEXTEND) && td->o.file_append) {
+               log_err("fio: can't append/extent with IO engine %s\n", td->io_ops->name);
+               ret = 1;
+       }
+
+       if (fio_option_is_set(o, gtod_cpu)) {
+               fio_gtod_init();
+               fio_gtod_set_cpu(o->gtod_cpu);
+               fio_gtod_offload = 1;
+       }
+
+       td->loops = o->loops;
+       if (!td->loops)
+               td->loops = 1;
+
+       if (td->o.block_error_hist && td->o.nr_files != 1) {
+               log_err("fio: block error histogram only available with "
+                       "with a single file per job, but %d files "
+                       "provided\n", td->o.nr_files);
+               ret = 1;
+       }
+
        return ret;
 }
 
@@ -699,42 +827,18 @@ static int exists_and_not_file(const char *filename)
        return 1;
 }
 
-static void td_fill_rand_seeds_os(struct thread_data *td)
-{
-       os_random_seed(td->rand_seeds[FIO_RAND_BS_OFF], &td->bsrange_state);
-       os_random_seed(td->rand_seeds[FIO_RAND_VER_OFF], &td->verify_state);
-       os_random_seed(td->rand_seeds[FIO_RAND_MIX_OFF], &td->rwmix_state);
-
-       if (td->o.file_service_type == FIO_FSERVICE_RANDOM)
-               os_random_seed(td->rand_seeds[FIO_RAND_FILE_OFF], &td->next_file_state);
-
-       os_random_seed(td->rand_seeds[FIO_RAND_FILE_SIZE_OFF], &td->file_size_state);
-       os_random_seed(td->rand_seeds[FIO_RAND_TRIM_OFF], &td->trim_state);
-
-       if (!td_random(td))
-               return;
-
-       if (td->o.rand_repeatable)
-               td->rand_seeds[FIO_RAND_BLOCK_OFF] = FIO_RANDSEED * td->thread_number;
-
-       os_random_seed(td->rand_seeds[FIO_RAND_BLOCK_OFF], &td->random_state);
-
-       os_random_seed(td->rand_seeds[FIO_RAND_SEQ_RAND_READ_OFF], &td->seq_rand_state[DDIR_READ]);
-       os_random_seed(td->rand_seeds[FIO_RAND_SEQ_RAND_WRITE_OFF], &td->seq_rand_state[DDIR_WRITE]);
-       os_random_seed(td->rand_seeds[FIO_RAND_SEQ_RAND_TRIM_OFF], &td->seq_rand_state[DDIR_TRIM]);
-}
-
 static void td_fill_rand_seeds_internal(struct thread_data *td)
 {
-       init_rand_seed(&td->__bsrange_state, td->rand_seeds[FIO_RAND_BS_OFF]);
-       init_rand_seed(&td->__verify_state, td->rand_seeds[FIO_RAND_VER_OFF]);
-       init_rand_seed(&td->__rwmix_state, td->rand_seeds[FIO_RAND_MIX_OFF]);
+       init_rand_seed(&td->bsrange_state, td->rand_seeds[FIO_RAND_BS_OFF]);
+       init_rand_seed(&td->verify_state, td->rand_seeds[FIO_RAND_VER_OFF]);
+       init_rand_seed(&td->rwmix_state, td->rand_seeds[FIO_RAND_MIX_OFF]);
 
        if (td->o.file_service_type == FIO_FSERVICE_RANDOM)
-               init_rand_seed(&td->__next_file_state, td->rand_seeds[FIO_RAND_FILE_OFF]);
+               init_rand_seed(&td->next_file_state, td->rand_seeds[FIO_RAND_FILE_OFF]);
 
-       init_rand_seed(&td->__file_size_state, td->rand_seeds[FIO_RAND_FILE_SIZE_OFF]);
-       init_rand_seed(&td->__trim_state, td->rand_seeds[FIO_RAND_TRIM_OFF]);
+       init_rand_seed(&td->file_size_state, td->rand_seeds[FIO_RAND_FILE_SIZE_OFF]);
+       init_rand_seed(&td->trim_state, td->rand_seeds[FIO_RAND_TRIM_OFF]);
+       init_rand_seed(&td->delay_state, td->rand_seeds[FIO_RAND_START_DELAY]);
 
        if (!td_random(td))
                return;
@@ -742,20 +846,28 @@ static void td_fill_rand_seeds_internal(struct thread_data *td)
        if (td->o.rand_repeatable)
                td->rand_seeds[FIO_RAND_BLOCK_OFF] = FIO_RANDSEED * td->thread_number;
 
-       init_rand_seed(&td->__random_state, td->rand_seeds[FIO_RAND_BLOCK_OFF]);
-       init_rand_seed(&td->__seq_rand_state[DDIR_READ], td->rand_seeds[FIO_RAND_SEQ_RAND_READ_OFF]);
-       init_rand_seed(&td->__seq_rand_state[DDIR_WRITE], td->rand_seeds[FIO_RAND_SEQ_RAND_WRITE_OFF]);
-       init_rand_seed(&td->__seq_rand_state[DDIR_TRIM], td->rand_seeds[FIO_RAND_SEQ_RAND_TRIM_OFF]);
+       init_rand_seed(&td->random_state, td->rand_seeds[FIO_RAND_BLOCK_OFF]);
+       init_rand_seed(&td->seq_rand_state[DDIR_READ], td->rand_seeds[FIO_RAND_SEQ_RAND_READ_OFF]);
+       init_rand_seed(&td->seq_rand_state[DDIR_WRITE], td->rand_seeds[FIO_RAND_SEQ_RAND_WRITE_OFF]);
+       init_rand_seed(&td->seq_rand_state[DDIR_TRIM], td->rand_seeds[FIO_RAND_SEQ_RAND_TRIM_OFF]);
 }
 
 void td_fill_rand_seeds(struct thread_data *td)
 {
-       if (td->o.use_os_rand)
-               td_fill_rand_seeds_os(td);
-       else
-               td_fill_rand_seeds_internal(td);
+       if (td->o.allrand_repeatable) {
+               unsigned int i;
+
+               for (i = 0; i < FIO_RAND_NR_OFFS; i++)
+                       td->rand_seeds[i] = FIO_RANDSEED * td->thread_number
+                               + i;
+       }
+
+       td_fill_rand_seeds_internal(td);
 
        init_rand_seed(&td->buf_state, td->rand_seeds[FIO_RAND_BUF_OFF]);
+       frand_copy(&td->buf_state_prev, &td->buf_state);
+
+       init_rand_seed(&td->dedupe_state, td->rand_seeds[FIO_DEDUPE_OFF]);
 }
 
 /*
@@ -830,10 +942,23 @@ static void init_flags(struct thread_data *td)
                td->flags |= TD_F_READ_IOLOG;
        if (o->refill_buffers)
                td->flags |= TD_F_REFILL_BUFFERS;
-       if (o->scramble_buffers)
+       /*
+        * Always scramble buffers if asked to
+        */
+       if (o->scramble_buffers && fio_option_is_set(o, scramble_buffers))
+               td->flags |= TD_F_SCRAMBLE_BUFFERS;
+       /*
+        * But also scramble buffers, unless we were explicitly asked
+        * to zero them.
+        */
+       if (o->scramble_buffers && !(o->zero_buffers &&
+           fio_option_is_set(o, zero_buffers)))
                td->flags |= TD_F_SCRAMBLE_BUFFERS;
        if (o->verify != VERIFY_NONE)
                td->flags |= TD_F_VER_NONE;
+
+       if (o->verify_async || o->io_submit_mode == IO_MODE_OFFLOAD)
+               td->flags |= TD_F_NEED_LOCK;
 }
 
 static int setup_random_seeds(struct thread_data *td)
@@ -879,11 +1004,12 @@ static struct fpre_keyword {
        { .keyword = NULL, },
        };
 
-static char *make_filename(char *buf, struct thread_options *o,
+static char *make_filename(char *buf, size_t buf_size,struct thread_options *o,
                           const char *jobname, int jobnum, int filenum)
 {
        struct fpre_keyword *f;
        char copy[PATH_MAX];
+       size_t dst_left = PATH_MAX - 1;
 
        if (!o->filename_format || !strlen(o->filename_format)) {
                sprintf(buf, "%s.%d.%d", jobname, jobnum, filenum);
@@ -893,7 +1019,9 @@ static char *make_filename(char *buf, struct thread_options *o,
        for (f = &fpre_keywords[0]; f->keyword; f++)
                f->strlen = strlen(f->keyword);
 
-       strcpy(buf, o->filename_format);
+       buf[buf_size - 1] = '\0';
+       strncpy(buf, o->filename_format, buf_size - 1);
+
        memset(copy, 0, sizeof(copy));
        for (f = &fpre_keywords[0]; f->keyword; f++) {
                do {
@@ -911,27 +1039,67 @@ static char *make_filename(char *buf, struct thread_options *o,
                        if (pre_len) {
                                strncpy(dst, buf, pre_len);
                                dst += pre_len;
+                               dst_left -= pre_len;
                        }
 
                        switch (f->key) {
-                       case FPRE_JOBNAME:
-                               dst += sprintf(dst, "%s", jobname);
+                       case FPRE_JOBNAME: {
+                               int ret;
+
+                               ret = snprintf(dst, dst_left, "%s", jobname);
+                               if (ret < 0)
+                                       break;
+                               else if (ret > dst_left) {
+                                       log_err("fio: truncated filename\n");
+                                       dst += dst_left;
+                                       dst_left = 0;
+                               } else {
+                                       dst += ret;
+                                       dst_left -= ret;
+                               }
                                break;
-                       case FPRE_JOBNUM:
-                               dst += sprintf(dst, "%d", jobnum);
+                               }
+                       case FPRE_JOBNUM: {
+                               int ret;
+
+                               ret = snprintf(dst, dst_left, "%d", jobnum);
+                               if (ret < 0)
+                                       break;
+                               else if (ret > dst_left) {
+                                       log_err("fio: truncated filename\n");
+                                       dst += dst_left;
+                                       dst_left = 0;
+                               } else {
+                                       dst += ret;
+                                       dst_left -= ret;
+                               }
                                break;
-                       case FPRE_FILENUM:
-                               dst += sprintf(dst, "%d", filenum);
+                               }
+                       case FPRE_FILENUM: {
+                               int ret;
+
+                               ret = snprintf(dst, dst_left, "%d", filenum);
+                               if (ret < 0)
+                                       break;
+                               else if (ret > dst_left) {
+                                       log_err("fio: truncated filename\n");
+                                       dst += dst_left;
+                                       dst_left = 0;
+                               } else {
+                                       dst += ret;
+                                       dst_left -= ret;
+                               }
                                break;
+                               }
                        default:
                                assert(0);
                                break;
                        }
 
                        if (post_start)
-                               strcpy(dst, buf + post_start);
+                               strncpy(dst, buf + post_start, dst_left);
 
-                       strcpy(buf, copy);
+                       strncpy(buf, copy, buf_size - 1);
                } while (1);
        }
 
@@ -955,6 +1123,7 @@ static int add_job(struct thread_data *td, const char *jobname, int job_add_num,
        char fname[PATH_MAX];
        int numjobs, file_alloced;
        struct thread_options *o = &td->o;
+       char logname[PATH_MAX + 32];
 
        /*
         * the def_thread is just for options, it's not a real job
@@ -988,10 +1157,10 @@ static int add_job(struct thread_data *td, const char *jobname, int job_add_num,
                file_alloced = 1;
 
                if (o->nr_files == 1 && exists_and_not_file(jobname))
-                       add_file(td, jobname);
+                       add_file(td, jobname, job_add_num, 0);
                else {
                        for (i = 0; i < o->nr_files; i++)
-                               add_file(td, make_filename(fname, o, jobname, td->thread_number, i));
+                               add_file(td, make_filename(fname, sizeof(fname), o, jobname, job_add_num, i), job_add_num, 0);
                }
        }
 
@@ -1045,14 +1214,71 @@ static int add_job(struct thread_data *td, const char *jobname, int job_add_num,
                goto err;
 
        if (o->lat_log_file) {
-               setup_log(&td->lat_log, o->log_avg_msec, IO_LOG_TYPE_LAT);
-               setup_log(&td->slat_log, o->log_avg_msec, IO_LOG_TYPE_SLAT);
-               setup_log(&td->clat_log, o->log_avg_msec, IO_LOG_TYPE_CLAT);
+               struct log_params p = {
+                       .td = td,
+                       .avg_msec = o->log_avg_msec,
+                       .log_type = IO_LOG_TYPE_LAT,
+                       .log_offset = o->log_offset,
+                       .log_gz = o->log_gz,
+                       .log_gz_store = o->log_gz_store,
+               };
+               const char *suf;
+
+               if (p.log_gz_store)
+                       suf = "log.fz";
+               else
+                       suf = "log";
+
+               snprintf(logname, sizeof(logname), "%s_lat.%d.%s",
+                               o->lat_log_file, td->thread_number, suf);
+               setup_log(&td->lat_log, &p, logname);
+               snprintf(logname, sizeof(logname), "%s_slat.%d.%s",
+                               o->lat_log_file, td->thread_number, suf);
+               setup_log(&td->slat_log, &p, logname);
+               snprintf(logname, sizeof(logname), "%s_clat.%d.%s",
+                               o->lat_log_file, td->thread_number, suf);
+               setup_log(&td->clat_log, &p, logname);
+       }
+       if (o->bw_log_file) {
+               struct log_params p = {
+                       .td = td,
+                       .avg_msec = o->log_avg_msec,
+                       .log_type = IO_LOG_TYPE_BW,
+                       .log_offset = o->log_offset,
+                       .log_gz = o->log_gz,
+                       .log_gz_store = o->log_gz_store,
+               };
+               const char *suf;
+
+               if (p.log_gz_store)
+                       suf = "log.fz";
+               else
+                       suf = "log";
+
+               snprintf(logname, sizeof(logname), "%s_bw.%d.%s",
+                               o->bw_log_file, td->thread_number, suf);
+               setup_log(&td->bw_log, &p, logname);
+       }
+       if (o->iops_log_file) {
+               struct log_params p = {
+                       .td = td,
+                       .avg_msec = o->log_avg_msec,
+                       .log_type = IO_LOG_TYPE_IOPS,
+                       .log_offset = o->log_offset,
+                       .log_gz = o->log_gz,
+                       .log_gz_store = o->log_gz_store,
+               };
+               const char *suf;
+
+               if (p.log_gz_store)
+                       suf = "log.fz";
+               else
+                       suf = "log";
+
+               snprintf(logname, sizeof(logname), "%s_iops.%d.%s",
+                               o->iops_log_file, td->thread_number, suf);
+               setup_log(&td->iops_log, &p, logname);
        }
-       if (o->bw_log_file)
-               setup_log(&td->bw_log, o->log_avg_msec, IO_LOG_TYPE_BW);
-       if (o->iops_log_file)
-               setup_log(&td->iops_log, o->log_avg_msec, IO_LOG_TYPE_IOPS);
 
        if (!o->name)
                o->name = strdup(jobname);
@@ -1107,7 +1333,7 @@ static int add_job(struct thread_data *td, const char *jobname, int job_add_num,
         */
        numjobs = o->numjobs;
        while (--numjobs) {
-               struct thread_data *td_new = get_new_job(0, td, 1);
+               struct thread_data *td_new = get_new_job(0, td, 1, jobname);
 
                if (!td_new)
                        goto err;
@@ -1115,17 +1341,28 @@ static int add_job(struct thread_data *td, const char *jobname, int job_add_num,
                td_new->o.numjobs = 1;
                td_new->o.stonewall = 0;
                td_new->o.new_group = 0;
+               td_new->subjob_number = numjobs;
 
                if (file_alloced) {
-                       td_new->o.filename = NULL;
+                       if (td_new->files) {
+                               struct fio_file *f;
+                               for_each_file(td_new, f, i) {
+                                       if (f->file_name)
+                                               sfree(f->file_name);
+                                       sfree(f);
+                               }
+                               free(td_new->files);
+                               td_new->files = NULL;
+                       }
                        td_new->files_index = 0;
                        td_new->files_size = 0;
-                       td_new->files = NULL;
+                       if (td_new->o.filename) {
+                               free(td_new->o.filename);
+                               td_new->o.filename = NULL;
+                       }
                }
 
-               job_add_num = numjobs - 1;
-
-               if (add_job(td_new, jobname, job_add_num, 1, client_type))
+               if (add_job(td_new, jobname, numjobs, 1, client_type))
                        goto err;
        }
 
@@ -1155,11 +1392,11 @@ void add_job_opts(const char **o, int client_type)
                        sprintf(jobname, "%s", o[i] + 5);
                }
                if (in_global && !td_parent)
-                       td_parent = get_new_job(1, &def_thread, 0);
+                       td_parent = get_new_job(1, &def_thread, 0, jobname);
                else if (!in_global && !td) {
                        if (!td_parent)
                                td_parent = &def_thread;
-                       td = get_new_job(0, td_parent, 0);
+                       td = get_new_job(0, td_parent, 0, jobname);
                }
                if (in_global)
                        fio_options_parse(td_parent, (char **) &o[i], 1, 0);
@@ -1207,11 +1444,12 @@ static int is_empty_or_comment(char *line)
 /*
  * This is our [ini] type file parser.
  */
-int parse_jobs_ini(char *file, int is_buf, int stonewall_flag, int type)
+int __parse_jobs_ini(struct thread_data *td,
+               char *file, int is_buf, int stonewall_flag, int type,
+               int nested, char *name, char ***popts, int *aopts, int *nopts)
 {
-       unsigned int global;
-       struct thread_data *td;
-       char *string, *name;
+       unsigned int global = 0;
+       char *string;
        FILE *f;
        char *p;
        int ret = 0, stonewall;
@@ -1221,6 +1459,9 @@ int parse_jobs_ini(char *file, int is_buf, int stonewall_flag, int type)
        char **opts;
        int i, alloc_opts, num_opts;
 
+       dprint(FD_PARSE, "Parsing ini file %s\n", file);
+       assert(td || !nested);
+
        if (is_buf)
                f = NULL;
        else {
@@ -1230,7 +1471,11 @@ int parse_jobs_ini(char *file, int is_buf, int stonewall_flag, int type)
                        f = fopen(file, "r");
 
                if (!f) {
-                       perror("fopen job file");
+                       int __err = errno;
+
+                       log_err("fio: unable to open '%s' job file\n", file);
+                       if (td)
+                               td_verror(td, __err, "job file open");
                        return 1;
                }
        }
@@ -1240,12 +1485,23 @@ int parse_jobs_ini(char *file, int is_buf, int stonewall_flag, int type)
        /*
         * it's really 256 + small bit, 280 should suffice
         */
-       name = malloc(280);
-       memset(name, 0, 280);
+       if (!nested) {
+               name = malloc(280);
+               memset(name, 0, 280);
+       }
 
-       alloc_opts = 8;
-       opts = malloc(sizeof(char *) * alloc_opts);
-       num_opts = 0;
+       opts = NULL;
+       if (nested && popts) {
+               opts = *popts;
+               alloc_opts = *aopts;
+               num_opts = *nopts;
+       }
+
+       if (!opts) {
+               alloc_opts = 8;
+               opts = malloc(sizeof(char *) * alloc_opts);
+               num_opts = 0;
+       }
 
        stonewall = stonewall_flag;
        do {
@@ -1266,58 +1522,73 @@ int parse_jobs_ini(char *file, int is_buf, int stonewall_flag, int type)
                strip_blank_front(&p);
                strip_blank_end(p);
 
+               dprint(FD_PARSE, "%s\n", p);
                if (is_empty_or_comment(p))
                        continue;
-               if (sscanf(p, "[%255[^\n]]", name) != 1) {
-                       if (inside_skip)
+
+               if (!nested) {
+                       if (sscanf(p, "[%255[^\n]]", name) != 1) {
+                               if (inside_skip)
+                                       continue;
+
+                               log_err("fio: option <%s> outside of "
+                                       "[] job section\n", p);
+                               ret = 1;
+                               break;
+                       }
+
+                       name[strlen(name) - 1] = '\0';
+
+                       if (skip_this_section(name)) {
+                               inside_skip = 1;
                                continue;
-                       log_err("fio: option <%s> outside of [] job section\n",
-                                                                       p);
-                       break;
-               }
+                       } else
+                               inside_skip = 0;
 
-               name[strlen(name) - 1] = '\0';
+                       dprint(FD_PARSE, "Parsing section [%s]\n", name);
 
-               if (skip_this_section(name)) {
-                       inside_skip = 1;
-                       continue;
-               } else
-                       inside_skip = 0;
+                       global = !strncmp(name, "global", 6);
 
-               global = !strncmp(name, "global", 6);
+                       if (dump_cmdline) {
+                               if (first_sect)
+                                       log_info("fio ");
+                               if (!global)
+                                       log_info("--name=%s ", name);
+                               first_sect = 0;
+                       }
 
-               if (dump_cmdline) {
-                       if (first_sect)
-                               log_info("fio ");
-                       if (!global)
-                               log_info("--name=%s ", name);
-                       first_sect = 0;
-               }
+                       td = get_new_job(global, &def_thread, 0, name);
+                       if (!td) {
+                               ret = 1;
+                               break;
+                       }
 
-               td = get_new_job(global, &def_thread, 0);
-               if (!td) {
-                       ret = 1;
-                       break;
-               }
+                       /*
+                        * Separate multiple job files by a stonewall
+                        */
+                       if (!global && stonewall) {
+                               td->o.stonewall = stonewall;
+                               stonewall = 0;
+                       }
 
-               /*
-                * Separate multiple job files by a stonewall
-                */
-               if (!global && stonewall) {
-                       td->o.stonewall = stonewall;
-                       stonewall = 0;
+                       num_opts = 0;
+                       memset(opts, 0, alloc_opts * sizeof(char *));
                }
-
-               num_opts = 0;
-               memset(opts, 0, alloc_opts * sizeof(char *));
+               else
+                       skip_fgets = 1;
 
                while (1) {
-                       if (is_buf)
-                               p = strsep(&file, "\n");
+                       if (!skip_fgets) {
+                               if (is_buf)
+                                       p = strsep(&file, "\n");
+                               else
+                                       p = fgets(string, 4096, f);
+                               if (!p)
+                                       break;
+                               dprint(FD_PARSE, "%s", p);
+                       }
                        else
-                               p = fgets(string, 4096, f);
-                       if (!p)
-                               break;
+                               skip_fgets = 0;
 
                        if (is_empty_or_comment(p))
                                continue;
@@ -1329,12 +1600,30 @@ int parse_jobs_ini(char *file, int is_buf, int stonewall_flag, int type)
                         * fgets() a new line at the top.
                         */
                        if (p[0] == '[') {
+                               if (nested) {
+                                       log_err("No new sections in included files\n");
+                                       return 1;
+                               }
+
                                skip_fgets = 1;
                                break;
                        }
 
                        strip_blank_end(p);
 
+                       if (!strncmp(p, "include", strlen("include"))) {
+                               char *filename = p + strlen("include") + 1;
+
+                               if ((ret = __parse_jobs_ini(td, filename,
+                                               is_buf, stonewall_flag, type, 1,
+                                               name, &opts, &alloc_opts, &num_opts))) {
+                                       log_err("Error %d while parsing include file %s\n",
+                                               ret, filename);
+                                       break;
+                               }
+                               continue;
+                       }
+
                        if (num_opts == alloc_opts) {
                                alloc_opts <<= 1;
                                opts = realloc(opts,
@@ -1345,6 +1634,13 @@ int parse_jobs_ini(char *file, int is_buf, int stonewall_flag, int type)
                        num_opts++;
                }
 
+               if (nested) {
+                       *popts = opts;
+                       *aopts = alloc_opts;
+                       *nopts = num_opts;
+                       goto out;
+               }
+
                ret = fio_options_parse(td, opts, num_opts, dump_cmdline);
                if (!ret)
                        ret = add_job(td, name, 0, 0, type);
@@ -1367,24 +1663,29 @@ int parse_jobs_ini(char *file, int is_buf, int stonewall_flag, int type)
                i++;
        }
 
-       for (i = 0; i < num_opts; i++)
-               free(opts[i]);
-
-       free(string);
-       free(name);
        free(opts);
+out:
+       free(string);
+       if (!nested)
+               free(name);
        if (!is_buf && f != stdin)
                fclose(f);
        return ret;
 }
 
+int parse_jobs_ini(char *file, int is_buf, int stonewall_flag, int type)
+{
+       return __parse_jobs_ini(NULL, file, is_buf, stonewall_flag, type,
+                       0, NULL, NULL, NULL, NULL);
+}
+
 static int fill_def_thread(void)
 {
        memset(&def_thread, 0, sizeof(def_thread));
 
        fio_getaffinity(getpid(), &def_thread.o.cpumask);
-       def_thread.o.timeout = def_timeout;
        def_thread.o.error_dump = 1;
+
        /*
         * fill default options
         */
@@ -1398,11 +1699,10 @@ static void usage(const char *name)
        printf("%s [options] [job options] <job file(s)>\n", name);
        printf("  --debug=options\tEnable debug logging. May be one/more of:\n"
                "\t\t\tprocess,file,io,mem,blktrace,verify,random,parse,\n"
-               "\t\t\tdiskutil,job,mutex,profile,time,net,rate\n");
+               "\t\t\tdiskutil,job,mutex,profile,time,net,rate,compress\n");
        printf("  --parse-only\t\tParse options only, don't start any IO\n");
        printf("  --output\t\tWrite output to file\n");
        printf("  --runtime\t\tRuntime in seconds\n");
-       printf("  --latency-log\t\tGenerate per-job latency logs\n");
        printf("  --bandwidth-log\tGenerate per-job bandwidth logs\n");
        printf("  --minimal\t\tMinimal (terse) output\n");
        printf("  --output-format=x\tOutput format (terse,json,normal)\n");
@@ -1434,9 +1734,17 @@ static void usage(const char *name)
        printf("  --server=args\t\tStart a backend fio server\n");
        printf("  --daemonize=pidfile\tBackground fio server, write pid to file\n");
        printf("  --client=hostname\tTalk to remote backend fio server at hostname\n");
+       printf("  --remote-config=file\tTell fio server to load this local job file\n");
        printf("  --idle-prof=option\tReport cpu idleness on a system or percpu basis\n"
                "\t\t\t(option=system,percpu) or run unit work\n"
                "\t\t\tcalibration only (option=calibrate)\n");
+#ifdef CONFIG_ZLIB
+       printf("  --inflate-log=log\tInflate and output compressed log\n");
+#endif
+       printf("  --trigger-file=file\tExecute trigger cmd when file exists\n");
+       printf("  --trigger-timeout=t\tExecute trigger af this time\n");
+       printf("  --trigger=cmd\t\tSet this command as local trigger\n");
+       printf("  --trigger-remote=cmd\tSet this command as remote trigger\n");
        printf("\nFio was written by Jens Axboe <jens.axboe@oracle.com>");
        printf("\n                   Jens Axboe <jaxboe@fusionio.com>");
        printf("\n                   Jens Axboe <axboe@fb.com>\n");
@@ -1504,6 +1812,10 @@ struct debug_level debug_levels[] = {
          .help = "Rate logging",
          .shift = FD_RATE,
        },
+       { .name = "compress",
+         .help = "Log compression logging",
+         .shift = FD_COMPRESS,
+       },
        { .name = NULL, },
 };
 
@@ -1605,12 +1917,34 @@ static int client_flag_set(char c)
        return 0;
 }
 
-void parse_cmd_client(void *client, char *opt)
+static void parse_cmd_client(void *client, char *opt)
 {
        fio_client_add_cmd_option(client, opt);
 }
 
-extern int fio_crctest(const char *);
+static void show_closest_option(const char *name)
+{
+       int best_option, best_distance;
+       int i, distance;
+
+       while (*name == '-')
+               name++;
+
+       best_option = -1;
+       best_distance = INT_MAX;
+       i = 0;
+       while (l_opts[i].name) {
+               distance = string_distance(name, l_opts[i].name);
+               if (distance < best_distance) {
+                       best_distance = distance;
+                       best_option = i;
+               }
+               i++;
+       }
+
+       if (best_option != -1)
+               log_err("Did you mean %s?\n", l_opts[best_option].name);
+}
 
 int parse_cmd_line(int argc, char *argv[], int client_type)
 {
@@ -1628,8 +1962,6 @@ int parse_cmd_line(int argc, char *argv[], int client_type)
        optind = 1;
 
        while ((c = getopt_long_only(argc, argv, ostr, l_opts, &lidx)) != -1) {
-               did_arg = 1;
-
                if ((c & FIO_CLIENT_FLAG) || client_flag_set(c)) {
                        parse_cmd_client(cur_client, argv[optind - 1]);
                        c &= ~FIO_CLIENT_FLAG;
@@ -1640,15 +1972,24 @@ int parse_cmd_line(int argc, char *argv[], int client_type)
                        smalloc_pool_size = atoi(optarg);
                        break;
                case 't':
-                       def_timeout = atoi(optarg);
+                       if (check_str_time(optarg, &def_timeout, 1)) {
+                               log_err("fio: failed parsing time %s\n", optarg);
+                               do_exit++;
+                               exit_val = 1;
+                       }
                        break;
                case 'l':
-                       write_lat_log = 1;
+                       log_err("fio: --latency-log is deprecated. Use per-job latency log options.\n");
+                       do_exit++;
+                       exit_val = 1;
                        break;
                case 'b':
                        write_bw_log = 1;
                        break;
                case 'o':
+                       if (f_out && f_out != stdout)
+                               fclose(f_out);
+
                        f_out = fopen(optarg, "w+");
                        if (!f_out) {
                                perror("fopen output");
@@ -1675,31 +2016,39 @@ int parse_cmd_line(int argc, char *argv[], int client_type)
                        else
                                output_format = FIO_OUTPUT_NORMAL;
                        break;
+               case 'f':
+                       append_terse_output = 1;
+                       break;
                case 'h':
+                       did_arg = 1;
                        if (!cur_client) {
                                usage(argv[0]);
                                do_exit++;
                        }
                        break;
                case 'c':
+                       did_arg = 1;
                        if (!cur_client) {
                                fio_show_option_help(optarg);
                                do_exit++;
                        }
                        break;
                case 'i':
+                       did_arg = 1;
                        if (!cur_client) {
                                fio_show_ioengine_help(optarg);
                                do_exit++;
                        }
                        break;
                case 's':
+                       did_arg = 1;
                        dump_cmdline = 1;
                        break;
                case 'r':
                        read_only = 1;
                        break;
                case 'v':
+                       did_arg = 1;
                        if (!cur_client) {
                                log_info("%s\n", fio_version_string);
                                do_exit++;
@@ -1723,12 +2072,12 @@ int parse_cmd_line(int argc, char *argv[], int client_type)
                case 'E': {
                        long long t = 0;
 
-                       if (str_to_decimal(optarg, &t, 0, NULL)) {
+                       if (check_str_time(optarg, &t, 1)) {
                                log_err("fio: failed parsing eta time %s\n", optarg);
                                exit_val = 1;
                                do_exit++;
                        }
-                       eta_new_line = t;
+                       eta_new_line = t / 1000;
                        break;
                        }
                case 'd':
@@ -1736,6 +2085,7 @@ int parse_cmd_line(int argc, char *argv[], int client_type)
                                do_exit++;
                        break;
                case 'P':
+                       did_arg = 1;
                        parse_only = 1;
                        break;
                case 'x': {
@@ -1754,7 +2104,17 @@ int parse_cmd_line(int argc, char *argv[], int client_type)
                        nr_job_sections++;
                        break;
                        }
+#ifdef CONFIG_ZLIB
+               case 'X':
+                       exit_val = iolog_file_inflate(optarg);
+                       did_arg++;
+                       do_exit++;
+                       break;
+#endif
                case 'p':
+                       did_arg = 1;
+                       if (exec_profile)
+                               free(exec_profile);
                        exec_profile = strdup(optarg);
                        break;
                case FIO_GETOPT_JOB: {
@@ -1764,8 +2124,9 @@ int parse_cmd_line(int argc, char *argv[], int client_type)
                        if (!strncmp(opt, "name", 4) && td) {
                                ret = add_job(td, td->o.name ?: "fio", 0, 0, client_type);
                                if (ret)
-                                       return 0;
+                                       goto out_free;
                                td = NULL;
+                               did_arg = 1;
                        }
                        if (!td) {
                                int is_section = !strncmp(opt, "name", 4);
@@ -1777,9 +2138,16 @@ int parse_cmd_line(int argc, char *argv[], int client_type)
                                if (is_section && skip_this_section(val))
                                        continue;
 
-                               td = get_new_job(global, &def_thread, 1);
-                               if (!td || ioengine_load(td))
-                                       return 0;
+                               td = get_new_job(global, &def_thread, 1, NULL);
+                               if (!td || ioengine_load(td)) {
+                                       if (td) {
+                                               put_job(td);
+                                               td = NULL;
+                                       }
+                                       do_exit++;
+                                       exit_val = 1;
+                                       break;
+                               }
                                fio_options_set_ioengine_opts(l_opts, td);
                        }
 
@@ -1796,12 +2164,18 @@ int parse_cmd_line(int argc, char *argv[], int client_type)
                                        td = NULL;
                                }
                                do_exit++;
+                               exit_val = 1;
                        }
 
                        if (!ret && !strcmp(opt, "ioengine")) {
                                free_ioengine(td);
-                               if (ioengine_load(td))
-                                       return 0;
+                               if (ioengine_load(td)) {
+                                       put_job(td);
+                                       td = NULL;
+                                       do_exit++;
+                                       exit_val = 1;
+                                       break;
+                               }
                                fio_options_set_ioengine_opts(l_opts, td);
                        }
                        break;
@@ -1809,6 +2183,10 @@ int parse_cmd_line(int argc, char *argv[], int client_type)
                case FIO_GETOPT_IOENGINE: {
                        const char *opt = l_opts[lidx].name;
                        char *val = optarg;
+
+                       if (!td)
+                               break;
+
                        ret = fio_cmd_ioengine_option_parse(td, opt, val);
                        break;
                }
@@ -1824,6 +2202,8 @@ int parse_cmd_line(int argc, char *argv[], int client_type)
                        }
                        break;
                case 'S':
+                       did_arg = 1;
+#ifndef CONFIG_NO_SHM
                        if (nr_clients) {
                                log_err("fio: can't be both client and server\n");
                                do_exit++;
@@ -1834,25 +2214,60 @@ int parse_cmd_line(int argc, char *argv[], int client_type)
                                fio_server_set_arg(optarg);
                        is_backend = 1;
                        backend = 1;
+#else
+                       log_err("fio: client/server requires SHM support\n");
+                       do_exit++;
+                       exit_val = 1;
+#endif
                        break;
                case 'D':
+                       if (pid_file)
+                               free(pid_file);
                        pid_file = strdup(optarg);
                        break;
                case 'I':
                        if ((ret = fio_idle_prof_parse_opt(optarg))) {
                                /* exit on error and calibration only */
+                               did_arg = 1;
                                do_exit++;
-                               if (ret == -1) 
+                               if (ret == -1)
                                        exit_val = 1;
                        }
                        break;
                case 'C':
+                       did_arg = 1;
                        if (is_backend) {
                                log_err("fio: can't be both client and server\n");
                                do_exit++;
                                exit_val = 1;
                                break;
                        }
+                       /* if --client parameter contains a pathname */
+                       if (0 == access(optarg, R_OK)) {
+                               /* file contains a list of host addrs or names */
+                               char hostaddr[_POSIX_HOST_NAME_MAX] = {0};
+                               char formatstr[8];
+                               FILE * hostf = fopen(optarg, "r");
+                               if (!hostf) {
+                                       log_err("fio: could not open client list file %s for read\n", optarg);
+                                       do_exit++;
+                                       exit_val = 1;
+                                       break;
+                               }
+                               sprintf(formatstr, "%%%ds", _POSIX_HOST_NAME_MAX-1);
+                               /* read at most _POSIX_HOST_NAME_MAX-1 chars from each record in this file */
+                               while (fscanf(hostf, formatstr, hostaddr) == 1) {
+                                       /* expect EVERY host in file to be valid */
+                                       if (fio_client_add(&fio_client_ops, hostaddr, &cur_client)) {
+                                               log_err("fio: failed adding client %s from file %s\n", hostaddr, optarg);
+                                               do_exit++;
+                                               exit_val = 1;
+                                               break;
+                                       }
+                               }
+                               fclose(hostf);
+                               break; /* no possibility of job file for "this client only" */
+                       }
                        if (fio_client_add(&fio_client_ops, optarg, &cur_client)) {
                                log_err("fio: failed adding client %s\n", optarg);
                                do_exit++;
@@ -1868,33 +2283,67 @@ int parse_cmd_line(int argc, char *argv[], int client_type)
                                    !strncmp(argv[optind], "-", 1))
                                        break;
 
-                               fio_client_add_ini_file(cur_client, argv[optind]);
+                               if (fio_client_add_ini_file(cur_client, argv[optind], 0))
+                                       break;
                                optind++;
                        }
                        break;
+               case 'R':
+                       did_arg = 1;
+                       if (fio_client_add_ini_file(cur_client, optarg, 1)) {
+                               do_exit++;
+                               exit_val = 1;
+                       }
+                       break;
                case 'T':
+                       did_arg = 1;
                        do_exit++;
-                       exit_val = fio_monotonic_clocktest();
+                       exit_val = fio_monotonic_clocktest(1);
                        break;
                case 'G':
+                       did_arg = 1;
                        do_exit++;
                        exit_val = fio_crctest(optarg);
                        break;
                case 'L': {
                        long long val;
 
-                       if (check_str_time(optarg, &val)) {
+                       if (check_str_time(optarg, &val, 1)) {
                                log_err("fio: failed parsing time %s\n", optarg);
                                do_exit++;
                                exit_val = 1;
                                break;
                        }
-                       status_interval = val * 1000;
+                       status_interval = val / 1000;
+                       break;
+                       }
+               case 'W':
+                       if (trigger_file)
+                               free(trigger_file);
+                       trigger_file = strdup(optarg);
                        break;
+               case 'H':
+                       if (trigger_cmd)
+                               free(trigger_cmd);
+                       trigger_cmd = strdup(optarg);
+                       break;
+               case 'J':
+                       if (trigger_remote_cmd)
+                               free(trigger_remote_cmd);
+                       trigger_remote_cmd = strdup(optarg);
+                       break;
+               case 'B':
+                       if (check_str_time(optarg, &trigger_timeout, 1)) {
+                               log_err("fio: failed parsing time %s\n", optarg);
+                               do_exit++;
+                               exit_val = 1;
                        }
+                       trigger_timeout /= 1000000;
+                       break;
                case '?':
                        log_err("%s: unrecognized option '%s'\n", argv[0],
                                                        argv[optind - 1]);
+                       show_closest_option(argv[optind - 1]);
                default:
                        do_exit++;
                        exit_val = 1;
@@ -1907,18 +2356,20 @@ int parse_cmd_line(int argc, char *argv[], int client_type)
        if (do_exit && !(is_backend || nr_clients))
                exit(exit_val);
 
-       if (nr_clients && fio_clients_connect()) {
-               do_exit++;
-               exit_val = 1;
-               return -1;
-       }
+       if (nr_clients && fio_clients_connect())
+               exit(1);
 
        if (is_backend && backend)
                return fio_start_server(pid_file);
+       else if (pid_file)
+               free(pid_file);
 
        if (td) {
-               if (!ret)
+               if (!ret) {
                        ret = add_job(td, td->o.name ?: "fio", 0, 0, client_type);
+                       if (ret)
+                               did_arg = 1;
+               }
        }
 
        while (!ret && optind < argc) {
@@ -1928,6 +2379,10 @@ int parse_cmd_line(int argc, char *argv[], int client_type)
                optind++;
        }
 
+out_free:
+       if (pid_file)
+               free(pid_file);
+
        return ini_idx;
 }
 
@@ -1963,7 +2418,7 @@ int parse_options(int argc, char *argv[])
 
        if (job_files > 0) {
                for (i = 0; i < job_files; i++) {
-                       if (fill_def_thread())
+                       if (i && fill_def_thread())
                                return 1;
                        if (nr_clients) {
                                if (fio_clients_send_ini(ini_file[i]))
@@ -1984,6 +2439,7 @@ int parse_options(int argc, char *argv[])
 
        free(ini_file);
        fio_options_free(&def_thread);
+       filesetup_mem_free();
 
        if (!thread_number) {
                if (parse_dryrun())
@@ -2005,12 +2461,6 @@ int parse_options(int argc, char *argv[])
                return 0;
        }
 
-       if (def_thread.o.gtod_offload) {
-               fio_gtod_init();
-               fio_gtod_offload = 1;
-               fio_gtod_cpu = def_thread.o.gtod_cpu;
-       }
-
        if (output_format == FIO_OUTPUT_NORMAL)
                log_info("%s\n", fio_version_string);