Merge git://git.kernel.dk/fio into steady-state
authorVincent Fu <Vincent.Fu@sandisk.com>
Tue, 13 Dec 2016 14:20:52 +0000 (09:20 -0500)
committerVincent Fu <Vincent.Fu@sandisk.com>
Tue, 13 Dec 2016 14:20:52 +0000 (09:20 -0500)
77 files changed:
FIO-VERSION-GEN
HOWTO
Makefile
README
REPORTING-BUGS
backend.c
blktrace.c
cconv.c
client.c
client.h
configure
diskutil.c
engines/e4defrag.c
engines/glusterfs_async.c
engines/libhdfs.c
engines/mmap.c
engines/mtd.c
engines/net.c
engines/posixaio.c
engines/rbd.c
engines/windowsaio.c
eta.c
examples/basic-verify.fio [new file with mode: 0644]
examples/jesd219.fio
file.h
filehash.c
filehash.h
filelock.c
filelock.h
filesetup.c
fio.1
fio.h
fio_time.h
flow.c
gclient.c
gettime.c
gfio.c
init.c
io_u.c
ioengines.c
iolog.c
iolog.h
lib/bloom.c
lib/bloom.h
lib/mountcheck.c
lib/strntol.c
libfio.c
mutex.c
options.c
options.h
os/os-mac.h
os/os-netbsd.h
os/os-openbsd.h
os/os.h
os/windows/install.wxs
os/windows/posix.c
oslib/libmtd.c
oslib/linux-dev-lookup.c
oslib/strlcat.c
parse.c
parse.h
rate-submit.c
server.c
server.h
smalloc.c
stat.c
stat.h
thread_options.h
time.c
tools/fio.service [new file with mode: 0644]
tools/hist/fiologparser_hist.py
tools/hist/fiologparser_hist.py.1 [new file with mode: 0644]
trim.c
trim.h
verify-state.h
verify.c
workqueue.c

index 7065a5790e2051bc05973d99e58803742147aef5..eac0e00adb0f291cead8d1e3cbccdbf513ff7552 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=FIO-VERSION-FILE
-DEF_VER=fio-2.13
+DEF_VER=fio-2.15
 
 LF='
 '
@@ -15,7 +15,7 @@ elif test -d .git -o -f .git &&
        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" ;;
@@ -38,5 +38,3 @@ test "$VN" = "$VC" || {
        echo >&2 "FIO_VERSION = $VN"
        echo "FIO_VERSION = $VN" >$GVF
 }
-
-
diff --git a/HOWTO b/HOWTO
index 6c674b27d549a28242d0dc77c5b1e5b2d6a1a06a..bfc60be50572a03074344d359719e886460a42d4 100644 (file)
--- a/HOWTO
+++ b/HOWTO
@@ -803,8 +803,8 @@ ioengine=str        Defines how the job issues io to the file. The following
                                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
@@ -864,7 +864,7 @@ iodepth_batch_complete_max=int This defines maximum pieces of IO to
                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.
 
@@ -986,7 +986,7 @@ random_distribution=str:float       By default, fio will use a completely uniform
                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
@@ -1027,7 +1027,7 @@ percentage_random=int     For a random workload, set how big a percentage should
                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
@@ -1066,6 +1066,10 @@ random_generator=str     Fio supports the following engines for generating
 
 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
@@ -1181,7 +1185,7 @@ numa_cpu_nodes=str Set this job running on specified NUMA nodes' CPUs. The
                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
@@ -1304,7 +1308,7 @@ mem=str           Fio can use various types of memory as the io unit buffer.
                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
@@ -1368,7 +1372,7 @@ pre_read=bool     If this is given, files will be pre-read into memory before
                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.
 
@@ -1410,7 +1414,7 @@ verify=str        If writing to a file, fio can verify the file contents
                        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.
@@ -1485,7 +1489,7 @@ verify_pattern=str        If set, fio will fill the io buffers with this
                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
 
@@ -1596,10 +1600,10 @@ read_iolog=str  Open an iolog with the specified file name and replay the
 
 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
@@ -1611,13 +1615,14 @@ 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.
@@ -1723,6 +1728,10 @@ log_store_compressed=bool        If set, fio will store the log files in a
                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
@@ -1957,7 +1966,7 @@ be the starting port number since fio will use a range of ports.
                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
@@ -1978,22 +1987,22 @@ be the starting port number since fio will use a range of ports.
 [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
@@ -2386,10 +2395,9 @@ Data direction is one of the following:
 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.
-
index 83414c3a612c0897981154df7bfa8f69556e7622..f2c05fc0bfffde7e88d2cba38f498a7fb381809d 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -26,7 +26,7 @@ OPTFLAGS= -g -ffast-math
 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
@@ -50,7 +50,7 @@ SOURCE :=     $(patsubst $(SRCDIR)/%,%,$(wildcard $(SRCDIR)/crc/*.c)) \
 
 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
@@ -316,7 +316,7 @@ override CFLAGS += -DFIO_VERSION='"$(FIO_VERSION)"'
        @$(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 -1 | \
                sed -e 's/^ *//' -e 's/$$/:/' >> $*.d
        @rm -f $*.d.tmp
 
@@ -355,7 +355,7 @@ init.o: init.c FIO-VERSION-FILE
        @$(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 -1 | \
                sed -e 's/^ *//' -e 's/$$/:/' >> $*.d
        @rm -f $*.d.tmp
 
@@ -431,7 +431,7 @@ clean: FORCE
        @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
@@ -443,6 +443,7 @@ doc: tools/plot/fio2gnuplot.1
        @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:
 
@@ -453,5 +454,6 @@ install: $(PROGS) $(SCRIPTS) tools/plot/fio2gnuplot.1 FORCE
        $(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)/
diff --git a/README b/README
index 5fa37f3eed33a15a15a38836cf0080edc81688fd..a8a4fdf3d07960d44c1c7ae8eeff85b3ab2550b0 100644 (file)
--- a/README
+++ b/README
@@ -149,7 +149,7 @@ $ fio
        --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).
@@ -169,7 +169,7 @@ $ fio
        --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.
@@ -233,7 +233,7 @@ sections.  The reserved 'global' section is always parsed and used.
 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.
 
index c6150d141c024e4e5ba9b76daa61a46473f0a236..d8876ae6870f691f0e5a777b398d71b19203937e 100644 (file)
@@ -2,8 +2,10 @@ Reporting a bug
 ---------------
 
 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
@@ -12,4 +14,8 @@ to report at least:
 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!
index 04067ebc0f7048fd9a8810362086fbbad4d0c31f..a048452d61c8e12b0138baad9bc05f125fb90151 100644 (file)
--- a/backend.c
+++ b/backend.c
@@ -441,11 +441,8 @@ static int wait_for_completions(struct thread_data *td, struct timeval *time)
        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
@@ -771,18 +768,18 @@ static bool exceeds_number_ios(struct thread_data *td)
        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;
@@ -793,26 +790,14 @@ static bool io_issue_bytes_exceeded(struct thread_data *td)
        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);
 }
 
 /*
@@ -1471,6 +1456,7 @@ static void *thread_main(void *data)
        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;
 
@@ -1675,7 +1661,7 @@ static void *thread_main(void *data)
        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));
@@ -1723,6 +1709,14 @@ static void *thread_main(void *data)
                        }
                }
 
+               /*
+                * 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;
 
                /*
@@ -1732,9 +1726,19 @@ static void *thread_main(void *data)
                 * 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])
@@ -1858,8 +1862,8 @@ static void dump_td_info(struct thread_data *td)
 /*
  * 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;
@@ -2097,7 +2101,8 @@ static bool waitee_running(struct thread_data *me)
 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())
index deb8b2d6d68eb465b1929fd9565cde900d9b408c..a3474cb57ee9825077684f3b3f7de96b272a2e4c 100644 (file)
@@ -216,15 +216,6 @@ static void t_bytes_align(struct thread_options *o, struct blk_io_trace *t)
        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.
  */
@@ -239,7 +230,7 @@ static void store_ipo(struct thread_data *td, unsigned long long offset,
        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)
@@ -297,7 +288,7 @@ static void handle_trace_discard(struct thread_data *td,
        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;
diff --git a/cconv.c b/cconv.c
index ebbac0aa1906c795b40ab6a13330b18c76c13f82..336805be5e60d8b8274a038ad803e9934ca2579e 100644 (file)
--- a/cconv.c
+++ b/cconv.c
@@ -131,8 +131,8 @@ void convert_thread_options_to_cpu(struct thread_options *o,
                }
 
                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]);
 
