#!/bin/sh
GVF=FIO-VERSION-FILE
-DEF_VER=fio-2.13
+DEF_VER=fio-2.15
LF='
'
VN=`git describe --match "fio-[0-9]*" --abbrev=4 HEAD 2>/dev/null` &&
case "$VN" in
*$LF*) (exit 1) ;;
- v[0-9]*)
+ fio-[0-9]*)
git update-index -q --refresh
test -z "`git diff-index --name-only HEAD --`" ||
VN="$VN-dirty" ;;
echo >&2 "FIO_VERSION = $VN"
echo "FIO_VERSION = $VN" >$GVF
}
-
-
cannot be modified. So random writes are not
possible. To imitate this, libhdfs engine
creates bunch of small files, and engine will
- pick a file out of those files based on the
- offset enerated by fio backend. Each jobs uses
+ pick a file out of those files based on the
+ offset generated by fio backend. Each jobs uses
it's own connection to HDFS.
mtd Read, write and erase an MTD character device
iodepth_batch_complete_min=1
iodepth_batch_complete_max=<iodepth>
- which means that we will retrieve at leat 1 IO and up to the
+ which means that we will retrieve at least 1 IO and up to the
whole submitted queue depth. If none of IO has been completed
yet, we will wait.
random Uniform random distribution
zipf Zipf distribution
pareto Pareto distribution
- gauss Normal (guassian) distribution
+ gauss Normal (gaussian) distribution
zoned Zoned random distribution
When using a zipf or pareto distribution, an input value
and random IO, at the given percentages. It is possible to
set different values for reads, writes, and trim. To do so,
simply use a comma separated list. See blocksize.
-
+
norandommap Normally fio will cover every block of the file when doing
random IO. If this option is given, fio will just get a
new random offset without looking at past io history. This
nice=int Run the job with the given nice value. See man nice(2).
+ On Windows, values less than -15 set the process class to "High";
+ -1 through -15 set "Above Normal"; 1 through 15 "Below Normal";
+ and above 15 "Idle" priority class.
+
prio=int Set the io priority value of this job. Linux limits us to
a positive value between 0 and 7, with 0 being the highest.
See man ionice(1). Refer to an appropriate manpage for
fio must be built on a system with libnuma-dev(el) installed.
numa_mem_policy=str Set this job's memory policy and corresponding NUMA
- nodes. Format of the argements:
+ nodes. Format of the arguments:
<mode>[:<nodelist>]
`mode' is one of the following memory policy:
default, prefer, bind, interleave, local
location should point there. So if it's mounted in /huge,
you would use mem=mmaphuge:/huge/somefile.
-iomem_align=int This indiciates the memory alignment of the IO memory buffers.
+iomem_align=int This indicates the memory alignment of the IO memory buffers.
Note that the given alignment is applied to the first IO unit
buffer, if using iodepth the alignment of the following buffers
are given by the bs used. In other words, if using a bs that is
starting the given IO operation. This will also clear
the 'invalidate' flag, since it is pointless to pre-read
and then drop the cache. This will only work for IO engines
- that are seekable, since they allow you to read the same data
+ that are seek-able, since they allow you to read the same data
multiple times. Thus it will not work on eg network or splice
IO.
crc32c Use a crc32c sum of the data area and store
it in the header of each block.
- crc32c-intel Use hardware assisted crc32c calcuation
+ crc32c-intel Use hardware assisted crc32c calculation
provided on SSE4.2 enabled processors. Falls
back to regular software crc32c, if not
supported by the system.
be a hex number that starts with either "0x" or "0X". Use
with verify=str. Also, verify_pattern supports %o format,
which means that for each block offset will be written and
- then verifyied back, e.g.:
+ then verified back, e.g.:
verify_pattern=%o
replay_no_stall=int When replaying I/O with read_iolog the default behavior
is to attempt to respect the time stamps within the log and
- replay them with the appropriate delay between IOPS. By
+ replay them with the appropriate delay between IOPS. By
setting this variable fio will not respect the timestamps and
attempt to replay them as fast as possible while still
- respecting ordering. The result is the same I/O pattern to a
+ respecting ordering. The result is the same I/O pattern to a
given device, but different timings.
replay_redirect=str While replaying I/O patterns using read_iolog the
mapping. Replay_redirect causes all IOPS to be replayed onto
the single specified device regardless of the device it was
recorded from. i.e. replay_redirect=/dev/sdc would cause all
- IO in the blktrace to be replayed onto /dev/sdc. This means
- multiple devices will be replayed onto a single, if the trace
- contains multiple devices. If you want multiple devices to be
- replayed concurrently to multiple redirected devices you must
- blkparse your trace into separate traces and replay them with
- independent fio invocations. Unfortuantely this also breaks
- the strict time ordering between multiple device accesses.
+ IO in the blktrace or iolog to be replayed onto /dev/sdc.
+ This means multiple devices will be replayed onto a single
+ device, if the trace contains multiple devices. If you want
+ multiple devices to be replayed concurrently to multiple
+ redirected devices you must blkparse your trace into separate
+ traces and replay them with independent fio invocations.
+ Unfortunately this also breaks the strict time ordering
+ between multiple device accesses.
replay_align=int Force alignment of IO offsets and lengths in a trace
to this power of 2 value.
the --inflate-log command line parameter. The files will be
stored with a .fz suffix.
+log_unix_epoch=bool If set, fio will log Unix timestamps to the log
+ files produced by enabling write_type_log for each log type, instead
+ of the default zero-based timestamps.
+
block_error_percentiles=bool If set, record errors in trim block-sized
units from writes and trims and output a histogram of
how many trims it took to get to errors, and what kind
connections rather than initiating an outgoing connection. The
hostname must be omitted if this option is used.
-[net] pingpong Normaly a network writer will just continue writing data, and
+[net] pingpong Normally a network writer will just continue writing data, and
a network reader will just consume packages. If pingpong=1
is set, a writer will send its normal payload to the reader,
then wait for the reader to send the same payload back. This
[e4defrag] inplace=int
Configure donor file blocks allocation strategy
0(default): Preallocate donor's file on init
- 1 : allocate space immidietly inside defragment event,
+ 1 : allocate space immediately inside defragment event,
and free right after event
[rbd] clustername=str Specifies the name of the Ceph cluster.
[rbd] rbdname=str Specifies the name of the RBD.
-[rbd] pool=str Specifies the naem of the Ceph pool containing RBD.
+[rbd] pool=str Specifies the name of the Ceph pool containing RBD.
[rbd] clientname=str Specifies the username (without the 'client.' prefix)
used to access the Ceph cluster. If the clustername is
- specified, the clientmae shall be the full type.id
+ specified, the clientname shall be the full type.id
string. If no type. prefix is given, fio will add
'client.' by default.
[mtd] skip_bad=bool Skip operations against known bad blocks.
[libhdfs] hdfsdirectory libhdfs will create chunk in this HDFS directory
-[libhdfs] chunck_size the size of the chunck to use for each file.
+[libhdfs] chunk_size the size of the chunk to use for each file.
6.0 Interpreting the output
The offset is the offset, in bytes, from the start of the file, for that
particular IO. The logging of the offset can be toggled with 'log_offset'.
-If windowed logging is enabled though 'log_avg_msec', then fio doesn't log
+If windowed logging is enabled through 'log_avg_msec', then fio doesn't log
individual IOs. Instead of logs the average values over the specified
period of time. Since 'data direction' and 'offset' are per-IO values,
they aren't applicable if windowed logging is enabled. If windowed logging
is enabled and 'log_max_value' is set, then fio logs maximum values in
that window instead of averages.
-
CFLAGS = -std=gnu99 -Wwrite-strings -Wall -Wdeclaration-after-statement $(OPTFLAGS) $(EXTFLAGS) $(BUILD_CFLAGS) -I. -I$(SRCDIR)
LIBS += -lm $(EXTLIBS)
PROGS = fio
-SCRIPTS = $(addprefix $(SRCDIR)/,tools/fio_generate_plots tools/plot/fio2gnuplot tools/genfio tools/fiologparser.py tools/fio_latency2csv.py)
+SCRIPTS = $(addprefix $(SRCDIR)/,tools/fio_generate_plots tools/plot/fio2gnuplot tools/genfio tools/fiologparser.py tools/fio_latency2csv.py tools/hist/fiologparser_hist.py)
ifndef CONFIG_FIO_NO_OPT
CFLAGS += -O3
ifdef CONFIG_LIBHDFS
HDFSFLAGS= -I $(JAVA_HOME)/include -I $(JAVA_HOME)/include/linux -I $(FIO_LIBHDFS_INCLUDE)
- HDFSLIB= -Wl,-rpath $(JAVA_HOME)/jre/lib/`uname -m`/server -L$(JAVA_HOME)/jre/lib/`uname -m`/server -ljvm $(FIO_LIBHDFS_LIB)/libhdfs.a
+ HDFSLIB= -Wl,-rpath $(JAVA_HOME)/jre/lib/$(FIO_HDFS_CPU)/server -L$(JAVA_HOME)/jre/lib/$(FIO_HDFS_CPU)/server -ljvm $(FIO_LIBHDFS_LIB)/libhdfs.a
CFLAGS += $(HDFSFLAGS)
SOURCE += engines/libhdfs.c
endif
@$(CC) -MM $(CFLAGS) $(CPPFLAGS) $(SRCDIR)/$*.c > $*.d
@mv -f $*.d $*.d.tmp
@sed -e 's|.*:|$*.o:|' < $*.d.tmp > $*.d
- @sed -e 's/.*://' -e 's/\\$$//' < $*.d.tmp | fmt -1 | \
+ @sed -e 's/.*://' -e 's/\\$$//' < $*.d.tmp | fmt -w 1 | \
sed -e 's/^ *//' -e 's/$$/:/' >> $*.d
@rm -f $*.d.tmp
@$(CC) -MM $(CFLAGS) $(CPPFLAGS) $(SRCDIR)/$*.c > $*.d
@mv -f $*.d $*.d.tmp
@sed -e 's|.*:|$*.o:|' < $*.d.tmp > $*.d
- @sed -e 's/.*://' -e 's/\\$$//' < $*.d.tmp | fmt -1 | \
+ @sed -e 's/.*://' -e 's/\\$$//' < $*.d.tmp | fmt -w 1 | \
sed -e 's/^ *//' -e 's/$$/:/' >> $*.d
@rm -f $*.d.tmp
@rm -f .depend $(FIO_OBJS) $(GFIO_OBJS) $(OBJS) $(T_OBJS) $(PROGS) $(T_PROGS) $(T_TEST_PROGS) core.* core gfio FIO-VERSION-FILE *.d lib/*.d oslib/*.d crc/*.d engines/*.d profiles/*.d t/*.d config-host.mak config-host.h y.tab.[ch] lex.yy.c exp/*.[do] lexer.h
distclean: clean FORCE
- @rm -f cscope.out fio.pdf fio_generate_plots.pdf fio2gnuplot.pdf
+ @rm -f cscope.out fio.pdf fio_generate_plots.pdf fio2gnuplot.pdf fiologparser_hist.pdf
cscope:
@cscope -b -R
@man -t ./fio.1 | ps2pdf - fio.pdf
@man -t tools/fio_generate_plots.1 | ps2pdf - fio_generate_plots.pdf
@man -t tools/plot/fio2gnuplot.1 | ps2pdf - fio2gnuplot.pdf
+ @man -t tools/hist/fiologparser_hist.py.1 | ps2pdf - fiologparser_hist.pdf
test:
$(INSTALL) -m 644 $(SRCDIR)/fio.1 $(DESTDIR)$(mandir)/man1
$(INSTALL) -m 644 $(SRCDIR)/tools/fio_generate_plots.1 $(DESTDIR)$(mandir)/man1
$(INSTALL) -m 644 $(SRCDIR)/tools/plot/fio2gnuplot.1 $(DESTDIR)$(mandir)/man1
+ $(INSTALL) -m 644 $(SRCDIR)/tools/hist/fiologparser_hist.py.1 $(DESTDIR)$(mandir)/man1
$(INSTALL) -m 755 -d $(DESTDIR)$(sharedir)
$(INSTALL) -m 644 $(SRCDIR)/tools/plot/*gpm $(DESTDIR)$(sharedir)/
--parse-only Parse options only, don't start any IO
--output Write output to file
--runtime Runtime in seconds
- --bandwidth-log Generate per-job bandwidth logs
+ --bandwidth-log Generate aggregate bandwidth logs
--minimal Minimal (terse) output
--output-format=type Output format (terse,json,json+,normal)
--terse-version=type Terse version output format (default 3, or 2 or 4).
--status-interval=t Force full status dump every 't' period passed
--section=name Only run specified section in job file.
Multiple sections can be specified.
- --alloc-size=kb Set smalloc pool to this size in kb (def 1024)
+ --alloc-size=kb Set smalloc pool to this size in kb (def 16384)
--warnings-fatal Fio parser warnings are fatal
--max-jobs Maximum number of threads/processes to support
--server=args Start backend server. See Client/Server section.
The --alloc-size switch allows one to use a larger pool size for smalloc.
If running large jobs with randommap enabled, fio can run out of memory.
Smalloc is an internal allocator for shared structures from a fixed size
-memory pool. The pool size defaults to 1024k and can grow to 128 pools.
+memory pool. The pool size defaults to 16M and can grow to 8 pools.
NOTE: While running .fio_smalloc.* backing store files are visible in /tmp.
---------------
If you notice anything that seems like a fio bug, please do send email
-to the list (fio@vger.kernel.org, see README) about it. You'll need
-to report at least:
+to the list (fio@vger.kernel.org, see README) about it. If you are not
+running the newest release of fio, upgrading first is recommended.
+
+When reporting a bug, you'll need to include:
1) A description of what you think the bug is
2) Environment (Linux distro version, kernel version). This is mostly
4) How to reproduce. Please include a full list of the parameters
passed to fio and the job file used (if any).
+A bug report can never have too much information. Any time information
+is left out and has to be asked for, it'll add to the turn-around time
+of getting to the bottom of it and committing a fix.
+
That's it!
int min_evts = 0;
int ret;
- if (td->flags & TD_F_REGROW_LOGS) {
- ret = io_u_quiesce(td);
- regrow_logs(td);
- return ret;
- }
+ if (td->flags & TD_F_REGROW_LOGS)
+ return io_u_quiesce(td);
/*
* if the queue is full, we MUST reap at least 1 event
return number_ios >= (td->o.number_ios * td->loops);
}
-static bool io_issue_bytes_exceeded(struct thread_data *td)
+static bool io_bytes_exceeded(struct thread_data *td, uint64_t *this_bytes)
{
unsigned long long bytes, limit;
if (td_rw(td))
- bytes = td->io_issue_bytes[DDIR_READ] + td->io_issue_bytes[DDIR_WRITE];
+ bytes = this_bytes[DDIR_READ] + this_bytes[DDIR_WRITE];
else if (td_write(td))
- bytes = td->io_issue_bytes[DDIR_WRITE];
+ bytes = this_bytes[DDIR_WRITE];
else if (td_read(td))
- bytes = td->io_issue_bytes[DDIR_READ];
+ bytes = this_bytes[DDIR_READ];
else
- bytes = td->io_issue_bytes[DDIR_TRIM];
+ bytes = this_bytes[DDIR_TRIM];
if (td->o.io_limit)
limit = td->o.io_limit;
return bytes >= limit || exceeds_number_ios(td);
}
-static bool io_complete_bytes_exceeded(struct thread_data *td)
+static bool io_issue_bytes_exceeded(struct thread_data *td)
{
- unsigned long long bytes, limit;
-
- if (td_rw(td))
- bytes = td->this_io_bytes[DDIR_READ] + td->this_io_bytes[DDIR_WRITE];
- else if (td_write(td))
- bytes = td->this_io_bytes[DDIR_WRITE];
- else if (td_read(td))
- bytes = td->this_io_bytes[DDIR_READ];
- else
- bytes = td->this_io_bytes[DDIR_TRIM];
-
- if (td->o.io_limit)
- limit = td->o.io_limit;
- else
- limit = td->o.size;
+ return io_bytes_exceeded(td, td->io_issue_bytes);
+}
- limit *= td->loops;
- return bytes >= limit || exceeds_number_ios(td);
+static bool io_complete_bytes_exceeded(struct thread_data *td)
+{
+ return io_bytes_exceeded(td, td->this_io_bytes);
}
/*
struct thread_data *td = fd->td;
struct thread_options *o = &td->o;
struct sk_out *sk_out = fd->sk_out;
+ int deadlock_loop_cnt;
int clear_state;
int ret;
if (rate_submit_init(td, sk_out))
goto err;
- fio_gettime(&td->epoch, NULL);
+ set_epoch_time(td, o->log_unix_epoch);
fio_getrusage(&td->ru_start);
memcpy(&td->bw_sample_time, &td->epoch, sizeof(td->epoch));
memcpy(&td->iops_sample_time, &td->epoch, sizeof(td->epoch));
}
}
+ /*
+ * If we took too long to shut down, the main thread could
+ * already consider us reaped/exited. If that happens, break
+ * out and clean up.
+ */
+ if (td->runstate >= TD_EXITED)
+ break;
+
clear_state = 1;
/*
* the rusage_sem, which would never get upped because
* this thread is waiting for the stat mutex.
*/
- check_update_rusage(td);
+ deadlock_loop_cnt = 0;
+ do {
+ check_update_rusage(td);
+ if (!fio_mutex_down_trylock(stat_mutex))
+ break;
+ usleep(1000);
+ if (deadlock_loop_cnt++ > 5000) {
+ log_err("fio seems to be stuck grabbing stat_mutex, forcibly exiting\n");
+ td->error = EDEADLK;
+ goto err;
+ }
+ } while (1);
- fio_mutex_down(stat_mutex);
if (td_read(td) && td->io_bytes[DDIR_READ])
update_runtime(td, elapsed_us, DDIR_READ);
if (td_write(td) && td->io_bytes[DDIR_WRITE])
/*
* Run over the job map and reap the threads that have exited, if any.
*/
-static void reap_threads(unsigned int *nr_running, unsigned int *t_rate,
- unsigned int *m_rate)
+static void reap_threads(unsigned int *nr_running, uint64_t *t_rate,
+ uint64_t *m_rate)
{
struct thread_data *td;
unsigned int cputhreads, realthreads, pending;
static void run_threads(struct sk_out *sk_out)
{
struct thread_data *td;
- unsigned int i, todo, nr_running, m_rate, t_rate, nr_started;
+ unsigned int i, todo, nr_running, nr_started;
+ uint64_t m_rate, t_rate;
uint64_t spent;
if (fio_gtod_offload && fio_start_gtod_thread())
t->bytes = (t->bytes + o->replay_align - 1) & ~(o->replay_align - 1);
}
-static void ipo_bytes_align(struct thread_options *o, struct io_piece *ipo)
-{
- if (!o->replay_align)
- return;
-
- ipo->offset &= ~(o->replay_align - 1);
-}
-
-
/*
* Store blk_io_trace data in an ipo for later retrieval.
*/
ipo->offset = offset * bs;
if (td->o.replay_scale)
ipo->offset = ipo->offset / td->o.replay_scale;
- ipo_bytes_align(&td->o, ipo);
+ ipo_bytes_align(td->o.replay_align, ipo);
ipo->len = bytes;
ipo->delay = ttime / 1000;
if (rw)
ipo->offset = t->sector * bs;
if (td->o.replay_scale)
ipo->offset = ipo->offset / td->o.replay_scale;
- ipo_bytes_align(&td->o, ipo);
+ ipo_bytes_align(td->o.replay_align, ipo);
ipo->len = t->bytes;
ipo->delay = ttime / 1000;
ipo->ddir = DDIR_TRIM;
}
o->rwmix[i] = le32_to_cpu(top->rwmix[i]);
- o->rate[i] = le32_to_cpu(top->rate[i]);
- o->ratemin[i] = le32_to_cpu(top->ratemin[i]);
+ o->rate[i] = le64_to_cpu(top->rate[i]);
+ o->ratemin[i] = le64_to_cpu(top->ratemin[i]);
o->rate_iops[i] = le32_to_cpu(top->rate_iops[i]);
o->rate_iops_min[i] = le32_to_cpu(top->rate_iops_min[i]);
o->log_offset = le32_to_cpu(top->log_offset);
o->log_gz = le32_to_cpu(top->log_gz);
o->log_gz_store = le32_to_cpu(top->log_gz_store);
+ o->log_unix_epoch = le32_to_cpu(top->log_unix_epoch);
o->norandommap = le32_to_cpu(top->norandommap);
o->softrandommap = le32_to_cpu(top->softrandommap);
o->bs_unaligned = le32_to_cpu(top->bs_unaligned);
o->replay_align = le32_to_cpu(top->replay_align);
o->replay_scale = le32_to_cpu(top->replay_scale);
o->per_job_logs = le32_to_cpu(top->per_job_logs);
+ o->write_bw_log = le32_to_cpu(top->write_bw_log);
+ o->write_lat_log = le32_to_cpu(top->write_lat_log);
+ o->write_iops_log = le32_to_cpu(top->write_iops_log);
+ o->write_hist_log = le32_to_cpu(top->write_hist_log);
o->trim_backlog = le64_to_cpu(top->trim_backlog);
o->rate_process = le32_to_cpu(top->rate_process);
top->log_offset = cpu_to_le32(o->log_offset);
top->log_gz = cpu_to_le32(o->log_gz);
top->log_gz_store = cpu_to_le32(o->log_gz_store);
+ top->log_unix_epoch = cpu_to_le32(o->log_unix_epoch);
top->norandommap = cpu_to_le32(o->norandommap);
top->softrandommap = cpu_to_le32(o->softrandommap);
top->bs_unaligned = cpu_to_le32(o->bs_unaligned);
top->replay_align = cpu_to_le32(o->replay_align);
top->replay_scale = cpu_to_le32(o->replay_scale);
top->per_job_logs = cpu_to_le32(o->per_job_logs);
+ top->write_bw_log = cpu_to_le32(o->write_bw_log);
+ top->write_lat_log = cpu_to_le32(o->write_lat_log);
+ top->write_iops_log = cpu_to_le32(o->write_iops_log);
+ top->write_hist_log = cpu_to_le32(o->write_hist_log);
for (i = 0; i < DDIR_RWDIR_CNT; i++) {
top->bs[i] = cpu_to_le32(o->bs[i]);
}
top->rwmix[i] = cpu_to_le32(o->rwmix[i]);
- top->rate[i] = cpu_to_le32(o->rate[i]);
- top->ratemin[i] = cpu_to_le32(o->ratemin[i]);
+ top->rate[i] = cpu_to_le64(o->rate[i]);
+ top->ratemin[i] = cpu_to_le64(o->ratemin[i]);
top->rate_iops[i] = cpu_to_le32(o->rate_iops[i]);
top->rate_iops_min[i] = cpu_to_le32(o->rate_iops_min[i]);
return fio_net_send_quit(client->fd);
}
-void fio_clients_terminate(void)
+static void fio_clients_terminate(void)
{
struct flist_head *entry;
struct fio_client *client;
je->files_open = le32_to_cpu(je->files_open);
for (i = 0; i < DDIR_RWDIR_CNT; i++) {
- je->m_rate[i] = le32_to_cpu(je->m_rate[i]);
- je->t_rate[i] = le32_to_cpu(je->t_rate[i]);
+ je->m_rate[i] = le64_to_cpu(je->m_rate[i]);
+ je->t_rate[i] = le64_to_cpu(je->t_rate[i]);
je->m_iops[i] = le32_to_cpu(je->m_iops[i]);
je->t_iops[i] = le32_to_cpu(je->t_iops[i]);
- je->rate[i] = le32_to_cpu(je->rate[i]);
+ je->rate[i] = le64_to_cpu(je->rate[i]);
je->iops[i] = le32_to_cpu(je->iops[i]);
}
strcpy((char *) dst->run_str, (char *) je->run_str);
}
-static void remove_reply_cmd(struct fio_client *client, struct fio_net_cmd *cmd)
+static bool remove_reply_cmd(struct fio_client *client, struct fio_net_cmd *cmd)
{
struct fio_net_cmd_reply *reply = NULL;
struct flist_head *entry;
if (!reply) {
log_err("fio: client: unable to find matching tag (%llx)\n", (unsigned long long) cmd->tag);
- return;
+ return false;
}
flist_del(&reply->list);
cmd->tag = reply->saved_tag;
free(reply);
+ return true;
}
int fio_client_wait_for_reply(struct fio_client *client, uint64_t tag)
fio_client_dec_jobs_eta(eta, client->ops->eta);
}
+static void client_flush_hist_samples(FILE *f, int hist_coarseness, void *samples,
+ uint64_t sample_size)
+{
+ struct io_sample *s;
+ int log_offset;
+ uint64_t i, j, nr_samples;
+ struct io_u_plat_entry *entry;
+ unsigned int *io_u_plat;
+
+ int stride = 1 << hist_coarseness;
+
+ if (!sample_size)
+ return;
+
+ s = __get_sample(samples, 0, 0);
+ log_offset = (s->__ddir & LOG_OFFSET_SAMPLE_BIT) != 0;
+
+ nr_samples = sample_size / __log_entry_sz(log_offset);
+
+ for (i = 0; i < nr_samples; i++) {
+
+ s = (struct io_sample *)((char *)__get_sample(samples, log_offset, i) +
+ i * sizeof(struct io_u_plat_entry));
+
+ entry = s->data.plat_entry;
+ io_u_plat = entry->io_u_plat;
+
+ fprintf(f, "%lu, %u, %u, ", (unsigned long) s->time,
+ io_sample_ddir(s), s->bs);
+ for (j = 0; j < FIO_IO_U_PLAT_NR - stride; j += stride) {
+ fprintf(f, "%lu, ", hist_sum(j, stride, io_u_plat, NULL));
+ }
+ fprintf(f, "%lu\n", (unsigned long)
+ hist_sum(FIO_IO_U_PLAT_NR - stride, stride, io_u_plat, NULL));
+
+ }
+}
+
static int fio_client_handle_iolog(struct fio_client *client,
struct fio_net_cmd *cmd)
{
struct cmd_iolog_pdu *pdu;
bool store_direct;
+ char *log_pathname;
pdu = convert_iolog(cmd, &store_direct);
if (!pdu) {
return 1;
}
+ /* allocate buffer big enough for next sprintf() call */
+ log_pathname = malloc(10 + strlen((char *)pdu->name) +
+ strlen(client->hostname));
+ if (!log_pathname) {
+ log_err("fio: memory allocation of unique pathname failed");
+ return -1;
+ }
+ /* generate a unique pathname for the log file using hostname */
+ sprintf(log_pathname, "%s.%s", pdu->name, client->hostname);
+
if (store_direct) {
ssize_t ret;
size_t sz;
int fd;
- fd = open((const char *) pdu->name,
+ fd = open((const char *) log_pathname,
O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd < 0) {
- log_err("fio: open log: %s\n", strerror(errno));
+ log_err("fio: open log %s: %s\n",
+ log_pathname, strerror(errno));
return 1;
}
return 0;
} else {
FILE *f;
-
- f = fopen((const char *) pdu->name, "w");
+ f = fopen((const char *) log_pathname, "w");
if (!f) {
- log_err("fio: fopen log: %s\n", strerror(errno));
+ log_err("fio: fopen log %s : %s\n",
+ log_pathname, strerror(errno));
return 1;
}
- flush_samples(f, pdu->samples,
- pdu->nr_samples * sizeof(struct io_sample));
+ if (pdu->log_type == IO_LOG_TYPE_HIST) {
+ client_flush_hist_samples(f, pdu->log_hist_coarseness, pdu->samples,
+ pdu->nr_samples * sizeof(struct io_sample));
+ } else {
+ flush_samples(f, pdu->samples,
+ pdu->nr_samples * sizeof(struct io_sample));
+ }
fclose(f);
return 0;
}
*/
nr_samples = le64_to_cpu(pdu->nr_samples);
- total = nr_samples * __log_entry_sz(le32_to_cpu(pdu->log_offset));
+ if (pdu->log_type == IO_LOG_TYPE_HIST)
+ total = nr_samples * (__log_entry_sz(le32_to_cpu(pdu->log_offset)) +
+ sizeof(struct io_u_plat_entry));
+ else
+ total = nr_samples * __log_entry_sz(le32_to_cpu(pdu->log_offset));
ret = malloc(total + sizeof(*pdu));
ret->nr_samples = nr_samples;
ret->log_type = le32_to_cpu(ret->log_type);
ret->compressed = le32_to_cpu(ret->compressed);
ret->log_offset = le32_to_cpu(ret->log_offset);
+ ret->log_hist_coarseness = le32_to_cpu(ret->log_hist_coarseness);
if (*store_direct)
return ret;
struct io_sample *s;
s = __get_sample(samples, ret->log_offset, i);
+ if (ret->log_type == IO_LOG_TYPE_HIST)
+ s = (struct io_sample *)((void *)s + sizeof(struct io_u_plat_entry) * i);
+
s->time = le64_to_cpu(s->time);
- s->val = le64_to_cpu(s->val);
+ s->data.val = le64_to_cpu(s->data.val);
s->__ddir = le32_to_cpu(s->__ddir);
s->bs = le32_to_cpu(s->bs);
so->offset = le64_to_cpu(so->offset);
}
+
+ if (ret->log_type == IO_LOG_TYPE_HIST) {
+ s->data.plat_entry = (struct io_u_plat_entry *)(((void *)s) + sizeof(*s));
+ s->data.plat_entry->list.next = NULL;
+ s->data.plat_entry->list.prev = NULL;
+ }
}
return ret;
case FIO_NET_CMD_ETA: {
struct jobs_eta *je = (struct jobs_eta *) cmd->payload;
- remove_reply_cmd(client, cmd);
+ if (!remove_reply_cmd(client, cmd))
+ break;
convert_jobs_eta(je);
handle_eta(client, cmd);
break;
extern void fio_client_add_cmd_option(void *, const char *);
extern int fio_client_add_ini_file(void *, const char *, bool);
extern int fio_client_terminate(struct fio_client *);
-extern void fio_clients_terminate(void);
extern struct fio_client *fio_get_client(struct fio_client *);
extern void fio_put_client(struct fio_client *);
extern int fio_client_update_options(struct fio_client *, struct thread_options *, uint64_t *);
FIO_CLIENT_TYPE_GUI = 2,
};
+extern int sum_stat_clients;
+extern struct thread_stat client_ts;
+extern struct group_run_stats client_gs;
+
#endif
fi
echo "CLOCK_MONOTONIC_PRECISE $clock_monotonic_precise"
+##########################################
+# clockid_t probe
+clockid_t="no"
+cat > $TMPC << EOF
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+int main(int argc, char **argv)
+{
+ clockid_t cid;
+ memset(&cid, 0, sizeof(cid));
+ return clock_gettime(cid, NULL);
+}
+EOF
+if compile_prog "" "$LIBS" "clockid_t"; then
+ clockid_t="yes"
+fi
+echo "clockid_t $clockid_t"
+
##########################################
# gettimeofday() probe
gettimeofday="no"
fi
echo "Rados Block Device engine $rbd"
+##########################################
+# check for rbd_poll
+rbd_poll="no"
+if test "$rbd" = "yes"; then
+cat > $TMPC << EOF
+#include <rbd/librbd.h>
+#include <sys/eventfd.h>
+
+int main(int argc, char **argv)
+{
+ rbd_image_t image;
+ rbd_completion_t comp;
+
+ int fd = eventfd(0, EFD_NONBLOCK);
+ rbd_set_image_notification(image, fd, EVENT_TYPE_EVENTFD);
+ rbd_poll_io_events(image, comp, 1);
+
+ return 0;
+}
+EOF
+if compile_prog "" "-lrbd -lrados" "rbd"; then
+ rbd_poll="yes"
+fi
+echo "rbd_poll $rbd_poll"
+fi
+
##########################################
# check for rbd_invaidate_cache()
rbd_inval="no"
if test "$hdfs_conf_error" = "1" ; then
exit 1
fi
+ FIO_HDFS_CPU=$cpu
+ if test "$FIO_HDFS_CPU" = "x86_64" ; then
+ FIO_HDFS_CPU="amd64"
+ fi
fi
echo "HDFS engine $libhdfs"
##########################################
# Check whether we have getmntinfo
+# These are originally added for BSDs, but may also work
+# on other operating systems with getmntinfo(3).
+
+# getmntinfo(3) for FreeBSD/DragonFlyBSD/OpenBSD.
+# Note that NetBSD needs -Werror to catch warning as error.
getmntinfo="no"
cat > $TMPC << EOF
#include <stdio.h>
#include <sys/mount.h>
int main(int argc, char **argv)
{
- struct statfs st;
+ struct statfs *st;
return getmntinfo(&st, MNT_NOWAIT);
}
EOF
-if compile_prog "" "" "getmntinfo"; then
+if compile_prog "-Werror" "" "getmntinfo"; then
getmntinfo="yes"
fi
echo "getmntinfo $getmntinfo"
+# getmntinfo(3) for NetBSD.
+getmntinfo_statvfs="no"
+cat > $TMPC << EOF
+#include <stdio.h>
+#include <sys/statvfs.h>
+int main(int argc, char **argv)
+{
+ struct statvfs *st;
+ return getmntinfo(&st, MNT_NOWAIT);
+}
+EOF
+# Skip the test if the one with statfs arg is detected.
+if test "$getmntinfo" != "yes" && compile_prog "-Werror" "" "getmntinfo_statvfs"; then
+ getmntinfo_statvfs="yes"
+ echo "getmntinfo_statvfs $getmntinfo_statvfs"
+fi
+
##########################################
# Check whether we have _Static_assert
static_assert="no"
if test "$clock_monotonic_precise" = "yes" ; then
output_sym "CONFIG_CLOCK_MONOTONIC_PRECISE"
fi
+if test "$clockid_t" = "yes"; then
+ output_sym "CONFIG_CLOCKID_T"
+fi
if test "$gettimeofday" = "yes" ; then
output_sym "CONFIG_GETTIMEOFDAY"
fi
if test "$rbd" = "yes" ; then
output_sym "CONFIG_RBD"
fi
+if test "$rbd_poll" = "yes" ; then
+ output_sym "CONFIG_RBD_POLL"
+fi
if test "$rbd_inval" = "yes" ; then
output_sym "CONFIG_RBD_INVAL"
fi
fi
if test "$libhdfs" = "yes" ; then
output_sym "CONFIG_LIBHDFS"
+ echo "FIO_HDFS_CPU=$FIO_HDFS_CPU" >> $config_host_mak
echo "JAVA_HOME=$JAVA_HOME" >> $config_host_mak
echo "FIO_LIBHDFS_INCLUDE=$FIO_LIBHDFS_INCLUDE" >> $config_host_mak
echo "FIO_LIBHDFS_LIB=$FIO_LIBHDFS_LIB" >> $config_host_mak
if test "$getmntinfo" = "yes" ; then
output_sym "CONFIG_GETMNTINFO"
fi
+if test "$getmntinfo_statvfs" = "yes" ; then
+ output_sym "CONFIG_GETMNTINFO_STATVFS"
+fi
if test "$static_assert" = "yes" ; then
output_sym "CONFIG_STATIC_ASSERT"
fi
dprint(FD_DISKUTIL, "add maj/min %d/%d: %s\n", majdev, mindev, path);
du = smalloc(sizeof(*du));
- if (!du) {
- log_err("fio: smalloc() pool exhausted\n");
+ if (!du)
return NULL;
- }
memset(du, 0, sizeof(*du));
INIT_FLIST_HEAD(&du->list);
static struct fio_option options[] = {
{
.name = "donorname",
+ .lname = "Donor Name",
.type = FIO_OPT_STR_STORE,
.off1 = offsetof(struct e4defrag_options, donor_name),
.help = "File used as a block donor",
},
{
.name = "inplace",
+ .lname = "In Place",
.type = FIO_OPT_INT,
.off1 = offsetof(struct e4defrag_options, inplace),
.minval = 0,
return FIO_Q_COMPLETED;
}
-int fio_gf_async_setup(struct thread_data *td)
+static int fio_gf_async_setup(struct thread_data *td)
{
struct gf_data *g;
int r;
.group = FIO_OPT_G_HDFS,
},
{
- .name = "chunck_size",
+ .name = "chunk_size",
+ .alias = "chunck_size",
+ .lname = "Chunk size",
.type = FIO_OPT_INT,
.off1 = offsetof(struct hdfsio_options, chunck_size),
.def = "1048576",
},
{
.name = "single_instance",
+ .lname = "Single Instance",
.type = FIO_OPT_BOOL,
.off1 = offsetof(struct hdfsio_options, single_instance),
.def = "1",
},
{
.name = "hdfs_use_direct",
+ .lname = "HDFS Use Direct",
.type = FIO_OPT_BOOL,
.off1 = offsetof(struct hdfsio_options, use_direct),
.def = "0",
#define MMAP_TOTAL_SZ (1 * 1024 * 1024 * 1024UL)
static unsigned long mmap_map_size;
-static unsigned long mmap_map_mask;
struct fio_mmap_data {
void *mmap_ptr;
(void) posix_madvise(fmd->mmap_ptr, fmd->mmap_sz, FIO_MADV_FREE);
#endif
-
err:
if (td->error && fmd->mmap_ptr)
munmap(fmd->mmap_ptr, length);
static int fio_mmapio_init(struct thread_data *td)
{
struct thread_options *o = &td->o;
- unsigned long shift, mask;
- if ((td->o.rw_min_bs & page_mask) &&
+ if ((o->rw_min_bs & page_mask) &&
(o->odirect || o->fsync_blocks || o->fdatasync_blocks)) {
log_err("fio: mmap options dictate a minimum block size of "
"%llu bytes\n", (unsigned long long) page_size);
return 1;
}
- mmap_map_size = MMAP_TOTAL_SZ / td->o.nr_files;
- mask = mmap_map_size;
- shift = 0;
- do {
- mask >>= 1;
- if (!mask)
- break;
- shift++;
- } while (1);
-
- mmap_map_mask = 1UL << shift;
+ mmap_map_size = MMAP_TOTAL_SZ / o->nr_files;
return 0;
}
#include "../verify.h"
#include "../oslib/libmtd.h"
-libmtd_t desc;
+static libmtd_t desc;
struct fio_mtd_data {
struct mtd_dev_info info;
return generic_close_file(td, f);
}
-int fio_mtd_get_file_size(struct thread_data *td, struct fio_file *f)
+static int fio_mtd_get_file_size(struct thread_data *td, struct fio_file *f)
{
struct mtd_dev_info info;
#ifdef CONFIG_TCP_NODELAY
{
.name = "nodelay",
+ .lname = "No Delay",
.type = FIO_OPT_BOOL,
.off1 = offsetof(struct netio_options, nodelay),
.help = "Use TCP_NODELAY on TCP connections",
},
{
.name = "pingpong",
+ .lname = "Ping Pong",
.type = FIO_OPT_STR_SET,
.off1 = offsetof(struct netio_options, pingpong),
.help = "Ping-pong IO requests",
r = 0;
restart:
- memset(suspend_list, 0, sizeof(*suspend_list));
+ memset(suspend_list, 0, sizeof(suspend_list));
suspend_entries = 0;
io_u_qiter(&td->io_u_all, io_u, i) {
int err;
#include <zipkin_c.h>
#endif
+#ifdef CONFIG_RBD_POLL
+/* add for poll */
+#include <poll.h>
+#include <sys/eventfd.h>
+#endif
+
struct fio_rbd_iou {
struct io_u *io_u;
rbd_completion_t completion;
rbd_image_t image;
struct io_u **aio_events;
struct io_u **sort_events;
+ int fd; /* add for poll */
};
struct rbd_options {
if (!rbd)
goto failed;
+ /* add for poll, init fd: -1 */
+ rbd->fd = -1;
+
rbd->aio_events = calloc(td->o.iodepth, sizeof(struct io_u *));
if (!rbd->aio_events)
goto failed;
}
+#ifdef CONFIG_RBD_POLL
+static bool _fio_rbd_setup_poll(struct rbd_data *rbd)
+{
+ int r;
+
+ /* add for rbd poll */
+ rbd->fd = eventfd(0, EFD_NONBLOCK);
+ if (rbd->fd < 0) {
+ log_err("eventfd failed.\n");
+ return false;
+ }
+
+ r = rbd_set_image_notification(rbd->image, rbd->fd, EVENT_TYPE_EVENTFD);
+ if (r < 0) {
+ log_err("rbd_set_image_notification failed.\n");
+ close(rbd->fd);
+ rbd->fd = -1;
+ return false;
+ }
+
+ return true;
+}
+#else
+static bool _fio_rbd_setup_poll(struct rbd_data *rbd)
+{
+ return true;
+}
+#endif
+
static int _fio_rbd_connect(struct thread_data *td)
{
struct rbd_data *rbd = td->io_ops_data;
log_err("rbd_open failed.\n");
goto failed_open;
}
+
+ if (!_fio_rbd_setup_poll(rbd))
+ goto failed_poll;
+
return 0;
+failed_poll:
+ rbd_close(rbd->image);
+ rbd->image = NULL;
failed_open:
rados_ioctx_destroy(rbd->io_ctx);
rbd->io_ctx = NULL;
if (!rbd)
return;
+ /* close eventfd */
+ if (rbd->fd != -1) {
+ close(rbd->fd);
+ rbd->fd = -1;
+ }
+
/* shutdown everything */
if (rbd->image) {
rbd_close(rbd->image);
struct rbd_data *rbd = td->io_ops_data;
unsigned int this_events = 0;
struct io_u *io_u;
- int i, sidx;
+ int i, sidx = 0;
+
+#ifdef CONFIG_RBD_POLL
+ int ret = 0;
+ int event_num = 0;
+ struct fio_rbd_iou *fri = NULL;
+ rbd_completion_t comps[min_evts];
- sidx = 0;
+ struct pollfd pfd;
+ pfd.fd = rbd->fd;
+ pfd.events = POLLIN;
+
+ ret = poll(&pfd, 1, -1);
+ if (ret <= 0)
+ return 0;
+
+ assert(pfd.revents & POLLIN);
+
+ event_num = rbd_poll_io_events(rbd->image, comps, min_evts);
+
+ for (i = 0; i < event_num; i++) {
+ fri = rbd_aio_get_arg(comps[i]);
+ io_u = fri->io_u;
+#else
io_u_qiter(&td->io_u_all, io_u, i) {
+#endif
if (!(io_u->flags & IO_U_F_FLIGHT))
continue;
if (rbd_io_u_seen(io_u))
if (r < 0) {
log_err("rbd_status failed.\n");
goto disconnect;
+ } else if (info.size == 0) {
+ log_err("image size should be larger than zero.\n");
+ r = -EINVAL;
+ goto disconnect;
}
+
dprint(FD_IO, "rbd-engine: image size: %lu\n", info.size);
/* taken from "net" engine. Pretend we deal with files,
if (!rc)
{
+ DWORD threadid;
+
ctx->iocp = hFile;
ctx->wd = wd;
- wd->iothread = CreateThread(NULL, 0, IoCompletionRoutine, ctx, 0, NULL);
- if (wd->iothread == NULL)
+ wd->iothread = CreateThread(NULL, 0, IoCompletionRoutine, ctx, 0, &threadid);
+
+ if (wd->iothread != NULL)
+ fio_setaffinity(threadid, td->o.cpumask);
+ else
log_err("windowsaio: failed to create io completion thread\n");
}
}
}
- eta_sec = (unsigned long) (elapsed * (1.0 / perc)) - elapsed;
+ if (perc == 0.0) {
+ eta_sec = timeout;
+ } else {
+ eta_sec = (unsigned long) (elapsed * (1.0 / perc)) - elapsed;
+ }
if (td->o.timeout &&
eta_sec > (timeout + done_secs - elapsed))
|| td->runstate == TD_SETTING_UP
|| td->runstate == TD_RAMP
|| td->runstate == TD_PRE_READING) {
- int t_eta = 0, r_eta = 0;
+ int64_t t_eta = 0, r_eta = 0;
unsigned long long rate_bytes;
/*
uint64_t start_delay = td->o.start_delay;
uint64_t ramp_time = td->o.ramp_time;
- t_eta = __timeout + start_delay + ramp_time;
+ t_eta = __timeout + start_delay;
+ if (!td->ramp_time_over) {
+ t_eta += ramp_time;
+ }
t_eta /= 1000000ULL;
if ((td->runstate == TD_RAMP) && in_ramp_time(td)) {
t_eta -= ramp_left;
}
}
- rate_bytes = ddir_rw_sum(td->o.rate);
+ rate_bytes = 0;
+ if (td_read(td))
+ rate_bytes = td->o.rate[DDIR_READ];
+ if (td_write(td))
+ rate_bytes += td->o.rate[DDIR_WRITE];
+ if (td_trim(td))
+ rate_bytes += td->o.rate[DDIR_TRIM];
+
if (rate_bytes) {
- r_eta = (bytes_total / 1024) / rate_bytes;
+ r_eta = bytes_total / rate_bytes;
r_eta += (td->o.start_delay / 1000000ULL);
}
static void calc_rate(int unified_rw_rep, unsigned long mtime,
unsigned long long *io_bytes,
- unsigned long long *prev_io_bytes, unsigned int *rate)
+ unsigned long long *prev_io_bytes, uint64_t *rate)
{
int i;
* Print status of the jobs we know about. This includes rate estimates,
* ETA, thread state, etc.
*/
-int calc_thread_status(struct jobs_eta *je, int force)
+bool calc_thread_status(struct jobs_eta *je, int force)
{
struct thread_data *td;
int i, unified_rw_rep;
- unsigned long rate_time, disp_time, bw_avg_time, *eta_secs;
+ uint64_t rate_time, disp_time, bw_avg_time, *eta_secs;
unsigned long long io_bytes[DDIR_RWDIR_CNT];
unsigned long long io_iops[DDIR_RWDIR_CNT];
struct timeval now;
if (!force) {
if (!(output_format & FIO_OUTPUT_NORMAL) &&
f_out == stdout)
- return 0;
+ return false;
if (temp_stall_ts || eta_print == FIO_ETA_NEVER)
- return 0;
+ return false;
if (!isatty(STDOUT_FILENO) && (eta_print != FIO_ETA_ALWAYS))
- return 0;
+ return false;
}
if (!ddir_rw_sum(rate_io_bytes))
if (!ddir_rw_sum(disp_io_bytes))
fill_start_time(&disp_prev_time);
- eta_secs = malloc(thread_number * sizeof(unsigned long));
- memset(eta_secs, 0, thread_number * sizeof(unsigned long));
+ eta_secs = malloc(thread_number * sizeof(uint64_t));
+ memset(eta_secs, 0, thread_number * sizeof(uint64_t));
je->elapsed_sec = (mtime_since_genesis() + 999) / 1000;
calc_rate(unified_rw_rep, rate_time, io_bytes, rate_io_bytes,
je->rate);
memcpy(&rate_prev_time, &now, sizeof(now));
- add_agg_sample(je->rate[DDIR_READ], DDIR_READ, 0);
- add_agg_sample(je->rate[DDIR_WRITE], DDIR_WRITE, 0);
- add_agg_sample(je->rate[DDIR_TRIM], DDIR_TRIM, 0);
+ add_agg_sample(sample_val(je->rate[DDIR_READ]), DDIR_READ, 0);
+ add_agg_sample(sample_val(je->rate[DDIR_WRITE]), DDIR_WRITE, 0);
+ add_agg_sample(sample_val(je->rate[DDIR_TRIM]), DDIR_TRIM, 0);
}
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)
- return 0;
+ return false;
calc_rate(unified_rw_rep, disp_time, io_bytes, disp_io_bytes, je->rate);
calc_iops(unified_rw_rep, disp_time, io_iops, disp_io_iops, je->iops);
memcpy(&disp_prev_time, &now, sizeof(now));
if (!force && !je->nr_running && !je->nr_pending)
- return 0;
+ return false;
je->nr_threads = thread_number;
update_condensed_str(__run_str, run_str);
memcpy(je->run_str, run_str, strlen(run_str));
- return 1;
+ return true;
}
void display_thread_status(struct jobs_eta *je)
--- /dev/null
+# The most basic form of data verification. Write the device randomly
+# in 4K chunks, then read it back and verify the contents.
+[write-and-verify]
+rw=randwrite
+bs=4k
+direct=1
+ioengine=libaio
+iodepth=16
+verify=crc32c
+# Use /dev/XXX. For running this on a file instead, remove the filename
+# option and add a size=32G (or whatever file size you want) instead.
+filename=/dev/XXX
iodepth=256
numjobs=4
bssplit=512/4:1024/1:1536/1:2048/1:2560/1:3072/1:3584/1:4k/67:8k/10:16k/7:32k/3:64k/3
+blockalign=4k
random_distribution=zoned:50/5:30/15:20/80
filename=/dev/nvme0n1
group_reporting=1
extern int get_fileno(struct thread_data *, const char *);
extern void free_release_files(struct thread_data *);
extern void filesetup_mem_free(void);
-void fio_file_reset(struct thread_data *, struct fio_file *);
-int fio_files_done(struct thread_data *);
+extern void fio_file_reset(struct thread_data *, struct fio_file *);
+extern bool fio_files_done(struct thread_data *);
+extern bool exists_and_not_regfile(const char *);
#endif
#include "flist.h"
#include "hash.h"
#include "filehash.h"
+#include "smalloc.h"
+#include "lib/bloom.h"
#define HASH_BUCKETS 512
#define HASH_MASK (HASH_BUCKETS - 1)
-unsigned int file_hash_size = HASH_BUCKETS * sizeof(struct flist_head);
+#define BLOOM_SIZE 16*1024*1024
+
+static unsigned int file_hash_size = HASH_BUCKETS * sizeof(struct flist_head);
static struct flist_head *file_hash;
static struct fio_mutex *hash_lock;
+static struct bloom *file_bloom;
static unsigned short hash(const char *name)
{
return alias;
}
+bool file_bloom_exists(const char *fname, bool set)
+{
+ return bloom_string(file_bloom, fname, strlen(fname), set);
+}
+
void file_hash_exit(void)
{
unsigned int i, has_entries = 0;
if (has_entries)
log_err("fio: file hash not empty on exit\n");
+ sfree(file_hash);
file_hash = NULL;
fio_mutex_remove(hash_lock);
hash_lock = NULL;
+ bloom_free(file_bloom);
+ file_bloom = NULL;
}
-void file_hash_init(void *ptr)
+void file_hash_init(void)
{
unsigned int i;
- file_hash = ptr;
+ file_hash = smalloc(file_hash_size);
+
for (i = 0; i < HASH_BUCKETS; i++)
INIT_FLIST_HEAD(&file_hash[i]);
hash_lock = fio_mutex_init(FIO_MUTEX_UNLOCKED);
+ file_bloom = bloom_new(BLOOM_SIZE);
}
#ifndef FIO_FILE_HASH_H
#define FIO_FILE_HASH_H
-extern unsigned int file_hash_size;
+#include "lib/types.h"
-extern void file_hash_init(void *);
+extern void file_hash_init(void);
extern void file_hash_exit(void);
extern struct fio_file *lookup_file_hash(const char *);
extern struct fio_file *add_file_hash(struct fio_file *);
extern void remove_file_hash(struct fio_file *);
extern void fio_file_hash_lock(void);
extern void fio_file_hash_unlock(void);
+extern bool file_bloom_exists(const char *, bool);
#endif
return ff;
}
-static int __fio_lock_file(const char *fname, int trylock)
+static bool __fio_lock_file(const char *fname, int trylock)
{
struct fio_filelock *ff;
uint32_t hash;
if (!ff) {
assert(!trylock);
- return 1;
+ return true;
}
if (!trylock) {
fio_mutex_down(&ff->lock);
- return 0;
+ return false;
}
if (!fio_mutex_down_trylock(&ff->lock))
- return 0;
+ return false;
fio_mutex_down(&fld->lock);
if (ff) {
fio_mutex_down(&ff->lock);
- return 0;
+ return false;
}
- return 1;
+ return true;
}
-int fio_trylock_file(const char *fname)
+bool fio_trylock_file(const char *fname)
{
return __fio_lock_file(fname, 1);
}
#ifndef FIO_LOCK_FILE_H
#define FIO_LOCK_FILE_H
+#include "lib/types.h"
+
extern void fio_lock_file(const char *);
-extern int fio_trylock_file(const char *);
+extern bool fio_trylock_file(const char *);
extern void fio_unlock_file(const char *);
extern int fio_filelock_init(void);
if (!fio_option_is_set(&td->o, random_generator)) {
log_info("fio: Switching to tausworthe64. Use the "
"random_generator= option to get rid of this "
- " warning.\n");
+ "warning.\n");
td->o.random_generator = FIO_RAND_GEN_TAUSWORTHE64;
return 0;
}
}
}
-static int __is_already_allocated(const char *fname)
+static bool __is_already_allocated(const char *fname, bool set)
{
struct flist_head *entry;
- char *filename;
+ bool ret;
- if (flist_empty(&filename_list))
- return 0;
+ ret = file_bloom_exists(fname, set);
+ if (!ret)
+ return ret;
flist_for_each(entry, &filename_list) {
- filename = flist_entry(entry, struct file_name, list)->filename;
+ struct file_name *fn;
- if (strcmp(filename, fname) == 0)
- return 1;
+ fn = flist_entry(entry, struct file_name, list);
+
+ if (!strcmp(fn->filename, fname))
+ return true;
}
- return 0;
+ return false;
}
-static int is_already_allocated(const char *fname)
+static bool is_already_allocated(const char *fname)
{
- int ret;
+ bool ret;
fio_file_hash_lock();
- ret = __is_already_allocated(fname);
+ ret = __is_already_allocated(fname, false);
fio_file_hash_unlock();
+
return ret;
}
fn->filename = strdup(fname);
fio_file_hash_lock();
- if (!__is_already_allocated(fname)) {
+ if (!__is_already_allocated(fname, true)) {
flist_add_tail(&fn->list, &filename_list);
fn = NULL;
}
}
}
-
static void free_already_allocated(void)
{
struct flist_head *entry, *tmp;
f = smalloc(sizeof(*f));
if (!f) {
- log_err("fio: smalloc OOM\n");
assert(0);
return NULL;
}
return f;
}
+bool exists_and_not_regfile(const char *filename)
+{
+ struct stat sb;
+
+ if (lstat(filename, &sb) == -1)
+ return false;
+
+#ifndef WIN32 /* NOT Windows */
+ if (S_ISREG(sb.st_mode))
+ return false;
+#else
+ /* \\.\ is the device namespace in Windows, where every file
+ * is a device node */
+ if (S_ISREG(sb.st_mode) && strncmp(filename, "\\\\.\\", 4) != 0)
+ return false;
+#endif
+
+ return true;
+}
+
int add_file(struct thread_data *td, const char *fname, int numjob, int inc)
{
int cur_files = td->files_index;
sprintf(file_name + len, "%s", fname);
/* clean cloned siblings using existing files */
- if (numjob && is_already_allocated(file_name))
+ if (numjob && is_already_allocated(file_name) &&
+ !exists_and_not_regfile(fname))
return 0;
f = alloc_new_file(td);
f->real_file_size = -1ULL;
f->file_name = smalloc_strdup(file_name);
- if (!f->file_name) {
- log_err("fio: smalloc OOM\n");
+ if (!f->file_name)
assert(0);
- }
get_file_type(f);
if (f->file_name) {
__f->file_name = smalloc_strdup(f->file_name);
- if (!__f->file_name) {
- log_err("fio: smalloc OOM\n");
+ if (!__f->file_name)
assert(0);
- }
__f->filetype = f->filetype;
}
lfsr_reset(&f->lfsr, td->rand_seeds[FIO_RAND_BLOCK_OFF]);
}
-int fio_files_done(struct thread_data *td)
+bool fio_files_done(struct thread_data *td)
{
struct fio_file *f;
unsigned int i;
for_each_file(td, f, i)
if (!fio_file_done(f))
- return 0;
+ return false;
- return 1;
+ return true;
}
/* free memory used in initialization phase only */
Limit run time to \fIruntime\fR seconds.
.TP
.B \-\-bandwidth\-log
-Generate per-job bandwidth logs.
+Generate aggregate bandwidth logs.
.TP
.B \-\-minimal
Print statistics in a terse, semicolon-delimited format.
\fBiodepth_batch_complete_max\fR=<iodepth>
.RE
-which means that we will retrieve at leat 1 IO and up to the
+which means that we will retrieve at least 1 IO and up to the
whole submitted queue depth. If none of IO has been completed
yet, we will wait.
decimal or a hex number). The verify_pattern if larger than a 32-bit quantity
has to be a hex number that starts with either "0x" or "0X". Use with
\fBverify\fP=str. Also, verify_pattern supports %o format, which means that for
-each block offset will be written and then verifyied back, e.g.:
+each block offset will be written and then verified back, e.g.:
.RS
.RS
\fBverify_pattern\fR=%o
not set, jobs with identical names will share the log filename. Default: true.
.TP
.BI write_bw_log \fR=\fPstr
-If given, write a bandwidth log of the jobs in this job file. Can be used to
-store data of the bandwidth of the jobs in their lifetime. The included
-fio_generate_plots script uses gnuplot to turn these text files into nice
-graphs. See \fBwrite_lat_log\fR for behaviour of given filename. For this
-option, the postfix is _bw.x.log, where x is the index of the job (1..N,
-where N is the number of jobs). If \fBper_job_logs\fR is false, then the
-filename will not include the job index. See the \fBLOG FILE FORMATS\fR
+If given, write a bandwidth log for this job. Can be used to store data of the
+bandwidth of the jobs in their lifetime. The included fio_generate_plots script
+uses gnuplot to turn these text files into nice graphs. See \fBwrite_lat_log\fR
+for behaviour of given filename. For this option, the postfix is _bw.x.log,
+where x is the index of the job (1..N, where N is the number of jobs). If
+\fBper_job_logs\fR is false, then the filename will not include the job index.
+See the \fBLOG FILE FORMATS\fR
section.
.TP
.BI write_lat_log \fR=\fPstr
decompressed with fio, using the \fB\-\-inflate-log\fR command line parameter.
The files will be stored with a \fB\.fz\fR suffix.
.TP
+.BI log_unix_epoch \fR=\fPbool
+If set, fio will log Unix timestamps to the log files produced by enabling
+\fBwrite_type_log\fR for each log type, instead of the default zero-based
+timestamps.
+.TP
.BI block_error_percentiles \fR=\fPbool
If set, record errors in trim block-sized units from writes and trims and output
a histogram of how many trims it took to get to errors, and what kind of error
The \fIoffset\fR is the offset, in bytes, from the start of the file, for that
particular IO. The logging of the offset can be toggled with \fBlog_offset\fR.
-If windowed logging is enabled though \fBlog_avg_msec\fR, then fio doesn't log
+If windowed logging is enabled through \fBlog_avg_msec\fR, then fio doesn't log
individual IOs. Instead of logs the average values over the specified
period of time. Since \fIdata direction\fR and \fIoffset\fR are per-IO values,
they aren't applicable if windowed logging is enabled. If windowed logging
* Rate state
*/
uint64_t rate_bps[DDIR_RWDIR_CNT];
- unsigned long rate_next_io_time[DDIR_RWDIR_CNT];
+ uint64_t rate_next_io_time[DDIR_RWDIR_CNT];
unsigned long rate_bytes[DDIR_RWDIR_CNT];
unsigned long rate_blocks[DDIR_RWDIR_CNT];
- unsigned long rate_io_issue_bytes[DDIR_RWDIR_CNT];
+ unsigned long long rate_io_issue_bytes[DDIR_RWDIR_CNT];
struct timeval lastrate[DDIR_RWDIR_CNT];
int64_t last_usec;
struct frand_state poisson_state;
struct timeval start; /* start of this loop */
struct timeval epoch; /* time job was started */
+ unsigned long long unix_epoch; /* Time job was started, unix epoch based. */
struct timeval last_issue;
long time_offset;
struct timeval tv_cache;
extern int eta_print;
extern int eta_new_line;
extern unsigned long done_secs;
-extern char *job_section;
extern int fio_gtod_offload;
extern int fio_gtod_cpu;
extern enum fio_cs fio_clock_source;
assert(!(io_u->ddir == DDIR_WRITE && !td_write(td)));
}
-#define REAL_MAX_JOBS 2048
+#define REAL_MAX_JOBS 4096
static inline int should_fsync(struct thread_data *td)
{
extern void add_job_opts(const char **, int);
extern char *num2str(uint64_t, int, int, int, int);
extern int ioengine_load(struct thread_data *);
-extern int parse_dryrun(void);
+extern bool parse_dryrun(void);
extern int fio_running_or_pending_io_threads(void);
extern int fio_set_fd_nonblocking(int, const char *);
extern void sig_show_status(int sig);
static inline enum fio_ioengine_flags td_ioengine_flags(struct thread_data *td)
{
- return (td->flags >> TD_ENG_FLAG_SHIFT) & TD_ENG_FLAG_MASK;
+ return (enum fio_ioengine_flags)
+ ((td->flags >> TD_ENG_FLAG_SHIFT) & TD_ENG_FLAG_MASK);
}
static inline void td_set_ioengine_flags(struct thread_data *td)
td->flags |= (td->io_ops->flags << TD_ENG_FLAG_SHIFT);
}
-static inline bool td_ioengine_flagged(struct thread_data *td, unsigned int val)
+static inline bool td_ioengine_flagged(struct thread_data *td,
+ enum fio_ioengine_flags flags)
{
- return ((td->flags >> TD_ENG_FLAG_SHIFT) & val) != 0;
+ return ((td->flags >> TD_ENG_FLAG_SHIFT) & flags) != 0;
}
extern void td_set_runstate(struct thread_data *, int);
* Allow 60 seconds for a job to quit on its own, otherwise reap with
* a vengeance.
*/
-#define FIO_REAP_TIMEOUT 60
+#define FIO_REAP_TIMEOUT 300
#define TERMINATE_ALL (-1U)
extern void fio_terminate_threads(unsigned int);
} \
} while (0)
-static inline int fio_fill_issue_time(struct thread_data *td)
+static inline bool fio_fill_issue_time(struct thread_data *td)
{
if (td->o.read_iolog_file ||
!td->o.disable_clat || !td->o.disable_slat || !td->o.disable_bw)
- return 1;
+ return true;
- return 0;
+ return false;
}
-static inline int __should_check_rate(struct thread_data *td,
- enum fio_ddir ddir)
+static inline bool __should_check_rate(struct thread_data *td,
+ enum fio_ddir ddir)
{
struct thread_options *o = &td->o;
*/
if (o->rate[ddir] || o->ratemin[ddir] || o->rate_iops[ddir] ||
o->rate_iops_min[ddir])
- return 1;
+ return true;
- return 0;
+ return false;
}
-static inline int should_check_rate(struct thread_data *td)
+static inline bool should_check_rate(struct thread_data *td)
{
- int ret = 0;
-
- if (td->bytes_done[DDIR_READ])
- ret |= __should_check_rate(td, DDIR_READ);
- if (td->bytes_done[DDIR_WRITE])
- ret |= __should_check_rate(td, DDIR_WRITE);
- if (td->bytes_done[DDIR_TRIM])
- ret |= __should_check_rate(td, DDIR_TRIM);
-
- return ret;
+ if (td->bytes_done[DDIR_READ] && __should_check_rate(td, DDIR_READ))
+ return true;
+ if (td->bytes_done[DDIR_WRITE] && __should_check_rate(td, DDIR_WRITE))
+ return true;
+ if (td->bytes_done[DDIR_TRIM] && __should_check_rate(td, DDIR_TRIM))
+ return true;
+
+ return false;
}
static inline unsigned int td_max_bs(struct thread_data *td)
extern bool in_ramp_time(struct thread_data *);
extern void fio_time_init(void);
extern void timeval_add_msec(struct timeval *, unsigned int);
+extern void set_epoch_time(struct thread_data *, int);
#endif
if (!flow) {
flow = smalloc(sizeof(*flow));
if (!flow) {
- log_err("fio: smalloc pool exhausted\n");
fio_mutex_up(flow_lock);
return NULL;
}
gdk_threads_leave();
}
-extern int sum_stat_clients;
-extern struct thread_stat client_ts;
-extern struct group_run_stats client_gs;
-
static int sum_stat_nr;
static void gfio_thread_status_op(struct fio_client *client,
char *minp, *maxp;
char tmp[64];
- if (!usec_to_msec(&min, &max, &mean, &dev))
+ if (usec_to_msec(&min, &max, &mean, &dev))
base = "(msec)";
minp = num2str(min, 6, 1, 0, 0);
uint64_t utime_since(const struct timeval *s, const struct timeval *e)
{
- long sec, usec;
+ int64_t sec, usec;
sec = e->tv_sec - s->tv_sec;
usec = e->tv_usec - s->tv_usec;
static void *server_thread(void *arg)
{
+ fio_server_create_sk_key();
is_backend = 1;
gfio_server_running = 1;
fio_start_server(NULL);
gfio_server_running = 0;
+ fio_server_destroy_sk_key();
return NULL;
}
static int nr_job_sections;
int exitall_on_terminate = 0;
-int exitall_on_terminate_error = 0;
int output_format = FIO_OUTPUT_NORMAL;
int eta_print = FIO_ETA_AUTO;
int eta_new_line = 0;
},
{
.name = (char *) "bandwidth-log",
- .has_arg = required_argument,
+ .has_arg = no_argument,
.val = 'b' | FIO_CLIENT_FLAG,
},
{
static void free_shm(void)
{
if (threads) {
- file_hash_exit();
flow_exit();
fio_debug_jobp = NULL;
free_threads_shm();
free(trigger_remote_cmd);
trigger_file = trigger_cmd = trigger_remote_cmd = NULL;
- options_free(fio_options, &def_thread);
+ options_free(fio_options, &def_thread.o);
fio_filelock_exit();
+ file_hash_exit();
scleanup();
}
*/
static int setup_thread_area(void)
{
- void *hash;
-
if (threads)
return 0;
do {
size_t size = max_jobs * sizeof(struct thread_data);
- size += file_hash_size;
size += sizeof(unsigned int);
#ifndef CONFIG_NO_SHM
#endif
memset(threads, 0, max_jobs * sizeof(struct thread_data));
- hash = (void *) threads + max_jobs * sizeof(struct thread_data);
- fio_debug_jobp = (void *) hash + file_hash_size;
+ fio_debug_jobp = (void *) threads + max_jobs * sizeof(struct thread_data);
*fio_debug_jobp = -1;
- file_hash_init(hash);
flow_init();
return p;
}
-static int exists_and_not_regfile(const char *filename)
-{
- struct stat sb;
-
- if (lstat(filename, &sb) == -1)
- return 0;
-
-#ifndef WIN32 /* NOT Windows */
- if (S_ISREG(sb.st_mode))
- return 0;
-#else
- /* \\.\ is the device namespace in Windows, where every file
- * is a device node */
- if (S_ISREG(sb.st_mode) && strncmp(filename, "\\\\.\\", 4) != 0)
- return 0;
-#endif
-
- return 1;
-}
-
static void init_rand_file_service(struct thread_data *td)
{
unsigned long nranges = td->o.nr_files << FIO_FSERVICE_SHIFT;
return buf;
}
-int parse_dryrun(void)
+bool parse_dryrun(void)
{
return dump_cmdline || parse_only;
}
if (setup_rate(td))
goto err;
- if (o->lat_log_file) {
+ if (o->write_lat_log) {
struct log_params p = {
.td = td,
.avg_msec = o->log_avg_msec,
.log_gz = o->log_gz,
.log_gz_store = o->log_gz_store,
};
+ const char *pre = o->lat_log_file ? o->lat_log_file : o->name;
const char *suf;
if (p.log_gz_store)
else
suf = "log";
- gen_log_name(logname, sizeof(logname), "lat", o->lat_log_file,
+ gen_log_name(logname, sizeof(logname), "lat", pre,
td->thread_number, suf, o->per_job_logs);
setup_log(&td->lat_log, &p, logname);
- gen_log_name(logname, sizeof(logname), "slat", o->lat_log_file,
+ gen_log_name(logname, sizeof(logname), "slat", pre,
td->thread_number, suf, o->per_job_logs);
setup_log(&td->slat_log, &p, logname);
- gen_log_name(logname, sizeof(logname), "clat", o->lat_log_file,
+ gen_log_name(logname, sizeof(logname), "clat", pre,
td->thread_number, suf, o->per_job_logs);
setup_log(&td->clat_log, &p, logname);
}
- if (o->hist_log_file) {
+ if (o->write_hist_log) {
struct log_params p = {
.td = td,
.avg_msec = o->log_avg_msec,
.log_gz = o->log_gz,
.log_gz_store = o->log_gz_store,
};
+ const char *pre = o->hist_log_file ? o->hist_log_file : o->name;
const char *suf;
+#ifndef CONFIG_ZLIB
+ if (td->client_type) {
+ log_err("fio: --write_hist_log requires zlib in client/server mode\n");
+ goto err;
+ }
+#endif
+
if (p.log_gz_store)
suf = "log.fz";
else
suf = "log";
- gen_log_name(logname, sizeof(logname), "clat_hist", o->hist_log_file,
+ gen_log_name(logname, sizeof(logname), "clat_hist", pre,
td->thread_number, suf, o->per_job_logs);
setup_log(&td->clat_hist_log, &p, logname);
}
- if (o->bw_log_file) {
+ if (o->write_bw_log) {
struct log_params p = {
.td = td,
.avg_msec = o->log_avg_msec,
.log_gz = o->log_gz,
.log_gz_store = o->log_gz_store,
};
+ const char *pre = o->bw_log_file ? o->bw_log_file : o->name;
const char *suf;
if (fio_option_is_set(o, bw_avg_time))
else
suf = "log";
- gen_log_name(logname, sizeof(logname), "bw", o->bw_log_file,
+ gen_log_name(logname, sizeof(logname), "bw", pre,
td->thread_number, suf, o->per_job_logs);
setup_log(&td->bw_log, &p, logname);
}
- if (o->iops_log_file) {
+ if (o->write_iops_log) {
struct log_params p = {
.td = td,
.avg_msec = o->log_avg_msec,
.log_gz = o->log_gz,
.log_gz_store = o->log_gz_store,
};
+ const char *pre = o->iops_log_file ? o->iops_log_file : o->name;
const char *suf;
if (fio_option_is_set(o, iops_avg_time))
else
suf = "log";
- gen_log_name(logname, sizeof(logname), "iops", o->iops_log_file,
+ gen_log_name(logname, sizeof(logname), "iops", pre,
td->thread_number, suf, o->per_job_logs);
setup_log(&td->iops_log, &p, logname);
}
/*
* This is our [ini] type file parser.
*/
-int __parse_jobs_ini(struct thread_data *td,
+static 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)
{
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(" --bandwidth-log\tGenerate per-job bandwidth logs\n");
+ printf(" --bandwidth-log\tGenerate aggregate bandwidth logs\n");
printf(" --minimal\t\tMinimal (terse) output\n");
printf(" --output-format=x\tOutput format (terse,json,json+,normal)\n");
printf(" --terse-version=x\tSet terse version output format to 'x'\n");
switch (c) {
case 'a':
smalloc_pool_size = atoi(optarg);
+ smalloc_pool_size <<= 10;
+ sinit();
break;
case 't':
if (check_str_time(optarg, &def_timeout, 1)) {
if (f->last_pos[ddir] < f->real_file_size) {
uint64_t pos;
- if (f->last_pos[ddir] == f->file_offset && o->ddir_seq_add < 0)
- f->last_pos[ddir] = f->real_file_size;
+ if (f->last_pos[ddir] == f->file_offset && o->ddir_seq_add < 0) {
+ if (f->real_file_size > f->io_size)
+ f->last_pos[ddir] = f->io_size;
+ else
+ f->last_pos[ddir] = f->real_file_size;
+ }
pos = f->last_pos[ddir] - f->file_offset;
if (pos && o->ddir_seq_add) {
if (pos >= f->real_file_size) {
if (o->ddir_seq_add > 0)
pos = f->file_offset;
- else
- pos = f->real_file_size + o->ddir_seq_add;
+ else {
+ if (f->real_file_size > f->io_size)
+ pos = f->io_size;
+ else
+ pos = f->real_file_size;
+
+ pos += o->ddir_seq_add;
+ }
}
}
int ddir = io_u->ddir;
unsigned int buflen = 0;
unsigned int minbs, maxbs;
- uint64_t frand_max;
- unsigned long r;
+ uint64_t frand_max, r;
assert(ddir_rw(ddir));
if (buflen < minbs)
buflen = minbs;
} else {
- long perc = 0;
+ long long perc = 0;
unsigned int i;
for (i = 0; i < td->o.bssplit_nr[ddir]; i++) {
buflen = bsp->bs;
perc += bsp->perc;
- if ((r * 100UL <= frand_max * perc) &&
+ if (!perc)
+ break;
+ if ((r / perc <= frand_max / 100ULL) &&
io_u_fits(td, io_u, buflen))
break;
}
completed += ret;
}
+ if (td->flags & TD_F_REGROW_LOGS)
+ regrow_logs(td);
+
return completed;
}
static enum fio_ddir rate_ddir(struct thread_data *td, enum fio_ddir ddir)
{
enum fio_ddir odir = ddir ^ 1;
- long usec, now;
+ long usec;
+ uint64_t now;
assert(ddir_rw(ddir));
now = utime_since_now(&td->start);
get_trim = 1;
}
- if (get_trim && !get_next_trim(td, io_u))
+ if (get_trim && get_next_trim(td, io_u))
return true;
}
td->io_issues[ddir]--;
td->io_issue_bytes[ddir] -= buflen;
td->rate_io_issue_bytes[ddir] -= buflen;
+ io_u_clear(td, io_u, IO_U_F_FLIGHT);
}
/*
if (ret) {
td_verror(td, ret, "fio_set_odirect");
- log_err("fio: the file system does not seem to support direct IO\n");
+ if (ret == ENOTTY) { /* ENOTTY suggests RAW device or ZFS */
+ log_err("fio: doing directIO to RAW devices or ZFS not supported\n");
+ } else {
+ log_err("fio: the file system does not seem to support direct IO\n");
+ }
+
goto err;
}
}
switch (ipo->file_action) {
case FIO_LOG_OPEN_FILE:
+ if (td->o.replay_redirect && fio_file_open(f)) {
+ dprint(FD_FILE, "iolog: ignoring re-open of file %s\n",
+ f->file_name);
+ break;
+ }
ret = td_io_open_file(td, f);
if (!ret)
break;
unsigned long long offset;
unsigned int bytes;
int reads, writes, waits, fileno = 0, file_action = 0; /* stupid gcc */
- char *fname, *act;
+ char *rfname, *fname, *act;
char *str, *p;
enum fio_ddir rw;
* for doing verifications.
*/
str = malloc(4096);
- fname = malloc(256+16);
+ rfname = fname = malloc(256+16);
act = malloc(256+16);
reads = writes = waits = 0;
struct io_piece *ipo;
int r;
- r = sscanf(p, "%256s %256s %llu %u", fname, act, &offset,
+ r = sscanf(p, "%256s %256s %llu %u", rfname, act, &offset,
&bytes);
+
+ if (td->o.replay_redirect)
+ fname = td->o.replay_redirect;
+
if (r == 4) {
/*
* Check action first
} else if (r == 2) {
rw = DDIR_INVAL;
if (!strcmp(act, "add")) {
- fileno = add_file(td, fname, 0, 1);
- file_action = FIO_LOG_ADD_FILE;
+ if (td->o.replay_redirect &&
+ get_fileno(td, fname) != -1) {
+ dprint(FD_FILE, "iolog: ignoring"
+ " re-add of file %s\n", fname);
+ } else {
+ fileno = add_file(td, fname, 0, 1);
+ file_action = FIO_LOG_ADD_FILE;
+ }
continue;
} else if (!strcmp(act, "open")) {
fileno = get_fileno(td, fname);
continue;
writes++;
} else if (rw == DDIR_WAIT) {
+ if (td->o.no_stall)
+ continue;
waits++;
} else if (rw == DDIR_INVAL) {
} else if (!ddir_sync(rw)) {
if (rw == DDIR_WAIT) {
ipo->delay = offset;
} else {
- ipo->offset = offset;
+ if (td->o.replay_scale)
+ ipo->offset = offset / td->o.replay_scale;
+ else
+ ipo->offset = offset;
+ ipo_bytes_align(td->o.replay_align, ipo);
+
ipo->len = bytes;
if (rw != DDIR_INVAL && bytes > td->o.max_bs[rw])
td->o.max_bs[rw] = bytes;
free(str);
free(act);
- free(fname);
+ free(rfname);
if (writes && read_only) {
log_err("fio: <%s> skips replay of %d writes due to"
const char *filename)
{
struct io_log *l;
+ int i;
+ struct io_u_plat_entry *entry;
+ struct flist_head *list;
l = scalloc(1, sizeof(*l));
INIT_FLIST_HEAD(&l->io_logs);
l->filename = strdup(filename);
l->td = p->td;
+ /* Initialize histogram lists for each r/w direction,
+ * with initial io_u_plat of all zeros:
+ */
+ for (i = 0; i < DDIR_RWDIR_CNT; i++) {
+ list = &l->hist_window[i].list;
+ INIT_FLIST_HEAD(list);
+ entry = calloc(1, sizeof(struct io_u_plat_entry));
+ flist_add(&entry->list, list);
+ }
+
if (l->td && l->td->o.io_submit_mode != IO_MODE_OFFLOAD) {
struct io_logs *p;
sfree(log);
}
-static inline unsigned long hist_sum(int j, int stride, unsigned int *io_u_plat)
+inline unsigned long hist_sum(int j, int stride, unsigned int *io_u_plat,
+ unsigned int *io_u_plat_last)
{
unsigned long sum;
int k;
- for (k = sum = 0; k < stride; k++)
- sum += io_u_plat[j + k];
+ if (io_u_plat_last) {
+ for (k = sum = 0; k < stride; k++)
+ sum += io_u_plat[j + k] - io_u_plat_last[j + k];
+ } else {
+ for (k = sum = 0; k < stride; k++)
+ sum += io_u_plat[j + k];
+ }
return sum;
}
-void flush_hist_samples(FILE *f, int hist_coarseness, void *samples,
- uint64_t sample_size)
+static void flush_hist_samples(FILE *f, int hist_coarseness, void *samples,
+ uint64_t sample_size)
{
struct io_sample *s;
int log_offset;
uint64_t i, j, nr_samples;
+ struct io_u_plat_entry *entry, *entry_before;
unsigned int *io_u_plat;
+ unsigned int *io_u_plat_before;
int stride = 1 << hist_coarseness;
for (i = 0; i < nr_samples; i++) {
s = __get_sample(samples, log_offset, i);
- io_u_plat = (unsigned int *) (uintptr_t) s->val;
- fprintf(f, "%lu, %u, %u, ", (unsigned long)s->time,
- io_sample_ddir(s), s->bs);
+
+ entry = s->data.plat_entry;
+ io_u_plat = entry->io_u_plat;
+
+ entry_before = flist_first_entry(&entry->list, struct io_u_plat_entry, list);
+ io_u_plat_before = entry_before->io_u_plat;
+
+ fprintf(f, "%lu, %u, %u, ", (unsigned long) s->time,
+ io_sample_ddir(s), s->bs);
for (j = 0; j < FIO_IO_U_PLAT_NR - stride; j += stride) {
- fprintf(f, "%lu, ", hist_sum(j, stride, io_u_plat));
+ fprintf(f, "%lu, ", hist_sum(j, stride, io_u_plat,
+ io_u_plat_before));
}
- fprintf(f, "%lu\n", (unsigned long)
- hist_sum(FIO_IO_U_PLAT_NR - stride, stride, io_u_plat));
- free(io_u_plat);
+ fprintf(f, "%lu\n", (unsigned long)
+ hist_sum(FIO_IO_U_PLAT_NR - stride, stride, io_u_plat,
+ io_u_plat_before));
+
+ flist_del(&entry_before->list);
+ free(entry_before);
}
}
s = __get_sample(samples, log_offset, i);
if (!log_offset) {
- fprintf(f, "%lu, %lu, %u, %u\n",
+ fprintf(f, "%lu, %" PRId64 ", %u, %u\n",
(unsigned long) s->time,
- (unsigned long) s->val,
+ s->data.val,
io_sample_ddir(s), s->bs);
} else {
struct io_sample_offset *so = (void *) s;
- fprintf(f, "%lu, %lu, %u, %u, %llu\n",
+ fprintf(f, "%lu, %" PRId64 ", %u, %u, %llu\n",
(unsigned long) s->time,
- (unsigned long) s->val,
+ s->data.val,
io_sample_ddir(s), s->bs,
(unsigned long long) so->offset);
}
cur_log = flist_first_entry(&log->io_logs, struct io_logs, list);
flist_del_init(&cur_log->list);
- if (log == log->td->clat_hist_log)
+ if (log->td && log == log->td->clat_hist_log)
flush_hist_samples(f, log->hist_coarseness, cur_log->log,
- cur_log->nr_samples * log_entry_sz(log));
+ log_sample_sz(log, cur_log));
else
- flush_samples(f, cur_log->log, cur_log->nr_samples * log_entry_sz(log));
+ flush_samples(f, cur_log->log, log_sample_sz(log, cur_log));
sfree(cur_log);
}
data->log->filename);
do {
if (c)
- dprint(FD_COMPRESS, "seq=%d, chunk=%lu\n", seq, c->len);
+ dprint(FD_COMPRESS, "seq=%d, chunk=%lu\n", seq,
+ (unsigned long) c->len);
c = get_new_chunk(seq);
stream.avail_out = GZ_CHUNK;
stream.next_out = c->buf;
total -= c->len;
c->len = GZ_CHUNK - stream.avail_out;
total += c->len;
- dprint(FD_COMPRESS, "seq=%d, chunk=%lu\n", seq, c->len);
+ dprint(FD_COMPRESS, "seq=%d, chunk=%lu\n", seq, (unsigned long) c->len);
if (ret != Z_STREAM_END) {
do {
c->len = GZ_CHUNK - stream.avail_out;
total += c->len;
flist_add_tail(&c->list, &list);
- dprint(FD_COMPRESS, "seq=%d, chunk=%lu\n", seq, c->len);
+ dprint(FD_COMPRESS, "seq=%d, chunk=%lu\n", seq,
+ (unsigned long) c->len);
} while (ret != Z_STREAM_END);
}
struct io_hist {
uint64_t samples;
unsigned long hist_last;
+ struct flist_head list;
+};
+
+
+union io_sample_data {
+ uint64_t val;
+ struct io_u_plat_entry *plat_entry;
};
+#define sample_val(value) ((union io_sample_data) { .val = value })
+#define sample_plat(plat) ((union io_sample_data) { .plat_entry = plat })
+
/*
* A single data sample
*/
struct io_sample {
uint64_t time;
- uint64_t val;
+ union io_sample_data data;
uint32_t __ddir;
uint32_t bs;
};
unsigned long avg_msec;
unsigned long avg_last;
- /*
- * Windowed latency histograms, for keeping track of when we need to
- * save a copy of the histogram every approximately hist_msec milliseconds.
- */
+ /*
+ * Windowed latency histograms, for keeping track of when we need to
+ * save a copy of the histogram every approximately hist_msec
+ * milliseconds.
+ */
struct io_hist hist_window[DDIR_RWDIR_CNT];
unsigned long hist_msec;
- int hist_coarseness;
+ unsigned int hist_coarseness;
pthread_mutex_t chunk_lock;
unsigned int chunk_seq;
return __log_entry_sz(log->log_offset);
}
+static inline size_t log_sample_sz(struct io_log *log, struct io_logs *cur_log)
+{
+ return cur_log->nr_samples * log_entry_sz(log);
+}
+
static inline struct io_sample *__get_sample(void *samples, int log_offset,
uint64_t sample)
{
log->log_type == IO_LOG_TYPE_SLAT;
}
+static inline void ipo_bytes_align(unsigned int replay_align, struct io_piece *ipo)
+{
+ if (replay_align)
+ return;
+
+ ipo->offset &= ~(replay_align - (uint64_t)1);
+}
+
extern void finalize_logs(struct thread_data *td, bool);
extern void setup_log(struct io_log **, struct log_params *, const char *);
extern void flush_log(struct io_log *, bool);
extern void flush_samples(FILE *, void *, uint64_t);
+extern unsigned long hist_sum(int, int, unsigned int *, unsigned int *);
extern void free_log(struct io_log *);
extern void fio_writeout_logs(bool);
extern void td_writeout_logs(struct thread_data *, bool);
#define BLOOM_SEED 0x8989
-struct bloom_hash hashes[] = {
+static struct bloom_hash hashes[] = {
{
.seed = BLOOM_SEED,
.fn = jhash,
#define N_HASHES 5
-#define MIN_ENTRIES 1073741824UL
-
struct bloom *bloom_new(uint64_t entries)
{
struct bloom *b;
b = malloc(sizeof(*b));
b->nentries = entries;
no_uints = (entries + BITS_PER_INDEX - 1) / BITS_PER_INDEX;
- no_uints = max((unsigned long) no_uints, MIN_ENTRIES);
b->map = calloc(no_uints, sizeof(uint32_t));
if (!b->map) {
free(b);
free(b);
}
-static int __bloom_check(struct bloom *b, uint32_t *data, unsigned int nwords,
- int set)
+static bool __bloom_check(struct bloom *b, const void *data, unsigned int len,
+ bool set)
{
uint32_t hash[N_HASHES];
int i, was_set;
for (i = 0; i < N_HASHES; i++) {
- hash[i] = hashes[i].fn(data, nwords, hashes[i].seed);
+ hash[i] = hashes[i].fn(data, len, hashes[i].seed);
hash[i] = hash[i] % b->nentries;
}
return was_set == N_HASHES;
}
-int bloom_set(struct bloom *b, uint32_t *data, unsigned int nwords)
+bool bloom_set(struct bloom *b, uint32_t *data, unsigned int nwords)
+{
+ return __bloom_check(b, data, nwords * sizeof(uint32_t), true);
+}
+
+bool bloom_string(struct bloom *b, const char *data, unsigned int len,
+ bool set)
{
- return __bloom_check(b, data, nwords, 1);
+ return __bloom_check(b, data, len, set);
}
#define FIO_BLOOM_H
#include <inttypes.h>
+#include "../lib/types.h"
struct bloom;
struct bloom *bloom_new(uint64_t entries);
void bloom_free(struct bloom *b);
-int bloom_set(struct bloom *b, uint32_t *data, unsigned int nwords);
+bool bloom_set(struct bloom *b, uint32_t *data, unsigned int nwords);
+bool bloom_string(struct bloom *b, const char *data, unsigned int len, bool);
#endif
#ifdef CONFIG_GETMNTENT
#include <mntent.h>
+#include "lib/mountcheck.h"
+
#define MTAB "/etc/mtab"
int device_is_mounted(const char *dev)
}
#elif defined(CONFIG_GETMNTINFO)
-/* for BSDs */
+/* for most BSDs */
#include <sys/param.h>
#include <sys/mount.h>
return 0;
}
+#elif defined(CONFIG_GETMNTINFO_STATVFS)
+/* for NetBSD */
+#include <sys/statvfs.h>
+
+int device_is_mounted(const char *dev)
+{
+ struct statvfs *st;
+ int i, ret;
+
+ ret = getmntinfo(&st, MNT_NOWAIT);
+ if (ret <= 0)
+ return 0;
+
+ for (i = 0; i < ret; i++) {
+ if (!strcmp(st[i].f_mntfromname, dev))
+ return 1;
+ }
+
+ return 0;
+}
+
#else
/* others */
#include <stdlib.h>
#include <limits.h>
+#include "lib/strntol.h"
+
long strntol(const char *str, size_t sz, char **end, int base)
{
/* Expect that digit representation of LONG_MAX/MIN
#include "os/os.h"
#include "filelock.h"
#include "helper_thread.h"
+#include "filehash.h"
/*
* Just expose an empty list, if the OS does not support disk util stats
void reset_all_stats(struct thread_data *td)
{
- struct timeval tv;
int i;
reset_io_counters(td, 1);
td->rwmix_issues = 0;
}
- fio_gettime(&tv, NULL);
- memcpy(&td->epoch, &tv, sizeof(tv));
- memcpy(&td->start, &tv, sizeof(tv));
- memcpy(&td->iops_sample_time, &tv, sizeof(tv));
- memcpy(&td->bw_sample_time, &tv, sizeof(tv));
- memcpy(&td->ss.prev_time, &tv, sizeof(tv));
+ set_epoch_time(td, td->o.log_unix_epoch);
+ memcpy(&td->start, &td->epoch, sizeof(struct timeval));
+ memcpy(&td->iops_sample_time, &td->epoch, sizeof(struct timeval));
+ memcpy(&td->bw_sample_time, &td->epoch, sizeof(struct timeval));
+ memcpy(&td->ss.prev_time, &td->epoch, sizeof(struct timeval));
lat_target_reset(td);
clear_rusage_stat(td);
return 1;
}
+ file_hash_init();
+
/*
* We need locale for number printing, if it isn't set then just
* go with the US format.
{
assert(mutex->magic == FIO_MUTEX_MAGIC);
pthread_cond_destroy(&mutex->cond);
+
+ /*
+ * Ensure any subsequent attempt to grab this mutex will fail
+ * with an assert, instead of just silently hanging.
+ */
+ memset(mutex, 0, sizeof(*mutex));
}
void fio_mutex_remove(struct fio_mutex *mutex)
t.tv_nsec = tv_s.tv_usec * 1000;
t.tv_sec += msecs / 1000;
- t.tv_nsec += ((msecs * 1000000) % 1000000000);
+ t.tv_nsec += ((msecs * 1000000ULL) % 1000000000);
if (t.tv_nsec >= 1000000000) {
t.tv_nsec -= 1000000000;
t.tv_sec++;
#define cb_data_to_td(data) container_of(data, struct thread_data, o)
-struct pattern_fmt_desc fmt_desc[] = {
+static struct pattern_fmt_desc fmt_desc[] = {
{
.fmt = "%o",
.len = FIELD_SIZE(struct io_u *, offset),
return 0;
}
+static int str_write_bw_log_cb(void *data, const char *str)
+{
+ struct thread_data *td = cb_data_to_td(data);
+
+ if (str)
+ td->o.bw_log_file = strdup(str);
+
+ td->o.write_bw_log = 1;
+ return 0;
+}
+
+static int str_write_lat_log_cb(void *data, const char *str)
+{
+ struct thread_data *td = cb_data_to_td(data);
+
+ if (str)
+ td->o.lat_log_file = strdup(str);
+
+ td->o.write_lat_log = 1;
+ return 0;
+}
+
+static int str_write_iops_log_cb(void *data, const char *str)
+{
+ struct thread_data *td = cb_data_to_td(data);
+
+ if (str)
+ td->o.iops_log_file = strdup(str);
+
+ td->o.write_iops_log = 1;
+ return 0;
+}
+
+static int str_write_hist_log_cb(void *data, const char *str)
+{
+ struct thread_data *td = cb_data_to_td(data);
+
+ if (str)
+ td->o.hist_log_file = strdup(str);
+
+ td->o.write_hist_log = 1;
+ return 0;
+}
+
static int rw_verify(struct fio_option *o, void *data)
{
struct thread_data *td = cb_data_to_td(data);
},
{ .ival = "gauss",
.oval = FIO_FSERVICE_GAUSS,
- .help = "Normal (guassian) distribution",
+ .help = "Normal (gaussian) distribution",
},
{ .ival = "roundrobin",
.oval = FIO_FSERVICE_RR,
{
.name = "write_bw_log",
.lname = "Write bandwidth log",
- .type = FIO_OPT_STR_STORE,
+ .type = FIO_OPT_STR,
.off1 = offsetof(struct thread_options, bw_log_file),
+ .cb = str_write_bw_log_cb,
.help = "Write log of bandwidth during run",
.category = FIO_OPT_C_LOG,
.group = FIO_OPT_G_INVALID,
{
.name = "write_lat_log",
.lname = "Write latency log",
- .type = FIO_OPT_STR_STORE,
+ .type = FIO_OPT_STR,
.off1 = offsetof(struct thread_options, lat_log_file),
+ .cb = str_write_lat_log_cb,
.help = "Write log of latency during run",
.category = FIO_OPT_C_LOG,
.group = FIO_OPT_G_INVALID,
{
.name = "write_iops_log",
.lname = "Write IOPS log",
- .type = FIO_OPT_STR_STORE,
+ .type = FIO_OPT_STR,
.off1 = offsetof(struct thread_options, iops_log_file),
+ .cb = str_write_iops_log_cb,
.help = "Write log of IOPS during run",
.category = FIO_OPT_C_LOG,
.group = FIO_OPT_G_INVALID,
{
.name = "write_hist_log",
.lname = "Write latency histogram logs",
- .type = FIO_OPT_STR_STORE,
+ .type = FIO_OPT_STR,
.off1 = offsetof(struct thread_options, hist_log_file),
+ .cb = str_write_hist_log_cb,
.help = "Write log of latency histograms during run",
.category = FIO_OPT_C_LOG,
.group = FIO_OPT_G_INVALID,
.help = "Install libz-dev(el) to get compression support",
},
#endif
+ {
+ .name = "log_unix_epoch",
+ .lname = "Log epoch unix",
+ .type = FIO_OPT_BOOL,
+ .off1 = offsetof(struct thread_options, log_unix_epoch),
+ .help = "Use Unix time in log files",
+ .category = FIO_OPT_C_LOG,
+ .group = FIO_OPT_G_INVALID,
+ },
{
.name = "block_error_percentiles",
.lname = "Block error percentiles",
},
{
.name = "disable_bw_measurement",
+ .alias = "disable_bw",
.lname = "Disable bandwidth stats",
.type = FIO_OPT_BOOL,
.off1 = offsetof(struct thread_options, disable_bw),
void fio_options_free(struct thread_data *td)
{
- options_free(fio_options, td);
+ options_free(fio_options, &td->o);
if (td->eo && td->io_ops && td->io_ops->options) {
options_free(td->io_ops->options, td->eo);
free(td->eo);
void del_opt_posval(const char *, const char *);
struct thread_data;
void fio_options_free(struct thread_data *);
-char *get_name_idx(char *, int);
int set_name_idx(char *, size_t, char *, int, bool);
extern char client_sockaddr_str[]; /* used with --client option */
typedef off_t off64_t;
+#ifndef CONFIG_CLOCKID_T
typedef unsigned int clockid_t;
+#endif
#define FIO_OS_DIRECTIO
static inline int fio_set_odirect(int fd)
#include <errno.h>
#include <lwp.h>
#include <sys/param.h>
+#include <sys/statvfs.h>
+#include <sys/ioctl.h>
+#include <sys/dkio.h>
+#include <sys/disklabel.h>
/* XXX hack to avoid confilcts between rbtree.h and <sys/rb.h> */
#define rb_node _rb_node
#include <sys/sysctl.h>
#include "../file.h"
#define FIO_HAVE_ODIRECT
-#define FIO_USE_GENERIC_BDEV_SIZE
#define FIO_USE_GENERIC_RAND
#define FIO_USE_GENERIC_INIT_RANDOM_STATE
+#define FIO_HAVE_FS_STAT
#define FIO_HAVE_GETTID
#undef FIO_HAVE_CPU_AFFINITY /* XXX notyet */
typedef off_t off64_t;
+static inline int blockdev_size(struct fio_file *f, unsigned long long *bytes)
+{
+ struct disklabel dl;
+
+ if (!ioctl(f->fd, DIOCGDINFO, &dl)) {
+ *bytes = ((unsigned long long)dl.d_secperunit) * dl.d_secsize;
+ return 0;
+ }
+
+ *bytes = 0;
+ return errno;
+}
+
static inline int blockdev_invalidate_cache(struct fio_file *f)
{
return EINVAL;
return (int) _lwp_self();
}
+static inline unsigned long long get_fs_free_size(const char *path)
+{
+ unsigned long long ret;
+ struct statvfs s;
+
+ if (statvfs(path, &s) < 0)
+ return -1ULL;
+
+ ret = s.f_frsize;
+ ret *= (unsigned long long) s.f_bfree;
+ return ret;
+}
+
#ifdef MADV_FREE
#define FIO_MADV_FREE MADV_FREE
#endif
-/* XXX NetBSD doesn't have getopt_long_only */
-#define getopt_long_only getopt_long
-
#endif
#include <errno.h>
#include <sys/param.h>
+#include <sys/statvfs.h>
+#include <sys/ioctl.h>
+#include <sys/dkio.h>
+#include <sys/disklabel.h>
/* XXX hack to avoid conflicts between rbtree.h and <sys/tree.h> */
#include <sys/sysctl.h>
#undef RB_BLACK
#include "../file.h"
#undef FIO_HAVE_ODIRECT
-#define FIO_USE_GENERIC_BDEV_SIZE
#define FIO_USE_GENERIC_RAND
#define FIO_USE_GENERIC_INIT_RANDOM_STATE
+#define FIO_HAVE_FS_STAT
#define FIO_HAVE_GETTID
#undef FIO_HAVE_CPU_AFFINITY /* XXX notyet */
typedef off_t off64_t;
+static inline int blockdev_size(struct fio_file *f, unsigned long long *bytes)
+{
+ struct disklabel dl;
+
+ if (!ioctl(f->fd, DIOCGDINFO, &dl)) {
+ *bytes = ((unsigned long long)dl.d_secperunit) * dl.d_secsize;
+ return 0;
+ }
+
+ *bytes = 0;
+ return errno;
+}
+
static inline int blockdev_invalidate_cache(struct fio_file *f)
{
return EINVAL;
return (int) pthread_self();
}
+static inline unsigned long long get_fs_free_size(const char *path)
+{
+ unsigned long long ret;
+ struct statvfs s;
+
+ if (statvfs(path, &s) < 0)
+ return -1ULL;
+
+ ret = s.f_frsize;
+ ret *= (unsigned long long) s.f_bfree;
+ return ret;
+}
+
#ifdef MADV_FREE
#define FIO_MADV_FREE MADV_FREE
#endif
#endif
#ifndef FIO_MAX_JOBS
-#define FIO_MAX_JOBS 2048
+#define FIO_MAX_JOBS 4096
#endif
#ifndef CONFIG_SOCKLEN_T
<Product Id="*"
Codepage="1252" Language="1033"
Manufacturer="fio" Name="fio"
- UpgradeCode="2338A332-5511-43CF-B9BD-5C60496CCFCC" Version="2.13">
+ UpgradeCode="2338A332-5511-43CF-B9BD-5C60496CCFCC" Version="2.15">
<Package
Description="Flexible IO Tester"
InstallerVersion="301" Keywords="Installer,MSI,Database"
/* Copied from http://blogs.msdn.com/b/joshpoley/archive/2007/12/19/date-time-formats-and-conversions.aspx */
void Time_tToSystemTime(time_t dosTime, SYSTEMTIME *systemTime)
{
- LARGE_INTEGER jan1970FT;
- LARGE_INTEGER utcFT;
- jan1970FT.QuadPart = 116444736000000000LL; // january 1st 1970
- utcFT.QuadPart = ((unsigned __int64)dosTime) * 10000000 + jan1970FT.QuadPart;
+ FILETIME utcFT;
+ LONGLONG jan1970;
+
+ jan1970 = Int32x32To64(dosTime, 10000000) + 116444736000000000;
+ utcFT.dwLowDateTime = (DWORD)jan1970;
+ utcFT.dwHighDateTime = jan1970 >> 32;
FileTimeToSystemTime((FILETIME*)&utcFT, systemTime);
}
Time_tToSystemTime(*t, &systime);
/* We don't know how long `buf` is, but assume it's rounded up from the minimum of 25 to 32 */
- StringCchPrintfA(buf, 31, "%s %s %d %02d:%02d:%02d %04d", dayOfWeek[systime.wDayOfWeek % 7], monthOfYear[(systime.wMonth - 1) % 12],
+ StringCchPrintfA(buf, 31, "%s %s %d %02d:%02d:%02d %04d\n", dayOfWeek[systime.wDayOfWeek % 7], monthOfYear[(systime.wMonth - 1) % 12],
systime.wDay, systime.wHour, systime.wMinute, systime.wSecond, systime.wYear);
return buf;
}
int nice(int incr)
{
- if (incr != 0) {
- errno = EINVAL;
- return -1;
- }
+ DWORD prioclass = NORMAL_PRIORITY_CLASS;
+
+ if (incr < -15)
+ prioclass = HIGH_PRIORITY_CLASS;
+ else if (incr < 0)
+ prioclass = ABOVE_NORMAL_PRIORITY_CLASS;
+ else if (incr > 15)
+ prioclass = IDLE_PRIORITY_CLASS;
+ else if (incr > 0)
+ prioclass = BELOW_NORMAL_PRIORITY_CLASS;
+
+ if (!SetPriorityClass(GetCurrentProcess(), prioclass))
+ log_err("fio: SetPriorityClass failed\n");
return 0;
}
len = mtd->oob_size - start;
memcpy(oob + start, tmp_buf + start, len);
}
+ free(tmp_buf);
return 0;
}
return 0;
}
-int do_oob_op(libmtd_t desc, const struct mtd_dev_info *mtd, int fd,
+static int do_oob_op(libmtd_t desc, const struct mtd_dev_info *mtd, int fd,
uint64_t start, uint64_t length, void *data, unsigned int cmd64,
unsigned int cmd)
{
#include <unistd.h>
#include "../os/os.h"
+#include "oslib/linux-dev-lookup.h"
int blktrace_lookup_device(const char *redirect, char *path, unsigned int maj,
unsigned int min)
#include <string.h>
+#include "oslib/strlcat.h"
size_t strlcat(char *dst, const char *src, size_t size)
{
handle_option(o, o->def, data);
}
-void option_init(struct fio_option *o)
+static void option_init(struct fio_option *o)
{
if (o->type == FIO_OPT_DEPRECATED || o->type == FIO_OPT_UNSUPPORTED)
return;
int pow2; /* must be a power-of-2 */
};
-typedef int (str_cb_fn)(void *, char *);
-
extern int parse_option(char *, const char *, struct fio_option *, struct fio_option **, void *, struct flist_head *);
extern void sort_options(char **, struct fio_option *, int);
extern int parse_cmd_option(const char *t, const char *l, struct fio_option *, void *, struct flist_head *);
extern int show_cmd_help(struct fio_option *, const char *);
extern void fill_default_options(void *, struct fio_option *);
-extern void option_init(struct fio_option *);
extern void options_init(struct fio_option *);
extern void options_free(struct fio_option *, void *);
typedef int (fio_opt_str_fn)(void *, const char *);
typedef int (fio_opt_str_val_fn)(void *, long long *);
typedef int (fio_opt_int_fn)(void *, int *);
-typedef int (fio_opt_str_set_fn)(void *);
-
-#define __td_var(start, offset) ((char *) start + (offset))
struct thread_options;
static inline void *td_var(struct thread_options *to, struct fio_option *o,
unsigned int offset)
{
+ void *ret;
+
if (o->prof_opts)
- return __td_var(o->prof_opts, offset);
+ ret = o->prof_opts;
+ else
+ ret = to;
- return __td_var(to, offset);
+ return (char *) ret + offset;
}
static inline int parse_is_percent(unsigned long long val)
if (td_io_init(td))
goto err_io_init;
- fio_gettime(&td->epoch, NULL);
+ set_epoch_time(td, td->o.log_unix_epoch);
fio_getrusage(&td->ru_start);
clear_io_state(td, 1);
struct sk_entry *entry;
entry = fio_net_prep_cmd(opcode, buf, size, tagptr, flags);
- fio_net_queue_entry(entry);
- return 0;
+ if (entry) {
+ fio_net_queue_entry(entry);
+ return 0;
+ }
+
+ return 1;
}
static int fio_net_send_simple_stack_cmd(int sk, uint16_t opcode, uint64_t tag)
{
dprint(FD_NET, "server: sending quit\n");
- return fio_net_queue_cmd(FIO_NET_CMD_QUIT, NULL, 0, 0, SK_F_SIMPLE);
+ return fio_net_queue_cmd(FIO_NET_CMD_QUIT, NULL, 0, NULL, SK_F_SIMPLE);
}
int fio_net_send_quit(int sk)
je->files_open = cpu_to_le32(je->files_open);
for (i = 0; i < DDIR_RWDIR_CNT; i++) {
- je->m_rate[i] = cpu_to_le32(je->m_rate[i]);
- je->t_rate[i] = cpu_to_le32(je->t_rate[i]);
+ je->m_rate[i] = cpu_to_le64(je->m_rate[i]);
+ je->t_rate[i] = cpu_to_le64(je->t_rate[i]);
je->m_iops[i] = cpu_to_le32(je->m_iops[i]);
je->t_iops[i] = cpu_to_le32(je->t_iops[i]);
- je->rate[i] = cpu_to_le32(je->rate[i]);
+ je->rate[i] = cpu_to_le64(je->rate[i]);
je->iops[i] = cpu_to_le32(je->iops[i]);
}
p.ts.latency_window = cpu_to_le64(ts->latency_window);
p.ts.latency_percentile.u.i = cpu_to_le64(fio_double_to_uint64(ts->latency_percentile.u.f));
- p.ts.nr_block_infos = le64_to_cpu(ts->nr_block_infos);
+ p.ts.nr_block_infos = cpu_to_le64(ts->nr_block_infos);
for (i = 0; i < p.ts.nr_block_infos; i++)
- p.ts.block_infos[i] = le32_to_cpu(ts->block_infos[i]);
+ p.ts.block_infos[i] = cpu_to_le32(ts->block_infos[i]);
p.ts.ss_dur = cpu_to_le64(ts->ss_dur);
p.ts.ss_state = cpu_to_le32(ts->ss_state);
}
#ifdef CONFIG_ZLIB
+
+static inline void __fio_net_prep_tail(z_stream *stream, void *out_pdu,
+ struct sk_entry **last_entry,
+ struct sk_entry *first)
+{
+ unsigned int this_len = FIO_SERVER_MAX_FRAGMENT_PDU - stream->avail_out;
+
+ *last_entry = fio_net_prep_cmd(FIO_NET_CMD_IOLOG, out_pdu, this_len,
+ NULL, SK_F_VEC | SK_F_INLINE | SK_F_FREE);
+ flist_add_tail(&(*last_entry)->list, &first->next);
+
+}
+
+/*
+ * Deflates the next input given, creating as many new packets in the
+ * linked list as necessary.
+ */
+static int __deflate_pdu_buffer(void *next_in, unsigned int next_sz, void **out_pdu,
+ struct sk_entry **last_entry, z_stream *stream,
+ struct sk_entry *first)
+{
+ int ret;
+
+ stream->next_in = next_in;
+ stream->avail_in = next_sz;
+ do {
+ if (! stream->avail_out) {
+
+ __fio_net_prep_tail(stream, *out_pdu, last_entry, first);
+
+ *out_pdu = malloc(FIO_SERVER_MAX_FRAGMENT_PDU);
+
+ stream->avail_out = FIO_SERVER_MAX_FRAGMENT_PDU;
+ stream->next_out = *out_pdu;
+ }
+
+ ret = deflate(stream, Z_BLOCK);
+
+ if (ret < 0) {
+ free(*out_pdu);
+ return 1;
+ }
+ } while (stream->avail_in);
+
+ return 0;
+}
+
+static int __fio_append_iolog_gz_hist(struct sk_entry *first, struct io_log *log,
+ struct io_logs *cur_log, z_stream *stream)
+{
+ struct sk_entry *entry;
+ void *out_pdu;
+ int ret, i, j;
+ int sample_sz = log_entry_sz(log);
+
+ out_pdu = malloc(FIO_SERVER_MAX_FRAGMENT_PDU);
+ stream->avail_out = FIO_SERVER_MAX_FRAGMENT_PDU;
+ stream->next_out = out_pdu;
+
+ for (i = 0; i < cur_log->nr_samples; i++) {
+ struct io_sample *s;
+ struct io_u_plat_entry *cur_plat_entry, *prev_plat_entry;
+ unsigned int *cur_plat, *prev_plat;
+
+ s = get_sample(log, cur_log, i);
+ ret = __deflate_pdu_buffer(s, sample_sz, &out_pdu, &entry, stream, first);
+ if (ret)
+ return ret;
+
+ /* Do the subtraction on server side so that client doesn't have to
+ * reconstruct our linked list from packets.
+ */
+ cur_plat_entry = s->data.plat_entry;
+ prev_plat_entry = flist_first_entry(&cur_plat_entry->list, struct io_u_plat_entry, list);
+ cur_plat = cur_plat_entry->io_u_plat;
+ prev_plat = prev_plat_entry->io_u_plat;
+
+ for (j = 0; j < FIO_IO_U_PLAT_NR; j++) {
+ cur_plat[j] -= prev_plat[j];
+ }
+
+ flist_del(&prev_plat_entry->list);
+ free(prev_plat_entry);
+
+ ret = __deflate_pdu_buffer(cur_plat_entry, sizeof(*cur_plat_entry),
+ &out_pdu, &entry, stream, first);
+
+ if (ret)
+ return ret;
+ }
+
+ __fio_net_prep_tail(stream, out_pdu, &entry, first);
+
+ return 0;
+}
+
static int __fio_append_iolog_gz(struct sk_entry *first, struct io_log *log,
struct io_logs *cur_log, z_stream *stream)
{
void *out_pdu;
int ret;
+ if (log->log_type == IO_LOG_TYPE_HIST)
+ return __fio_append_iolog_gz_hist(first, log, cur_log, stream);
+
stream->next_in = (void *) cur_log->log;
stream->avail_in = cur_log->nr_samples * log_entry_sz(log);
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);
struct io_sample *s = get_sample(log, cur_log, i);
s->time = cpu_to_le64(s->time);
- s->val = cpu_to_le64(s->val);
+ s->data.val = cpu_to_le64(s->data.val);
s->__ddir = cpu_to_le32(s->__ddir);
s->bs = cpu_to_le32(s->bs);
assert(sk_out->sk != -1);
- fio_net_queue_cmd(FIO_NET_CMD_SERVER_START, NULL, 0, 0, SK_F_SIMPLE);
+ fio_net_queue_cmd(FIO_NET_CMD_SERVER_START, NULL, 0, NULL, SK_F_SIMPLE);
}
int fio_server_get_verify_state(const char *name, int threadnumber,
dprint(FD_NET, "server: request verify state\n");
rep = smalloc(sizeof(*rep));
- if (!rep) {
- log_err("fio: smalloc pool too small\n");
+ if (!rep)
return ENOMEM;
- }
__fio_mutex_init(&rep->lock, FIO_MUTEX_LOCKED);
rep->data = NULL;
};
enum {
- FIO_SERVER_VER = 57,
+ FIO_SERVER_VER = 60,
FIO_SERVER_MAX_FRAGMENT_PDU = 1024,
FIO_SERVER_MAX_CMD_MB = 2048,
uint32_t log_type;
uint32_t compressed;
uint32_t log_offset;
+ uint32_t log_hist_coarseness;
uint8_t name[FIO_NET_NAME_MAX];
struct io_sample samples[0];
};
#define SMALLOC_BPL (SMALLOC_BPB * SMALLOC_BPI)
#define INITIAL_SIZE 16*1024*1024 /* new pool size */
-#define MAX_POOLS 8 /* maximum number of pools to setup */
+#define INITIAL_POOLS 8 /* maximum number of pools to setup */
+
+#define MAX_POOLS 16
#define SMALLOC_PRE_RED 0xdeadbeefU
#define SMALLOC_POST_RED 0x5aa55aa5U
return ffz(word) + start;
}
-static int add_pool(struct pool *pool, unsigned int alloc_size)
+static bool add_pool(struct pool *pool, unsigned int alloc_size)
{
int bitmap_blocks;
int mmap_flags;
void *ptr;
+ if (nr_pools == MAX_POOLS)
+ return false;
+
#ifdef SMALLOC_REDZONE
alloc_size += sizeof(unsigned int);
#endif
goto out_fail;
nr_pools++;
- return 0;
+ return true;
out_fail:
log_err("smalloc: failed adding pool\n");
if (pool->map)
munmap(pool->map, pool->mmap_size);
- return 1;
+ return false;
}
void sinit(void)
{
- int i, ret;
+ bool ret;
+ int i;
- for (i = 0; i < MAX_POOLS; i++) {
- ret = add_pool(&mp[i], smalloc_pool_size);
- if (ret)
+ for (i = 0; i < INITIAL_POOLS; i++) {
+ ret = add_pool(&mp[nr_pools], smalloc_pool_size);
+ if (!ret)
break;
}
break;
} while (1);
+ log_err("smalloc: OOM. Consider using --alloc-size to increase the "
+ "shared memory available.\n");
return NULL;
}
free(ovals);
}
-int calc_lat(struct io_stat *is, unsigned long *min, unsigned long *max,
- double *mean, double *dev)
+bool calc_lat(struct io_stat *is, unsigned long *min, unsigned long *max,
+ double *mean, double *dev)
{
double n = (double) is->samples;
if (n == 0)
- return 0;
+ return false;
*min = is->min_val;
*max = is->max_val;
else
*dev = 0;
- return 1;
+ return true;
}
void show_group_stats(struct group_run_stats *rs, struct buf_output *out)
const char *base = "(usec)";
char *minp, *maxp;
- if (!usec_to_msec(&min, &max, &mean, &dev))
+ if (usec_to_msec(&min, &max, &mean, &dev))
base = "(msec)";
minp = num2str(min, 6, 1, 0, 0);
if (!ddir_rw_sum(ts->io_bytes) && !ddir_rw_sum(ts->total_io_u))
return;
+
+ memset(time_buf, 0, sizeof(time_buf));
time(&time_p);
os_ctime_r((const time_t *) &time_p, time_buf, sizeof(time_buf));
log_buf(out, "\n");
}
-void json_add_job_opts(struct json_object *root, const char *name,
- struct flist_head *opt_list, bool num_jobs)
+static void json_add_job_opts(struct json_object *root, const char *name,
+ struct flist_head *opt_list, bool num_jobs)
{
struct json_object *dir_object;
struct flist_head *entry;
os_ctime_r((const time_t *) &now.tv_sec, time_buf,
sizeof(time_buf));
- time_buf[strlen(time_buf) - 1] = '\0';
+ if (time_buf[strlen(time_buf) - 1] == '\n')
+ time_buf[strlen(time_buf) - 1] = '\0';
root = json_create_object();
json_object_add_value_string(root, "fio version", fio_version_string);
return iolog->pending;
}
-static void __add_log_sample(struct io_log *iolog, unsigned long val,
+static void __add_log_sample(struct io_log *iolog, union io_sample_data data,
enum fio_ddir ddir, unsigned int bs,
unsigned long t, uint64_t offset)
{
s = get_sample(iolog, cur_log, cur_log->nr_samples);
- s->val = val;
- s->time = t;
+ s->data = data;
+ s->time = t + (iolog->td ? iolog->td->unix_epoch : 0);
io_sample_set_ddir(iolog, s, ddir);
s->bs = bs;
* had actual samples done.
*/
if (iolog->avg_window[ddir].samples) {
- unsigned long val;
+ union io_sample_data data;
if (log_max)
- val = iolog->avg_window[ddir].max_val;
+ data.val = iolog->avg_window[ddir].max_val;
else
- val = iolog->avg_window[ddir].mean.u.f + 0.50;
+ data.val = iolog->avg_window[ddir].mean.u.f + 0.50;
- __add_log_sample(iolog, val, ddir, 0, elapsed, 0);
+ __add_log_sample(iolog, data, ddir, 0, elapsed, 0);
}
reset_io_stat(&iolog->avg_window[ddir]);
}
static long add_log_sample(struct thread_data *td, struct io_log *iolog,
- unsigned long val, enum fio_ddir ddir,
+ union io_sample_data data, enum fio_ddir ddir,
unsigned int bs, uint64_t offset)
{
unsigned long elapsed, this_window;
* If no time averaging, just add the log sample.
*/
if (!iolog->avg_msec) {
- __add_log_sample(iolog, val, ddir, bs, elapsed, offset);
+ __add_log_sample(iolog, data, ddir, bs, elapsed, offset);
return 0;
}
* Add the sample. If the time period has passed, then
* add that entry to the log and clear.
*/
- add_stat_sample(&iolog->avg_window[ddir], val);
+ add_stat_sample(&iolog->avg_window[ddir], data.val);
/*
* If period hasn't passed, adding the above sample is all we
_add_stat_to_log(td->iops_log, elapsed, td->o.log_max != 0);
}
-void add_agg_sample(unsigned long val, enum fio_ddir ddir, unsigned int bs)
+void add_agg_sample(union io_sample_data data, enum fio_ddir ddir, unsigned int bs)
{
struct io_log *iolog;
return;
iolog = agg_io_log[ddir];
- __add_log_sample(iolog, val, ddir, bs, mtime_since_genesis(), 0);
+ __add_log_sample(iolog, data, ddir, bs, mtime_since_genesis(), 0);
}
static void add_clat_percentile_sample(struct thread_stat *ts,
add_stat_sample(&ts->clat_stat[ddir], usec);
if (td->clat_log)
- add_log_sample(td, td->clat_log, usec, ddir, bs, offset);
+ add_log_sample(td, td->clat_log, sample_val(usec), ddir, bs,
+ offset);
if (ts->clat_percentiles)
add_clat_percentile_sample(ts, usec, ddir);
if (this_window >= iolog->hist_msec) {
unsigned int *io_u_plat;
- unsigned int *dst;
+ struct io_u_plat_entry *dst;
/*
* Make a byte-for-byte copy of the latency histogram
* log file.
*/
io_u_plat = (unsigned int *) td->ts.io_u_plat[ddir];
- dst = malloc(FIO_IO_U_PLAT_NR * sizeof(unsigned int));
- memcpy(dst, io_u_plat,
+ dst = malloc(sizeof(struct io_u_plat_entry));
+ memcpy(&(dst->io_u_plat), io_u_plat,
FIO_IO_U_PLAT_NR * sizeof(unsigned int));
- __add_log_sample(iolog, (unsigned long )dst, ddir, bs,
+ flist_add(&dst->list, &hw->list);
+ __add_log_sample(iolog, sample_plat(dst), ddir, bs,
elapsed, offset);
/*
add_stat_sample(&ts->slat_stat[ddir], usec);
if (td->slat_log)
- add_log_sample(td, td->slat_log, usec, ddir, bs, offset);
+ add_log_sample(td, td->slat_log, sample_val(usec), ddir, bs, offset);
td_io_u_unlock(td);
}
add_stat_sample(&ts->lat_stat[ddir], usec);
if (td->lat_log)
- add_log_sample(td, td->lat_log, usec, ddir, bs, offset);
+ add_log_sample(td, td->lat_log, sample_val(usec), ddir, bs,
+ offset);
td_io_u_unlock(td);
}
add_stat_sample(&ts->bw_stat[io_u->ddir], rate);
if (td->bw_log)
- add_log_sample(td, td->bw_log, rate, io_u->ddir, bytes, io_u->offset);
+ add_log_sample(td, td->bw_log, sample_val(rate), io_u->ddir,
+ bytes, io_u->offset);
td->stat_io_bytes[io_u->ddir] = td->this_io_bytes[io_u->ddir];
td_io_u_unlock(td);
if (td->o.min_bs[ddir] == td->o.max_bs[ddir])
bs = td->o.min_bs[ddir];
- next = add_log_sample(td, td->bw_log, rate, ddir, bs, 0);
+ next = add_log_sample(td, td->bw_log, sample_val(rate),
+ ddir, bs, 0);
next_log = min(next_log, next);
}
add_stat_sample(&ts->iops_stat[io_u->ddir], 1);
if (td->iops_log)
- add_log_sample(td, td->iops_log, 1, io_u->ddir, bytes, io_u->offset);
+ add_log_sample(td, td->iops_log, sample_val(1), io_u->ddir,
+ bytes, io_u->offset);
td->stat_io_blocks[io_u->ddir] = td->this_io_blocks[io_u->ddir];
td_io_u_unlock(td);
if (td->o.min_bs[ddir] == td->o.max_bs[ddir])
bs = td->o.min_bs[ddir];
- next = add_log_sample(td, td->iops_log, iops, ddir, bs, 0);
+ next = add_log_sample(td, td->iops_log,
+ sample_val(iops), ddir, bs, 0);
next_log = min(next_log, next);
}
next = min(td->o.iops_avg_time, td->o.bw_avg_time);
continue;
}
- if (!per_unit_log(td->bw_log)) {
+ if (td->bw_log && !per_unit_log(td->bw_log)) {
tmp = add_bw_samples(td, &now);
if (tmp < next)
next = tmp;
}
- if (!per_unit_log(td->iops_log)) {
+ if (td->iops_log && !per_unit_log(td->iops_log)) {
tmp = add_iops_samples(td, &now);
if (tmp < next)
next = tmp;
#define BLOCK_INFO_STATE(block_info) \
((block_info) >> BLOCK_INFO_STATE_SHIFT)
#define BLOCK_INFO(state, trim_cycles) \
- ((trim_cycles) | ((state) << BLOCK_INFO_STATE_SHIFT))
+ ((trim_cycles) | ((unsigned int) (state) << BLOCK_INFO_STATE_SHIFT))
#define BLOCK_INFO_SET_STATE(block_info, state) \
BLOCK_INFO(state, BLOCK_INFO_TRIMS(block_info))
enum block_info_state {
uint32_t files_open;
- uint32_t m_rate[DDIR_RWDIR_CNT], t_rate[DDIR_RWDIR_CNT];
+ uint64_t m_rate[DDIR_RWDIR_CNT], t_rate[DDIR_RWDIR_CNT];
uint32_t m_iops[DDIR_RWDIR_CNT], t_iops[DDIR_RWDIR_CNT];
- uint32_t rate[DDIR_RWDIR_CNT];
+ uint64_t rate[DDIR_RWDIR_CNT];
uint32_t iops[DDIR_RWDIR_CNT];
uint64_t elapsed_sec;
uint64_t eta_sec;
uint8_t run_str[];
} __attribute__((packed));
+struct io_u_plat_entry {
+ struct flist_head list;
+ unsigned int io_u_plat[FIO_IO_U_PLAT_NR];
+};
+
extern struct fio_mutex *stat_mutex;
extern struct jobs_eta *get_jobs_eta(bool force, size_t *size);
extern struct json_object * show_thread_status(struct thread_stat *ts, struct group_run_stats *rs, struct flist_head *, struct buf_output *);
extern void show_group_stats(struct group_run_stats *rs, struct buf_output *);
-extern int calc_thread_status(struct jobs_eta *je, int force);
+extern bool calc_thread_status(struct jobs_eta *je, int force);
extern void display_thread_status(struct jobs_eta *je);
extern void show_run_stats(void);
extern void __show_run_stats(void);
extern void init_thread_stat(struct thread_stat *ts);
extern void init_group_run_stat(struct group_run_stats *gs);
extern void eta_to_str(char *str, unsigned long eta_sec);
-extern int calc_lat(struct io_stat *is, unsigned long *min, unsigned long *max, double *mean, double *dev);
+extern bool calc_lat(struct io_stat *is, unsigned long *min, unsigned long *max, double *mean, double *dev);
extern unsigned int calc_clat_percentiles(unsigned int *io_u_plat, unsigned long nr, fio_fp64_t *plist, unsigned int **output, unsigned int *maxv, unsigned int *minv);
extern void stat_calc_lat_m(struct thread_stat *ts, double *io_u_lat);
extern void stat_calc_lat_u(struct thread_stat *ts, double *io_u_lat);
unsigned int, uint64_t);
extern void add_slat_sample(struct thread_data *, enum fio_ddir, unsigned long,
unsigned int, uint64_t);
-extern void add_agg_sample(unsigned long, enum fio_ddir, unsigned int);
+extern void add_agg_sample(union io_sample_data, enum fio_ddir, unsigned int);
extern void add_iops_sample(struct thread_data *, struct io_u *,
unsigned int);
extern void add_bw_sample(struct thread_data *, struct io_u *,
extern struct io_log *agg_io_log[DDIR_RWDIR_CNT];
extern int write_bw_log;
-static inline int usec_to_msec(unsigned long *min, unsigned long *max,
- double *mean, double *dev)
+static inline bool usec_to_msec(unsigned long *min, unsigned long *max,
+ double *mean, double *dev)
{
if (*min > 1000 && *max > 1000 && *mean > 1000.0 && *dev > 1000.0) {
*min /= 1000;
*max /= 1000;
*mean /= 1000.0;
*dev /= 1000.0;
- return 0;
+ return true;
}
- return 1;
+ return false;
}
/*
* Worst level condensing would be 1:5, so allow enough room for that
unsigned int log_offset;
unsigned int log_gz;
unsigned int log_gz_store;
+ unsigned int log_unix_epoch;
unsigned int norandommap;
unsigned int softrandommap;
unsigned int bs_unaligned;
char *read_iolog_file;
char *write_iolog_file;
+
+ unsigned int write_bw_log;
+ unsigned int write_lat_log;
+ unsigned int write_iops_log;
+ unsigned int write_hist_log;
+
char *bw_log_file;
char *lat_log_file;
char *iops_log_file;
char *exec_prerun;
char *exec_postrun;
- unsigned int rate[DDIR_RWDIR_CNT];
- unsigned int ratemin[DDIR_RWDIR_CNT];
+ uint64_t rate[DDIR_RWDIR_CNT];
+ uint64_t ratemin[DDIR_RWDIR_CNT];
unsigned int ratecycle;
unsigned int io_submit_mode;
unsigned int rate_iops[DDIR_RWDIR_CNT];
uint32_t log_offset;
uint32_t log_gz;
uint32_t log_gz_store;
+ uint32_t log_unix_epoch;
uint32_t norandommap;
uint32_t softrandommap;
uint32_t bs_unaligned;
uint32_t fsync_on_close;
uint32_t bs_is_seq_rand;
+ uint32_t pad1;
uint32_t random_distribution;
uint32_t exitall_error;
uint8_t read_iolog_file[FIO_TOP_STR_MAX];
uint8_t write_iolog_file[FIO_TOP_STR_MAX];
+
+ uint32_t write_bw_log;
+ uint32_t write_lat_log;
+ uint32_t write_iops_log;
+ uint32_t write_hist_log;
+
uint8_t bw_log_file[FIO_TOP_STR_MAX];
uint8_t lat_log_file[FIO_TOP_STR_MAX];
uint8_t iops_log_file[FIO_TOP_STR_MAX];
uint8_t exec_prerun[FIO_TOP_STR_MAX];
uint8_t exec_postrun[FIO_TOP_STR_MAX];
- uint32_t rate[DDIR_RWDIR_CNT];
- uint32_t ratemin[DDIR_RWDIR_CNT];
+ uint64_t rate[DDIR_RWDIR_CNT];
+ uint64_t ratemin[DDIR_RWDIR_CNT];
uint32_t ratecycle;
uint32_t io_submit_mode;
uint32_t rate_iops[DDIR_RWDIR_CNT];
fio_gettime(&genesis, NULL);
}
+void set_epoch_time(struct thread_data *td, int log_unix_epoch)
+{
+ fio_gettime(&td->epoch, NULL);
+ if (log_unix_epoch) {
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ td->unix_epoch = (unsigned long long)(tv.tv_sec) * 1000 +
+ (unsigned long long)(tv.tv_usec) / 1000;
+ }
+}
+
void fill_start_time(struct timeval *t)
{
memcpy(t, &genesis, sizeof(genesis));
--- /dev/null
+[Unit]
+
+Description=flexible I/O tester server
+After=network.target
+
+[Service]
+
+Type=simple
+PIDFile=/run/fio.pid
+ExecStart=/usr/bin/fio --server
4000, 39, 1152, 1546.962, 1545.785, 1627.192, 1640.019, 1691.204, 1744
...
- Notes:
-
- * end-times are calculated to be uniform increments of the --interval value given,
- regardless of when histogram samples are reported. Of note:
-
- * Intervals with no samples are omitted. In the example above this means
- "no statistics from 2 to 3 seconds" and "39 samples influenced the statistics
- of the interval from 3 to 4 seconds".
-
- * Intervals with a single sample will have the same value for all statistics
-
- * The number of samples is unweighted, corresponding to the total number of samples
- which have any effect whatsoever on the interval.
-
- * Min statistics are computed using value of the lower boundary of the first bin
- (in increasing bin order) with non-zero samples in it. Similarly for max,
- we take the upper boundary of the last bin with non-zero samples in it.
- This is semantically identical to taking the 0th and 100th percentiles with a
- 50% bin-width buffer (because percentiles are computed using mid-points of
- the bins). This enforces the following nice properties:
-
- * min <= 50th <= 90th <= 95th <= 99th <= max
-
- * min and max are strict lower and upper bounds on the actual
- min / max seen by fio (and reported in *_clat.* with averaging turned off).
-
- * Average statistics use a standard weighted arithmetic mean.
-
- * Percentile statistics are computed using the weighted percentile method as
- described here: https://en.wikipedia.org/wiki/Percentile#Weighted_percentile
- See weights() method for details on how weights are computed for individual
- samples. In process_interval() we further multiply by the height of each bin
- to get weighted histograms.
-
- * We convert files given on the command line, assumed to be fio histogram files,
- on-the-fly into their corresponding differenced files i.e. non-cumulative histograms
- because fio outputs cumulative histograms, but we want histograms corresponding
- to individual time intervals. An individual histogram file can contain the cumulative
- histograms for multiple different r/w directions (notably when --rw=randrw). This
- is accounted for by tracking each r/w direction separately. In the statistics
- reported we ultimately merge *all* histograms (regardless of r/w direction).
-
- * The value of *_GROUP_NR in stat.h (and *_BITS) determines how many latency bins
- fio outputs when histogramming is enabled. Namely for the current default of
- GROUP_NR=19, we get 1,216 bins with a maximum latency of approximately 17
- seconds. For certain applications this may not be sufficient. With GROUP_NR=24
- we have 1,536 bins, giving us a maximum latency of 541 seconds (~ 9 minutes). If
- you expect your application to experience latencies greater than 17 seconds,
- you will need to recompile fio with a larger GROUP_NR, e.g. with:
-
- sed -i.bak 's/^#define FIO_IO_U_PLAT_GROUP_NR 19\n/#define FIO_IO_U_PLAT_GROUP_NR 24/g' stat.h
- make fio
-
- Quick reference table for the max latency corresponding to a sampling of
- values for GROUP_NR:
-
- GROUP_NR | # bins | max latency bin value
- 19 | 1216 | 16.9 sec
- 20 | 1280 | 33.8 sec
- 21 | 1344 | 67.6 sec
- 22 | 1408 | 2 min, 15 sec
- 23 | 1472 | 4 min, 32 sec
- 24 | 1536 | 9 min, 4 sec
- 25 | 1600 | 18 min, 8 sec
- 26 | 1664 | 36 min, 16 sec
-
- * At present this program automatically detects the number of histogram bins in
- the log files, and adjusts the bin latency values accordingly. In particular if
- you use the --log_hist_coarseness parameter of fio, you get output files with
- a number of bins according to the following table (note that the first
- row is identical to the table above):
-
- coarse \ GROUP_NR
- 19 20 21 22 23 24 25 26
- -------------------------------------------------------
- 0 [[ 1216, 1280, 1344, 1408, 1472, 1536, 1600, 1664],
- 1 [ 608, 640, 672, 704, 736, 768, 800, 832],
- 2 [ 304, 320, 336, 352, 368, 384, 400, 416],
- 3 [ 152, 160, 168, 176, 184, 192, 200, 208],
- 4 [ 76, 80, 84, 88, 92, 96, 100, 104],
- 5 [ 38, 40, 42, 44, 46, 48, 50, 52],
- 6 [ 19, 20, 21, 22, 23, 24, 25, 26],
- 7 [ N/A, 10, N/A, 11, N/A, 12, N/A, 13],
- 8 [ N/A, 5, N/A, N/A, N/A, 6, N/A, N/A]]
-
- For other values of GROUP_NR and coarseness, this table can be computed like this:
-
- bins = [1216,1280,1344,1408,1472,1536,1600,1664]
- max_coarse = 8
- fncn = lambda z: list(map(lambda x: z/2**x if z % 2**x == 0 else nan, range(max_coarse + 1)))
- np.transpose(list(map(fncn, bins)))
-
- Also note that you can achieve the same downsampling / log file size reduction
- by pre-processing (before inputting into this script) with half_bins.py.
-
- * If you have not adjusted GROUP_NR for your (high latency) application, then you
- will see the percentiles computed by this tool max out at the max latency bin
- value as in the first table above, and in this plot (where GROUP_NR=19 and thus we see
- a max latency of ~16.7 seconds in the red line):
-
- https://www.cronburg.com/fio/max_latency_bin_value_bug.png
-
- * Motivation for, design decisions, and the implementation process are
- described in further detail here:
-
- https://www.cronburg.com/fio/cloud-latency-problem-measurement/
-
@author Karl Cronburg <karl.cronburg@gmail.com>
"""
import os
__NON_HIST_COLUMNS = 3
__TOTAL_COLUMNS = __HIST_COLUMNS + __NON_HIST_COLUMNS
-def sequential_diffs(head_row, times, rws, hists):
- """ Take the difference of sequential (in time) histograms with the same
- r/w direction, returning a new array of differenced histograms. """
- result = np.empty(shape=(0, __HIST_COLUMNS))
- result_times = np.empty(shape=(1, 0))
- for i in range(8):
- idx = np.where(rws == i)
- diff = np.diff(np.append(head_row[i], hists[idx], axis=0), axis=0).astype(int)
- result = np.append(diff, result, axis=0)
- result_times = np.append(times[idx], result_times)
- idx = np.argsort(result_times)
- return result[idx]
-
-def read_chunk(head_row, rdr, sz):
- """ Read the next chunk of size sz from the given reader, computing the
- differences across neighboring histogram samples.
- """
+def read_chunk(rdr, sz):
+ """ Read the next chunk of size sz from the given reader. """
try:
""" StopIteration occurs when the pandas reader is empty, and AttributeError
occurs if rdr is None due to the file being empty. """
except (StopIteration, AttributeError):
return None
- """ Extract array of just the times, and histograms matrix without times column.
- Then, take the sequential difference of each of the rows in the histogram
- matrix. This is necessary because fio outputs *cumulative* histograms as
- opposed to histograms with counts just for a particular interval. """
+ """ Extract array of just the times, and histograms matrix without times column. """
times, rws, szs = new_arr[:,0], new_arr[:,1], new_arr[:,2]
hists = new_arr[:,__NON_HIST_COLUMNS:]
- hists_diff = sequential_diffs(head_row, times, rws, hists)
times = times.reshape((len(times),1))
- arr = np.append(times, hists_diff, axis=1)
+ arr = np.append(times, hists, axis=1)
- """ hists[-1] will be the row we need to start our differencing with the
- next time we call read_chunk() on the same rdr """
- return arr, hists[-1]
+ return arr
def get_min(fps, arrs):
""" Find the file with the current first row with the smallest start time """
- return min([fp for fp in fps if not arrs[fp] is None], key=lambda fp: arrs.get(fp)[0][0][0])
+ return min([fp for fp in fps if not arrs[fp] is None], key=lambda fp: arrs.get(fp)[0][0])
def histogram_generator(ctx, fps, sz):
- """ head_row for a particular file keeps track of the last (cumulative)
- histogram we read so that we have a reference point to subtract off
- when computing sequential differences. """
- head_row = np.zeros(shape=(1, __HIST_COLUMNS))
- head_rows = {fp: {i: head_row for i in range(8)} for fp in fps}
-
# Create a chunked pandas reader for each of the files:
rdrs = {}
for fp in fps:
rdrs[fp] = pandas.read_csv(fp, dtype=int, header=None, chunksize=sz)
except ValueError as e:
if e.message == 'No columns to parse from file':
- if not ctx.nowarn: sys.stderr.write("WARNING: Empty input file encountered.\n")
+ if ctx.warn: sys.stderr.write("WARNING: Empty input file encountered.\n")
rdrs[fp] = None
else:
raise(e)
- # Initial histograms and corresponding head_rows:
- arrs = {fp: read_chunk(head_rows[fp], rdr, sz) for fp,rdr in rdrs.items()}
+ # Initial histograms from disk:
+ arrs = {fp: read_chunk(rdr, sz) for fp,rdr in rdrs.items()}
while True:
try:
fp = get_min(fps, arrs)
except ValueError:
return
- arr, head_row = arrs[fp]
+ arr = arrs[fp]
yield np.insert(arr[0], 1, fps.index(fp))
- arrs[fp] = arr[1:], head_row
- head_rows[fp] = head_row
+ arrs[fp] = arr[1:]
- if arrs[fp][0].shape[0] == 0:
- arrs[fp] = read_chunk(head_rows[fp], rdrs[fp], sz)
+ if arrs[fp].shape[0] == 0:
+ arrs[fp] = read_chunk(rdrs[fp], sz)
def _plat_idx_to_val(idx, edge=0.5, FIO_IO_U_PLAT_BITS=6, FIO_IO_U_PLAT_VAL=64):
""" Taken from fio's stat.c for calculating the latency value of a bin
def main(ctx):
+ if ctx.job_file:
+ try:
+ from configparser import SafeConfigParser, NoOptionError
+ except ImportError:
+ from ConfigParser import SafeConfigParser, NoOptionError
+
+ cp = SafeConfigParser(allow_no_value=True)
+ with open(ctx.job_file, 'r') as fp:
+ cp.readfp(fp)
+
+ if ctx.interval is None:
+ # Auto detect --interval value
+ for s in cp.sections():
+ try:
+ hist_msec = cp.get(s, 'log_hist_msec')
+ if hist_msec is not None:
+ ctx.interval = int(hist_msec)
+ except NoOptionError:
+ pass
+
+ if ctx.interval is None:
+ ctx.interval = 1000
+
# Automatically detect how many columns are in the input files,
# calculate the corresponding 'coarseness' parameter used to generate
# those files, and calculate the appropriate bin latency values:
arr = arr.astype(int)
if arr.size > 0:
+ # Jump immediately to the start of the input, rounding
+ # down to the nearest multiple of the interval (useful when --log_unix_epoch
+ # was used to create these histograms):
+ if start == 0 and arr[0][0] - ctx.max_latency > end:
+ start = arr[0][0] - ctx.max_latency
+ start = start - (start % ctx.interval)
+ end = start + ctx.interval
+
process_interval(ctx, arr, start, end)
# Update arr to throw away samples we no longer need - samples which
help='number of seconds of data to process at a time')
arg('-i', '--interval',
- default=1000,
type=int,
- help='interval width (ms)')
+ help='interval width (ms), default 1000 ms')
arg('-d', '--divisor',
required=False,
type=int,
help='number of decimal places to print floats to')
- arg('--nowarn',
- dest='nowarn',
- action='store_false',
- default=True,
- help='do not print any warning messages to stderr')
+ arg('--warn',
+ dest='warn',
+ action='store_true',
+ default=False,
+ help='print warning messages to stderr')
arg('--group_nr',
default=19,
type=int,
help='FIO_IO_U_PLAT_GROUP_NR as defined in stat.h')
+ arg('--job-file',
+ default=None,
+ type=str,
+ help='Optional argument pointing to the job file used to create the '
+ 'given histogram files. Useful for auto-detecting --log_hist_msec and '
+ '--log_unix_epoch (in fio) values.')
+
main(p.parse_args())
--- /dev/null
+.TH fiologparser_hist.py 1 "August 18, 2016"
+.SH NAME
+fiologparser_hist.py \- Calculate statistics from fio histograms
+.SH SYNOPSIS
+.B fiologparser_hist.py
+[\fIoptions\fR] [clat_hist_files]...
+.SH DESCRIPTION
+.B fiologparser_hist.py
+is a utility for converting *_clat_hist* files
+generated by fio into a CSV of latency statistics including minimum,
+average, maximum latency, and 50th, 95th, and 99th percentiles.
+.SH EXAMPLES
+.PP
+.nf
+$ fiologparser_hist.py *_clat_hist*
+end-time, samples, min, avg, median, 90%, 95%, 99%, max
+1000, 15, 192, 1678.107, 1788.859, 1856.076, 1880.040, 1899.208, 1888.000
+2000, 43, 152, 1642.368, 1714.099, 1816.659, 1845.552, 1888.131, 1888.000
+4000, 39, 1152, 1546.962, 1545.785, 1627.192, 1640.019, 1691.204, 1744
+...
+.fi
+.PP
+
+.SH OPTIONS
+.TP
+.BR \-\-help
+Print these options.
+.TP
+.BR \-\-buff_size \fR=\fPint
+Number of samples to buffer into numpy at a time. Default is 10,000.
+This can be adjusted to help performance.
+.TP
+.BR \-\-max_latency \fR=\fPint
+Number of seconds of data to process at a time. Defaults to 20 seconds,
+in order to handle the 17 second upper bound on latency in histograms
+reported by fio. This should be increased if fio has been
+run with a larger maximum latency. Lowering this when a lower maximum
+latency is known can improve performance. See NOTES for more details.
+.TP
+.BR \-i ", " \-\-interval \fR=\fPint
+Interval at which statistics are reported. Defaults to 1000 ms. This
+should be set a minimum of the value for \fBlog_hist_msec\fR as given
+to fio.
+.TP
+.BR \-d ", " \-\-divisor \fR=\fPint
+Divide statistics by this value. Defaults to 1. Useful if you want to
+convert latencies from milliseconds to seconds (\fBdivisor\fR=\fP1000\fR).
+.TP
+.BR \-\-warn
+Enables warning messages printed to stderr, useful for debugging.
+.TP
+.BR \-\-group_nr \fR=\fPint
+Set this to the value of \fIFIO_IO_U_PLAT_GROUP_NR\fR as defined in
+\fPstat.h\fR if fio has been recompiled. Defaults to 19, the
+current value used in fio. See NOTES for more details.
+
+.SH NOTES
+end-times are calculated to be uniform increments of the \fB\-\-interval\fR value given,
+regardless of when histogram samples are reported. Of note:
+
+.RS
+Intervals with no samples are omitted. In the example above this means
+"no statistics from 2 to 3 seconds" and "39 samples influenced the statistics
+of the interval from 3 to 4 seconds".
+.LP
+Intervals with a single sample will have the same value for all statistics
+.RE
+
+.PP
+The number of samples is unweighted, corresponding to the total number of samples
+which have any effect whatsoever on the interval.
+
+Min statistics are computed using value of the lower boundary of the first bin
+(in increasing bin order) with non-zero samples in it. Similarly for max,
+we take the upper boundary of the last bin with non-zero samples in it.
+This is semantically identical to taking the 0th and 100th percentiles with a
+50% bin-width buffer (because percentiles are computed using mid-points of
+the bins). This enforces the following nice properties:
+
+.RS
+min <= 50th <= 90th <= 95th <= 99th <= max
+.LP
+min and max are strict lower and upper bounds on the actual
+min / max seen by fio (and reported in *_clat.* with averaging turned off).
+.RE
+
+.PP
+Average statistics use a standard weighted arithmetic mean.
+
+Percentile statistics are computed using the weighted percentile method as
+described here: \fIhttps://en.wikipedia.org/wiki/Percentile#Weighted_percentile\fR.
+See weights() method for details on how weights are computed for individual
+samples. In process_interval() we further multiply by the height of each bin
+to get weighted histograms.
+
+We convert files given on the command line, assumed to be fio histogram files,
+An individual histogram file can contain the
+histograms for multiple different r/w directions (notably when \fB\-\-rw\fR=\fPrandrw\fR). This
+is accounted for by tracking each r/w direction separately. In the statistics
+reported we ultimately merge *all* histograms (regardless of r/w direction).
+
+The value of *_GROUP_NR in \fIstat.h\fR (and *_BITS) determines how many latency bins
+fio outputs when histogramming is enabled. Namely for the current default of
+GROUP_NR=19, we get 1,216 bins with a maximum latency of approximately 17
+seconds. For certain applications this may not be sufficient. With GROUP_NR=24
+we have 1,536 bins, giving us a maximum latency of 541 seconds (~ 9 minutes). If
+you expect your application to experience latencies greater than 17 seconds,
+you will need to recompile fio with a larger GROUP_NR, e.g. with:
+
+.RS
+.PP
+.nf
+sed -i.bak 's/^#define FIO_IO_U_PLAT_GROUP_NR 19\n/#define FIO_IO_U_PLAT_GROUP_NR 24/g' stat.h
+make fio
+.fi
+.PP
+.RE
+
+.PP
+Quick reference table for the max latency corresponding to a sampling of
+values for GROUP_NR:
+
+.RS
+.PP
+.nf
+GROUP_NR | # bins | max latency bin value
+19 | 1216 | 16.9 sec
+20 | 1280 | 33.8 sec
+21 | 1344 | 67.6 sec
+22 | 1408 | 2 min, 15 sec
+23 | 1472 | 4 min, 32 sec
+24 | 1536 | 9 min, 4 sec
+25 | 1600 | 18 min, 8 sec
+26 | 1664 | 36 min, 16 sec
+.fi
+.PP
+.RE
+
+.PP
+At present this program automatically detects the number of histogram bins in
+the log files, and adjusts the bin latency values accordingly. In particular if
+you use the \fB\-\-log_hist_coarseness\fR parameter of fio, you get output files with
+a number of bins according to the following table (note that the first
+row is identical to the table above):
+
+.RS
+.PP
+.nf
+coarse \\ GROUP_NR
+ 19 20 21 22 23 24 25 26
+ -------------------------------------------------------
+ 0 [[ 1216, 1280, 1344, 1408, 1472, 1536, 1600, 1664],
+ 1 [ 608, 640, 672, 704, 736, 768, 800, 832],
+ 2 [ 304, 320, 336, 352, 368, 384, 400, 416],
+ 3 [ 152, 160, 168, 176, 184, 192, 200, 208],
+ 4 [ 76, 80, 84, 88, 92, 96, 100, 104],
+ 5 [ 38, 40, 42, 44, 46, 48, 50, 52],
+ 6 [ 19, 20, 21, 22, 23, 24, 25, 26],
+ 7 [ N/A, 10, N/A, 11, N/A, 12, N/A, 13],
+ 8 [ N/A, 5, N/A, N/A, N/A, 6, N/A, N/A]]
+.fi
+.PP
+.RE
+
+.PP
+For other values of GROUP_NR and coarseness, this table can be computed like this:
+
+.RS
+.PP
+.nf
+bins = [1216,1280,1344,1408,1472,1536,1600,1664]
+max_coarse = 8
+fncn = lambda z: list(map(lambda x: z/2**x if z % 2**x == 0 else nan, range(max_coarse + 1)))
+np.transpose(list(map(fncn, bins)))
+.fi
+.PP
+.RE
+
+.PP
+If you have not adjusted GROUP_NR for your (high latency) application, then you
+will see the percentiles computed by this tool max out at the max latency bin
+value as in the first table above, and in this plot (where GROUP_NR=19 and thus we see
+a max latency of ~16.7 seconds in the red line):
+
+.RS
+\fIhttps://www.cronburg.com/fio/max_latency_bin_value_bug.png
+.RE
+
+.PP
+Motivation for, design decisions, and the implementation process are
+described in further detail here:
+
+.RS
+\fIhttps://www.cronburg.com/fio/cloud-latency-problem-measurement/
+.RE
+
+.SH AUTHOR
+.B fiologparser_hist.py
+and this manual page were written by Karl Cronburg <karl.cronburg@gmail.com>.
+.SH "REPORTING BUGS"
+Report bugs to the \fBfio\fR mailing list <fio@vger.kernel.org>.
#include "trim.h"
#ifdef FIO_HAVE_TRIM
-int get_next_trim(struct thread_data *td, struct io_u *io_u)
+bool get_next_trim(struct thread_data *td, struct io_u *io_u)
{
struct io_piece *ipo;
* this io_u is from a requeue, we already filled the offsets
*/
if (io_u->file)
- return 0;
+ return true;
if (flist_empty(&td->trim_list))
- return 1;
+ return false;
assert(td->trim_entries);
ipo = flist_first_entry(&td->trim_list, struct io_piece, trim_list);
if (r) {
dprint(FD_VERIFY, "failed file %s open\n",
io_u->file->file_name);
- return 1;
+ return false;
}
}
io_u->xfer_buflen = io_u->buflen;
dprint(FD_VERIFY, "get_next_trim: ret io_u %p\n", io_u);
- return 0;
+ return true;
}
-int io_u_should_trim(struct thread_data *td, struct io_u *io_u)
+bool io_u_should_trim(struct thread_data *td, struct io_u *io_u)
{
unsigned long long val;
uint64_t frand_max;
unsigned long r;
if (!td->o.trim_percentage)
- return 0;
+ return false;
frand_max = rand_max(&td->trim_state);
r = __rand(&td->trim_state);
#include "fio.h"
#ifdef FIO_HAVE_TRIM
-extern int __must_check get_next_trim(struct thread_data *td, struct io_u *io_u);
-extern int io_u_should_trim(struct thread_data *td, struct io_u *io_u);
+extern bool __must_check get_next_trim(struct thread_data *td, struct io_u *io_u);
+extern bool io_u_should_trim(struct thread_data *td, struct io_u *io_u);
/*
* Determine whether a given io_u should be logged for verify or
}
#else
-static inline int get_next_trim(struct thread_data *td, struct io_u *io_u)
+static inline bool get_next_trim(struct thread_data *td, struct io_u *io_u)
{
- return 1;
+ return false;
}
-static inline int io_u_should_trim(struct thread_data *td, struct io_u *io_u)
+static inline bool io_u_should_trim(struct thread_data *td, struct io_u *io_u)
{
- return 0;
+ return false;
}
static inline void remove_trim_entry(struct thread_data *td, struct io_piece *ipo)
{
#include <stdint.h>
#include <string.h>
+#include <limits.h>
struct thread_rand32_state {
uint32_t s[4];
(void)cpy_pattern(td->o.buffer_pattern, td->o.buffer_pattern_bytes, p, len);
}
-void __fill_buffer(struct thread_options *o, unsigned long seed, void *p,
- unsigned int len)
+static void __fill_buffer(struct thread_options *o, unsigned long seed, void *p,
+ unsigned int len)
{
__fill_random_buf_percentage(seed, p, o->compress_percentage, len, len, o->buffer_pattern, o->buffer_pattern_bytes);
}
-unsigned long fill_buffer(struct thread_data *td, void *p, unsigned int len)
+static unsigned long fill_buffer(struct thread_data *td, void *p,
+ unsigned int len)
{
struct frand_state *fs = &td->verify_state;
struct thread_options *o = &td->o;
SW_F_IDLE = 1 << 0,
SW_F_RUNNING = 1 << 1,
SW_F_EXIT = 1 << 2,
- SW_F_EXITED = 1 << 3,
- SW_F_ACCOUNTED = 1 << 4,
- SW_F_ERROR = 1 << 5,
+ SW_F_ACCOUNTED = 1 << 3,
+ SW_F_ERROR = 1 << 4,
};
static struct submit_worker *__get_submit_worker(struct workqueue *wq,
{
struct submit_worker *sw = data;
struct workqueue *wq = sw->wq;
- unsigned int eflags = 0, ret = 0;
+ unsigned int ret = 0;
FLIST_HEAD(local_list);
sk_out_assign(sw->sk_out);
wq->ops.update_acct_fn(sw);
done:
- pthread_mutex_lock(&sw->lock);
- sw->flags |= (SW_F_EXITED | eflags);
- pthread_mutex_unlock(&sw->lock);
sk_out_drop();
return NULL;
}
goto err;
wq->workers = smalloc(wq->max_workers * sizeof(struct submit_worker));
+ if (!wq->workers)
+ goto err;
for (i = 0; i < wq->max_workers; i++)
if (start_worker(wq, i, sk_out))