/config.log
/cscope.out
/fio
+/gfio
+/t/axmap
+/t/fio-btrace2fio
+/t/fio-dedupe
+/t/fio-genzipf
+/t/fio-verify-state
+/t/gen-rand
+/t/ieee754
+/t/lfsr-test
+/t/stest
y.tab.*
lex.yy.c
*.un~
doc/output
+/tags
+/TAGS
#!/bin/sh
GVF=FIO-VERSION-FILE
-DEF_VER=fio-3.2
+DEF_VER=fio-3.3
LF='
'
.. option:: --eta=when
Specifies when real-time ETA estimate should be printed. `when` may be
- `always`, `never` or `auto`.
+ `always`, `never` or `auto`. `auto` is the default, it prints ETA
+ when requested if the output is a TTY. `always` disregards the output
+ type, and prints ETA when requested. `never` never prints ETA.
+
+.. option:: --eta-interval=time
+
+ By default, fio requests client ETA status roughly every second. With
+ this option, the interval is configurable. Fio imposes a minimum
+ allowed time to avoid flooding the console, less than 250 msec is
+ not supported.
.. option:: --eta-newline=time
(https://en.wikipedia.org/wiki/Poisson_point_process). The lambda will be
10^6 / IOPS for the given workload.
+.. option:: rate_ignore_thinktime=bool
+
+ By default, fio will attempt to catch up to the specified rate setting,
+ if any kind of thinktime setting was used. If this option is set, then
+ fio will ignore the thinktime and continue doing IO at the specified
+ rate, instead of entering a catch-up mode after thinktime is done.
+
I/O latency
~~~~~~~~~~~
*/
static long long usec_for_io(struct thread_data *td, enum fio_ddir ddir)
{
- uint64_t secs, remainder, bps, bytes, iops;
+ uint64_t bps = td->rate_bps[ddir];
assert(!(td->flags & TD_F_CHILD));
- bytes = td->rate_io_issue_bytes[ddir];
- bps = td->rate_bps[ddir];
if (td->o.rate_process == RATE_PROCESS_POISSON) {
- uint64_t val;
+ uint64_t val, iops;
+
iops = bps / td->o.bs[ddir];
val = (int64_t) (1000000 / iops) *
-logf(__rand_0_1(&td->poisson_state[ddir]));
td->last_usec[ddir] += val;
return td->last_usec[ddir];
} else if (bps) {
- secs = bytes / bps;
- remainder = bytes % bps;
+ uint64_t bytes = td->rate_io_issue_bytes[ddir];
+ uint64_t secs = bytes / bps;
+ uint64_t remainder = bytes % bps;
+
return remainder * 1000000 / bps + secs * 1000000;
}
return 0;
}
+static void handle_thinktime(struct thread_data *td, enum fio_ddir ddir)
+{
+ unsigned long long b;
+ uint64_t total;
+ int left;
+
+ b = ddir_rw_sum(td->io_blocks);
+ if (b % td->o.thinktime_blocks)
+ return;
+
+ io_u_quiesce(td);
+
+ total = 0;
+ if (td->o.thinktime_spin)
+ total = usec_spin(td->o.thinktime_spin);
+
+ left = td->o.thinktime - total;
+ if (left)
+ total += usec_sleep(td, left);
+
+ /*
+ * If we're ignoring thinktime for the rate, add the number of bytes
+ * we would have done while sleeping, minus one block to ensure we
+ * start issuing immediately after the sleep.
+ */
+ if (total && td->rate_bps[ddir] && td->o.rate_ign_think) {
+ uint64_t missed = (td->rate_bps[ddir] * total) / 1000000ULL;
+ uint64_t bs = td->o.min_bs[ddir];
+ uint64_t usperop = bs * 1000000ULL / td->rate_bps[ddir];
+ uint64_t over;
+
+ if (usperop <= total)
+ over = bs;
+ else
+ over = (usperop - total) / usperop * -bs;
+
+ td->rate_io_issue_bytes[ddir] += (missed - over);
+ }
+}
+
/*
* Main IO worker function. It retrieves io_u's to process and queues
* and reaps them, checking for rate and errors along the way.
int err = PTR_ERR(io_u);
io_u = NULL;
+ ddir = DDIR_INVAL;
if (err == -EBUSY) {
ret = FIO_Q_BUSY;
goto reap;
if (!in_ramp_time(td) && td->o.latency_target)
lat_target_check(td);
- if (td->o.thinktime) {
- unsigned long long b;
-
- b = ddir_rw_sum(td->io_blocks);
- if (!(b % td->o.thinktime_blocks)) {
- int left;
-
- io_u_quiesce(td);
-
- if (td->o.thinktime_spin)
- usec_spin(td->o.thinktime_spin);
-
- left = td->o.thinktime - td->o.thinktime_spin;
- if (left)
- usec_sleep(td, left);
- }
- }
+ if (ddir_rw(ddir) && td->o.thinktime)
+ handle_thinktime(td, ddir);
}
check_update_rusage(td);
o->trim_backlog = le64_to_cpu(top->trim_backlog);
o->rate_process = le32_to_cpu(top->rate_process);
+ o->rate_ign_think = le32_to_cpu(top->rate_ign_think);
for (i = 0; i < FIO_IO_U_LIST_MAX_LEN; i++)
o->percentile_list[i].u.f = fio_uint64_to_double(le64_to_cpu(top->percentile_list[i].u.i));
top->offset_increment = __cpu_to_le64(o->offset_increment);
top->number_ios = __cpu_to_le64(o->number_ios);
top->rate_process = cpu_to_le32(o->rate_process);
+ top->rate_ign_think = cpu_to_le32(o->rate_ign_think);
for (i = 0; i < FIO_IO_U_LIST_MAX_LEN; i++)
top->percentile_list[i].u.i = __cpu_to_le64(fio_double_to_uint64(o->percentile_list[i].u.f));
struct client_eta *eta;
int skipped = 0;
+ if (eta_print == FIO_ETA_NEVER)
+ return;
+
dprint(FD_NET, "client: request eta (%d)\n", nr_clients);
eta = calloc(1, sizeof(*eta) + __THREAD_RUNSTR_SZ(REAL_MAX_JOBS));
int timeout;
fio_gettime(&ts, NULL);
- if (mtime_since(&eta_ts, &ts) >= 900) {
+ if (eta_time_within_slack(mtime_since(&eta_ts, &ts))) {
request_client_etas(ops);
memcpy(&eta_ts, &ts, sizeof(ts));
#endif
+#ifdef FIO_INTERNAL
+#define ARRAY_SIZE(x) (sizeof((x)) / (sizeof((x)[0])))
+#define FIELD_SIZE(s, f) (sizeof(((typeof(s))0)->f))
+#endif
+
#endif
}
}
+/*
+ * Allow a little slack - if we're within 95% of the time, allow ETA.
+ */
+bool eta_time_within_slack(unsigned int time)
+{
+ return time > ((eta_interval_msec * 95) / 100);
+}
+
/*
* Print status of the jobs we know about. This includes rate estimates,
* ETA, thread state, etc.
disp_time = mtime_since(&disp_prev_time, &now);
- /*
- * Allow a little slack, the target is to print it every 1000 msecs
- */
- if (!force && disp_time < 900)
+ if (!force && !eta_time_within_slack(disp_time))
return false;
calc_rate(unified_rw_rep, disp_time, io_bytes, disp_io_bytes, je->rate);
iops_str[ddir] = num2str(je->iops[ddir], 4, 1, 0, N2S_NONE);
}
- left = sizeof(output) - (p - output) - 1;
+ left = sizeof(output) - (p - output) - 2;
if (je->rate[DDIR_TRIM] || je->iops[DDIR_TRIM])
l = snprintf(p, left,
rate_str[DDIR_READ], rate_str[DDIR_WRITE],
iops_str[DDIR_READ], iops_str[DDIR_WRITE],
eta_str);
+ if (l > left)
+ l = left;
p += l;
if (l >= 0 && l < linelen_last)
p += sprintf(p, "%*s", linelen_last - l, "");
}
td->files_index++;
- if (f->filetype == FIO_TYPE_FILE)
- td->nr_normal_files++;
if (td->o.numjobs > 1)
set_already_allocated(file_name);
td->o.nr_files = 0;
td->o.open_files = 0;
td->files_index = 0;
- td->nr_normal_files = 0;
}
void fio_file_reset(struct thread_data *td, struct fio_file *f)
.TP
.BI \-\-eta \fR=\fPwhen
Specifies when real\-time ETA estimate should be printed. \fIwhen\fR may
-be `always', `never' or `auto'.
+be `always', `never' or `auto'. `auto' is the default, it prints ETA when
+requested if the output is a TTY. `always' disregards the output type, and
+prints ETA when requested. `never' never prints ETA.
+.TP
+.BI \-\-eta\-interval \fR=\fPtime
+By default, fio requests client ETA status roughly every second. With this
+option, the interval is configurable. Fio imposes a minimum allowed time to
+avoid flooding the console, less than 250 msec is not supported.
.TP
.BI \-\-eta\-newline \fR=\fPtime
Force a new line for every \fItime\fR period passed. When the unit is omitted,
flow, known as the Poisson process
(\fIhttps://en.wikipedia.org/wiki/Poisson_point_process\fR). The lambda will be
10^6 / IOPS for the given workload.
+.TP
+.BI rate_ignore_thinktime \fR=\fPbool
+By default, fio will attempt to catch up to the specified rate setting, if any
+kind of thinktime setting was used. If this option is set, then fio will
+ignore the thinktime and continue doing IO at the specified rate, instead of
+entering a catch-up mode after thinktime is done.
.SS "I/O latency"
.TP
.BI latency_target \fR=\fPtime
.RE
.SH AUTHORS
.B fio
-was written by Jens Axboe <jens.axboe@oracle.com>,
-now Jens Axboe <axboe@fb.com>.
+was written by Jens Axboe <axboe@kernel.dk>.
.br
This man page was written by Aaron Carroll <aaronc@cse.unsw.edu.au> based
on documentation by Jens Axboe.
unsigned int files_index;
unsigned int nr_open_files;
unsigned int nr_done_files;
- unsigned int nr_normal_files;
union {
unsigned int next_file;
struct frand_state next_file_state;
extern int read_only;
extern int eta_print;
extern int eta_new_line;
+extern unsigned int eta_interval_msec;
extern unsigned long done_secs;
extern int fio_gtod_offload;
extern int fio_gtod_cpu;
extern struct thread_data *threads;
+extern bool eta_time_within_slack(unsigned int time);
+
static inline void fio_ro_check(const struct thread_data *td, struct io_u *io_u)
{
assert(!(io_u->ddir == DDIR_WRITE && !td_write(td)));
extern const char *fio_get_arch_string(int);
extern const char *fio_get_os_string(int);
-#ifdef FIO_INTERNAL
-#define ARRAY_SIZE(x) (sizeof((x)) / (sizeof((x)[0])))
-#define FIELD_SIZE(s, f) (sizeof(((typeof(s))0)->f))
-#endif
-
enum {
__FIO_OUTPUT_TERSE = 0,
__FIO_OUTPUT_JSON = 1,
#include <sys/ipc.h>
#include <sys/types.h>
#include <sys/stat.h>
+#include <dlfcn.h>
#include "fio.h"
#ifndef FIO_NO_HAVE_SHM_H
int exitall_on_terminate = 0;
int output_format = FIO_OUTPUT_NORMAL;
int eta_print = FIO_ETA_AUTO;
+unsigned int eta_interval_msec = 1000;
int eta_new_line = 0;
FILE *f_out = NULL;
FILE *f_err = NULL;
.has_arg = required_argument,
.val = 'e' | FIO_CLIENT_FLAG,
},
+ {
+ .name = (char *) "eta-interval",
+ .has_arg = required_argument,
+ .val = 'O' | FIO_CLIENT_FLAG,
+ },
{
.name = (char *) "eta-newline",
.has_arg = required_argument,
}
if (td->io_ops) {
+ struct ioengine_ops *ops;
+ void *dlhandle;
+
/* An engine is loaded, but the requested ioengine
* may have changed.
*/
return 0;
}
+ /*
+ * Name of file and engine may be different, load ops
+ * for this name and see if they match. If they do, then
+ * the engine is unchanged.
+ */
+ dlhandle = td->io_ops_dlhandle;
+ ops = load_ioengine(td);
+ if (ops == td->io_ops && dlhandle == td->io_ops_dlhandle) {
+ if (dlhandle)
+ dlclose(dlhandle);
+ return 0;
+ }
+
+ if (dlhandle && dlhandle != td->io_ops_dlhandle)
+ dlclose(dlhandle);
+
/* Unload the old engine. */
free_ioengine(td);
}
printf(" --trigger=cmd\t\tSet this command as local trigger\n");
printf(" --trigger-remote=cmd\tSet this command as remote trigger\n");
printf(" --aux-path=path\tUse this path for fio state generated files\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");
+ printf("\nFio was written by Jens Axboe <axboe@kernel.dk>\n");
}
#ifdef FIO_INC_DEBUG
log_err("fio: failed parsing eta time %s\n", optarg);
exit_val = 1;
do_exit++;
+ break;
}
eta_new_line = t / 1000;
+ if (!eta_new_line) {
+ log_err("fio: eta new line time too short\n");
+ exit_val = 1;
+ do_exit++;
+ }
+ break;
+ }
+ case 'O': {
+ long long t = 0;
+
+ if (check_str_time(optarg, &t, 1)) {
+ log_err("fio: failed parsing eta interval %s\n", optarg);
+ exit_val = 1;
+ do_exit++;
+ break;
+ }
+ eta_interval_msec = t / 1000;
+ if (eta_interval_msec < DISK_UTIL_MSEC) {
+ log_err("fio: eta interval time too short (%umsec min)\n", DISK_UTIL_MSEC);
+ exit_val = 1;
+ do_exit++;
+ }
break;
}
case 'd':
return odir;
/*
- * Both directions are ahead of rate. sleep the min
- * switch if necissary
+ * Both directions are ahead of rate. sleep the min,
+ * switch if necessary
*/
if (td->rate_next_io_time[ddir] <=
- td->rate_next_io_time[odir]) {
+ td->rate_next_io_time[odir]) {
usec = td->rate_next_io_time[ddir] - now;
} else {
usec = td->rate_next_io_time[odir] - now;
if (td->o.io_submit_mode == IO_MODE_INLINE)
io_u_quiesce(td);
- usec = usec_sleep(td, usec);
-
+ usec_sleep(td, usec);
return ddir;
}
td->eo = NULL;
}
- if (td->io_ops_dlhandle)
+ if (td->io_ops_dlhandle) {
dlclose(td->io_ops_dlhandle);
+ td->io_ops_dlhandle = NULL;
+ }
td->io_ops = NULL;
}
.category = FIO_OPT_C_IO,
.group = FIO_OPT_G_RATE,
},
+ {
+ .name = "rate_ignore_thinktime",
+ .lname = "Rate ignore thinktime",
+ .type = FIO_OPT_BOOL,
+ .off1 = offsetof(struct thread_options, rate_ign_think),
+ .help = "Rated IO ignores thinktime settings",
+ .parent = "rate",
+ .category = FIO_OPT_C_IO,
+ .group = FIO_OPT_G_RATE,
+ },
{
.name = "max_latency",
.lname = "Max Latency (usec)",
<Product Id="*"
Codepage="1252" Language="1033"
Manufacturer="fio" Name="fio"
- UpgradeCode="2338A332-5511-43CF-B9BD-5C60496CCFCC" Version="3.2">
+ UpgradeCode="2338A332-5511-43CF-B9BD-5C60496CCFCC" Version="3.3">
<Package
Description="Flexible IO Tester"
InstallerVersion="301" Keywords="Installer,MSI,Database"
{
FILETIME utcFT;
LONGLONG jan1970;
+ SYSTEMTIME tempSystemTime;
jan1970 = Int32x32To64(dosTime, 10000000) + 116444736000000000;
utcFT.dwLowDateTime = (DWORD)jan1970;
utcFT.dwHighDateTime = jan1970 >> 32;
- FileTimeToSystemTime((FILETIME*)&utcFT, systemTime);
+ FileTimeToSystemTime((FILETIME*)&utcFT, &tempSystemTime);
+ SystemTimeToTzSpecificLocalTime(NULL, &tempSystemTime, systemTime);
}
char* ctime_r(const time_t *t, char *buf)
#include <math.h>
#include <float.h>
+#include "compiler/compiler.h"
#include "parse.h"
#include "debug.h"
#include "options.h"
#include "y.tab.h"
#endif
+static const char *opt_type_names[] = {
+ "OPT_INVALID",
+ "OPT_STR",
+ "OPT_STR_MULTI",
+ "OPT_STR_VAL",
+ "OPT_STR_VAL_TIME",
+ "OPT_STR_STORE",
+ "OPT_RANGE",
+ "OPT_INT",
+ "OPT_BOOL",
+ "OPT_FLOAT_LIST",
+ "OPT_STR_SET",
+ "OPT_DEPRECATED",
+ "OPT_UNSUPPORTED",
+};
+
static struct fio_option *__fio_options;
static int vp_cmp(const void *p1, const void *p2)
*ptr = (val); \
} while (0)
+static const char *opt_type_name(struct fio_option *o)
+{
+ compiletime_assert(ARRAY_SIZE(opt_type_names) - 1 == FIO_OPT_UNSUPPORTED,
+ "opt_type_names[] index");
+
+ if (o->type <= FIO_OPT_UNSUPPORTED)
+ return opt_type_names[o->type];
+
+ return "OPT_UNKNOWN?";
+}
+
static int __handle_option(struct fio_option *o, const char *ptr, void *data,
int first, int more, int curr)
{
struct value_pair posval[PARSE_MAX_VP];
int i, all_skipped = 1;
- dprint(FD_PARSE, "__handle_option=%s, type=%d, ptr=%s\n", o->name,
- o->type, ptr);
+ dprint(FD_PARSE, "__handle_option=%s, type=%s, ptr=%s\n", o->name,
+ opt_type_name(o), ptr);
if (!ptr && o->type != FIO_OPT_STR_SET && o->type != FIO_OPT_STR) {
log_err("Option %s requires an argument\n", o->name);
FIO_OPT_FLOAT_LIST,
FIO_OPT_STR_SET,
FIO_OPT_DEPRECATED,
- FIO_OPT_UNSUPPORTED,
+ FIO_OPT_UNSUPPORTED, /* keep this last */
};
/*
static int handle_probe_cmd(struct fio_net_cmd *cmd)
{
struct cmd_client_probe_pdu *pdu = (struct cmd_client_probe_pdu *) cmd->payload;
- struct cmd_probe_reply_pdu probe;
uint64_t tag = cmd->tag;
+ struct cmd_probe_reply_pdu probe = {
+#ifdef CONFIG_BIG_ENDIAN
+ .bigendian = 1,
+#endif
+ .os = FIO_OS,
+ .arch = FIO_ARCH,
+ .bpp = sizeof(void *),
+ .cpus = __cpu_to_le32(cpus_online()),
+ };
dprint(FD_NET, "server: sending probe reply\n");
strcpy(me, (char *) pdu->server);
- memset(&probe, 0, sizeof(probe));
gethostname((char *) probe.hostname, sizeof(probe.hostname));
-#ifdef CONFIG_BIG_ENDIAN
- probe.bigendian = 1;
-#endif
strncpy((char *) probe.fio_version, fio_version_string, sizeof(probe.fio_version) - 1);
- probe.os = FIO_OS;
- probe.arch = FIO_ARCH;
- probe.bpp = sizeof(void *);
- probe.cpus = __cpu_to_le32(cpus_online());
-
/*
* If the client supports compression and we do too, then enable it
*/
static int fio_append_iolog_gz(struct sk_entry *first, struct io_log *log)
{
+ z_stream stream = {
+ .zalloc = Z_NULL,
+ .zfree = Z_NULL,
+ .opaque = Z_NULL,
+ };
int ret = 0;
- z_stream stream;
-
- memset(&stream, 0, sizeof(stream));
- stream.zalloc = Z_NULL;
- stream.zfree = Z_NULL;
- stream.opaque = Z_NULL;
if (deflateInit(&stream, Z_DEFAULT_COMPRESSION) != Z_OK)
return 1;
int fio_send_iolog(struct thread_data *td, struct io_log *log, const char *name)
{
- struct cmd_iolog_pdu pdu;
+ struct cmd_iolog_pdu pdu = {
+ .nr_samples = cpu_to_le64(iolog_nr_samples(log)),
+ .thread_number = cpu_to_le32(td->thread_number),
+ .log_type = cpu_to_le32(log->log_type),
+ .log_hist_coarseness = cpu_to_le32(log->hist_coarseness),
+ };
struct sk_entry *first;
struct flist_head *entry;
int ret = 0;
- pdu.nr_samples = cpu_to_le64(iolog_nr_samples(log));
- pdu.thread_number = cpu_to_le32(td->thread_number);
- pdu.log_type = cpu_to_le32(log->log_type);
- pdu.log_hist_coarseness = cpu_to_le32(log->hist_coarseness);
-
if (!flist_empty(&log->chunk_list))
pdu.compressed = __cpu_to_le32(STORE_COMPRESSED);
else if (use_zlib)
void fio_server_send_add_job(struct thread_data *td)
{
- struct cmd_add_job_pdu pdu;
+ struct cmd_add_job_pdu pdu = {
+ .thread_number = cpu_to_le32(td->thread_number),
+ .groupid = cpu_to_le32(td->groupid),
+ };
- memset(&pdu, 0, sizeof(pdu));
- pdu.thread_number = cpu_to_le32(td->thread_number);
- pdu.groupid = cpu_to_le32(td->groupid);
convert_thread_options_to_net(&pdu.top, &td->o);
fio_net_queue_cmd(FIO_NET_CMD_ADD_JOB, &pdu, sizeof(pdu), NULL,
ret = inet_pton(AF_INET, host, inp);
if (ret != 1) {
- struct addrinfo hints, *res;
-
- memset(&hints, 0, sizeof(hints));
- hints.ai_family = ipv6 ? AF_INET6 : AF_INET;
- hints.ai_socktype = SOCK_STREAM;
+ struct addrinfo *res, hints = {
+ .ai_family = ipv6 ? AF_INET6 : AF_INET,
+ .ai_socktype = SOCK_STREAM,
+ };
ret = getaddrinfo(host, NULL, &hints, &res);
if (ret) {
static void set_sig_handlers(void)
{
- struct sigaction act;
+ struct sigaction act = {
+ .sa_handler = sig_int,
+ .sa_flags = SA_RESTART,
+ };
- memset(&act, 0, sizeof(act));
- act.sa_handler = sig_int;
- act.sa_flags = SA_RESTART;
sigaction(SIGINT, &act, NULL);
}
};
enum {
- FIO_SERVER_VER = 68,
+ FIO_SERVER_VER = 69,
FIO_SERVER_MAX_FRAGMENT_PDU = 1024,
FIO_SERVER_MAX_CMD_MB = 2048,
unsigned int rate_iops[DDIR_RWDIR_CNT];
unsigned int rate_iops_min[DDIR_RWDIR_CNT];
unsigned int rate_process;
+ unsigned int rate_ign_think;
char *ioscheduler;
uint32_t rate_iops[DDIR_RWDIR_CNT];
uint32_t rate_iops_min[DDIR_RWDIR_CNT];
uint32_t rate_process;
+ uint32_t rate_ign_think;
+ uint32_t pad;
uint8_t ioscheduler[FIO_TOP_STR_MAX];
The script takes the title of the plot as only argument. It does
not offer any additional options.
.SH AUTHOR
-fio_generate_plots was written by Jens Axboe <jens.axboe@oracle.com>,
-now Jens Axboe <jaxboe@fusionio.com>.
+fio_generate_plots was written by Jens Axboe <axboe@kernel.dk>
.PP
This manual page was written by Martin Steigerwald <ms@teamix.de>,
for the Debian project (but may be used by others).