@@ -187,6 +187,7 @@ void convert_thread_options_to_cpu(struct thread_options *o,
        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);
@@ -283,6 +284,10 @@ void convert_thread_options_to_cpu(struct thread_options *o,
        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);
@@ -383,6 +388,7 @@ void convert_thread_options_to_net(struct thread_options_pack *top,
        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);
@@ -462,6 +468,10 @@ void convert_thread_options_to_net(struct thread_options_pack *top,
        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]);
@@ -499,8 +509,8 @@ void convert_thread_options_to_net(struct thread_options_pack *top,
                }
 
                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]);
 
index f1c7596364373d96a97c08e3ddb0cdaeb14b886e..48d4c5296c8a135f4256512a815529b1b87d9541 100644 (file)
--- a/client.c
+++ b/client.c
@@ -557,7 +557,7 @@ int fio_client_terminate(struct fio_client *client)
        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;
@@ -1150,11 +1150,11 @@ static void convert_jobs_eta(struct jobs_eta *je)
        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]);
        }
 
@@ -1198,7 +1198,7 @@ void fio_client_sum_jobs_eta(struct jobs_eta *dst, struct jobs_eta *je)
        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;
@@ -1214,12 +1214,13 @@ static void remove_reply_cmd(struct fio_client *client, struct fio_net_cmd *cmd)
 
        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)
