Merge branch 'master' into gfio
Conflicts:
eta.c
fio.h
init.c
options.c
stat.c
Signed-off-by: Jens Axboe <axboe@kernel.dk>
diff --git a/FIO-VERSION-GEN b/FIO-VERSION-GEN
index 6811d29..f9a8ec6 100755
--- a/FIO-VERSION-GEN
+++ b/FIO-VERSION-GEN
@@ -1,7 +1,7 @@
#!/bin/sh
GVF=FIO-VERSION-FILE
-DEF_VER=fio-2.0.14
+DEF_VER=fio-2.0.15
LF='
'
diff --git a/HOWTO b/HOWTO
index 6a880a7..7dc7d48 100644
--- a/HOWTO
+++ b/HOWTO
@@ -285,6 +285,32 @@
stdin or stdout. Which of the two depends on the read/write
direction set.
+filename_format=str
+ If sharing multiple files between jobs, it is usually necessary
+ to have fio generate the exact names that you want. By default,
+ fio will name a file based on the default file format
+ specification of jobname.jobnumber.filenumber. With this
+ option, that can be customized. Fio will recognize and replace
+ the following keywords in this string:
+
+ $jobname
+ The name of the worker thread or process.
+
+ $jobnum
+ The incremental number of the worker thread or
+ process.
+
+ $filenum
+ The incremental number of the file for that worker
+ thread or process.
+
+ To have dependent jobs share a set of files, this option can
+ be set to have fio generate filenames that are shared between
+ the two. For instance, if testfiles.$filenum is specified,
+ file number 4 for any job will be named testfiles.4. The
+ default of $jobname.$jobnum.$filenum will be used if
+ no other format specifier is given.
+
opendir=str Tell fio to recursively add any file it can find in this
directory and down the file system tree.
@@ -405,7 +431,7 @@
fill_device=bool
fill_fs=bool Sets size to something really large and waits for ENOSPC (no
space left on device) as the terminating condition. Only makes
- sense with sequential write. For a read workload, the mount
+ sense with sequential write. For a read workload, the mount
point will be filled first then IO started on the result. This
option doesn't make sense if operating on a raw device node,
since the size of that is already known by the file system.
diff --git a/Makefile b/Makefile
index 21e6ad3..1f58d58 100644
--- a/Makefile
+++ b/Makefile
@@ -76,6 +76,9 @@
ifndef CONFIG_STRSEP
SOURCE += lib/strsep.c
endif
+ifndef CONFIG_STRCASESTR
+ SOURCE += lib/strcasestr.c
+endif
ifndef CONFIG_GETOPT_LONG_ONLY
SOURCE += lib/getopt_long.c
endif
@@ -173,7 +176,11 @@
endif
endif
-INSTALL = install
+ifeq ($(CONFIG_TARGET_OS), SunOS)
+ INSTALL = ginstall
+else
+ INSTALL = install
+endif
prefix = /usr/local
bindir = $(prefix)/bin
diff --git a/cconv.c b/cconv.c
index ea25e0a..57c76e3 100644
--- a/cconv.c
+++ b/cconv.c
@@ -27,6 +27,7 @@
string_to_cpu(&o->name, top->name);
string_to_cpu(&o->directory, top->directory);
string_to_cpu(&o->filename, top->filename);
+ string_to_cpu(&o->filename_format, top->filename_format);
string_to_cpu(&o->opendir, top->opendir);
string_to_cpu(&o->ioengine, top->ioengine);
string_to_cpu(&o->mmapfile, top->mmapfile);
@@ -45,6 +46,7 @@
o->td_ddir = le32_to_cpu(top->td_ddir);
o->rw_seq = le32_to_cpu(top->rw_seq);
o->kb_base = le32_to_cpu(top->kb_base);
+ o->unit_base = le32_to_cpu(top->kb_base);
o->ddir_seq_nr = le32_to_cpu(top->ddir_seq_nr);
o->ddir_seq_add = le64_to_cpu(top->ddir_seq_add);
o->iodepth = le32_to_cpu(top->iodepth);
@@ -214,6 +216,7 @@
string_to_net(top->name, o->name);
string_to_net(top->directory, o->directory);
string_to_net(top->filename, o->filename);
+ string_to_net(top->filename_format, o->filename_format);
string_to_net(top->opendir, o->opendir);
string_to_net(top->ioengine, o->ioengine);
string_to_net(top->mmapfile, o->mmapfile);
@@ -232,6 +235,7 @@
top->td_ddir = cpu_to_le32(o->td_ddir);
top->rw_seq = cpu_to_le32(o->rw_seq);
top->kb_base = cpu_to_le32(o->kb_base);
+ top->unit_base = cpu_to_le32(o->kb_base);
top->ddir_seq_nr = cpu_to_le32(o->ddir_seq_nr);
top->iodepth = cpu_to_le32(o->iodepth);
top->iodepth_low = cpu_to_le32(o->iodepth_low);
diff --git a/client.c b/client.c
index fe6d75e..ffccc5d 100644
--- a/client.c
+++ b/client.c
@@ -755,6 +755,7 @@
dst->total_err_count = le64_to_cpu(src->total_err_count);
dst->first_error = le32_to_cpu(src->first_error);
dst->kb_base = le32_to_cpu(src->kb_base);
+ dst->unit_base = le32_to_cpu(src->unit_base);
}
static void convert_gs(struct group_run_stats *dst, struct group_run_stats *src)
@@ -771,6 +772,7 @@
}
dst->kb_base = le32_to_cpu(src->kb_base);
+ dst->unit_base = le32_to_cpu(src->unit_base);
dst->groupid = le32_to_cpu(src->groupid);
dst->unified_rw_rep = le32_to_cpu(src->unified_rw_rep);
}
@@ -887,6 +889,7 @@
je->eta_sec = le64_to_cpu(je->eta_sec);
je->nr_threads = le32_to_cpu(je->nr_threads);
je->is_pow2 = le32_to_cpu(je->is_pow2);
+ je->unit_base = le32_to_cpu(je->unit_base);
}
void fio_client_sum_jobs_eta(struct jobs_eta *dst, struct jobs_eta *je)
diff --git a/configure b/configure
index 4e2202f..1ca461d 100755
--- a/configure
+++ b/configure
@@ -872,6 +872,22 @@
echo "strsep $strsep"
##########################################
+# strcasestr() probe
+strcasestr="no"
+cat > $TMPC << EOF
+#include <string.h>
+int main(int argc, char **argv)
+{
+ strcasestr(NULL, NULL);
+ return 0;
+}
+EOF
+if compile_prog "" "" "strcasestr"; then
+ strcasestr="yes"
+fi
+echo "strcasestr $strcasestr"
+
+##########################################
# getopt_long_only() probe
getopt_long_only="no"
cat > $TMPC << EOF
@@ -1113,6 +1129,9 @@
if test "$strsep" = "yes" ; then
output_sym "CONFIG_STRSEP"
fi
+if test "$strcasestr" = "yes" ; then
+ output_sym "CONFIG_STRCASESTR"
+fi
if test "$getopt_long_only" = "yes" ; then
output_sym "CONFIG_GETOPT_LONG_ONLY"
fi
diff --git a/engines/net.c b/engines/net.c
index a70e56e..566ad2d 100644
--- a/engines/net.c
+++ b/engines/net.c
@@ -978,7 +978,7 @@
.options = options,
.option_struct_size = sizeof(struct netio_options),
.flags = FIO_SYNCIO | FIO_DISKLESSIO | FIO_UNIDIR |
- FIO_PIPEIO,
+ FIO_PIPEIO | FIO_BIT_BASED,
};
static int str_hostname_cb(void *data, const char *input)
diff --git a/eta.c b/eta.c
index 531b876..769ac14 100644
--- a/eta.c
+++ b/eta.c
@@ -319,6 +319,7 @@
unified_rw_rep += td->o.unified_rw_rep;
if (is_power_of_2(td->o.kb_base))
je->is_pow2 = 1;
+ je->unit_base = td->o.unit_base;
if (td->o.bw_avg_time < bw_avg_time)
bw_avg_time = td->o.bw_avg_time;
if (td->runstate == TD_RUNNING || td->runstate == TD_VERIFYING
@@ -449,8 +450,8 @@
if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
char *tr, *mr;
- mr = num2str(je->m_rate[0] + je->m_rate[1], 4, 0, je->is_pow2);
- tr = num2str(je->t_rate[0] + je->t_rate[1], 4, 0, je->is_pow2);
+ mr = num2str(je->m_rate[0] + je->m_rate[1], 4, 0, je->is_pow2, 8);
+ tr = num2str(je->t_rate[0] + je->t_rate[1], 4, 0, je->is_pow2, 8);
p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
free(tr);
free(mr);
@@ -477,8 +478,8 @@
for (ddir = DDIR_READ; ddir < DDIR_RWDIR_CNT; ddir++) {
rate_str[ddir] = num2str(je->rate[ddir], 5,
- 1024, je->is_pow2);
- iops_str[ddir] = num2str(je->iops[ddir], 4, 1, 0);
+ 1024, je->is_pow2, je->unit_base);
+ iops_str[ddir] = num2str(je->iops[ddir], 4, 1, 0, 0);
}
left = sizeof(output) - (p - output) - 1;
diff --git a/filesetup.c b/filesetup.c
index 96be3d1..9edcac1 100644
--- a/filesetup.c
+++ b/filesetup.c
@@ -719,13 +719,14 @@
int setup_files(struct thread_data *td)
{
unsigned long long total_size, extend_size;
+ struct thread_options *o = &td->o;
struct fio_file *f;
unsigned int i;
int err = 0, need_extend;
dprint(FD_FILE, "setup files\n");
- if (td->o.read_iolog_file)
+ if (o->read_iolog_file)
goto done;
/*
@@ -753,15 +754,16 @@
total_size += f->real_file_size;
}
- if (td->o.fill_device)
+ if (o->fill_device)
td->fill_device_size = get_fs_free_counts(td);
/*
* device/file sizes are zero and no size given, punt
*/
- if ((!total_size || total_size == -1ULL) && !td->o.size &&
- !(td->io_ops->flags & FIO_NOIO) && !td->o.fill_device) {
- log_err("%s: you need to specify size=\n", td->o.name);
+ if ((!total_size || total_size == -1ULL) && !o->size &&
+ !(td->io_ops->flags & FIO_NOIO) && !o->fill_device &&
+ !(o->nr_files && (o->file_size_low || o->file_size_high))) {
+ log_err("%s: you need to specify size=\n", o->name);
td_verror(td, EINVAL, "total_file_size");
return 1;
}
@@ -776,27 +778,26 @@
for_each_file(td, f, i) {
f->file_offset = get_start_offset(td);
- if (!td->o.file_size_low) {
+ if (!o->file_size_low) {
/*
* no file size range given, file size is equal to
* total size divided by number of files. if that is
* zero, set it to the real file size.
*/
- f->io_size = td->o.size / td->o.nr_files;
+ f->io_size = o->size / o->nr_files;
if (!f->io_size)
f->io_size = f->real_file_size - f->file_offset;
- } else if (f->real_file_size < td->o.file_size_low ||
- f->real_file_size > td->o.file_size_high) {
- if (f->file_offset > td->o.file_size_low)
+ } else if (f->real_file_size < o->file_size_low ||
+ f->real_file_size > o->file_size_high) {
+ if (f->file_offset > o->file_size_low)
goto err_offset;
/*
* file size given. if it's fixed, use that. if it's a
* range, generate a random size in-between.
*/
- if (td->o.file_size_low == td->o.file_size_high) {
- f->io_size = td->o.file_size_low
- - f->file_offset;
- } else {
+ if (o->file_size_low == o->file_size_high)
+ f->io_size = o->file_size_low - f->file_offset;
+ else {
f->io_size = get_rand_file_size(td)
- f->file_offset;
}
@@ -806,15 +807,15 @@
if (f->io_size == -1ULL)
total_size = -1ULL;
else {
- if (td->o.size_percent)
- f->io_size = (f->io_size * td->o.size_percent) / 100;
+ if (o->size_percent)
+ f->io_size = (f->io_size * o->size_percent) / 100;
total_size += f->io_size;
}
if (f->filetype == FIO_TYPE_FILE &&
(f->io_size + f->file_offset) > f->real_file_size &&
!(td->io_ops->flags & FIO_DISKLESSIO)) {
- if (!td->o.create_on_open) {
+ if (!o->create_on_open) {
need_extend++;
extend_size += (f->io_size + f->file_offset);
} else
@@ -823,8 +824,8 @@
}
}
- if (!td->o.size || td->o.size > total_size)
- td->o.size = total_size;
+ if (!o->size || o->size > total_size)
+ o->size = total_size;
/*
* See if we need to extend some files
@@ -833,7 +834,7 @@
temp_stall_ts = 1;
if (output_format == FIO_OUTPUT_NORMAL)
log_info("%s: Laying out IO file(s) (%u file(s) /"
- " %lluMB)\n", td->o.name, need_extend,
+ " %lluMB)\n", o->name, need_extend,
extend_size >> 20);
for_each_file(td, f, i) {
@@ -844,7 +845,7 @@
assert(f->filetype == FIO_TYPE_FILE);
fio_file_clear_extend(f);
- if (!td->o.fill_device) {
+ if (!o->fill_device) {
old_len = f->real_file_size;
extend_len = f->io_size + f->file_offset -
old_len;
@@ -867,23 +868,23 @@
if (err)
return err;
- if (!td->o.zone_size)
- td->o.zone_size = td->o.size;
+ if (!o->zone_size)
+ o->zone_size = o->size;
/*
* iolog already set the total io size, if we read back
* stored entries.
*/
- if (!td->o.read_iolog_file)
- td->total_io_size = td->o.size * td->o.loops;
+ if (!o->read_iolog_file)
+ td->total_io_size = o->size * o->loops;
done:
- if (td->o.create_only)
+ if (o->create_only)
td->done = 1;
return 0;
err_offset:
- log_err("%s: you need to specify valid offset=\n", td->o.name);
+ log_err("%s: you need to specify valid offset=\n", o->name);
return 1;
}
diff --git a/fio.1 b/fio.1
index 2464dd5..dc13b98 100644
--- a/fio.1
+++ b/fio.1
@@ -151,6 +151,34 @@
reserved name, meaning stdin or stdout, depending on the read/write direction
set.
.TP
+.BI filename_format \fR=\fPstr
+If sharing multiple files between jobs, it is usually necessary to have
+fio generate the exact names that you want. By default, fio will name a file
+based on the default file format specification of
+\fBjobname.jobnumber.filenumber\fP. With this option, that can be
+customized. Fio will recognize and replace the following keywords in this
+string:
+.RS
+.RS
+.TP
+.B $jobname
+The name of the worker thread or process.
+.TP
+.B $jobnum
+The incremental number of the worker thread or process.
+.TP
+.B $filenum
+The incremental number of the file for that worker thread or process.
+.RE
+.P
+To have dependent jobs share a set of files, this option can be set to
+have fio generate filenames that are shared between the two. For instance,
+if \fBtestfiles.$filenum\fR is specified, file number 4 for any job will
+be named \fBtestfiles.4\fR. The default of \fB$jobname.$jobnum.$filenum\fR
+will be used if no other format specifier is given.
+.RE
+.P
+.TP
.BI lockfile \fR=\fPstr
Fio defaults to not locking any files before it does IO to them. If a file or
file descriptor is shared, fio can serialize IO to that file to make the end
@@ -169,6 +197,7 @@
Read-write locking on the file. Many readers may access the file at the same
time, but writes get exclusive access.
.RE
+.RE
.P
.BI opendir \fR=\fPstr
Recursively open any files below directory \fIstr\fR.
diff --git a/fio.h b/fio.h
index 1cb44e6..c8c8b7a 100644
--- a/fio.h
+++ b/fio.h
@@ -442,7 +442,7 @@
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 **, int);
-extern char *num2str(unsigned long, int, int, int);
+extern char *num2str(unsigned long, int, int, int, int);
extern int ioengine_load(struct thread_data *);
extern unsigned long page_mask;
diff --git a/init.c b/init.c
index 27370bb..8c80298 100644
--- a/init.c
+++ b/init.c
@@ -26,6 +26,7 @@
#include "idletime.h"
#include "lib/getopt.h"
+#include "lib/strcasestr.h"
const char fio_version_string[] = FIO_VERSION;
@@ -567,6 +568,13 @@
}
}
+ if (!o->unit_base) {
+ if (td->io_ops->flags & FIO_BIT_BASED)
+ o->unit_base = 1;
+ else
+ o->unit_base = 8;
+ }
+
#ifndef CONFIG_FDATASYNC
if (o->fdatasync_blocks) {
log_info("fio: this platform does not support fdatasync()"
@@ -807,6 +815,82 @@
return 0;
}
+enum {
+ FPRE_NONE = 0,
+ FPRE_JOBNAME,
+ FPRE_JOBNUM,
+ FPRE_FILENUM
+};
+
+static struct fpre_keyword {
+ const char *keyword;
+ size_t strlen;
+ int key;
+} fpre_keywords[] = {
+ { .keyword = "$jobname", .key = FPRE_JOBNAME, },
+ { .keyword = "$jobnum", .key = FPRE_JOBNUM, },
+ { .keyword = "$filenum", .key = FPRE_FILENUM, },
+ { .keyword = NULL, },
+ };
+
+static char *make_filename(char *buf, struct thread_options *o,
+ const char *jobname, int jobnum, int filenum)
+{
+ struct fpre_keyword *f;
+ char copy[PATH_MAX];
+
+ if (!o->filename_format || !strlen(o->filename_format)) {
+ sprintf(buf, "%s.%d.%d", jobname, jobnum, filenum);
+ return NULL;
+ }
+
+ for (f = &fpre_keywords[0]; f->keyword; f++)
+ f->strlen = strlen(f->keyword);
+
+ strcpy(buf, o->filename_format);
+ memset(copy, 0, sizeof(copy));
+ for (f = &fpre_keywords[0]; f->keyword; f++) {
+ do {
+ size_t pre_len, post_start = 0;
+ char *str, *dst = copy;
+
+ str = strcasestr(buf, f->keyword);
+ if (!str)
+ break;
+
+ pre_len = str - buf;
+ if (strlen(str) != f->strlen)
+ post_start = pre_len + f->strlen;
+
+ if (pre_len) {
+ strncpy(dst, buf, pre_len);
+ dst += pre_len;
+ }
+
+ switch (f->key) {
+ case FPRE_JOBNAME:
+ dst += sprintf(dst, "%s", jobname);
+ break;
+ case FPRE_JOBNUM:
+ dst += sprintf(dst, "%d", jobnum);
+ break;
+ case FPRE_FILENUM:
+ dst += sprintf(dst, "%d", filenum);
+ break;
+ default:
+ assert(0);
+ break;
+ }
+
+ if (post_start)
+ strcpy(dst, buf + post_start);
+
+ strcpy(buf, copy);
+ } while (1);
+ }
+
+ return buf;
+}
/*
* Adds a job to the list of things todo. Sanitizes the various options
* to make sure we don't have conflicts, and initializes various
@@ -818,6 +902,7 @@
unsigned int i;
char fname[PATH_MAX];
int numjobs, file_alloced;
+ struct thread_options *o = &td->o;
/*
* the def_thread is just for options, it's not a real job
@@ -843,21 +928,18 @@
if (ioengine_load(td))
goto err;
- if (td->o.odirect)
+ if (o->odirect)
td->io_ops->flags |= FIO_RAWIO;
file_alloced = 0;
- if (!td->o.filename && !td->files_index && !td->o.read_iolog_file) {
+ if (!o->filename && !td->files_index && !o->read_iolog_file) {
file_alloced = 1;
- if (td->o.nr_files == 1 && exists_and_not_file(jobname))
+ if (o->nr_files == 1 && exists_and_not_file(jobname))
add_file(td, jobname);
else {
- for (i = 0; i < td->o.nr_files; i++) {
- sprintf(fname, "%s.%d.%d", jobname,
- td->thread_number, i);
- add_file(td, fname);
- }
+ for (i = 0; i < o->nr_files; i++)
+ add_file(td, make_filename(fname, o, jobname, td->thread_number, i));
}
}
@@ -882,9 +964,9 @@
td->mutex = fio_mutex_init(FIO_MUTEX_LOCKED);
- td->ts.clat_percentiles = td->o.clat_percentiles;
- td->ts.percentile_precision = td->o.percentile_precision;
- memcpy(td->ts.percentile_list, td->o.percentile_list, sizeof(td->o.percentile_list));
+ td->ts.clat_percentiles = o->clat_percentiles;
+ td->ts.percentile_precision = o->percentile_precision;
+ memcpy(td->ts.percentile_list, o->percentile_list, sizeof(o->percentile_list));
for (i = 0; i < DDIR_RWDIR_CNT; i++) {
td->ts.clat_stat[i].min_val = ULONG_MAX;
@@ -892,9 +974,9 @@
td->ts.lat_stat[i].min_val = ULONG_MAX;
td->ts.bw_stat[i].min_val = ULONG_MAX;
}
- td->ddir_seq_nr = td->o.ddir_seq_nr;
+ td->ddir_seq_nr = o->ddir_seq_nr;
- if ((td->o.stonewall || td->o.new_group) && prev_group_jobs) {
+ if ((o->stonewall || o->new_group) && prev_group_jobs) {
prev_group_jobs = 0;
groupid++;
}
@@ -910,18 +992,18 @@
if (setup_rate(td))
goto err;
- if (td->o.lat_log_file) {
- setup_log(&td->lat_log, td->o.log_avg_msec, IO_LOG_TYPE_LAT);
- setup_log(&td->slat_log, td->o.log_avg_msec, IO_LOG_TYPE_SLAT);
- setup_log(&td->clat_log, td->o.log_avg_msec, IO_LOG_TYPE_CLAT);
+ if (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);
}
- if (td->o.bw_log_file)
- setup_log(&td->bw_log, td->o.log_avg_msec, IO_LOG_TYPE_BW);
- if (td->o.iops_log_file)
- setup_log(&td->iops_log, td->o.log_avg_msec, IO_LOG_TYPE_IOPS);
+ if (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 (!td->o.name)
- td->o.name = strdup(jobname);
+ if (!o->name)
+ o->name = strdup(jobname);
if (output_format == FIO_OUTPUT_NORMAL) {
if (!job_add_num) {
@@ -931,20 +1013,19 @@
if (!(td->io_ops->flags & FIO_NOIO)) {
char *c1, *c2, *c3, *c4, *c5, *c6;
- c1 = fio_uint_to_kmg(td->o.min_bs[DDIR_READ]);
- c2 = fio_uint_to_kmg(td->o.max_bs[DDIR_READ]);
- c3 = fio_uint_to_kmg(td->o.min_bs[DDIR_WRITE]);
- c4 = fio_uint_to_kmg(td->o.max_bs[DDIR_WRITE]);
- c5 = fio_uint_to_kmg(td->o.min_bs[DDIR_TRIM]);
- c6 = fio_uint_to_kmg(td->o.max_bs[DDIR_TRIM]);
+ c1 = fio_uint_to_kmg(o->min_bs[DDIR_READ]);
+ c2 = fio_uint_to_kmg(o->max_bs[DDIR_READ]);
+ c3 = fio_uint_to_kmg(o->min_bs[DDIR_WRITE]);
+ c4 = fio_uint_to_kmg(o->max_bs[DDIR_WRITE]);
+ c5 = fio_uint_to_kmg(o->min_bs[DDIR_TRIM]);
+ c6 = fio_uint_to_kmg(o->max_bs[DDIR_TRIM]);
log_info("%s: (g=%d): rw=%s, bs=%s-%s/%s-%s/%s-%s,"
" ioengine=%s, iodepth=%u\n",
td->o.name, td->groupid,
- ddir_str(td->o.td_ddir),
+ ddir_str(o->td_ddir),
c1, c2, c3, c4, c5, c6,
- td->io_ops->name,
- td->o.iodepth);
+ td->io_ops->name, o->iodepth);
free(c1);
free(c2);
@@ -961,7 +1042,7 @@
* recurse add identical jobs, clear numjobs and stonewall options
* as they don't apply to sub-jobs
*/
- numjobs = td->o.numjobs;
+ numjobs = o->numjobs;
while (--numjobs) {
struct thread_data *td_new = get_new_job(0, td, 1);
diff --git a/ioengine.h b/ioengine.h
index 5503957..493d4b5 100644
--- a/ioengine.h
+++ b/ioengine.h
@@ -162,6 +162,7 @@
FIO_PIPEIO = 1 << 7, /* input/output no seekable */
FIO_BARRIER = 1 << 8, /* engine supports barriers */
FIO_MEMALIGN = 1 << 9, /* engine wants aligned memory */
+ FIO_BIT_BASED = 1 << 10, /* engine uses a bit base (e.g. uses Kbit as opposed to KB) */
};
/*
diff --git a/lib/num2str.c b/lib/num2str.c
index 559cbeb..a104192 100644
--- a/lib/num2str.c
+++ b/lib/num2str.c
@@ -5,12 +5,13 @@
/*
* Cheesy number->string conversion, complete with carry rounding error.
*/
-char *num2str(unsigned long num, int maxlen, int base, int pow2)
+char *num2str(unsigned long num, int maxlen, int base, int pow2, int unit_base)
{
- char postfix[] = { ' ', 'K', 'M', 'G', 'P', 'E' };
- unsigned int thousand[] = { 1000, 1024 };
+ const char *postfix[] = { "", "K", "M", "G", "P", "E" };
+ const char *byte_postfix[] = { "", "B", "bit" };
+ const unsigned int thousand[] = { 1000, 1024 };
unsigned int modulo, decimals;
- int post_index, carry = 0;
+ int byte_post_index = 0, post_index, carry = 0;
char tmp[32];
char *buf;
@@ -19,6 +20,16 @@
for (post_index = 0; base > 1; post_index++)
base /= thousand[!!pow2];
+ switch (unit_base) {
+ case 1:
+ byte_post_index = 2;
+ num *= 8;
+ break;
+ case 8:
+ byte_post_index = 1;
+ break;
+ }
+
modulo = -1U;
while (post_index < sizeof(postfix)) {
sprintf(tmp, "%lu", num);
@@ -33,7 +44,8 @@
if (modulo == -1U) {
done:
- sprintf(buf, "%lu%c", num, postfix[post_index]);
+ sprintf(buf, "%lu%s%s", num, postfix[post_index],
+ byte_postfix[byte_post_index]);
return buf;
}
@@ -53,6 +65,7 @@
modulo = (modulo + 9) / 10;
} while (1);
- sprintf(buf, "%lu.%u%c", num, modulo, postfix[post_index]);
+ sprintf(buf, "%lu.%u%s%s", num, modulo, postfix[post_index],
+ byte_postfix[byte_post_index]);
return buf;
}
diff --git a/lib/strcasestr.c b/lib/strcasestr.c
new file mode 100644
index 0000000..92cf24c
--- /dev/null
+++ b/lib/strcasestr.c
@@ -0,0 +1,25 @@
+#include <ctype.h>
+#include <stddef.h>
+
+char *strcasestr(const char *s1, const char *s2)
+{
+ const char *s = s1;
+ const char *p = s2;
+
+ do {
+ if (!*p)
+ return (char *) s1;
+ if ((*p == *s) ||
+ (tolower(*p) == tolower(*s))) {
+ ++p;
+ ++s;
+ } else {
+ p = s2;
+ if (!*s)
+ return NULL;
+ s = ++s1;
+ }
+ } while (1);
+
+ return *p ? NULL : (char *) s1;
+}
diff --git a/lib/strcasestr.h b/lib/strcasestr.h
new file mode 100644
index 0000000..43d61df
--- /dev/null
+++ b/lib/strcasestr.h
@@ -0,0 +1,13 @@
+#ifdef CONFIG_STRCASESTR
+
+#include <string.h>
+
+#else
+
+#ifndef FIO_STRCASESTR_H
+#define FIO_STRCASESTR_H
+
+char *strcasestr(const char *haystack, const char *needle);
+
+#endif
+#endif
diff --git a/options.c b/options.c
index 00e542b..b8ce5a7 100644
--- a/options.c
+++ b/options.c
@@ -949,6 +949,25 @@
return 0;
}
+static int unit_base_verify(struct fio_option *o, void *data)
+{
+ struct thread_data *td = data;
+
+ /* 0 = default, pick based on engine
+ * 1 = use bits
+ * 8 = use bytes
+ */
+ if (td->o.unit_base != 0 &&
+ td->o.unit_base != 1 &&
+ td->o.unit_base != 8) {
+ log_err("fio: unit_base set to nonsensical value: %u\n",
+ td->o.unit_base);
+ return 1;
+ }
+
+ return 0;
+}
+
/*
* Option grouping
*/
@@ -1148,6 +1167,32 @@
.group = FIO_OPT_G_FILENAME,
},
{
+ .name = "filename_format",
+ .type = FIO_OPT_STR_STORE,
+ .off1 = td_var_offset(filename_format),
+ .prio = -1, /* must come after "directory" */
+ .help = "Override default $jobname.$jobnum.$filenum naming",
+ .def = "$jobname.$jobnum.$filenum",
+ },
+ {
+ .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 = "unit_base",
+ .type = FIO_OPT_INT,
+ .off1 = td_var_offset(unit_base),
+ .verify = unit_base_verify,
+ .prio = 1,
+ .def = "0",
+ .help = "Bit multiple of result summary data (8 for byte, 1 for bit)",
+ },
+ {
.name = "lockfile",
.lname = "Lockfile",
.type = FIO_OPT_STR,
diff --git a/os/windows/install.wxs b/os/windows/install.wxs
index 37216b4..1494a64 100755
--- a/os/windows/install.wxs
+++ b/os/windows/install.wxs
@@ -10,7 +10,7 @@
<Product Id="*"
Codepage="1252" Language="1033"
Manufacturer="fio" Name="fio"
- UpgradeCode="2338A332-5511-43CF-B9BD-5C60496CCFCC" Version="2.0.14">
+ UpgradeCode="2338A332-5511-43CF-B9BD-5C60496CCFCC" Version="2.0.15">
<Package
Description="Flexible IO Tester"
InstallerVersion="301" Keywords="Installer,MSI,Database"
diff --git a/server.c b/server.c
index b7e9922..32c1d7a 100644
--- a/server.c
+++ b/server.c
@@ -936,6 +936,7 @@
}
dst->kb_base = cpu_to_le32(src->kb_base);
+ dst->unit_base = cpu_to_le32(src->unit_base);
dst->groupid = cpu_to_le32(src->groupid);
dst->unified_rw_rep = cpu_to_le32(src->unified_rw_rep);
}
@@ -1018,6 +1019,7 @@
p.ts.total_err_count = cpu_to_le64(ts->total_err_count);
p.ts.first_error = cpu_to_le32(ts->first_error);
p.ts.kb_base = cpu_to_le32(ts->kb_base);
+ p.ts.unit_base = cpu_to_le32(ts->unit_base);
convert_gs(&p.rs, rs);
diff --git a/stat.c b/stat.c
index 402a73f..245197a 100644
--- a/stat.c
+++ b/stat.c
@@ -274,12 +274,12 @@
if (!rs->max_run[i])
continue;
- p1 = num2str(rs->io_kb[i], 6, rs->kb_base, i2p);
- p2 = num2str(rs->agg[i], 6, rs->kb_base, i2p);
- p3 = num2str(rs->min_bw[i], 6, rs->kb_base, i2p);
- p4 = num2str(rs->max_bw[i], 6, rs->kb_base, i2p);
+ p1 = num2str(rs->io_kb[i], 6, rs->kb_base, i2p, 8);
+ p2 = num2str(rs->agg[i], 6, rs->kb_base, i2p, rs->unit_base);
+ p3 = num2str(rs->min_bw[i], 6, rs->kb_base, i2p, rs->unit_base);
+ p4 = num2str(rs->max_bw[i], 6, rs->kb_base, i2p, rs->unit_base);
- log_info("%s: io=%sB, aggrb=%sB/s, minb=%sB/s, maxb=%sB/s,"
+ log_info("%s: io=%s, aggrb=%s/s, minb=%s/s, maxb=%s/s,"
" mint=%llumsec, maxt=%llumsec\n",
rs->unified_rw_rep ? " MIXED" : ddir_str[i],
p1, p2, p3, p4, rs->min_run[i], rs->max_run[i]);
@@ -348,8 +348,8 @@
if (!usec_to_msec(&min, &max, &mean, &dev))
base = "(msec)";
- minp = num2str(min, 6, 1, 0);
- maxp = num2str(max, 6, 1, 0);
+ minp = num2str(min, 6, 1, 0, 0);
+ maxp = num2str(max, 6, 1, 0, 0);
log_info(" %s %s: min=%s, max=%s, avg=%5.02f,"
" stdev=%5.02f\n", name, base, minp, maxp, mean, dev);
@@ -377,13 +377,13 @@
runt = ts->runtime[ddir];
bw = (1000 * ts->io_bytes[ddir]) / runt;
- io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p);
- bw_p = num2str(bw, 6, 1, i2p);
+ io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p, 8);
+ bw_p = num2str(bw, 6, 1, i2p, ts->unit_base);
iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt;
- iops_p = num2str(iops, 6, 1, 0);
+ iops_p = num2str(iops, 6, 1, 0, 0);
- log_info(" %s: io=%sB, bw=%sB/s, iops=%s, runt=%6llumsec\n",
+ log_info(" %s: io=%s, bw=%s/s, iops=%s, runt=%6llumsec\n",
rs->unified_rw_rep ? "mixed" : ddir_str[ddir],
io_p, bw_p, iops_p, ts->runtime[ddir]);
@@ -405,8 +405,15 @@
ts->percentile_precision);
}
if (calc_lat(&ts->bw_stat[ddir], &min, &max, &mean, &dev)) {
- double p_of_agg = 100.0;
- const char *bw_str = "KB";
+ double p_of_agg = 100.0, fkb_base = (double)rs->kb_base;
+ const char *bw_str = (rs->unit_base == 1 ? "Kbit" : "KB");
+
+ if (rs->unit_base == 1) {
+ min *= 8.0;
+ max *= 8.0;
+ mean *= 8.0;
+ dev *= 8.0;
+ }
if (rs->agg[ddir]) {
p_of_agg = mean * 100 / (double) rs->agg[ddir];
@@ -414,15 +421,15 @@
p_of_agg = 100.0;
}
- if (mean > 999999.9) {
- min /= 1000.0;
- max /= 1000.0;
- mean /= 1000.0;
- dev /= 1000.0;
- bw_str = "MB";
+ if (mean > fkb_base * fkb_base) {
+ min /= fkb_base;
+ max /= fkb_base;
+ mean /= fkb_base;
+ dev /= fkb_base;
+ bw_str = (rs->unit_base == 1 ? "Mbit" : "MB");
}
- log_info(" bw (%s/s) : min=%5lu, max=%5lu, per=%3.2f%%,"
+ log_info(" bw (%-4s/s): min=%5lu, max=%5lu, per=%3.2f%%,"
" avg=%5.02f, stdev=%5.02f\n", bw_str, min, max,
p_of_agg, mean, dev);
}
@@ -1128,6 +1135,7 @@
struct thread_stat *threadstats, *ts;
int i, j, nr_ts, last_ts, idx;
int kb_base_warned = 0;
+ int unit_base_warned = 0;
struct json_object *root = NULL;
struct json_array *array = NULL;
@@ -1204,11 +1212,16 @@
ts->pid = td->pid;
ts->kb_base = td->o.kb_base;
+ ts->unit_base = td->o.unit_base;
ts->unified_rw_rep = td->o.unified_rw_rep;
} else if (ts->kb_base != td->o.kb_base && !kb_base_warned) {
log_info("fio: kb_base differs for jobs in group, using"
" %u as the base\n", ts->kb_base);
kb_base_warned = 1;
+ } else if (ts->unit_base != td->o.unit_base && !unit_base_warned) {
+ log_info("fio: unit_base differs for jobs in group, using"
+ " %u as the base\n", ts->unit_base);
+ unit_base_warned = 1;
}
ts->continue_on_error = td->o.continue_on_error;
@@ -1234,6 +1247,7 @@
ts = &threadstats[i];
rs = &runstats[ts->groupid];
rs->kb_base = ts->kb_base;
+ rs->unit_base = ts->unit_base;
rs->unified_rw_rep += ts->unified_rw_rep;
for (j = 0; j < DDIR_RWDIR_CNT; j++) {
diff --git a/stat.h b/stat.h
index a3b391c..76a71d4 100644
--- a/stat.h
+++ b/stat.h
@@ -9,6 +9,7 @@
uint64_t io_kb[DDIR_RWDIR_CNT];
uint64_t agg[DDIR_RWDIR_CNT];
uint32_t kb_base;
+ uint32_t unit_base;
uint32_t groupid;
uint32_t unified_rw_rep;
};
@@ -173,6 +174,7 @@
uint32_t first_error;
uint32_t kb_base;
+ uint32_t unit_base;
};
struct jobs_eta {
@@ -187,6 +189,7 @@
uint64_t elapsed_sec;
uint64_t eta_sec;
uint32_t is_pow2;
+ uint32_t unit_base;
/*
* Network 'copy' of run_str[]
diff --git a/thread_options.h b/thread_options.h
index d577f61..a794949 100644
--- a/thread_options.h
+++ b/thread_options.h
@@ -50,12 +50,14 @@
char *name;
char *directory;
char *filename;
+ char *filename_format;
char *opendir;
char *ioengine;
char *mmapfile;
enum td_ddir td_ddir;
unsigned int rw_seq;
unsigned int kb_base;
+ unsigned int unit_base;
unsigned int ddir_seq_nr;
long ddir_seq_add;
unsigned int iodepth;
@@ -257,12 +259,14 @@
uint8_t name[FIO_TOP_STR_MAX];
uint8_t directory[FIO_TOP_STR_MAX];
uint8_t filename[FIO_TOP_STR_MAX];
+ uint8_t filename_format[FIO_TOP_STR_MAX];
uint8_t opendir[FIO_TOP_STR_MAX];
uint8_t ioengine[FIO_TOP_STR_MAX];
uint8_t mmapfile[FIO_TOP_STR_MAX];
uint32_t td_ddir;
uint32_t rw_seq;
uint32_t kb_base;
+ uint32_t unit_base;
uint32_t ddir_seq_nr;
uint64_t ddir_seq_add;
uint32_t iodepth;