#!/bin/sh
GVF=FIO-VERSION-FILE
-DEF_VER=fio-2.0.14
+DEF_VER=fio-2.0.15
LF='
'
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.
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.
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
endif
endif
-INSTALL = install
+ifeq ($(CONFIG_TARGET_OS), SunOS)
+ INSTALL = ginstall
+else
+ INSTALL = install
+endif
prefix = /usr/local
bindir = $(prefix)/bin
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);
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);
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);
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);
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)
}
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);
}
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)
fi
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"
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
.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)
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
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);
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;
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;
/*
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;
}
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;
}
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
}
}
- 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
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) {
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;
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;
}
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
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.
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;
#include "idletime.h"
#include "lib/getopt.h"
+#include "lib/strcasestr.h"
const char fio_version_string[] = FIO_VERSION;
}
}
+ 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()"
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
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
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));
}
}
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;
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++;
}
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) {
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);
* 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);
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) */
};
/*
/*
* 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;
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);
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;
}
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;
}
--- /dev/null
+#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;
+}
--- /dev/null
+#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
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
*/
.category = FIO_OPT_C_FILE,
.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",
<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"
}
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);
}
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);
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]);
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);
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]);
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];
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);
}
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;
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;
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++) {
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;
};
uint32_t first_error;
uint32_t kb_base;
+ uint32_t unit_base;
};
struct jobs_eta {
uint64_t elapsed_sec;
uint64_t eta_sec;
uint32_t is_pow2;
+ uint32_t unit_base;
/*
* Network 'copy' of run_str[]
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;
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;