@@ -1266,11 +1267,50 @@ static void handle_eta(struct fio_client *client, struct fio_net_cmd *cmd)
        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) {
@@ -1278,15 +1318,26 @@ static int fio_client_handle_iolog(struct fio_client *client,
                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;
                }
 
@@ -1302,15 +1353,20 @@ static int fio_client_handle_iolog(struct fio_client *client,
                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;
        }
@@ -1410,7 +1466,11 @@ static struct cmd_iolog_pdu *convert_iolog_gz(struct fio_net_cmd *cmd,
         */
        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;
 
@@ -1493,6 +1553,7 @@ static struct cmd_iolog_pdu *convert_iolog(struct fio_net_cmd *cmd,
        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;
@@ -1502,8 +1563,11 @@ static struct cmd_iolog_pdu *convert_iolog(struct fio_net_cmd *cmd,
                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);
 
@@ -1512,6 +1576,12 @@ static struct cmd_iolog_pdu *convert_iolog(struct fio_net_cmd *cmd,
 
                        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;
@@ -1621,7 +1691,8 @@ int fio_handle_client(struct fio_client *client)
        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;
index ddacf785d0dfcd0e76aa979bd87f979ff80379b3..fc9c19693a9ff0c60bfcee6429755344b72cdfdf 100644 (file)
--- a/client.h
+++ b/client.h
@@ -131,7 +131,6 @@ extern struct fio_client *fio_client_add_explicit(struct client_ops *, const cha
 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 *);
@@ -145,5 +144,9 @@ enum {
        FIO_CLIENT_TYPE_GUI             = 2,
 };
 
+extern int sum_stat_clients;
+extern struct thread_stat client_ts;
+extern struct group_run_stats client_gs;
+
 #endif
 
index 93c372006660b4ad26dfd8eb346e19ac51c161f2..833b6d33464b378382fbcdb8d7acca9f49fccb73 100755 (executable)
--- a/configure
+++ b/configure
@@ -793,6 +793,25 @@ EOF
 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"
@@ -1315,6 +1334,32 @@ if test "$disable_rbd" != "yes"  && compile_prog "" "-lrbd -lrados" "rbd"; then
 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"
@@ -1489,6 +1534,10 @@ if test "$libhdfs" = "yes" ; then
   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"
 
@@ -1598,6 +1647,11 @@ echo "getmntent                     $getmntent"
 
 ##########################################
 # 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>
@@ -1605,15 +1659,32 @@ cat > $TMPC << EOF
 #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"
@@ -1718,6 +1789,9 @@ fi
 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
@@ -1805,6 +1879,9 @@ 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
@@ -1829,6 +1906,7 @@ if test "$gf_trim" = "yes" ; then
 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
@@ -1856,6 +1934,9 @@ fi
 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
index 0f7a64209b57747139e66705a8fd2f71ebbe4697..27ddb46b7fecdae75114874210ed2ae135d21bd6 100644 (file)
@@ -292,10 +292,8 @@ static struct disk_util *disk_util_add(struct thread_data *td, int majdev,
        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);
index c599c9868ee6f40baaa61422e4daad60a6c4751e..e53636eb26e382b3e318abef48bd3b211d2695f1 100644 (file)
@@ -45,6 +45,7 @@ struct e4defrag_options {
 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",
@@ -53,6 +54,7 @@ static struct fio_option options[] = {
        },
        {
                .name   = "inplace",
+               .lname  = "In Place",
                .type   = FIO_OPT_INT,
                .off1   = offsetof(struct e4defrag_options, inplace),
                .minval = 0,
index 8e42a84999147838d316926d6504183ec7f31ae0..f46cb263dd781e9f1180d8b19b8ec5c2eb157f04 100644 (file)
@@ -137,7 +137,7 @@ failed:
        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;
index fba17c4fcbc1033767e6afcc8981852fc50f6cdc..96a0871d873ee8813876ce4ee47709de8b3c5b58 100644 (file)
@@ -80,7 +80,9 @@ static struct fio_option options[] = {
                .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",
@@ -90,6 +92,7 @@ static struct fio_option options[] = {
        },
        {
                .name   = "single_instance",
+               .lname  = "Single Instance",
                .type   = FIO_OPT_BOOL,
                .off1   = offsetof(struct hdfsio_options, single_instance),
                .def    = "1",
@@ -99,6 +102,7 @@ static struct fio_option options[] = {
        },
        {
                .name   = "hdfs_use_direct",
+               .lname  = "HDFS Use Direct",
                .type   = FIO_OPT_BOOL,
                .off1   = offsetof(struct hdfsio_options, use_direct),
                .def    = "0",
index 14e4013de83cac82d09a1eafe013c18797b08970..c479ed39497ef8c3bd8c70e380cdd6e3f18c8be1 100644 (file)
@@ -20,7 +20,6 @@
 #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;
@@ -72,7 +71,6 @@ static int fio_mmap_file(struct thread_data *td, struct fio_file *f,
                (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);
@@ -208,26 +206,15 @@ static int fio_mmapio_queue(struct thread_data *td, struct io_u *io_u)
 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;
 }
 
index 7b92c836692f388ad3dad8b179ef9687e10694dc..3c22a1b15be99032b53d1bb70a27cf1109811cde 100644 (file)
@@ -16,7 +16,7 @@
 #include "../verify.h"
 #include "../oslib/libmtd.h"
 
-libmtd_t desc;
+static libmtd_t desc;
 
 struct fio_mtd_data {
        struct mtd_dev_info info;
@@ -168,7 +168,7 @@ static int fio_mtd_close_file(struct thread_data *td, struct fio_file *f)
        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;
 
index f24efc1d7fab63fbeab16a48c09b1ef3f3769668..5f1401c6e04fb9aa9d1249750e25a4404fb8c9a9 100644 (file)
@@ -135,6 +135,7 @@ static struct fio_option options[] = {
 #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",
@@ -153,6 +154,7 @@ static struct fio_option options[] = {
        },
        {
                .name   = "pingpong",
+               .lname  = "Ping Pong",
                .type   = FIO_OPT_STR_SET,
                .off1   = offsetof(struct netio_options, pingpong),
                .help   = "Ping-pong IO requests",
index e5411b75363ce0cac218eeeffbaf8771a3b50804..bddb1ec3f2697e43dff46dcc726ed31e419f27f0 100644 (file)
@@ -109,7 +109,7 @@ static int fio_posixaio_getevents(struct thread_data *td, unsigned int min,
 
        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;
index 5e17fbe73a7fab5cc1b04431c3df7965d76acd01..ee2ce813b9ae2d6e3bb2ac3649a9e909573ea7f0 100644 (file)
 #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;
@@ -29,6 +35,7 @@ struct rbd_data {
        rbd_image_t image;
        struct io_u **aio_events;
        struct io_u **sort_events;
+       int fd; /* add for poll */
 };
 
 struct rbd_options {
@@ -104,6 +111,9 @@ static int _fio_setup_rbd_data(struct thread_data *td,
        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;
@@ -127,6 +137,35 @@ 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;
@@ -188,8 +227,15 @@ static int _fio_rbd_connect(struct thread_data *td)
                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;
@@ -205,6 +251,12 @@ static void _fio_rbd_disconnect(struct rbd_data *rbd)
        if (!rbd)
                return;
 
+       /* close eventfd */
+       if (rbd->fd != -1) {
+               close(rbd->fd);
+               rbd->fd = -1;
+       }
+
        /* shutdown everything */
        if (rbd->image) {
                rbd_close(rbd->image);
@@ -304,10 +356,32 @@ static int rbd_iter_events(struct thread_data *td, unsigned int *events,
        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))
@@ -521,7 +595,12 @@ static int fio_rbd_setup(struct thread_data *td)
        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,
index 0e164b604b1b9b00c6a96601dbe1c57635a962e5..f5cb04838a31f70dc6cf801239e33df16800f54c 100644 (file)
@@ -113,10 +113,15 @@ static int fio_windowsaio_init(struct thread_data *td)
 
                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");
                }
 
diff --git a/eta.c b/eta.c
index ffab34e2c740c8d38ac3c808a062013f6c0c1abd..19afad5b04293df75201ee00914a862964f78b20 100644 (file)
--- a/eta.c
+++ b/eta.c
@@ -225,7 +225,11 @@ static unsigned long thread_eta(struct thread_data *td)
                        }
                }
 
-               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))
@@ -235,7 +239,7 @@ static unsigned long thread_eta(struct thread_data *td)
                        || 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;
 
                /*
@@ -247,7 +251,10 @@ static unsigned long thread_eta(struct thread_data *td)
                        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)) {
@@ -259,9 +266,16 @@ static unsigned long thread_eta(struct thread_data *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);
                }
 
@@ -285,7 +299,7 @@ static unsigned long thread_eta(struct thread_data *td)
 
 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;
 
@@ -337,11 +351,11 @@ static void calc_iops(int unified_rw_rep, unsigned long mtime,
  * 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;
@@ -354,12 +368,12 @@ int calc_thread_status(struct jobs_eta *je, int force)
        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))
@@ -367,8 +381,8 @@ int calc_thread_status(struct jobs_eta *je, int force)
        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;
 
@@ -468,9 +482,9 @@ int calc_thread_status(struct jobs_eta *je, int force)
                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);
@@ -479,7 +493,7 @@ int calc_thread_status(struct jobs_eta *je, int force)
         * 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);
@@ -487,12 +501,12 @@ int calc_thread_status(struct jobs_eta *je, int force)
        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)
diff --git a/examples/basic-verify.fio b/examples/basic-verify.fio
new file mode 100644 (file)
index 0000000..7871aeb
--- /dev/null
@@ -0,0 +1,12 @@
+# 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
index ab2c40e2957de256f610cc0bc23a7b7a8b19c906..24f16f77e93bfba1b177b93844be31ab93deb92e 100644 (file)
@@ -14,6 +14,7 @@ rwmixwrite=60
 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
diff --git a/file.h b/file.h
index 0cf622fcbb213a7922ecb96a8e7b7f88bd76b2f2..6f34dd5c3d315608778f638e6be45ef7f4726cd1 100644 (file)
--- a/file.h
+++ b/file.h
@@ -209,7 +209,8 @@ extern void dup_files(struct thread_data *, struct thread_data *);
 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
index 0d61f54cca0b2f8c526cd639f9749716069aa8ff..edeeab4863c8ff831391f539e53492f1c78df5c7 100644 (file)
@@ -5,14 +5,19 @@
 #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)
 {
@@ -95,6 +100,11 @@ struct fio_file *add_file_hash(struct fio_file *f)
        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;
@@ -107,18 +117,23 @@ void file_hash_exit(void)
        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);
 }
index f316b208481c902d010e707f6f6ceb4e602a209a..5fecc3b100b31db7340c1a955b33c45fa2f49299 100644 (file)
@@ -1,14 +1,15 @@
 #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
index b1130071ea982ad38e7277ec6c0b7886f4c84bf2..6e84970fc0064cafaeb0945befd5f4355fd9cf7b 100644 (file)
@@ -165,7 +165,7 @@ static struct fio_filelock *fio_hash_get(uint32_t hash, int trylock)
        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;
@@ -180,16 +180,16 @@ static int __fio_lock_file(const char *fname, int trylock)
 
        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);
 
@@ -206,13 +206,13 @@ static int __fio_lock_file(const char *fname, int trylock)
 
        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);
 }
index 97d13b7a62f1a62e7dbd6fc4d591588c865831a5..4551bb0427ec6fc12e726bb4161b50ad98d79c40 100644 (file)
@@ -1,8 +1,10 @@
 #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);
index 5db44c294d2de3fccb2ac669a7abf4cc3f3d09fe..969e7cc0a70e4a06ce6c287f6f88dcc8d0c87b68 100644 (file)
@@ -1098,7 +1098,7 @@ static int check_rand_gen_limits(struct thread_data *td, struct fio_file *f,
        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;
        }
@@ -1242,31 +1242,35 @@ static void get_file_type(struct fio_file *f)
        }
 }
 
-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;
 }
 
@@ -1278,7 +1282,7 @@ static void set_already_allocated(const char *fname)
        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;
        }
@@ -1290,7 +1294,6 @@ static void set_already_allocated(const char *fname)
        }
 }
 
-
 static void free_already_allocated(void)
 {
        struct flist_head *entry, *tmp;
@@ -1316,7 +1319,6 @@ static struct fio_file *alloc_new_file(struct thread_data *td)
 
        f = smalloc(sizeof(*f));
        if (!f) {
-               log_err("fio: smalloc OOM\n");
                assert(0);
                return NULL;
        }
@@ -1327,6 +1329,26 @@ static struct fio_file *alloc_new_file(struct thread_data *td)
        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;
@@ -1343,7 +1365,8 @@ int add_file(struct thread_data *td, const char *fname, int numjob, int inc)
        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);
@@ -1378,10 +1401,8 @@ int add_file(struct thread_data *td, const char *fname, int numjob, int inc)
                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);
 
@@ -1584,10 +1605,8 @@ void dup_files(struct thread_data *td, struct thread_data *org)
 
                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;
                }
@@ -1643,16 +1662,16 @@ void fio_file_reset(struct thread_data *td, struct fio_file *f)
                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 */
diff --git a/fio.1 b/fio.1
index cb2f373ae533b0f491592805fac47b1a68828832..270798a47bc0ddaffb7c3a2c4d4497ce2f20456d 100644 (file)
--- a/fio.1
+++ b/fio.1
@@ -30,7 +30,7 @@ dump of the latency buckets.
 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.
@@ -764,7 +764,7 @@ Example #1:
 \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.
 
@@ -1368,7 +1368,7 @@ fio will fill 1/2/3/4 bytes of the buffer at the time(it can be either a
 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
@@ -1506,13 +1506,13 @@ If set, this generates bw/clat/iops log with per file private filenames. If
 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
@@ -1590,6 +1590,11 @@ If set, fio will store the log files in a compressed format. They can be
 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
@@ -2364,7 +2369,7 @@ IO is a TRIM
 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
diff --git a/fio.h b/fio.h
index aca5afa7c0d91f36a1820156c4afd8fd76a92443..5726befcb9abc1f4423246862a73cdc5affd89d7 100644 (file)
--- a/fio.h
+++ b/fio.h
@@ -270,10 +270,10 @@ struct thread_data {
         * 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;
@@ -312,6 +312,7 @@ struct thread_data {
 
        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;
@@ -454,7 +455,6 @@ extern int read_only;
 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;
@@ -479,7 +479,7 @@ static inline void fio_ro_check(const struct thread_data *td, struct io_u *io_u)
        assert(!(io_u->ddir == DDIR_WRITE && !td_write(td)));
 }
 
-#define REAL_MAX_JOBS          2048
+#define REAL_MAX_JOBS          4096
 
 static inline int should_fsync(struct thread_data *td)
 {
@@ -517,7 +517,7 @@ extern void td_fill_verify_state_seed(struct thread_data *);
 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);
@@ -567,7 +567,8 @@ enum {
 
 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)
@@ -575,9 +576,10 @@ 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);
@@ -589,7 +591,7 @@ extern const char *runstate_to_name(int runstate);
  * 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);
@@ -646,17 +648,17 @@ extern void lat_target_reset(struct thread_data *);
        }       \
 } 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;
 
@@ -665,23 +667,21 @@ static inline int __should_check_rate(struct thread_data *td,
         */
        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)
index e31ea0974da5d5168ba4db93b977cb0fd9624a63..b49cc828713e4c67d53e362372760cd510510353 100644 (file)
@@ -20,5 +20,6 @@ extern bool ramp_time_over(struct thread_data *);
 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
diff --git a/flow.c b/flow.c
index e0ac13521f453ed32f2e1ce2fbcdaeb93e70adcc..42b6dd75adbe63a4bbfc30ae26f5715a149e63d7 100644 (file)
--- a/flow.c
+++ b/flow.c
@@ -58,7 +58,6 @@ static struct fio_flow *flow_get(unsigned int id)
        if (!flow) {
                flow = smalloc(sizeof(*flow));
                if (!flow) {
-                       log_err("fio: smalloc pool exhausted\n");
                        fio_mutex_up(flow_lock);
                        return NULL;
                }
index 9c32474669dbf535e3875757baf29947e752e395..23b0899322052f20744c149561351829325169f1 100644 (file)
--- a/gclient.c
+++ b/gclient.c
@@ -280,10 +280,6 @@ static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd
        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,
@@ -1012,7 +1008,7 @@ static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min,
        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);
index 73b48b0463a0efb00144bae289d6de377b197dbf..85ba7cba8954aa3a70ae5e1faf9a76ab6ab22c72 100644 (file)
--- a/gettime.c
+++ b/gettime.c
@@ -381,7 +381,7 @@ void fio_clock_init(void)
 
 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;
diff --git a/gfio.c b/gfio.c
index 37c181894d752e95c2d24cd5299f50d1ae0feba4..ce180911db343ae8b7b94054262f53ae023523a1 100644 (file)
--- a/gfio.c
+++ b/gfio.c
@@ -459,10 +459,12 @@ static int send_job_file(struct gui_entry *ge)
 
 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;
 }
 
diff --git a/init.c b/init.c
index 9f69b3c2394667d03053931e2adcb18d78f5cd3a..963afbf0cd62f2537c22b67c7fe2ae72367ff35e 100644 (file)
--- a/init.c
+++ b/init.c
@@ -48,7 +48,6 @@ static char **job_sections;
 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;
@@ -105,7 +104,7 @@ static struct option l_opts[FIO_NR_OPTIONS] = {
        },
        {
                .name           = (char *) "bandwidth-log",
-               .has_arg        = required_argument,
+               .has_arg        = no_argument,
                .val            = 'b' | FIO_CLIENT_FLAG,
        },
        {
@@ -300,7 +299,6 @@ void free_threads_shm(void)
 static void free_shm(void)
 {
        if (threads) {
-               file_hash_exit();
                flow_exit();
                fio_debug_jobp = NULL;
                free_threads_shm();
@@ -311,8 +309,9 @@ static void free_shm(void)
        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();
 }
 
@@ -324,8 +323,6 @@ static void free_shm(void)
  */
 static int setup_thread_area(void)
 {
-       void *hash;
-
        if (threads)
                return 0;
 
@@ -336,7 +333,6 @@ static int setup_thread_area(void)
        do {
                size_t size = max_jobs * sizeof(struct thread_data);
 
-               size += file_hash_size;
                size += sizeof(unsigned int);
 
 #ifndef CONFIG_NO_SHM
@@ -368,10 +364,8 @@ static int setup_thread_area(void)
 #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();
 
@@ -905,26 +899,6 @@ static const char *get_engine_name(const char *str)
        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;
@@ -1249,7 +1223,7 @@ static char *make_filename(char *buf, size_t buf_size,struct thread_options *o,
        return buf;
 }
 
-int parse_dryrun(void)
+bool parse_dryrun(void)
 {
        return dump_cmdline || parse_only;
 }
@@ -1416,7 +1390,7 @@ static int add_job(struct thread_data *td, const char *jobname, int job_add_num,
        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,
@@ -1427,6 +1401,7 @@ static int add_job(struct thread_data *td, const char *jobname, int job_add_num,
                        .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)
@@ -1434,20 +1409,20 @@ static int add_job(struct thread_data *td, const char *jobname, int job_add_num,
                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,
@@ -1458,19 +1433,27 @@ static int add_job(struct thread_data *td, const char *jobname, int job_add_num,
                        .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,
@@ -1481,6 +1464,7 @@ static int add_job(struct thread_data *td, const char *jobname, int job_add_num,
                        .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))
@@ -1496,11 +1480,11 @@ static int add_job(struct thread_data *td, const char *jobname, int job_add_num,
                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,
@@ -1511,6 +1495,7 @@ static int add_job(struct thread_data *td, const char *jobname, int job_add_num,
                        .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))
@@ -1526,7 +1511,7 @@ static int add_job(struct thread_data *td, const char *jobname, int job_add_num,
                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);
        }
@@ -1700,7 +1685,7 @@ static int is_empty_or_comment(char *line)
 /*
  * 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)
 {
@@ -2026,7 +2011,7 @@ static void usage(const char *name)
        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");
@@ -2336,6 +2321,8 @@ int parse_cmd_line(int argc, char *argv[], int client_type)
                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)) {
diff --git a/io_u.c b/io_u.c
index 22701274cf3cc5aa2fe78d70a0d25171432281df..7420629740f458ca644fcc2de47891f359d78cd5 100644 (file)
--- a/io_u.c
+++ b/io_u.c
@@ -362,8 +362,12 @@ static int get_next_seq_offset(struct thread_data *td, struct fio_file *f,
        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) {
@@ -378,8 +382,14 @@ static int get_next_seq_offset(struct thread_data *td, struct fio_file *f,
                        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;
+                               }
                        }
                }
 
@@ -521,8 +531,7 @@ static unsigned int __get_next_buflen(struct thread_data *td, struct io_u *io_u,
        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));
 
@@ -551,7 +560,7 @@ static unsigned int __get_next_buflen(struct thread_data *td, struct io_u *io_u,
                        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++) {
@@ -559,7 +568,9 @@ static unsigned int __get_next_buflen(struct thread_data *td, struct io_u *io_u,
 
                                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;
                        }
@@ -642,13 +653,17 @@ int io_u_quiesce(struct thread_data *td)
                        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);
@@ -1500,7 +1515,7 @@ static bool check_get_trim(struct thread_data *td, struct io_u *io_u)
                        get_trim = 1;
                }
 
-               if (get_trim && !get_next_trim(td, io_u))
+               if (get_trim && get_next_trim(td, io_u))
                        return true;
        }
 
index ae55f951d413fbaeaf92dae3c8eb0746167d4c9d..1b58168608046390146765b7f546c9b9d964736e 100644 (file)
@@ -298,6 +298,7 @@ int td_io_queue(struct thread_data *td, struct io_u *io_u)
                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);
        }
 
        /*
@@ -483,7 +484,12 @@ int td_io_open_file(struct thread_data *td, struct fio_file *f)
 
                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;
                }
        }
diff --git a/iolog.c b/iolog.c
index 975ce6f7a481c438025992f4b828b6d69378b290..93938905bebe4e7557e2d3c22abecf6cd230789c 100644 (file)
--- a/iolog.c
+++ b/iolog.c
@@ -109,6 +109,11 @@ static int ipo_special(struct thread_data *td, struct io_piece *ipo)
 
        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;
@@ -346,7 +351,7 @@ static int read_iolog2(struct thread_data *td, FILE *f)
        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;
 
@@ -357,7 +362,7 @@ static int read_iolog2(struct thread_data *td, FILE *f)
         * for doing verifications.
         */
        str = malloc(4096);
-       fname = malloc(256+16);
+       rfname = fname = malloc(256+16);
        act = malloc(256+16);
 
        reads = writes = waits = 0;
@@ -365,8 +370,12 @@ static int read_iolog2(struct thread_data *td, FILE *f)
                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
@@ -392,8 +401,14 @@ static int read_iolog2(struct thread_data *td, FILE *f)
                } 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);
@@ -421,6 +436,8 @@ static int read_iolog2(struct thread_data *td, FILE *f)
                                continue;
                        writes++;
                } else if (rw == DDIR_WAIT) {
+                       if (td->o.no_stall)
+                               continue;
                        waits++;
                } else if (rw == DDIR_INVAL) {
                } else if (!ddir_sync(rw)) {
@@ -437,7 +454,12 @@ static int read_iolog2(struct thread_data *td, FILE *f)
                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;
@@ -451,7 +473,7 @@ static int read_iolog2(struct thread_data *td, FILE *f)
 
        free(str);
        free(act);
-       free(fname);
+       free(rfname);
 
        if (writes && read_only) {
                log_err("fio: <%s> skips replay of %d writes due to"
@@ -576,6 +598,9 @@ void setup_log(struct io_log **log, struct log_params *p,
               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);
@@ -589,6 +614,16 @@ void setup_log(struct io_log **log, struct log_params *p,
        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;
 
@@ -661,24 +696,32 @@ void free_log(struct io_log *log)
        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;
        
@@ -692,15 +735,25 @@ void flush_hist_samples(FILE *f, int hist_coarseness, void *samples,
 
        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);
        }
 }
 
@@ -722,16 +775,16 @@ void flush_samples(FILE *f, void *samples, uint64_t sample_size)
                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);
                }
@@ -1034,11 +1087,11 @@ void flush_log(struct io_log *log, bool do_append)
                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);
        }
@@ -1122,7 +1175,8 @@ static int gz_work(struct iolog_flush_data *data)
                                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;
@@ -1159,7 +1213,7 @@ static int gz_work(struct iolog_flush_data *data)
        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 {
@@ -1170,7 +1224,8 @@ static int gz_work(struct iolog_flush_data *data)
                        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);
        }
 
diff --git a/iolog.h b/iolog.h
index 011179a020ccd05830be26ca1d82053417623f32..60ee3e920f48e3b1f0fa67aa51f7083e9c32d2c4 100644 (file)
--- a/iolog.h
+++ b/iolog.h
@@ -21,14 +21,24 @@ struct io_stat {
 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;
 };
@@ -109,13 +119,14 @@ struct io_log {
        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;
@@ -148,6 +159,11 @@ static inline size_t log_entry_sz(struct io_log *log)
        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)
 {
@@ -253,10 +269,19 @@ static inline bool inline_log(struct io_log *log)
                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);
index ee4ba0baabcc80c40d13ede42ccb204d4530c9d9..fa38db9551469f2eaaef29b5bce51bee2e71e487 100644 (file)
@@ -35,7 +35,7 @@ static uint32_t bloom_fnv(const void *buf, uint32_t len, uint32_t seed)
 
 #define BLOOM_SEED     0x8989
 
-struct bloom_hash hashes[] = {
+static struct bloom_hash hashes[] = {
        {
                .seed = BLOOM_SEED,
                .fn = jhash,
@@ -60,8 +60,6 @@ struct bloom_hash hashes[] = {
 
 #define N_HASHES       5
 
-#define MIN_ENTRIES    1073741824UL
-
 struct bloom *bloom_new(uint64_t entries)
 {
        struct bloom *b;
@@ -72,7 +70,6 @@ struct bloom *bloom_new(uint64_t entries)
        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);
@@ -88,14 +85,14 @@ void bloom_free(struct bloom *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;
        }
 
@@ -113,7 +110,13 @@ static int __bloom_check(struct bloom *b, uint32_t *data, unsigned int nwords,
        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);
 }
index 127ed9b77fb2100e692291f2dc0a155a23687617..141ead9a68c3f3fef76655bd8b6b9f5735a9459f 100644 (file)
@@ -2,11 +2,13 @@
 #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
index e37e9f927b0c3cc2ecfca6cd26ea7017eb3c083f..0aec7441f91d412a01fc17d1ec083415f3f58e26 100644 (file)
@@ -4,6 +4,8 @@
 #ifdef CONFIG_GETMNTENT
 #include <mntent.h>
 
+#include "lib/mountcheck.h"
+
 #define MTAB   "/etc/mtab"
 
 int device_is_mounted(const char *dev)
@@ -30,7 +32,7 @@ int device_is_mounted(const char *dev)
 }
 
 #elif defined(CONFIG_GETMNTINFO)
-/* for BSDs */
+/* for most BSDs */
 #include <sys/param.h>
 #include <sys/mount.h>
 
@@ -51,6 +53,27 @@ int device_is_mounted(const char *dev)
        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 */
 
index 713f63bb0c683201f61b545f6ba7386f3dc19055..adf45bd4d6b87996b63eaaf3d50641bd8f431873 100644 (file)
@@ -2,6 +2,8 @@
 #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
index 20ce7cdf7d99f315777ed9d12813866154df765a..960daf69f6c0d145998a58bbbcdb225b861c60d6 100644 (file)
--- a/libfio.c
+++ b/libfio.c
@@ -34,6 +34,7 @@
 #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
@@ -134,7 +135,6 @@ void clear_io_state(struct thread_data *td, int all)
 
 void reset_all_stats(struct thread_data *td)
 {
-       struct timeval tv;
        int i;
 
        reset_io_counters(td, 1);
@@ -148,12 +148,11 @@ void reset_all_stats(struct thread_data *td)
                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);
@@ -379,6 +378,8 @@ int initialize_fio(char *envp[])
                return 1;
        }
 
+       file_hash_init();
+
        /*
         * We need locale for number printing, if it isn't set then just
         * go with the US format.
diff --git a/mutex.c b/mutex.c
index 758092218477e0eb5dc0da8a5049aba2f6e9e908..5e5a0648161de8767bbee98e42ac71d1bef94062 100644 (file)
--- a/mutex.c
+++ b/mutex.c
@@ -22,6 +22,12 @@ void __fio_mutex_remove(struct fio_mutex *mutex)
 {
        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)
@@ -156,7 +162,7 @@ int fio_mutex_down_timeout(struct fio_mutex *mutex, unsigned int msecs)
        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++;
index e0db19ba836286133d681b259b6c00afb4cd5aa1..4c4f52c001c03b89df76ee5547d391a316df2c1e 100644 (file)
--- a/options.c
+++ b/options.c
@@ -22,7 +22,7 @@ char client_sockaddr_str[INET6_ADDRSTRLEN] = { 0 };
 
 #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),
@@ -1383,6 +1383,50 @@ static int str_size_cb(void *data, unsigned long long *__val)
        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);
@@ -2185,7 +2229,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                          },
                          { .ival = "gauss",
                            .oval = FIO_FSERVICE_GAUSS,
-                           .help = "Normal (guassian) distribution",
+                           .help = "Normal (gaussian) distribution",
                          },
                          { .ival = "roundrobin",
                            .oval = FIO_FSERVICE_RR,
@@ -3579,8 +3623,9 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
        {
                .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,
@@ -3588,8 +3633,9 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
        {
                .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,
@@ -3597,8 +3643,9 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
        {
                .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,
@@ -3638,8 +3685,9 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
        {
                .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,
@@ -3719,6 +3767,15 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                .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",
@@ -3935,6 +3992,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
        },
        {
                .name   = "disable_bw_measurement",
+               .alias  = "disable_bw",
                .lname  = "Disable bandwidth stats",
                .type   = FIO_OPT_BOOL,
                .off1   = offsetof(struct thread_options, disable_bw),
@@ -4818,7 +4876,7 @@ void del_opt_posval(const char *optname, const char *ival)
 
 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);
index 874141dcd29c4208e93ebb6914747765951f1a9f..83a58e27d50718ba91d909795aec5f419440ca2a 100644 (file)
--- a/options.h
+++ b/options.h
@@ -17,7 +17,6 @@ void add_opt_posval(const char *, const char *, const char *);
 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 */
index 76d388e9201f357b8c6867ca9f8e0e5c799e1602..0903a6fec5c4cd508ff5fefeca69a7e0b9652063 100644 (file)
@@ -35,7 +35,9 @@
 
 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)
index 4b0269e6cab673311576689d514db5b09d49d822..2133d7a11b6dff9648d885c4285140ce8806a827 100644 (file)
@@ -6,6 +6,10 @@
 #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>
@@ -16,9 +20,9 @@
 #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;
@@ -55,11 +72,21 @@ static inline int gettid(void)
        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
index b1d8e832f36b68e30a3e496d7a83324f8bcf2a65..3343cbdfbb18384ded0d4cfe0b145216cbf27590 100644 (file)
@@ -5,6 +5,10 @@
 
 #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
@@ -14,9 +18,9 @@
 #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;
@@ -53,6 +70,19 @@ static inline int gettid(void)
        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
diff --git a/os/os.h b/os/os.h
index 4f267c282db1c0f6925a8bd54d966ee428bae217..16bca6841f0c92270e5ac47150a33254039194bc 100644 (file)
--- a/os/os.h
+++ b/os/os.h
@@ -171,7 +171,7 @@ extern int fio_cpus_split(os_cpu_mask_t *mask, unsigned int cpu);
 #endif
 
 #ifndef FIO_MAX_JOBS
-#define FIO_MAX_JOBS           2048
+#define FIO_MAX_JOBS           4096
 #endif
 
 #ifndef CONFIG_SOCKLEN_T
index f8d377301f5e9650614ad01402edcaabc3a4c975..25cb269f56a037461eeb9506b27a06d366e698e1 100755 (executable)
@@ -10,7 +10,7 @@
        <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"
index fd3d9ab38111c0ecd46f86c664b62a7ec15bb145..bbd93e979522e098d9a438e0ccfdc10da0b89ec0 100755 (executable)
@@ -232,10 +232,12 @@ char *dlerror(void)
 /* 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);
 }
@@ -248,7 +250,7 @@ char* ctime_r(const time_t *t, char *buf)
 
     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;
 }
@@ -645,10 +647,19 @@ int setgid(gid_t gid)
 
 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;
 }
index 5c9eac2778c69a5b4bead766f70d74bdc0939705..24e9db9cf062da307cd50cc11c17416152efdc73 100644 (file)
@@ -1116,6 +1116,7 @@ static int legacy_auto_oob_layout(const struct mtd_dev_info *mtd, int fd,
                len = mtd->oob_size - start;
                memcpy(oob + start, tmp_buf + start, len);
        }
+       free(tmp_buf);
 
        return 0;
 }
@@ -1190,7 +1191,7 @@ int mtd_write(libmtd_t desc, const struct mtd_dev_info *mtd, int fd, int eb,
        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)
 {
index 3a415dde00ea55a73467a691c2bdfb7a325ac1f8..2bbd14a070739c28a74e0be30d0723cbbb7eb589 100644 (file)
@@ -6,6 +6,7 @@
 #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)
index 643d4966b4e5369b72c8df9b320b880fc7ca160a..3329b8339fa2409930b6e15bcdb7e2d9d6da0908 100644 (file)
@@ -1,4 +1,5 @@
 #include <string.h>
+#include "oslib/strlcat.h"
 
 size_t strlcat(char *dst, const char *src, size_t size)
 {
diff --git a/parse.c b/parse.c
index 086f7864b8b6eb16ee0aa6527e86a32079833bfd..8ed4619e6e08f765b783e3c59f3bf7b5632faf2d 100644 (file)
--- a/parse.c
+++ b/parse.c
@@ -1250,7 +1250,7 @@ void fill_default_options(void *data, struct fio_option *options)
                        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;
diff --git a/parse.h b/parse.h
index aa00a679ef4fd918f0260b25d0e32b38ae83ad7b..7ba4e37b984bd46b17092acbc0c03d120f0c1f57 100644 (file)
--- a/parse.h
+++ b/parse.h
@@ -80,14 +80,11 @@ struct fio_option {
        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 *);
 
@@ -107,18 +104,19 @@ extern int string_distance_ok(const char *s1, int dist);
 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)
index 2efbdcba610902dd2c344c45f8a980dbd15c819e..42927ffeaf4125dcefccc8fe964d2bc448197fea 100644 (file)
@@ -123,7 +123,7 @@ static int io_workqueue_init_worker_fn(struct submit_worker *sw)
        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);
 
index 780f09f4ac8d7fb69da798968bd43368ece23b86..2e05415240b23a921574483be1745ed0fe09854e 100644 (file)
--- a/server.c
+++ b/server.c
@@ -578,8 +578,12 @@ static int fio_net_queue_cmd(uint16_t opcode, void *buf, off_t size,
        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)
@@ -622,7 +626,7 @@ static int fio_net_queue_quit(void)
 {
        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)
@@ -908,11 +912,11 @@ static int handle_send_eta_cmd(struct fio_net_cmd *cmd)
                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]);
                }
 
@@ -1539,9 +1543,9 @@ void fio_server_send_ts(struct thread_stat *ts, struct group_run_stats *rs)
        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);
@@ -1684,6 +1688,102 @@ void fio_server_send_du(void)
 }
 
 #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)
 {
@@ -1691,6 +1791,9 @@ static int __fio_append_iolog_gz(struct sk_entry *first, struct io_log *log,
        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);
 
@@ -1835,6 +1938,7 @@ int fio_send_iolog(struct thread_data *td, struct io_log *log, const char *name)
        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);
@@ -1860,7 +1964,7 @@ int fio_send_iolog(struct thread_data *td, struct io_log *log, const char *name)
                        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);
 
@@ -1913,7 +2017,7 @@ void fio_server_send_start(struct thread_data *td)
 
        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,
@@ -1929,10 +2033,8 @@ 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;
index 173aadce44f487415628deaee0a65287db84b8ae..3a1d0b0205a5e96c6d9cb594d19daeec1bcf3ca1 100644 (file)
--- a/server.h
+++ b/server.h
@@ -38,7 +38,7 @@ struct fio_net_cmd_reply {
 };
 
 enum {
-       FIO_SERVER_VER                  = 57,
+       FIO_SERVER_VER                  = 60,
 
        FIO_SERVER_MAX_FRAGMENT_PDU     = 1024,
        FIO_SERVER_MAX_CMD_MB           = 2048,
@@ -183,6 +183,7 @@ struct cmd_iolog_pdu {
        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];
 };
index 6f647c060e087c198ce9b580cbef72ff03227847..d038ac64ccfb8dedbe2801ed3630dafbb121b97c 100644 (file)
--- a/smalloc.c
+++ b/smalloc.c
@@ -26,7 +26,9 @@
 #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
@@ -149,12 +151,15 @@ static int find_next_zero(int word, int start)
        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
@@ -191,21 +196,22 @@ static int add_pool(struct pool *pool, unsigned int alloc_size)
                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;
        }
 
@@ -444,6 +450,8 @@ void *smalloc(size_t size)
                break;
        } while (1);
 
+       log_err("smalloc: OOM. Consider using --alloc-size to increase the "
+               "shared memory available.\n");
        return NULL;
 }
 
diff --git a/stat.c b/stat.c
index 80e3b1681245de82e76f2f763088624335374651..3e57e544e1e421a3f4fd350b5b55d303371a46ce 100644 (file)
--- a/stat.c
+++ b/stat.c
@@ -257,13 +257,13 @@ out:
                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;
@@ -274,7 +274,7 @@ int calc_lat(struct io_stat *is, unsigned long *min, unsigned long *max,
        else
                *dev = 0;
 
-       return 1;
+       return true;
 }
 
 void show_group_stats(struct group_run_stats *rs, struct buf_output *out)
@@ -364,7 +364,7 @@ static void display_lat(const char *name, unsigned long min, unsigned long max,
        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);
@@ -696,6 +696,8 @@ static void show_thread_status_normal(struct thread_stat *ts,
 
        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));
@@ -1120,8 +1122,8 @@ static void show_thread_status_terse_v3_v4(struct thread_stat *ts,
        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;
@@ -1742,7 +1744,8 @@ void __show_run_stats(void)
 
                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);
@@ -2096,7 +2099,7 @@ static struct io_logs *get_cur_log(struct io_log *iolog)
        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)
 {
@@ -2113,8 +2116,8 @@ static void __add_log_sample(struct io_log *iolog, unsigned long val,
 
                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;
 
@@ -2182,14 +2185,14 @@ static void __add_stat_to_log(struct io_log *iolog, enum fio_ddir ddir,
         * 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]);
@@ -2205,7 +2208,7 @@ static void _add_stat_to_log(struct io_log *iolog, unsigned long elapsed,
 }
 
 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;
@@ -2219,7 +2222,7 @@ static long add_log_sample(struct thread_data *td, struct io_log *iolog,
         * 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;
        }
 
@@ -2227,7 +2230,7 @@ static long add_log_sample(struct thread_data *td, struct io_log *iolog,
         * 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
@@ -2267,7 +2270,7 @@ void finalize_logs(struct thread_data *td, bool unit_logs)
                _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;
 
@@ -2275,7 +2278,7 @@ void add_agg_sample(unsigned long val, enum fio_ddir ddir, unsigned int bs)
                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,
@@ -2299,7 +2302,8 @@ void add_clat_sample(struct thread_data *td, enum fio_ddir ddir,
        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);
@@ -2315,7 +2319,7 @@ void add_clat_sample(struct thread_data *td, enum fio_ddir 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
@@ -2325,10 +2329,11 @@ void add_clat_sample(struct thread_data *td, enum fio_ddir ddir,
                         * 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);
 
                        /*
@@ -2357,7 +2362,7 @@ void add_slat_sample(struct thread_data *td, enum fio_ddir ddir,
        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);
 }
@@ -2375,7 +2380,8 @@ void add_lat_sample(struct thread_data *td, enum fio_ddir ddir,
        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);
 }
@@ -2396,7 +2402,8 @@ void add_bw_sample(struct thread_data *td, struct io_u *io_u,
        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);
@@ -2441,7 +2448,8 @@ static int add_bw_samples(struct thread_data *td, struct timeval *t)
                        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);
                }
 
@@ -2469,7 +2477,8 @@ void add_iops_sample(struct thread_data *td, struct io_u *io_u,
        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);
@@ -2514,7 +2523,8 @@ static int add_iops_samples(struct thread_data *td, struct timeval *t)
                        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);
                }
 
@@ -2550,12 +2560,12 @@ int calc_log_samples(void)
                        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;
diff --git a/stat.h b/stat.h
index b145b5ee850ad19c7c1805fd7bd5211411991cf4..22083da79b967dd4d263f69dc81ac4f99fc6c05f 100644 (file)
--- a/stat.h
+++ b/stat.h
@@ -123,7 +123,7 @@ struct group_run_stats {
 #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 {
@@ -244,9 +244,9 @@ struct jobs_eta {
 
        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;
@@ -260,6 +260,11 @@ struct jobs_eta {
        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);
@@ -269,7 +274,7 @@ extern void stat_exit(void);
 
 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);
@@ -281,7 +286,7 @@ extern void sum_group_stats(struct group_run_stats *dst, struct group_run_stats
 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);
@@ -296,7 +301,7 @@ extern void add_clat_sample(struct thread_data *, enum fio_ddir, unsigned long,
                                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 *,
@@ -306,18 +311,18 @@ extern int calc_log_samples(void);
 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
index d1e74380a41d1edb28d4533a42d97012073d725c..dd5b9ef72a1dfd3d93f061f2053a4c5b35c09a24 100644 (file)
@@ -135,6 +135,7 @@ struct thread_options {
        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;
@@ -236,6 +237,12 @@ struct thread_options {
 
        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;
@@ -248,8 +255,8 @@ struct thread_options {
        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];
@@ -397,11 +404,13 @@ struct thread_options_pack {
        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;
@@ -498,6 +507,12 @@ struct thread_options_pack {
 
        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];
@@ -510,8 +525,8 @@ struct thread_options_pack {
        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];
diff --git a/time.c b/time.c
index f1c5d3fe613e85c4ce0713701aee5c45afdcabdd..f5dc04969f3d47020fb4f61fa3ddf5dcfcdf0e09 100644 (file)
--- a/time.c
+++ b/time.c
@@ -151,6 +151,17 @@ void set_genesis_time(void)
        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));
diff --git a/tools/fio.service b/tools/fio.service
new file mode 100644 (file)
index 0000000..21de0b7
--- /dev/null
@@ -0,0 +1,10 @@
+[Unit]
+
+Description=flexible I/O tester server
+After=network.target
+
+[Service]
+
+Type=simple
+PIDFile=/run/fio.pid
+ExecStart=/usr/bin/fio --server
index ce98d2ecd75581bc66e8c3de3b94da91690fe1c1..ead5e543eff1a835195bf67422fe428a0ffff013 100755 (executable)
             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
@@ -188,23 +81,8 @@ __HIST_COLUMNS = 1216
 __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. """
@@ -212,32 +90,20 @@ def read_chunk(head_row, rdr, sz):
     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:
@@ -245,13 +111,13 @@ def histogram_generator(ctx, fps, sz):
             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:
@@ -259,13 +125,12 @@ def histogram_generator(ctx, fps, sz):
             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
@@ -390,6 +255,29 @@ def guess_max_from_bins(ctx, hist_cols):
 
 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:
@@ -426,6 +314,14 @@ def main(ctx):
             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
@@ -456,9 +352,8 @@ if __name__ == '__main__':
         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,
@@ -471,16 +366,23 @@ if __name__ == '__main__':
         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())
 
diff --git a/tools/hist/fiologparser_hist.py.1 b/tools/hist/fiologparser_hist.py.1
new file mode 100644 (file)
index 0000000..ed22c74
--- /dev/null
@@ -0,0 +1,201 @@
+.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>.
diff --git a/trim.c b/trim.c
index 434554129453915515a5b64d47bbed6e914bf765..78cf67244efa22534604fd6b6652379457a571a2 100644 (file)
--- a/trim.c
+++ b/trim.c
@@ -11,7 +11,7 @@
 #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;
 
@@ -19,9 +19,9 @@ int get_next_trim(struct thread_data *td, struct io_u *io_u)
         * 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);
@@ -53,7 +53,7 @@ int get_next_trim(struct thread_data *td, struct io_u *io_u)
                if (r) {
                        dprint(FD_VERIFY, "failed file %s open\n",
                                        io_u->file->file_name);
-                       return 1;
+                       return false;
                }
        }
 
@@ -64,17 +64,17 @@ int get_next_trim(struct thread_data *td, struct io_u *io_u)
        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);
diff --git a/trim.h b/trim.h
index 6584606253f8edcab29e65d70f6bf23a6d49bc8d..37f5d7c871127a8bf5858a3fda620684a0e8438d 100644 (file)
--- a/trim.h
+++ b/trim.h
@@ -4,8 +4,8 @@
 #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
@@ -20,13 +20,13 @@ static inline void remove_trim_entry(struct thread_data *td, struct io_piece *ip
 }
 
 #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)
 {
index 901aa0a47bcec6d016485e4c6b518c8fa65171d0..e46265e48d7a6c4ddbbac34a458e26d3272d60da 100644 (file)
@@ -3,6 +3,7 @@
 
 #include <stdint.h>
 #include <string.h>
+#include <limits.h>
 
 struct thread_rand32_state {
        uint32_t s[4];
index 3d9e26879f0adb914298386b02f9e3c4e9ea2416..790ab31d3cddd68c0aa4379c9d811d048956efa1 100644 (file)
--- a/verify.c
+++ b/verify.c
@@ -41,13 +41,14 @@ void fill_buffer_pattern(struct thread_data *td, void *p, unsigned int len)
        (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;
index 2e01b584544b7434ae68081bf4282f8afa2e8a5c..1131400fd23a5acc8479c813fef0135c4987cf30 100644 (file)
@@ -15,9 +15,8 @@ enum {
        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,
@@ -131,7 +130,7 @@ static void *worker_thread(void *data)
 {
        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);
@@ -206,9 +205,6 @@ handle_work:
                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;
 }
@@ -327,6 +323,8 @@ int workqueue_init(struct thread_data *td, struct workqueue *wq,
                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))