Merge branch 'master' into client-server
authorJens Axboe <axboe@kernel.dk>
Wed, 12 Oct 2011 07:47:25 +0000 (09:47 +0200)
committerJens Axboe <axboe@kernel.dk>
Wed, 12 Oct 2011 07:47:25 +0000 (09:47 +0200)
Conflicts:
init.c
io_u.c
os/windows/install.wxs
os/windows/version.h

Signed-off-by: Jens Axboe <axboe@kernel.dk>
60 files changed:
HOWTO
Makefile
README
SERVER-TODO [new file with mode: 0644]
arch/arch-alpha.h
arch/arch-arm.h
arch/arch-generic.h
arch/arch-hppa.h
arch/arch-ia64.h
arch/arch-mips.h
arch/arch-ppc.h
arch/arch-s390.h
arch/arch-sh.h
arch/arch-sparc.h
arch/arch-sparc64.h
arch/arch-x86.h
arch/arch-x86_64.h
arch/arch.h
client.c [new file with mode: 0644]
crc/crc16.c
crc/crc16.h
debug.c
debug.h
engines/net.c
eta.c
examples/netio
filehash.c
fio.1
fio.c
fio.h
fio_generate_plots
fio_version.h [new file with mode: 0644]
hash.h
ieee754.c [new file with mode: 0644]
ieee754.h [new file with mode: 0644]
init.c
io_u.c
iolog.c [new file with mode: 0644]
iolog.h
log.c
log.h
options.c
os/os-aix.h
os/os-freebsd.h
os/os-hpux.h
os/os-linux.h
os/os-mac.h
os/os-netbsd.h
os/os-solaris.h
os/os-windows.h
os/os.h
os/windows/install.wxs
os/windows/version.h
parse.c
server.c [new file with mode: 0644]
server.h [new file with mode: 0644]
stat.c
stat.h [new file with mode: 0644]
t/ieee754.c [new file with mode: 0644]
t/log.c [new file with mode: 0644]

diff --git a/HOWTO b/HOWTO
index cc2df9b72421486d96d47e2d72220a0a5848c55a..2e4cdbdf146e17394a8ebcd481097e43846131ba 100644 (file)
--- a/HOWTO
+++ b/HOWTO
@@ -267,7 +267,7 @@ filename=str        Fio normally makes up a filename based on the job name,
                files between threads in a job or several jobs, specify
                a filename for each of them to override the default. If
                the ioengine used is 'net', the filename is the host, port,
-               and protocol to use in the format of =host/port/protocol.
+               and protocol to use in the format of =host,port,protocol.
                See ioengine=net for more. If the ioengine is file based, you
                can specify a number of files by separating the names with a
                ':' colon. So if you wanted a job to open /dev/sda and /dev/sdb
@@ -864,6 +864,9 @@ exitall             When one job finishes, terminate the rest. The default is
 bwavgtime=int  Average the calculated bandwidth over the given time. Value
                is specified in milliseconds.
 
+iopsavgtime=int        Average the calculated IOPS over the given time. Value
+               is specified in milliseconds.
+
 create_serialize=bool  If true, serialize the file creating for the jobs.
                        This may be handy to avoid interleaving of data
                        files, which may greatly depend on the filesystem
@@ -1104,6 +1107,9 @@ write_lat_log=str Same as write_bw_log, except that this option stores io
                and foo_lat.log. This helps fio_generate_plot fine the logs
                automatically.
 
+write_bw_log=str If given, write an IOPS log of the jobs in this job
+               file. See write_bw_log.
+
 lockmem=int    Pin down the specified amount of memory with mlock(2). Can
                potentially be used instead of removing memory or booting
                with less memory to simulate a smaller amount of memory.
index 85942a0aa822f26f9cad784da9ef11284a648b49..8b8898c9909fa8058dcbb45497d5d2f23d079771 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -2,7 +2,7 @@ CC      = gcc
 DEBUGFLAGS = -D_FORTIFY_SOURCE=2 -DFIO_INC_DEBUG
 CPPFLAGS= -D_GNU_SOURCE -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 \
        $(DEBUGFLAGS)
-OPTFLAGS= -O2 -fno-omit-frame-pointer -g $(EXTFLAGS)
+OPTFLAGS= -O3 -fno-omit-frame-pointer -g $(EXTFLAGS)
 CFLAGS = -std=gnu99 -Wwrite-strings -Wall $(OPTFLAGS)
 LIBS   = -lm $(EXTLIBS)
 PROGS  = fio
@@ -14,7 +14,7 @@ SOURCE = gettime.c fio.c ioengines.c init.c stat.c log.c time.c filesetup.c \
                rbtree.c smalloc.c filehash.c profile.c debug.c lib/rand.c \
                lib/num2str.c $(wildcard crc/*.c) engines/cpu.c \
                engines/mmap.c engines/sync.c engines/null.c engines/net.c \
-               memalign.c
+               memalign.c server.c client.c iolog.c ieee754.c
 
 ifeq ($(UNAME), Linux)
   SOURCE += diskutil.c fifo.c blktrace.c helpers.c cgroup.c trim.c \
@@ -22,7 +22,7 @@ ifeq ($(UNAME), Linux)
                engines/splice.c engines/syslet-rw.c engines/guasi.c \
                engines/binject.c engines/rdma.c profiles/tiobench.c
   LIBS += -lpthread -ldl -lrt -laio
-  CFLAGS += -rdynamic
+  LDFLAGS += -rdynamic
 endif
 ifeq ($(UNAME), SunOS)
   SOURCE += fifo.c lib/strsep.c helpers.c engines/posixaio.c \
@@ -33,12 +33,12 @@ endif
 ifeq ($(UNAME), FreeBSD)
   SOURCE += helpers.c engines/posixaio.c
   LIBS  += -lpthread -lrt
-  CFLAGS += -rdynamic
+  LDFLAGS += -rdynamic
 endif
 ifeq ($(UNAME), NetBSD)
   SOURCE += helpers.c engines/posixaio.c
   LIBS  += -lpthread -lrt
-  CFLAGS += -rdynamic
+  LDFLAGS += -rdynamic
 endif
 ifeq ($(UNAME), AIX)
   SOURCE += fifo.c helpers.c lib/getopt_long.c engines/posixaio.c
@@ -63,9 +63,16 @@ endif
 
 OBJS = $(SOURCE:.c=.o)
 
-T_OBJS = t/stest.o
-T_OBJS += mutex.o smalloc.o
-T_PROGS = t/stest
+T_SMALLOC_OBJS = t/stest.o
+T_SMALLOC_OBJS += mutex.o smalloc.o t/log.o
+T_SMALLOC_PROGS = t/stest
+
+T_IEEE_OBJS = t/ieee754.o
+T_IEEE_OBJS += ieee754.o
+T_IEEE_PROGS = t/ieee754
+
+T_OBJS = $(T_SMALLOC_OBJS)
+T_OBJS += $(T_IEEE_OBJS)
 
 ifneq ($(findstring $(MAKEFLAGS),s),s)
 ifndef V
@@ -84,8 +91,11 @@ all: .depend $(PROGS) $(SCRIPTS)
 .c.o: .depend
        $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) $(CPPFLAGS) $<
 
-t/stest: $(T_OBJS)
-       $(QUIET_CC)$(CC) $(LDFLAGS) $(CFLAGS) -o $@ $(T_OBJS) $(LIBS) $(LDFLAGS)
+t/stest: $(T_SMALLOC_OBJS)
+       $(QUIET_CC)$(CC) $(LDFLAGS) $(CFLAGS) -o $@ $(T_SMALLOC_OBJS) $(LIBS) $(LDFLAGS)
+
+t/ieee754: $(T_IEEE_OBJS)
+       $(QUIET_CC)$(CC) $(LDFLAGS) $(CFLAGS) -o $@ $(T_IEEE_OBJS) $(LIBS) $(LDFLAGS)
 
 fio: $(OBJS)
        $(QUIET_CC)$(CC) $(LDFLAGS) $(CFLAGS) -o $@ $(OBJS) $(LIBS) $(LDFLAGS)
diff --git a/README b/README
index 0eac41fe9da051a5979cc43adb77532003bb43dd..26b59099e5e86886a8255a0a36a34fb7b36d2574 100644 (file)
--- a/README
+++ b/README
@@ -133,23 +133,25 @@ $ fio
        --debug                 Enable some debugging options (see below)
        --output                Write output to file
        --timeout               Runtime in seconds
-       --latency-log   Generate per-job latency logs
-       --bandwidth-log Generate per-job bandwidth logs
+       --latency-log           Generate per-job latency logs
+       --bandwidth-log         Generate per-job bandwidth logs
        --minimal               Minimal (terse) output
        --version               Print version info and exit
        --terse-version=type    Terse version output format
        --help                  Print this page
-       --cmdhelp=cmd   Print command help, "all" for all of them
+       --cmdhelp=cmd           Print command help, "all" for all of them
        --showcmd               Turn a job file into command line options
        --readonly              Turn on safety read-only checks, preventing
-                                       writes
+                               writes
        --eta=when              When ETA estimate should be printed
-                                       May be "always", "never" or "auto"
-       --section=name  Only run specified section in job file. Multiple
-                               sections can be specified.
+                               May be "always", "never" or "auto"
+       --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)
        --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.
+       --client=host           Connect to specified backend.
 
 
 Any parameters following the options will be assumed to be job files,
@@ -315,6 +317,59 @@ The job file parameters are:
 
 
 
+Client/server
+------------
+
+Normally you would run fio as a stand-alone application on the machine
+where the IO workload should be generated. However, it is also possible to
+run the frontend and backend of fio separately. This makes it possible to
+have a fio server running on the machine(s) where the IO workload should
+be running, while controlling it from another machine.
+
+To start the server, you would do:
+
+fio --server=args
+
+on that machine, where args defines what fio listens to. The arguments
+are of the form 'type:hostname or IP:port'. 'type' is either 'ip' for
+TCP/IP, or 'sock' for a local unix domain socket. 'hostname' is either
+a hostname or IP address, and 'port' is the port to listen to (only valid
+for TCP/IP, not a local socket). Some examples:
+
+1) fio --server
+
+   Start a fio server, listening on all interfaces on the default port (8765).
+
+2) fio --server=ip:hostname:4444
+
+   Start a fio server, listening on IP belonging to hostname and on port 4444.
+
+3) fio --server=:4444
+
+   Start a fio server, listening on all interfaces on port 4444.
+
+4) fio --server=1.2.3.4
+
+   Start a fio server, listening on IP 1.2.3.4 on the default port.
+
+5) fio --server=sock:/tmp/fio.sock
+
+   Start a fio server, listening on the local socket /tmp/fio.sock.
+
+When a server is running, you can connect to it from a client. The client
+is run with:
+
+fio --local-args --client=server --remote-args <job file(s)>
+
+where --local-args are arguments that are local to the client where it is
+running, 'server' is the connect string, and --remote-args and <job file(s)>
+are sent to the server. The 'server' string follows the same format as it
+does on the server side, to allow IP/hostname/socket and port strings.
+You can connect to multiple clients as well, to do that you could run:
+
+fio --client=server2 --client=server2 <job file(s)>
+
+
 Platforms
 ---------
 
diff --git a/SERVER-TODO b/SERVER-TODO
new file mode 100644 (file)
index 0000000..bc52988
--- /dev/null
@@ -0,0 +1,3 @@
+- Better error handling (test it)
+- Collate ETA output from multiple connections into 1
+- If group_reporting is set, collate final output from multiple connections
index e8132a048a1a56d90892957eb627107c72d546d2..c0f784f287baa2787da007a402c2880c98912071 100644 (file)
@@ -1,7 +1,7 @@
 #ifndef ARCH_ALPHA_H
 #define ARCH_ALPHA_H
 
-#define ARCH   (arch_alpha)
+#define FIO_ARCH       (arch_alpha)
 
 #ifndef __NR_ioprio_set
 #define __NR_ioprio_set                442
index b0cfd8087efbcd3361f087c2c960e444484f8203..658b6884e6cf57a5da054a4936c0425438caadc4 100644 (file)
@@ -1,7 +1,7 @@
 #ifndef ARCH_ARM_H
 #define ARCH_ARM_H
 
-#define ARCH   (arch_arm)
+#define FIO_ARCH       (arch_arm)
 
 #ifndef __NR_ioprio_set
 #define __NR_ioprio_set                314
index c7b0ca0f0e0b8fb4ee6700d879ccb625a128cb0c..a0b71f8c588dfde1acb38457bb9056ba3315a685 100644 (file)
@@ -1,7 +1,7 @@
 #ifndef ARCH_GENERIC_H
 #define ARCH_GENERIC_H
 
-#define ARCH   (arch_generic)
+#define FIO_ARCH       (arch_generic)
 
 #define nop                    do { } while (0)
 #define read_barrier()         __asm__ __volatile__("": : :"memory")
index c865a891a6afb18b45227deaf6064ac856c995a3..c1c079ed0165757c51e7a2be6c156f60d65a2698 100644 (file)
@@ -1,7 +1,7 @@
 #ifndef ARCH_HPPA_H
 #define ARCH_HPPA_H
 
-#define ARCH   (arch_hppa)
+#define FIO_ARCH       (arch_hppa)
 
 #ifndef __NR_ioprio_set
 #define __NR_ioprio_set                267
index 056f6365597424240c73d3129f8d6b74ac1e76f5..f4464c48b2fa146b6386d8c49cab5179f7484692 100644 (file)
@@ -1,7 +1,7 @@
 #ifndef ARCH_IA64_H
 #define ARCH_IA64_H
 
-#define ARCH   (arch_ia64)
+#define FIO_ARCH       (arch_ia64)
 
 #ifndef __NR_ioprio_set
 #define __NR_ioprio_set                1274
index 759d3a9fba0fae0d643d0a85b17fa5a5aaba0297..0b781d1dad3d794b2a7c8363d95112f4a5e46fd0 100644 (file)
@@ -1,7 +1,7 @@
 #ifndef ARCH_MIPS64_H
 #define ARCH_MIPS64_H
 
-#define ARCH   (arch_mips)
+#define FIO_ARCH       (arch_mips)
 
 #ifndef __NR_ioprio_set
 #define __NR_ioprio_set                314
index d495a1b21aff4df9e4bd16c71f47435c04fa88dc..b790a55e898163cfc317c860403577f8b8bebb6c 100644 (file)
@@ -1,7 +1,7 @@
 #ifndef ARCH_PPC_H
 #define ARCH_PPH_H
 
-#define ARCH   (arch_ppc)
+#define FIO_ARCH       (arch_ppc)
 
 #ifndef __NR_ioprio_set
 #define __NR_ioprio_set                273
index 06477503be02859a41bd3186d1c2666ed7abc64f..fe5179134891d66d944797ee8132f2a306e4513c 100644 (file)
@@ -1,7 +1,7 @@
 #ifndef ARCH_S390_H
 #define ARCH_S390_H
 
-#define ARCH   (arch_s390)
+#define FIO_ARCH       (arch_s390)
 
 #ifndef __NR_ioprio_set
 #define __NR_ioprio_set                282
index f5f313d8f749d32974cf5e02924556aef7b30556..9acbbbee9c3fe3e0a93c146e2c3e161817f0a78a 100644 (file)
@@ -3,7 +3,7 @@
 #ifndef ARCH_SH_H
 #define ARCH_SH_H
 
-#define ARCH   (arch_sh)
+#define FIO_ARCH       (arch_sh)
 
 #ifndef __NR_ioprio_set
 #define __NR_ioprio_set        288
index cd552ab7c94fd8ce613e64b38739a4be6741c2f7..fe47b80374e6d1afb9a843134f46adfb0081de29 100644 (file)
@@ -1,7 +1,7 @@
 #ifndef ARCH_SPARC_H
 #define ARCH_SPARC_H
 
-#define ARCH   (arch_sparc)
+#define FIO_ARCH       (arch_sparc)
 
 #ifndef __NR_ioprio_set
 #define __NR_ioprio_set                196
index 332cf9179388377ebfc12bf85458f1dd6b73d5ea..e793ae52b7ee0cc100f02d910491f16e81d0bb2e 100644 (file)
@@ -1,7 +1,7 @@
 #ifndef ARCH_SPARC64_H
 #define ARCH_SPARC64_H
 
-#define ARCH   (arch_sparc64)
+#define FIO_ARCH       (arch_sparc64)
 
 #ifndef __NR_ioprio_set
 #define __NR_ioprio_set                196
index 2e803cbe5ba13c96e2f377516dca6def34bc013f..1ededd845a869b8751c64ed0a06e5bd41aad3273 100644 (file)
@@ -1,7 +1,7 @@
 #ifndef ARCH_X86_H
 #define ARCH_X86_H
 
-#define ARCH   (arch_i386)
+#define FIO_ARCH       (arch_i386)
 
 #ifndef __NR_ioprio_set
 #define __NR_ioprio_set                289
index f2dcf497f63ec89ca88c8c3817de407c7a1d80c9..29e681f2f8533926451e731460b69a499fc0aa53 100644 (file)
@@ -1,7 +1,7 @@
 #ifndef ARCH_X86_64_h
 #define ARCH_X86_64_h
 
-#define ARCH   (arch_x86_64)
+#define FIO_ARCH       (arch_x86_64)
 
 #ifndef __NR_ioprio_set
 #define __NR_ioprio_set                251
index d598652bb2e8ced3a44769d6d72fa440e3ade079..4ad49a42b74f0c5b08e4f245f49571ea0b96fc5e 100644 (file)
@@ -8,7 +8,7 @@
 #endif
 
 enum {
-       arch_x86_64,
+       arch_x86_64 = 1,
        arch_i386,
        arch_ppc,
        arch_ia64,
@@ -21,6 +21,8 @@ enum {
        arch_hppa,
 
        arch_generic,
+
+       arch_nr,
 };
 
 enum {
diff --git a/client.c b/client.c
new file mode 100644 (file)
index 0000000..e443dc3
--- /dev/null
+++ b/client.c
@@ -0,0 +1,942 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <limits.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/poll.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <signal.h>
+
+#include "fio.h"
+#include "server.h"
+#include "flist.h"
+#include "hash.h"
+
+struct client_eta {
+       struct jobs_eta eta;
+       unsigned int pending;
+};
+
+struct fio_client {
+       struct flist_head list;
+       struct flist_head hash_list;
+       struct flist_head arg_list;
+       struct sockaddr_in addr;
+       struct sockaddr_un addr_un;
+       char *hostname;
+       int port;
+       int fd;
+
+       char *name;
+
+       int state;
+
+       int skip_newline;
+       int is_sock;
+
+       struct flist_head eta_list;
+       struct client_eta *eta_in_flight;
+
+       struct flist_head cmd_list;
+
+       uint16_t argc;
+       char **argv;
+};
+
+static struct timeval eta_tv;
+
+enum {
+       Client_created          = 0,
+       Client_connected        = 1,
+       Client_started          = 2,
+       Client_stopped          = 3,
+       Client_exited           = 4,
+};
+
+static FLIST_HEAD(client_list);
+static FLIST_HEAD(eta_list);
+
+static FLIST_HEAD(arg_list);
+
+static struct thread_stat client_ts;
+static struct group_run_stats client_gs;
+static int sum_stat_clients;
+static int sum_stat_nr;
+
+#define FIO_CLIENT_HASH_BITS   7
+#define FIO_CLIENT_HASH_SZ     (1 << FIO_CLIENT_HASH_BITS)
+#define FIO_CLIENT_HASH_MASK   (FIO_CLIENT_HASH_SZ - 1)
+static struct flist_head client_hash[FIO_CLIENT_HASH_SZ];
+
+static int handle_client(struct fio_client *client);
+static void dec_jobs_eta(struct client_eta *eta);
+
+static void fio_client_add_hash(struct fio_client *client)
+{
+       int bucket = hash_long(client->fd, FIO_CLIENT_HASH_BITS);
+
+       bucket &= FIO_CLIENT_HASH_MASK;
+       flist_add(&client->hash_list, &client_hash[bucket]);
+}
+
+static void fio_client_remove_hash(struct fio_client *client)
+{
+       if (!flist_empty(&client->hash_list))
+               flist_del_init(&client->hash_list);
+}
+
+static void fio_init fio_client_hash_init(void)
+{
+       int i;
+
+       for (i = 0; i < FIO_CLIENT_HASH_SZ; i++)
+               INIT_FLIST_HEAD(&client_hash[i]);
+}
+
+static struct fio_client *find_client_by_fd(int fd)
+{
+       int bucket = hash_long(fd, FIO_CLIENT_HASH_BITS) & FIO_CLIENT_HASH_MASK;
+       struct fio_client *client;
+       struct flist_head *entry;
+
+       flist_for_each(entry, &client_hash[bucket]) {
+               client = flist_entry(entry, struct fio_client, hash_list);
+
+               if (client->fd == fd)
+                       return client;
+       }
+
+       return NULL;
+}
+
+static void remove_client(struct fio_client *client)
+{
+       dprint(FD_NET, "client: removed <%s>\n", client->hostname);
+       flist_del(&client->list);
+
+       fio_client_remove_hash(client);
+
+       if (!flist_empty(&client->eta_list)) {
+               flist_del_init(&client->eta_list);
+               dec_jobs_eta(client->eta_in_flight);
+       }
+
+       free(client->hostname);
+       if (client->argv)
+               free(client->argv);
+       if (client->name)
+               free(client->name);
+
+       free(client);
+       nr_clients--;
+       sum_stat_clients--;
+}
+
+static void __fio_client_add_cmd_option(struct fio_client *client,
+                                       const char *opt)
+{
+       int index;
+
+       index = client->argc++;
+       client->argv = realloc(client->argv, sizeof(char *) * client->argc);
+       client->argv[index] = strdup(opt);
+       dprint(FD_NET, "client: add cmd %d: %s\n", index, opt);
+}
+
+void fio_client_add_cmd_option(void *cookie, const char *opt)
+{
+       struct fio_client *client = cookie;
+       struct flist_head *entry;
+
+       if (!client || !opt)
+               return;
+
+       __fio_client_add_cmd_option(client, opt);
+
+       /*
+        * Duplicate arguments to shared client group
+        */
+       flist_for_each(entry, &arg_list) {
+               client = flist_entry(entry, struct fio_client, arg_list);
+
+               __fio_client_add_cmd_option(client, opt);
+       }
+}
+
+int fio_client_add(const char *hostname, void **cookie)
+{
+       struct fio_client *existing = *cookie;
+       struct fio_client *client;
+
+       if (existing) {
+               /*
+                * We always add our "exec" name as the option, hence 1
+                * means empty.
+                */
+               if (existing->argc == 1)
+                       flist_add_tail(&existing->arg_list, &arg_list);
+               else {
+                       while (!flist_empty(&arg_list))
+                               flist_del_init(arg_list.next);
+               }
+       }
+
+       client = malloc(sizeof(*client));
+       memset(client, 0, sizeof(*client));
+
+       INIT_FLIST_HEAD(&client->list);
+       INIT_FLIST_HEAD(&client->hash_list);
+       INIT_FLIST_HEAD(&client->arg_list);
+       INIT_FLIST_HEAD(&client->eta_list);
+       INIT_FLIST_HEAD(&client->cmd_list);
+
+       if (fio_server_parse_string(hostname, &client->hostname,
+                                       &client->is_sock, &client->port,
+                                       &client->addr.sin_addr))
+               return -1;
+
+       client->fd = -1;
+
+       __fio_client_add_cmd_option(client, "fio");
+
+       flist_add(&client->list, &client_list);
+       nr_clients++;
+       dprint(FD_NET, "client: added <%s>\n", client->hostname);
+       *cookie = client;
+       return 0;
+}
+
+static int fio_client_connect_ip(struct fio_client *client)
+{
+       int fd;
+
+       client->addr.sin_family = AF_INET;
+       client->addr.sin_port = htons(client->port);
+
+       fd = socket(AF_INET, SOCK_STREAM, 0);
+       if (fd < 0) {
+               log_err("fio: socket: %s\n", strerror(errno));
+               return -1;
+       }
+
+       if (connect(fd, (struct sockaddr *) &client->addr, sizeof(client->addr)) < 0) {
+               log_err("fio: connect: %s\n", strerror(errno));
+               log_err("fio: failed to connect to %s:%u\n", client->hostname,
+                                                               client->port);
+               close(fd);
+               return -1;
+       }
+
+       return fd;
+}
+
+static int fio_client_connect_sock(struct fio_client *client)
+{
+       struct sockaddr_un *addr = &client->addr_un;
+       fio_socklen_t len;
+       int fd;
+
+       memset(addr, 0, sizeof(*addr));
+       addr->sun_family = AF_UNIX;
+       strcpy(addr->sun_path, client->hostname);
+
+       fd = socket(AF_UNIX, SOCK_STREAM, 0);
+       if (fd < 0) {
+               log_err("fio: socket: %s\n", strerror(errno));
+               return -1;
+       }
+
+       len = sizeof(addr->sun_family) + strlen(addr->sun_path) + 1;
+       if (connect(fd, (struct sockaddr *) addr, len) < 0) {
+               log_err("fio: connect; %s\n", strerror(errno));
+               close(fd);
+               return -1;
+       }
+
+       return fd;
+}
+
+static int fio_client_connect(struct fio_client *client)
+{
+       int fd;
+
+       dprint(FD_NET, "client: connect to host %s\n", client->hostname);
+
+       if (client->is_sock)
+               fd = fio_client_connect_sock(client);
+       else
+               fd = fio_client_connect_ip(client);
+
+       dprint(FD_NET, "client: %s connected %d\n", client->hostname, fd);
+
+       if (fd < 0)
+               return 1;
+
+       client->fd = fd;
+       fio_client_add_hash(client);
+       client->state = Client_connected;
+       return 0;
+}
+
+void fio_clients_terminate(void)
+{
+       struct flist_head *entry;
+       struct fio_client *client;
+
+       dprint(FD_NET, "client: terminate clients\n");
+
+       flist_for_each(entry, &client_list) {
+               client = flist_entry(entry, struct fio_client, list);
+
+               fio_net_send_simple_cmd(client->fd, FIO_NET_CMD_QUIT, 0, NULL);
+       }
+}
+
+static void sig_int(int sig)
+{
+       dprint(FD_NET, "client: got signal %d\n", sig);
+       fio_clients_terminate();
+}
+
+static void client_signal_handler(void)
+{
+       struct sigaction act;
+
+       memset(&act, 0, sizeof(act));
+       act.sa_handler = sig_int;
+       act.sa_flags = SA_RESTART;
+       sigaction(SIGINT, &act, NULL);
+
+       memset(&act, 0, sizeof(act));
+       act.sa_handler = sig_int;
+       act.sa_flags = SA_RESTART;
+       sigaction(SIGTERM, &act, NULL);
+}
+
+static void probe_client(struct fio_client *client)
+{
+       dprint(FD_NET, "client: send probe\n");
+
+       fio_net_send_simple_cmd(client->fd, FIO_NET_CMD_PROBE, 0, &client->cmd_list);
+}
+
+static int send_client_cmd_line(struct fio_client *client)
+{
+       struct cmd_single_line_pdu *cslp;
+       struct cmd_line_pdu *clp;
+       unsigned long offset;
+       unsigned int *lens;
+       void *pdu;
+       size_t mem;
+       int i, ret;
+
+       dprint(FD_NET, "client: send cmdline %d\n", client->argc);
+
+       lens = malloc(client->argc * sizeof(unsigned int));
+
+       /*
+        * Find out how much mem we need
+        */
+       for (i = 0, mem = 0; i < client->argc; i++) {
+               lens[i] = strlen(client->argv[i]) + 1;
+               mem += lens[i];
+       }
+
+       /*
+        * We need one cmd_line_pdu, and argc number of cmd_single_line_pdu
+        */
+       mem += sizeof(*clp) + (client->argc * sizeof(*cslp));
+
+       pdu = malloc(mem);
+       clp = pdu;
+       offset = sizeof(*clp);
+
+       for (i = 0; i < client->argc; i++) {
+               uint16_t arg_len = lens[i];
+
+               cslp = pdu + offset;
+               strcpy((char *) cslp->text, client->argv[i]);
+               cslp->len = cpu_to_le16(arg_len);
+               offset += sizeof(*cslp) + arg_len;
+       }
+
+       free(lens);
+       clp->lines = cpu_to_le16(client->argc);
+       ret = fio_net_send_cmd(client->fd, FIO_NET_CMD_JOBLINE, pdu, mem, 0);
+       free(pdu);
+       return ret;
+}
+
+int fio_clients_connect(void)
+{
+       struct fio_client *client;
+       struct flist_head *entry, *tmp;
+       int ret;
+
+       dprint(FD_NET, "client: connect all\n");
+
+       client_signal_handler();
+
+       flist_for_each_safe(entry, tmp, &client_list) {
+               client = flist_entry(entry, struct fio_client, list);
+
+               ret = fio_client_connect(client);
+               if (ret) {
+                       remove_client(client);
+                       continue;
+               }
+
+               probe_client(client);
+
+               if (client->argc > 1)
+                       send_client_cmd_line(client);
+       }
+
+       return !nr_clients;
+}
+
+/*
+ * Send file contents to server backend. We could use sendfile(), but to remain
+ * more portable lets just read/write the darn thing.
+ */
+static int fio_client_send_ini(struct fio_client *client, const char *filename)
+{
+       struct stat sb;
+       char *p, *buf;
+       off_t len;
+       int fd, ret;
+
+       dprint(FD_NET, "send ini %s to %s\n", filename, client->hostname);
+
+       fd = open(filename, O_RDONLY);
+       if (fd < 0) {
+               log_err("fio: job file <%s> open: %s\n", filename, strerror(errno));
+               return 1;
+       }
+
+       if (fstat(fd, &sb) < 0) {
+               log_err("fio: job file stat: %s\n", strerror(errno));
+               close(fd);
+               return 1;
+       }
+
+       buf = malloc(sb.st_size);
+
+       len = sb.st_size;
+       p = buf;
+       do {
+               ret = read(fd, p, len);
+               if (ret > 0) {
+                       len -= ret;
+                       if (!len)
+                               break;
+                       p += ret;
+                       continue;
+               } else if (!ret)
+                       break;
+               else if (errno == EAGAIN || errno == EINTR)
+                       continue;
+       } while (1);
+
+       if (len) {
+               log_err("fio: failed reading job file %s\n", filename);
+               close(fd);
+               free(buf);
+               return 1;
+       }
+
+       ret = fio_net_send_cmd(client->fd, FIO_NET_CMD_JOB, buf, sb.st_size, 0);
+       free(buf);
+       close(fd);
+       return ret;
+}
+
+int fio_clients_send_ini(const char *filename)
+{
+       struct fio_client *client;
+       struct flist_head *entry, *tmp;
+
+       flist_for_each_safe(entry, tmp, &client_list) {
+               client = flist_entry(entry, struct fio_client, list);
+
+               if (fio_client_send_ini(client, filename))
+                       remove_client(client);
+       }
+
+       return !nr_clients;
+}
+
+static void convert_io_stat(struct io_stat *dst, struct io_stat *src)
+{
+       dst->max_val    = le64_to_cpu(src->max_val);
+       dst->min_val    = le64_to_cpu(src->min_val);
+       dst->samples    = le64_to_cpu(src->samples);
+
+       /*
+        * Floats arrive as IEEE 754 encoded uint64_t, convert back to double
+        */
+       dst->mean.u.f   = fio_uint64_to_double(le64_to_cpu(dst->mean.u.i));
+       dst->S.u.f      = fio_uint64_to_double(le64_to_cpu(dst->S.u.i));
+}
+
+static void convert_ts(struct thread_stat *dst, struct thread_stat *src)
+{
+       int i, j;
+
+       dst->error      = le32_to_cpu(src->error);
+       dst->groupid    = le32_to_cpu(src->groupid);
+       dst->pid        = le32_to_cpu(src->pid);
+       dst->members    = le32_to_cpu(src->members);
+
+       for (i = 0; i < 2; i++) {
+               convert_io_stat(&dst->clat_stat[i], &src->clat_stat[i]);
+               convert_io_stat(&dst->slat_stat[i], &src->slat_stat[i]);
+               convert_io_stat(&dst->lat_stat[i], &src->lat_stat[i]);
+               convert_io_stat(&dst->bw_stat[i], &src->bw_stat[i]);
+       }
+
+       dst->usr_time           = le64_to_cpu(src->usr_time);
+       dst->sys_time           = le64_to_cpu(src->sys_time);
+       dst->ctx                = le64_to_cpu(src->ctx);
+       dst->minf               = le64_to_cpu(src->minf);
+       dst->majf               = le64_to_cpu(src->majf);
+       dst->clat_percentiles   = le64_to_cpu(src->clat_percentiles);
+
+       for (i = 0; i < FIO_IO_U_LIST_MAX_LEN; i++) {
+               fio_fp64_t *fps = &src->percentile_list[i];
+               fio_fp64_t *fpd = &dst->percentile_list[i];
+
+               fpd->u.f = fio_uint64_to_double(le64_to_cpu(fps->u.i));
+       }
+
+       for (i = 0; i < FIO_IO_U_MAP_NR; i++) {
+               dst->io_u_map[i]        = le32_to_cpu(src->io_u_map[i]);
+               dst->io_u_submit[i]     = le32_to_cpu(src->io_u_submit[i]);
+               dst->io_u_complete[i]   = le32_to_cpu(src->io_u_complete[i]);
+       }
+
+       for (i = 0; i < FIO_IO_U_LAT_U_NR; i++) {
+               dst->io_u_lat_u[i]      = le32_to_cpu(src->io_u_lat_u[i]);
+               dst->io_u_lat_m[i]      = le32_to_cpu(src->io_u_lat_m[i]);
+       }
+
+       for (i = 0; i < 2; i++)
+               for (j = 0; j < FIO_IO_U_PLAT_NR; j++)
+                       dst->io_u_plat[i][j] = le32_to_cpu(src->io_u_plat[i][j]);
+
+       for (i = 0; i < 3; i++) {
+               dst->total_io_u[i]      = le64_to_cpu(src->total_io_u[i]);
+               dst->short_io_u[i]      = le64_to_cpu(src->short_io_u[i]);
+       }
+
+       dst->total_submit       = le64_to_cpu(src->total_submit);
+       dst->total_complete     = le64_to_cpu(src->total_complete);
+
+       for (i = 0; i < 2; i++) {
+               dst->io_bytes[i]        = le64_to_cpu(src->io_bytes[i]);
+               dst->runtime[i]         = le64_to_cpu(src->runtime[i]);
+       }
+
+       dst->total_run_time     = le64_to_cpu(src->total_run_time);
+       dst->continue_on_error  = le16_to_cpu(src->continue_on_error);
+       dst->total_err_count    = le64_to_cpu(src->total_err_count);
+       dst->first_error        = le32_to_cpu(src->first_error);
+       dst->kb_base            = le32_to_cpu(src->kb_base);
+}
+
+static void convert_gs(struct group_run_stats *dst, struct group_run_stats *src)
+{
+       int i;
+
+       for (i = 0; i < 2; i++) {
+               dst->max_run[i]         = le64_to_cpu(src->max_run[i]);
+               dst->min_run[i]         = le64_to_cpu(src->min_run[i]);
+               dst->max_bw[i]          = le64_to_cpu(src->max_bw[i]);
+               dst->min_bw[i]          = le64_to_cpu(src->min_bw[i]);
+               dst->io_kb[i]           = le64_to_cpu(src->io_kb[i]);
+               dst->agg[i]             = le64_to_cpu(src->agg[i]);
+       }
+
+       dst->kb_base    = le32_to_cpu(src->kb_base);
+       dst->groupid    = le32_to_cpu(src->groupid);
+}
+
+static void handle_ts(struct fio_net_cmd *cmd)
+{
+       struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
+
+       convert_ts(&p->ts, &p->ts);
+       convert_gs(&p->rs, &p->rs);
+
+       show_thread_status(&p->ts, &p->rs);
+
+       if (sum_stat_clients == 1)
+               return;
+
+       sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
+       sum_group_stats(&client_gs, &p->rs);
+
+       client_ts.members++;
+       client_ts.groupid = p->ts.groupid;
+
+       if (++sum_stat_nr == sum_stat_clients) {
+               strcpy(client_ts.name, "All clients");
+               show_thread_status(&client_ts, &client_gs);
+       }
+}
+
+static void handle_gs(struct fio_net_cmd *cmd)
+{
+       struct group_run_stats *gs = (struct group_run_stats *) cmd->payload;
+
+       convert_gs(gs, gs);
+       show_group_stats(gs);
+}
+
+static void convert_jobs_eta(struct jobs_eta *je)
+{
+       int i;
+
+       je->nr_running          = le32_to_cpu(je->nr_running);
+       je->nr_ramp             = le32_to_cpu(je->nr_ramp);
+       je->nr_pending          = le32_to_cpu(je->nr_pending);
+       je->files_open          = le32_to_cpu(je->files_open);
+       je->m_rate              = le32_to_cpu(je->m_rate);
+       je->t_rate              = le32_to_cpu(je->t_rate);
+       je->m_iops              = le32_to_cpu(je->m_iops);
+       je->t_iops              = le32_to_cpu(je->t_iops);
+
+       for (i = 0; i < 2; i++) {
+               je->rate[i]     = le32_to_cpu(je->rate[i]);
+               je->iops[i]     = le32_to_cpu(je->iops[i]);
+       }
+
+       je->elapsed_sec         = le64_to_cpu(je->elapsed_sec);
+       je->eta_sec             = le64_to_cpu(je->eta_sec);
+}
+
+static void sum_jobs_eta(struct jobs_eta *dst, struct jobs_eta *je)
+{
+       int i;
+
+       dst->nr_running         += je->nr_running;
+       dst->nr_ramp            += je->nr_ramp;
+       dst->nr_pending         += je->nr_pending;
+       dst->files_open         += je->files_open;
+       dst->m_rate             += je->m_rate;
+       dst->t_rate             += je->t_rate;
+       dst->m_iops             += je->m_iops;
+       dst->t_iops             += je->t_iops;
+
+       for (i = 0; i < 2; i++) {
+               dst->rate[i]    += je->rate[i];
+               dst->iops[i]    += je->iops[i];
+       }
+
+       dst->elapsed_sec        += je->elapsed_sec;
+
+       if (je->eta_sec > dst->eta_sec)
+               dst->eta_sec = je->eta_sec;
+}
+
+static void dec_jobs_eta(struct client_eta *eta)
+{
+       if (!--eta->pending) {
+               display_thread_status(&eta->eta);
+               free(eta);
+       }
+}
+
+static void remove_reply_cmd(struct fio_client *client, struct fio_net_cmd *cmd)
+{
+       struct fio_net_int_cmd *icmd = NULL;
+       struct flist_head *entry;
+
+       flist_for_each(entry, &client->cmd_list) {
+               icmd = flist_entry(entry, struct fio_net_int_cmd, list);
+
+               if (cmd->tag == (uintptr_t) icmd)
+                       break;
+
+               icmd = NULL;
+       }
+
+       if (!icmd) {
+               log_err("fio: client: unable to find matching tag\n");
+               return;
+       }
+
+       flist_del(&icmd->list);
+       cmd->tag = icmd->saved_tag;
+       free(icmd);
+}
+
+static void handle_eta(struct fio_client *client, struct fio_net_cmd *cmd)
+{
+       struct jobs_eta *je = (struct jobs_eta *) cmd->payload;
+       struct client_eta *eta = (struct client_eta *) (uintptr_t) cmd->tag;
+
+       dprint(FD_NET, "client: got eta tag %p, %d\n", eta, eta->pending);
+
+       assert(client->eta_in_flight == eta);
+
+       client->eta_in_flight = NULL;
+       flist_del_init(&client->eta_list);
+
+       convert_jobs_eta(je);
+       sum_jobs_eta(&eta->eta, je);
+       dec_jobs_eta(eta);
+}
+
+static void handle_probe(struct fio_client *client, struct fio_net_cmd *cmd)
+{
+       struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
+       const char *os, *arch;
+       char bit[16];
+
+       os = fio_get_os_string(probe->os);
+       if (!os)
+               os = "unknown";
+
+       arch = fio_get_arch_string(probe->arch);
+       if (!arch)
+               os = "unknown";
+
+       sprintf(bit, "%d-bit", probe->bpp * 8);
+
+       log_info("hostname=%s, be=%u, %s, os=%s, arch=%s, fio=%u.%u.%u\n",
+               probe->hostname, probe->bigendian, bit, os, arch,
+               probe->fio_major, probe->fio_minor, probe->fio_patch);
+
+       if (!client->name)
+               client->name = strdup((char *) probe->hostname);
+}
+
+static int handle_client(struct fio_client *client)
+{
+       struct fio_net_cmd *cmd;
+
+       dprint(FD_NET, "client: handle %s\n", client->hostname);
+
+       cmd = fio_net_recv_cmd(client->fd);
+       if (!cmd)
+               return 0;
+
+       dprint(FD_NET, "client: got cmd op %s from %s\n",
+                               fio_server_op(cmd->opcode), client->hostname);
+
+       switch (cmd->opcode) {
+       case FIO_NET_CMD_QUIT:
+               remove_client(client);
+               free(cmd);
+               break;
+       case FIO_NET_CMD_TEXT: {
+               const char *buf = (const char *) cmd->payload;
+               const char *name;
+               int fio_unused ret;
+
+               name = client->name ? client->name : client->hostname;
+
+               if (!client->skip_newline)
+                       fprintf(f_out, "<%s> ", name);
+               ret = fwrite(buf, cmd->pdu_len, 1, f_out);
+               fflush(f_out);
+               client->skip_newline = strchr(buf, '\n') == NULL;
+               free(cmd);
+               break;
+               }
+       case FIO_NET_CMD_TS:
+               handle_ts(cmd);
+               free(cmd);
+               break;
+       case FIO_NET_CMD_GS:
+               handle_gs(cmd);
+               free(cmd);
+               break;
+       case FIO_NET_CMD_ETA:
+               remove_reply_cmd(client, cmd);
+               handle_eta(client, cmd);
+               free(cmd);
+               break;
+       case FIO_NET_CMD_PROBE:
+               remove_reply_cmd(client, cmd);
+               handle_probe(client, cmd);
+               free(cmd);
+               break;
+       case FIO_NET_CMD_START:
+               client->state = Client_started;
+               free(cmd);
+               break;
+       case FIO_NET_CMD_STOP:
+               client->state = Client_stopped;
+               free(cmd);
+               break;
+       default:
+               log_err("fio: unknown client op: %s\n", fio_server_op(cmd->opcode));
+               free(cmd);
+               break;
+       }
+
+       return 1;
+}
+
+static void request_client_etas(void)
+{
+       struct fio_client *client;
+       struct flist_head *entry;
+       struct client_eta *eta;
+       int skipped = 0;
+
+       dprint(FD_NET, "client: request eta (%d)\n", nr_clients);
+
+       eta = malloc(sizeof(*eta));
+       memset(&eta->eta, 0, sizeof(eta->eta));
+       eta->pending = nr_clients;
+
+       flist_for_each(entry, &client_list) {
+               client = flist_entry(entry, struct fio_client, list);
+
+               if (!flist_empty(&client->eta_list)) {
+                       skipped++;
+                       continue;
+               }
+
+               assert(!client->eta_in_flight);
+               flist_add_tail(&client->eta_list, &eta_list);
+               client->eta_in_flight = eta;
+               fio_net_send_simple_cmd(client->fd, FIO_NET_CMD_SEND_ETA,
+                                       (uintptr_t) eta, &client->cmd_list);
+       }
+
+       while (skipped--)
+               dec_jobs_eta(eta);
+
+       dprint(FD_NET, "client: requested eta tag %p\n", eta);
+}
+
+static int client_check_cmd_timeout(struct fio_client *client,
+                                   struct timeval *now)
+{
+       struct fio_net_int_cmd *cmd;
+       struct flist_head *entry, *tmp;
+       int ret = 0;
+
+       flist_for_each_safe(entry, tmp, &client->cmd_list) {
+               cmd = flist_entry(entry, struct fio_net_int_cmd, list);
+
+               if (mtime_since(&cmd->tv, now) < FIO_NET_CLIENT_TIMEOUT)
+                       continue;
+
+               log_err("fio: client %s, timeout on cmd %s\n", client->hostname,
+                                               fio_server_op(cmd->cmd.opcode));
+               flist_del(&cmd->list);
+               free(cmd);
+               ret = 1;
+       }
+
+       return flist_empty(&client->cmd_list) && ret;
+}
+
+static int fio_client_timed_out(void)
+{
+       struct fio_client *client;
+       struct flist_head *entry, *tmp;
+       struct timeval tv;
+       int ret = 0;
+
+       gettimeofday(&tv, NULL);
+
+       flist_for_each_safe(entry, tmp, &client_list) {
+               client = flist_entry(entry, struct fio_client, list);
+
+               if (flist_empty(&client->cmd_list))
+                       continue;
+
+               if (!client_check_cmd_timeout(client, &tv))
+                       continue;
+
+               log_err("fio: client %s timed out\n", client->hostname);
+               remove_client(client);
+               ret = 1;
+       }
+
+       return ret;
+}
+
+int fio_handle_clients(void)
+{
+       struct fio_client *client;
+       struct flist_head *entry;
+       struct pollfd *pfds;
+       int i, ret = 0;
+
+       gettimeofday(&eta_tv, NULL);
+
+       pfds = malloc(nr_clients * sizeof(struct pollfd));
+
+       sum_stat_clients = nr_clients;
+       init_thread_stat(&client_ts);
+       init_group_run_stat(&client_gs);
+
+       while (!exit_backend && nr_clients) {
+               i = 0;
+               flist_for_each(entry, &client_list) {
+                       client = flist_entry(entry, struct fio_client, list);
+
+                       pfds[i].fd = client->fd;
+                       pfds[i].events = POLLIN;
+                       i++;
+               }
+
+               assert(i == nr_clients);
+
+               do {
+                       struct timeval tv;
+
+                       gettimeofday(&tv, NULL);
+                       if (mtime_since(&eta_tv, &tv) >= 900) {
+                               request_client_etas();
+                               memcpy(&eta_tv, &tv, sizeof(tv));
+
+                               if (fio_client_timed_out())
+                                       break;
+                       }
+
+                       ret = poll(pfds, nr_clients, 100);
+                       if (ret < 0) {
+                               if (errno == EINTR)
+                                       continue;
+                               log_err("fio: poll clients: %s\n", strerror(errno));
+                               break;
+                       } else if (!ret)
+                               continue;
+               } while (ret <= 0);
+
+               for (i = 0; i < nr_clients; i++) {
+                       if (!(pfds[i].revents & POLLIN))
+                               continue;
+
+                       client = find_client_by_fd(pfds[i].fd);
+                       if (!client) {
+                               log_err("fio: unknown client fd %d\n", pfds[i].fd);
+                               continue;
+                       }
+                       if (!handle_client(client)) {
+                               log_info("client: host=%s disconnected\n",
+                                               client->hostname);
+                               remove_client(client);
+                       }
+               }
+       }
+
+       free(pfds);
+       return 0;
+}
index ac7983a24151184169f9a1977794a4e41b7cc831..d9c4e4917643e2539ac72f8712894cb09ac2c4c0 100644 (file)
@@ -43,11 +43,12 @@ unsigned short const crc16_table[256] = {
        0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040
 };
 
-unsigned short crc16(unsigned char const *buffer, unsigned int len)
+unsigned short crc16(const void *buffer, unsigned int len)
 {
+       const unsigned char *cp = (const unsigned char *) buffer;
        unsigned short crc = 0;
 
        while (len--)
-               crc = crc16_byte(crc, *buffer++);
+               crc = crc16_byte(crc, *cp++);
        return crc;
 }
index 841378d4c587e05ee39993116bcbb0eaa100bab6..6c078a4e682cca11fc4233a3a566dfcb9f721327 100644 (file)
@@ -17,7 +17,7 @@
 
 extern unsigned short const crc16_table[256];
 
-extern unsigned short crc16(const unsigned char *buffer, unsigned int len);
+extern unsigned short crc16(const void *buffer, unsigned int len);
 
 static inline unsigned short crc16_byte(unsigned short crc,
                                        const unsigned char data)
diff --git a/debug.c b/debug.c
index 013cd53c214debe2cbb7be1ca8842c436390ff96..5e9806335fc630e01da4f8a55c5ed042dd939488 100644 (file)
--- a/debug.c
+++ b/debug.c
@@ -16,8 +16,8 @@ void __dprint(int type, const char *str, ...)
            && pid != *fio_debug_jobp)
                return;
 
-       log_info("%-8s ", debug_levels[type].name);
-       log_info("%-5u ", (int) pid);
+       log_local("%-8s ", debug_levels[type].name);
+       log_local("%-5u ", (int) pid);
 
        va_start(args, str);
        log_valist(str, args);
diff --git a/debug.h b/debug.h
index 3473d6f7379934da2f42309eec4daa385b5a8fed..af71d625fc10c20433cc5a53285de61c70ad02dc 100644 (file)
--- a/debug.h
+++ b/debug.h
@@ -18,6 +18,7 @@ enum {
        FD_MUTEX,
        FD_PROFILE,
        FD_TIME,
+       FD_NET,
        FD_DEBUG_MAX,
 };
 
index 6866ba2d840465a0dfbeb85372016704c22c5ace..d6821a4092e7898aa0fea956a6069931fde3a8b2 100644 (file)
@@ -14,7 +14,9 @@
 #include <netdb.h>
 #include <sys/poll.h>
 #include <sys/types.h>
+#include <sys/stat.h>
 #include <sys/socket.h>
+#include <sys/un.h>
 
 #include "../fio.h"
 
@@ -22,10 +24,11 @@ struct netio_data {
        int listenfd;
        int send_to_net;
        int use_splice;
-       int net_protocol;
+       int type;
        int pipes[2];
        char host[64];
        struct sockaddr_in addr;
+       struct sockaddr_un addr_un;
 };
 
 struct udp_close_msg {
@@ -36,6 +39,10 @@ struct udp_close_msg {
 enum {
        FIO_LINK_CLOSE = 0x89,
        FIO_LINK_CLOSE_MAGIC = 0x6c696e6b,
+
+       FIO_TYPE_TCP    = 1,
+       FIO_TYPE_UDP    = 2,
+       FIO_TYPE_UNIX   = 3,
 };
 
 /*
@@ -226,7 +233,7 @@ static int fio_netio_send(struct thread_data *td, struct io_u *io_u)
        int ret, flags = OS_MSG_DONTWAIT;
 
        do {
-               if (nd->net_protocol == IPPROTO_UDP) {
+               if (nd->type == FIO_TYPE_UDP) {
                        struct sockaddr *to = (struct sockaddr *) &nd->addr;
 
                        ret = sendto(io_u->file->fd, io_u->xfer_buf,
@@ -279,12 +286,8 @@ static int fio_netio_recv(struct thread_data *td, struct io_u *io_u)
        int ret, flags = OS_MSG_DONTWAIT;
 
        do {
-               if (nd->net_protocol == IPPROTO_UDP) {
-#ifdef __hpux
-                       int len = sizeof(nd->addr);
-#else
-                       socklen_t len = sizeof(nd->addr);
-#endif
+               if (nd->type == FIO_TYPE_UDP) {
+                       fio_socklen_t len = sizeof(nd->addr);
                        struct sockaddr *from = (struct sockaddr *) &nd->addr;
 
                        ret = recvfrom(io_u->file->fd, io_u->xfer_buf,
@@ -318,12 +321,14 @@ static int fio_netio_queue(struct thread_data *td, struct io_u *io_u)
        fio_ro_check(td, io_u);
 
        if (io_u->ddir == DDIR_WRITE) {
-               if (!nd->use_splice || nd->net_protocol == IPPROTO_UDP)
+               if (!nd->use_splice || nd->type == FIO_TYPE_UDP ||
+                   nd->type == FIO_TYPE_UNIX) 
                        ret = fio_netio_send(td, io_u);
                else
                        ret = fio_netio_splice_out(td, io_u);
        } else if (io_u->ddir == DDIR_READ) {
-               if (!nd->use_splice || nd->net_protocol == IPPROTO_UDP)
+               if (!nd->use_splice || nd->type == FIO_TYPE_UDP ||
+                   nd->type == FIO_TYPE_UDP)
                        ret = fio_netio_recv(td, io_u);
                else
                        ret = fio_netio_splice_in(td, io_u);
@@ -354,25 +359,50 @@ static int fio_netio_queue(struct thread_data *td, struct io_u *io_u)
 static int fio_netio_connect(struct thread_data *td, struct fio_file *f)
 {
        struct netio_data *nd = td->io_ops->data;
-       int type;
+       int type, domain;
 
-       if (nd->net_protocol == IPPROTO_TCP)
+       if (nd->type == FIO_TYPE_TCP) {
+               domain = AF_INET;
                type = SOCK_STREAM;
-       else
+       } else if (nd->type == FIO_TYPE_UDP) {
+               domain = AF_INET;
                type = SOCK_DGRAM;
+       } else if (nd->type == FIO_TYPE_UNIX) {
+               domain = AF_UNIX;
+               type = SOCK_STREAM;
+       } else {
+               log_err("fio: bad network type %d\n", nd->type);
+               f->fd = -1;
+               return 1;
+       }
 
-       f->fd = socket(AF_INET, type, nd->net_protocol);
+       f->fd = socket(domain, type, 0);
        if (f->fd < 0) {
                td_verror(td, errno, "socket");
                return 1;
        }
 
-       if (nd->net_protocol == IPPROTO_UDP)
+       if (nd->type == FIO_TYPE_UDP)
                return 0;
+       else if (nd->type == FIO_TYPE_TCP) {
+               fio_socklen_t len = sizeof(nd->addr);
 
-       if (connect(f->fd, (struct sockaddr *) &nd->addr, sizeof(nd->addr)) < 0) {
-               td_verror(td, errno, "connect");
-               return 1;
+               if (connect(f->fd, (struct sockaddr *) &nd->addr, len) < 0) {
+                       td_verror(td, errno, "connect");
+                       close(f->fd);
+                       return 1;
+               }
+       } else {
+               struct sockaddr_un *addr = &nd->addr_un;
+               fio_socklen_t len;
+
+               len = sizeof(addr->sun_family) + strlen(addr->sun_path) + 1;
+
+               if (connect(f->fd, (struct sockaddr *) addr, len) < 0) {
+                       td_verror(td, errno, "connect");
+                       close(f->fd);
+                       return 1;
+               }
        }
 
        return 0;
@@ -381,13 +411,9 @@ static int fio_netio_connect(struct thread_data *td, struct fio_file *f)
 static int fio_netio_accept(struct thread_data *td, struct fio_file *f)
 {
        struct netio_data *nd = td->io_ops->data;
-#ifdef __hpux
-       int socklen = sizeof(nd->addr);
-#else
-       socklen_t socklen = sizeof(nd->addr);
-#endif
+       fio_socklen_t socklen = sizeof(nd->addr);
 
-       if (nd->net_protocol == IPPROTO_UDP) {
+       if (nd->type == FIO_TYPE_UDP) {
                f->fd = nd->listenfd;
                return 0;
        }
@@ -408,10 +434,16 @@ static int fio_netio_accept(struct thread_data *td, struct fio_file *f)
 
 static int fio_netio_open_file(struct thread_data *td, struct fio_file *f)
 {
+       int ret;
+
        if (td_read(td))
-               return fio_netio_accept(td, f);
+               ret = fio_netio_accept(td, f);
        else
-               return fio_netio_connect(td, f);
+               ret = fio_netio_connect(td, f);
+
+       if (ret)
+               f->fd = -1;
+       return ret;
 }
 
 static void fio_netio_udp_close(struct thread_data *td, struct fio_file *f)
@@ -438,14 +470,14 @@ static int fio_netio_close_file(struct thread_data *td, struct fio_file *f)
         * If this is an UDP connection, notify the receiver that we are
         * closing down the link
         */
-       if (nd->net_protocol == IPPROTO_UDP)
+       if (nd->type == FIO_TYPE_UDP)
                fio_netio_udp_close(td, f);
 
        return generic_close_file(td, f);
 }
 
-static int fio_netio_setup_connect(struct thread_data *td, const char *host,
-                                  unsigned short port)
+static int fio_netio_setup_connect_inet(struct thread_data *td,
+                                       const char *host, unsigned short port)
 {
        struct netio_data *nd = td->io_ops->data;
 
@@ -467,17 +499,72 @@ static int fio_netio_setup_connect(struct thread_data *td, const char *host,
        return 0;
 }
 
-static int fio_netio_setup_listen(struct thread_data *td, short port)
+static int fio_netio_setup_connect_unix(struct thread_data *td,
+                                       const char *path)
+{
+       struct netio_data *nd = td->io_ops->data;
+       struct sockaddr_un *soun = &nd->addr_un;
+
+       soun->sun_family = AF_UNIX;
+       strcpy(soun->sun_path, path);
+       return 0;
+}
+
+static int fio_netio_setup_connect(struct thread_data *td, const char *host,
+                                  unsigned short port)
+{
+       struct netio_data *nd = td->io_ops->data;
+
+       if (nd->type == FIO_TYPE_UDP || nd->type == FIO_TYPE_TCP)
+               return fio_netio_setup_connect_inet(td, host, port);
+       else
+               return fio_netio_setup_connect_unix(td, host);
+}
+
+static int fio_netio_setup_listen_unix(struct thread_data *td, const char *path)
+{
+       struct netio_data *nd = td->io_ops->data;
+       struct sockaddr_un *addr = &nd->addr_un;
+       mode_t mode;
+       int len, fd;
+
+       fd = socket(AF_UNIX, SOCK_STREAM, 0);
+       if (fd < 0) {
+               log_err("fio: socket: %s\n", strerror(errno));
+               return -1;
+       }
+
+       mode = umask(000);
+
+       memset(addr, 0, sizeof(*addr));
+       addr->sun_family = AF_UNIX;
+       strcpy(addr->sun_path, path);
+       unlink(path);
+
+       len = sizeof(addr->sun_family) + strlen(path) + 1;
+
+       if (bind(fd, (struct sockaddr *) addr, len) < 0) {
+               log_err("fio: bind: %s\n", strerror(errno));
+               close(fd);
+               return -1;
+       }
+
+       umask(mode);
+       nd->listenfd = fd;
+       return 0;
+}
+
+static int fio_netio_setup_listen_inet(struct thread_data *td, short port)
 {
        struct netio_data *nd = td->io_ops->data;
        int fd, opt, type;
 
-       if (nd->net_protocol == IPPROTO_TCP)
+       if (nd->type == FIO_TYPE_TCP)
                type = SOCK_STREAM;
        else
                type = SOCK_DGRAM;
 
-       fd = socket(AF_INET, type, nd->net_protocol);
+       fd = socket(AF_INET, type, 0);
        if (fd < 0) {
                td_verror(td, errno, "socket");
                return 1;
@@ -503,12 +590,33 @@ static int fio_netio_setup_listen(struct thread_data *td, short port)
                td_verror(td, errno, "bind");
                return 1;
        }
-       if (nd->net_protocol == IPPROTO_TCP && listen(fd, 1) < 0) {
+
+       nd->listenfd = fd;
+       return 0;
+}
+
+static int fio_netio_setup_listen(struct thread_data *td, const char *path,
+                                 short port)
+{
+       struct netio_data *nd = td->io_ops->data;
+       int ret;
+
+       if (nd->type == FIO_TYPE_UDP || nd->type == FIO_TYPE_TCP)
+               ret = fio_netio_setup_listen_inet(td, port);
+       else
+               ret = fio_netio_setup_listen_unix(td, path);
+
+       if (ret)
+               return ret;
+       if (nd->type == FIO_TYPE_UDP)
+               return 0;
+
+       if (listen(nd->listenfd, 10) < 0) {
                td_verror(td, errno, "listen");
+               nd->listenfd = -1;
                return 1;
        }
 
-       nd->listenfd = fd;
        return 0;
 }
 
@@ -531,7 +639,7 @@ static int fio_netio_init(struct thread_data *td)
 
        strcpy(buf, td->o.filename);
 
-       sep = strchr(buf, '/');
+       sep = strchr(buf, ',');
        if (!sep)
                goto bad_host;
 
@@ -543,31 +651,34 @@ static int fio_netio_init(struct thread_data *td)
 
        modep = NULL;
        portp = sep;
-       sep = strchr(portp, '/');
+       sep = strchr(portp, ',');
        if (sep) {
                *sep = '\0';
                modep = sep + 1;
        }
-               
-       port = strtol(portp, NULL, 10);
-       if (!port || port > 65535)
+
+       if (!strncmp("tcp", modep, strlen(modep)) ||
+           !strncmp("TCP", modep, strlen(modep)))
+               nd->type = FIO_TYPE_TCP;
+       else if (!strncmp("udp", modep, strlen(modep)) ||
+                !strncmp("UDP", modep, strlen(modep)))
+               nd->type = FIO_TYPE_UDP;
+       else if (!strncmp("unix", modep, strlen(modep)) ||
+                !strncmp("UNIX", modep, strlen(modep)))
+               nd->type = FIO_TYPE_UNIX;
+       else
                goto bad_host;
 
-       if (modep) {
-               if (!strncmp("tcp", modep, strlen(modep)) ||
-                   !strncmp("TCP", modep, strlen(modep)))
-                       nd->net_protocol = IPPROTO_TCP;
-               else if (!strncmp("udp", modep, strlen(modep)) ||
-                        !strncmp("UDP", modep, strlen(modep)))
-                       nd->net_protocol = IPPROTO_UDP;
-               else
+       if (nd->type != FIO_TYPE_UNIX) {
+               port = strtol(portp, NULL, 10);
+               if (!port || port > 65535)
                        goto bad_host;
        } else
-               nd->net_protocol = IPPROTO_TCP;
+               port = 0;
 
        if (td_read(td)) {
                nd->send_to_net = 0;
-               ret = fio_netio_setup_listen(td, port);
+               ret = fio_netio_setup_listen(td, host, port);
        } else {
                nd->send_to_net = 1;
                ret = fio_netio_setup_connect(td, host, port);
diff --git a/eta.c b/eta.c
index d93bf1a16f80ff51f9b025149568f3a199472970..6118d1af21b5afccee8c9f106bc4cbd93b557c61 100644 (file)
--- a/eta.c
+++ b/eta.c
@@ -230,32 +230,28 @@ static void calc_iops(unsigned long mtime, unsigned long long *io_iops,
  * Print status of the jobs we know about. This includes rate estimates,
  * ETA, thread state, etc.
  */
-void print_thread_status(void)
+int calc_thread_status(struct jobs_eta *je, int force)
 {
-       unsigned long elapsed = (mtime_since_genesis() + 999) / 1000;
-       int i, nr_ramp, nr_running, nr_pending, t_rate, m_rate;
-       int t_iops, m_iops, files_open;
        struct thread_data *td;
-       char eta_str[128];
-       double perc = 0.0;
-       unsigned long long io_bytes[2], io_iops[2];
-       unsigned long rate_time, disp_time, bw_avg_time, *eta_secs, eta_sec;
+       int i;
+       unsigned long rate_time, disp_time, bw_avg_time, *eta_secs;
+       unsigned long long io_bytes[2];
+       unsigned long long io_iops[2];
        struct timeval now;
 
        static unsigned long long rate_io_bytes[2];
        static unsigned long long disp_io_bytes[2];
        static unsigned long long disp_io_iops[2];
        static struct timeval rate_prev_time, disp_prev_time;
-       static unsigned int rate[2], iops[2];
-       static int linelen_last;
-       static int eta_good;
        int i2p = 0;
 
-       if (temp_stall_ts || terse_output || eta_print == FIO_ETA_NEVER)
-               return;
+       if (!force) {
+               if (temp_stall_ts || terse_output || eta_print == FIO_ETA_NEVER)
+                       return 0;
 
-       if (!isatty(STDOUT_FILENO) && (eta_print != FIO_ETA_ALWAYS))
-               return;
+               if (!isatty(STDOUT_FILENO) && (eta_print != FIO_ETA_ALWAYS))
+                       return 0;
+       }
 
        if (!rate_io_bytes[0] && !rate_io_bytes[1])
                fill_start_time(&rate_prev_time);
@@ -265,32 +261,31 @@ void print_thread_status(void)
        eta_secs = malloc(thread_number * sizeof(unsigned long));
        memset(eta_secs, 0, thread_number * sizeof(unsigned long));
 
+       je->elapsed_sec = (mtime_since_genesis() + 999) / 1000;
+
        io_bytes[0] = io_bytes[1] = 0;
        io_iops[0] = io_iops[1] = 0;
-       nr_pending = nr_running = t_rate = m_rate = t_iops = m_iops = 0;
-       nr_ramp = 0;
        bw_avg_time = ULONG_MAX;
-       files_open = 0;
        for_each_td(td, i) {
                if (td->o.bw_avg_time < bw_avg_time)
                        bw_avg_time = td->o.bw_avg_time;
                if (td->runstate == TD_RUNNING || td->runstate == TD_VERIFYING
                    || td->runstate == TD_FSYNCING
                    || td->runstate == TD_PRE_READING) {
-                       nr_running++;
-                       t_rate += td->o.rate[0] + td->o.rate[1];
-                       m_rate += td->o.ratemin[0] + td->o.ratemin[1];
-                       t_iops += td->o.rate_iops[0] + td->o.rate_iops[1];
-                       m_iops += td->o.rate_iops_min[0] +
+                       je->nr_running++;
+                       je->t_rate += td->o.rate[0] + td->o.rate[1];
+                       je->m_rate += td->o.ratemin[0] + td->o.ratemin[1];
+                       je->t_iops += td->o.rate_iops[0] + td->o.rate_iops[1];
+                       je->m_iops += td->o.rate_iops_min[0] +
                                        td->o.rate_iops_min[1];
-                       files_open += td->nr_open_files;
+                       je->files_open += td->nr_open_files;
                } else if (td->runstate == TD_RAMP) {
-                       nr_running++;
-                       nr_ramp++;
+                       je->nr_running++;
+                       je->nr_ramp++;
                } else if (td->runstate < TD_RUNNING)
-                       nr_pending++;
+                       je->nr_pending++;
 
-               if (elapsed >= 3)
+               if (je->elapsed_sec >= 3)
                        eta_secs[i] = thread_eta(td);
                else
                        eta_secs[i] = INT_MAX;
@@ -306,37 +301,32 @@ void print_thread_status(void)
        }
 
        if (exitall_on_terminate)
-               eta_sec = INT_MAX;
+               je->eta_sec = INT_MAX;
        else
-               eta_sec = 0;
+               je->eta_sec = 0;
 
        for_each_td(td, i) {
                if (!i2p && is_power_of_2(td->o.kb_base))
                        i2p = 1;
                if (exitall_on_terminate) {
-                       if (eta_secs[i] < eta_sec)
-                               eta_sec = eta_secs[i];
+                       if (eta_secs[i] < je->eta_sec)
+                               je->eta_sec = eta_secs[i];
                } else {
-                       if (eta_secs[i] > eta_sec)
-                               eta_sec = eta_secs[i];
+                       if (eta_secs[i] > je->eta_sec)
+                               je->eta_sec = eta_secs[i];
                }
        }
 
        free(eta_secs);
 
-       if (eta_sec != INT_MAX && elapsed) {
-               perc = (double) elapsed / (double) (elapsed + eta_sec);
-               eta_to_str(eta_str, eta_sec);
-       }
-
        fio_gettime(&now, NULL);
        rate_time = mtime_since(&rate_prev_time, &now);
 
        if (write_bw_log && rate_time > bw_avg_time && !in_ramp_time(td)) {
-               calc_rate(rate_time, io_bytes, rate_io_bytes, rate);
+               calc_rate(rate_time, io_bytes, rate_io_bytes, je->rate);
                memcpy(&rate_prev_time, &now, sizeof(now));
-               add_agg_sample(rate[DDIR_READ], DDIR_READ, 0);
-               add_agg_sample(rate[DDIR_WRITE], DDIR_WRITE, 0);
+               add_agg_sample(je->rate[DDIR_READ], DDIR_READ, 0);
+               add_agg_sample(je->rate[DDIR_WRITE], DDIR_WRITE, 0);
        }
 
        disp_time = mtime_since(&disp_prev_time, &now);
@@ -344,35 +334,55 @@ void print_thread_status(void)
        /*
         * Allow a little slack, the target is to print it every 1000 msecs
         */
-       if (disp_time < 900)
-               return;
+       if (!force && disp_time < 900)
+               return 0;
 
-       calc_rate(disp_time, io_bytes, disp_io_bytes, rate);
-       calc_iops(disp_time, io_iops, disp_io_iops, iops);
+       calc_rate(disp_time, io_bytes, disp_io_bytes, je->rate);
+       calc_iops(disp_time, io_iops, disp_io_iops, je->iops);
 
        memcpy(&disp_prev_time, &now, sizeof(now));
 
-       if (!nr_running && !nr_pending)
-               return;
+       if (!force && !je->nr_running && !je->nr_pending)
+               return 0;
+
+       je->nr_threads = thread_number;
+       memcpy(je->run_str, run_str, thread_number * sizeof(char));
+
+       return 1;
+}
 
-       printf("Jobs: %d (f=%d)", nr_running, files_open);
-       if (m_rate || t_rate) {
+void display_thread_status(struct jobs_eta *je)
+{
+       static int linelen_last;
+       static int eta_good;
+       char output[512], *p = output;
+       char eta_str[128];
+       double perc = 0.0;
+       int i2p = 0;
+
+       if (je->eta_sec != INT_MAX && je->elapsed_sec) {
+               perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
+               eta_to_str(eta_str, je->eta_sec);
+       }
+
+       p += sprintf(p, "Jobs: %d (f=%d)", je->nr_running, je->files_open);
+       if (je->m_rate || je->t_rate) {
                char *tr, *mr;
 
-               mr = num2str(m_rate, 4, 0, i2p);
-               tr = num2str(t_rate, 4, 0, i2p);
-               printf(", CR=%s/%s KB/s", tr, mr);
+               mr = num2str(je->m_rate, 4, 0, i2p);
+               tr = num2str(je->t_rate, 4, 0, i2p);
+               p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
                free(tr);
                free(mr);
-       } else if (m_iops || t_iops)
-               printf(", CR=%d/%d IOPS", t_iops, m_iops);
-       if (eta_sec != INT_MAX && nr_running) {
+       } else if (je->m_iops || je->t_iops)
+               p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
+       if (je->eta_sec != INT_MAX && je->nr_running) {
                char perc_str[32];
                char *iops_str[2];
                char *rate_str[2];
                int l;
 
-               if ((!eta_sec && !eta_good) || nr_ramp == nr_running)
+               if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
                        strcpy(perc_str, "-.-% done");
                else {
                        eta_good = 1;
@@ -380,17 +390,18 @@ void print_thread_status(void)
                        sprintf(perc_str, "%3.1f%% done", perc);
                }
 
-               rate_str[0] = num2str(rate[0], 5, 10, i2p);
-               rate_str[1] = num2str(rate[1], 5, 10, i2p);
+               rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
+               rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
 
-               iops_str[0] = num2str(iops[0], 4, 1, 0);
-               iops_str[1] = num2str(iops[1], 4, 1, 0);
+               iops_str[0] = num2str(je->iops[0], 4, 1, 0);
+               iops_str[1] = num2str(je->iops[1], 4, 1, 0);
 
-               l = printf(": [%s] [%s] [%s/%s /s] [%s/%s iops] [eta %s]",
-                                run_str, perc_str, rate_str[0], rate_str[1],
-                                iops_str[0], iops_str[1], eta_str);
+               l = sprintf(p, ": [%s] [%s] [%s/%s /s] [%s/%s iops] [eta %s]",
+                               je->run_str, perc_str, rate_str[0],
+                               rate_str[1], iops_str[0], iops_str[1], eta_str);
+               p += l;
                if (l >= 0 && l < linelen_last)
-                       printf("%*s", linelen_last - l, "");
+                       p += sprintf(p, "%*s", linelen_last - l, "");
                linelen_last = l;
 
                free(rate_str[0]);
@@ -398,10 +409,30 @@ void print_thread_status(void)
                free(iops_str[0]);
                free(iops_str[1]);
        }
-       printf("\r");
+       p += sprintf(p, "\r");
+
+       printf("%s", output);
        fflush(stdout);
 }
 
+void print_thread_status(void)
+{
+       struct jobs_eta *je;
+       size_t size;
+
+       if (!thread_number)
+               return;
+
+       size = sizeof(*je) + thread_number * sizeof(char) + 1;
+       je = malloc(size);
+       memset(je, 0, size);
+
+       if (calc_thread_status(je, 0))
+               display_thread_status(je);
+
+       free(je);
+}
+
 void print_status_init(int thr_number)
 {
        run_str[thr_number] = 'P';
index 2aa092882184d3032242faf9ae48c9884baab9ef..5b07468a6a560eb6710bc23b19518d27a27f476b 100644 (file)
@@ -1,8 +1,12 @@
 # Example network job, just defines two clients that send/recv data
 [global]
 ioengine=net
-#the below defaults to a tcp connection, add /udp at the end for udp
-filename=localhost/8888
+#this would use UDP over localhost, port 8888
+#filename=localhost,8888,udp
+#this would use a local domain socket /tmp/fio.sock
+#filename=/tmp/fio.sock,,unix
+#TCP, port 8888, localhost
+filename=localhost,8888,tcp
 bs=4k
 size=10g
 #set the below option to enable end-to-end data integrity tests
@@ -12,4 +16,5 @@ size=10g
 rw=read
 
 [sender]
+startdelay=1
 rw=write
index 1df7db0c2f2d754fd4ae7bbc674ac408d7ea7848..1bb1eb237606096a8d0bbec6b0ec583f34bafc00 100644 (file)
@@ -3,7 +3,7 @@
 
 #include "fio.h"
 #include "flist.h"
-#include "crc/crc16.h"
+#include "hash.h"
 
 #define HASH_BUCKETS   512
 #define HASH_MASK      (HASH_BUCKETS - 1)
@@ -15,7 +15,7 @@ static struct fio_mutex *hash_lock;
 
 static unsigned short hash(const char *name)
 {
-       return crc16((const unsigned char *) name, strlen(name)) & HASH_MASK;
+       return jhash(name, strlen(name), 0) & HASH_MASK;
 }
 
 void remove_file_hash(struct fio_file *f)
diff --git a/fio.1 b/fio.1
index 513caa9703544d3e96bdbe28be563c4eef93a047..0517e33fa87e698c4ee08f6dc2eeeacedf7d9127 100644 (file)
--- a/fio.1
+++ b/fio.1
@@ -416,8 +416,9 @@ itself and for debugging and testing purposes.
 .TP
 .B net
 Transfer over the network.  \fBfilename\fR must be set appropriately to
-`\fIhost\fR/\fIport\fR' regardless of data direction.  If receiving, only the
-\fIport\fR argument is used.
+`\fIhost\fR,\fIport\fR,\fItype\fR' regardless of data direction. \fItype\fR
+is one of \fBtcp\fR, \fBudp\fR, or \fBunix\fR. For UNIX domain sockets,
+the \fIhost\fR parameter is a file system path.
 .TP
 .B netsplice
 Like \fBnet\fR, but uses \fIsplice\fR\|(2) and \fIvmsplice\fR\|(2) to map data
@@ -673,6 +674,10 @@ Terminate all jobs when one finishes.  Default: wait for each job to finish.
 Average bandwidth calculations over the given time in milliseconds.  Default:
 500ms.
 .TP
+.BI iopsavgtime \fR=\fPint
+Average IOPS calculations over the given time in milliseconds.  Default:
+500ms.
+.TP
 .BI create_serialize \fR=\fPbool
 If true, serialize file creation for the jobs.  Default: true.
 .TP
@@ -845,6 +850,11 @@ Same as \fBwrite_bw_log\fR, but writes I/O completion latencies.  If no
 filename is given with this option, the default filename of "jobname_type.log"
 is used. Even if the filename is given, fio will still append the type of log.
 .TP
+.BI write_iops_log \fR=\fPstr
+Same as \fBwrite_bw_log\fR, but writes IOPS. If no filename is given with this
+option, the default filename of "jobname_type.log" is used. Even if the
+filename is given, fio will still append the type of log.
+.TP
 .BI disable_lat \fR=\fPbool
 Disable measurements of total latency numbers. Useful only for cutting
 back the number of calls to gettimeofday, as that does impact performance at
diff --git a/fio.c b/fio.c
index b492889f740b1745922fe2fb0a2388b73d91f7b2..b242f9445cb872c14c0ec008cba1f257dd4f53f1 100644 (file)
--- a/fio.c
+++ b/fio.c
@@ -46,6 +46,7 @@
 #include "profile.h"
 #include "lib/rand.h"
 #include "memalign.h"
+#include "server.h"
 
 unsigned long page_mask;
 unsigned long page_size;
@@ -74,9 +75,52 @@ unsigned long arch_flags = 0;
 
 struct io_log *agg_io_log[2];
 
-#define TERMINATE_ALL          (-1)
 #define JOB_START_TIMEOUT      (5 * 1000)
 
+static const char *fio_os_strings[os_nr] = {
+       "Invalid",
+       "Linux",
+       "AIX",
+       "FreeBSD",
+       "HP-UX",
+       "OSX",
+       "NetBSD",
+       "Solaris",
+       "Windows"
+};
+
+static const char *fio_arch_strings[arch_nr] = {
+       "Invalid",
+       "x86-64",
+       "x86",
+       "ppc",
+       "ia64",
+       "s390",
+       "alpha",
+       "sparc",
+       "sparc64",
+       "arm",
+       "sh",
+       "hppa",
+       "generic"
+};
+
+const char *fio_get_os_string(int nr)
+{
+       if (nr < os_nr)
+               return fio_os_strings[nr];
+
+       return NULL;
+}
+
+const char *fio_get_arch_string(int nr)
+{
+       if (nr < arch_nr)
+               return fio_arch_strings[nr];
+
+       return NULL;
+}
+
 void td_set_runstate(struct thread_data *td, int runstate)
 {
        if (td->runstate == runstate)
@@ -87,7 +131,7 @@ void td_set_runstate(struct thread_data *td, int runstate)
        td->runstate = runstate;
 }
 
-static void terminate_threads(int group_id)
+void fio_terminate_threads(int group_id)
 {
        struct thread_data *td;
        int i;
@@ -121,10 +165,15 @@ static void terminate_threads(int group_id)
 static void sig_int(int sig)
 {
        if (threads) {
-               log_info("\nfio: terminating on signal %d\n", sig);
-               fflush(stdout);
-               exit_value = 128;
-               terminate_threads(TERMINATE_ALL);
+               if (is_backend)
+                       fio_server_got_signal(sig);
+               else {
+                       log_info("\nfio: terminating on signal %d\n", sig);
+                       fflush(stdout);
+                       exit_value = 128;
+               }
+
+               fio_terminate_threads(TERMINATE_ALL);
        }
 }
 
@@ -137,7 +186,9 @@ static void *disk_thread_main(void *data)
                if (!threads)
                        break;
                update_io_ticks();
-               print_thread_status();
+
+               if (!is_backend)
+                       print_thread_status();
        }
 
        return NULL;
@@ -178,6 +229,13 @@ static void set_sig_handlers(void)
        act.sa_handler = sig_int;
        act.sa_flags = SA_RESTART;
        sigaction(SIGTERM, &act, NULL);
+
+       if (is_backend) {
+               memset(&act, 0, sizeof(act));
+               act.sa_handler = sig_int;
+               act.sa_flags = SA_RESTART;
+               sigaction(SIGPIPE, &act, NULL);
+       }
 }
 
 /*
@@ -205,7 +263,7 @@ static int __check_min_rate(struct thread_data *td, struct timeval *now,
        if (mtime_since(&td->start, now) < 2000)
                return 0;
 
-       iops += td->io_blocks[ddir];
+       iops += td->this_io_blocks[ddir];
        bytes += td->this_io_bytes[ddir];
        ratemin += td->o.ratemin[ddir];
        rate_iops += td->o.rate_iops[ddir];
@@ -744,7 +802,7 @@ sync_done:
                if (!in_ramp_time(td) && should_check_rate(td, bytes_done)) {
                        if (check_min_rate(td, &comp_time, bytes_done)) {
                                if (exitall_on_terminate)
-                                       terminate_threads(td->groupid);
+                                       fio_terminate_threads(td->groupid);
                                td_verror(td, EIO, "check_min_rate");
                                break;
                        }
@@ -972,8 +1030,10 @@ static int keep_running(struct thread_data *td)
 
 static void reset_io_counters(struct thread_data *td)
 {
-       td->ts.stat_io_bytes[0] = td->ts.stat_io_bytes[1] = 0;
+       td->stat_io_bytes[0] = td->stat_io_bytes[1] = 0;
        td->this_io_bytes[0] = td->this_io_bytes[1] = 0;
+       td->stat_io_blocks[0] = td->stat_io_blocks[1] = 0;
+       td->this_io_blocks[0] = td->this_io_blocks[1] = 0;
        td->zone_bytes = 0;
        td->rate_bytes[0] = td->rate_bytes[1] = 0;
        td->rate_blocks[0] = td->rate_blocks[1] = 0;
@@ -1167,19 +1227,17 @@ static void *thread_main(void *data)
        }
 
        fio_gettime(&td->epoch, NULL);
-       getrusage(RUSAGE_SELF, &td->ts.ru_start);
+       getrusage(RUSAGE_SELF, &td->ru_start);
 
        clear_state = 0;
        while (keep_running(td)) {
                fio_gettime(&td->start, NULL);
-               memcpy(&td->ts.stat_sample_time[0], &td->start,
-                               sizeof(td->start));
-               memcpy(&td->ts.stat_sample_time[1], &td->start,
-                               sizeof(td->start));
+               memcpy(&td->bw_sample_time, &td->start, sizeof(td->start));
+               memcpy(&td->iops_sample_time, &td->start, sizeof(td->start));
                memcpy(&td->tv_cache, &td->start, sizeof(td->start));
 
                if (td->o.ratemin[0] || td->o.ratemin[1])
-                       memcpy(&td->lastrate, &td->ts.stat_sample_time,
+                       memcpy(&td->lastrate, &td->bw_sample_time,
                                                        sizeof(td->lastrate));
 
                if (clear_state)
@@ -1228,40 +1286,48 @@ static void *thread_main(void *data)
        td->ts.io_bytes[1] = td->io_bytes[1];
 
        fio_mutex_down(writeout_mutex);
-       if (td->ts.bw_log) {
+       if (td->bw_log) {
                if (td->o.bw_log_file) {
-                       finish_log_named(td, td->ts.bw_log,
+                       finish_log_named(td, td->bw_log,
                                                td->o.bw_log_file, "bw");
                } else
-                       finish_log(td, td->ts.bw_log, "bw");
+                       finish_log(td, td->bw_log, "bw");
        }
-       if (td->ts.lat_log) {
+       if (td->lat_log) {
                if (td->o.lat_log_file) {
-                       finish_log_named(td, td->ts.lat_log,
+                       finish_log_named(td, td->lat_log,
                                                td->o.lat_log_file, "lat");
                } else
-                       finish_log(td, td->ts.lat_log, "lat");
+                       finish_log(td, td->lat_log, "lat");
        }
-       if (td->ts.slat_log) {
+       if (td->slat_log) {
                if (td->o.lat_log_file) {
-                       finish_log_named(td, td->ts.slat_log,
+                       finish_log_named(td, td->slat_log,
                                                td->o.lat_log_file, "slat");
                } else
-                       finish_log(td, td->ts.slat_log, "slat");
+                       finish_log(td, td->slat_log, "slat");
        }
-       if (td->ts.clat_log) {
+       if (td->clat_log) {
                if (td->o.lat_log_file) {
-                       finish_log_named(td, td->ts.clat_log,
+                       finish_log_named(td, td->clat_log,
                                                td->o.lat_log_file, "clat");
                } else
-                       finish_log(td, td->ts.clat_log, "clat");
+                       finish_log(td, td->clat_log, "clat");
        }
+       if (td->iops_log) {
+               if (td->o.iops_log_file) {
+                       finish_log_named(td, td->iops_log,
+                                               td->o.iops_log_file, "iops");
+               } else
+                       finish_log(td, td->iops_log, "iops");
+       }
+
        fio_mutex_up(writeout_mutex);
        if (td->o.exec_postrun)
                exec_string(td->o.exec_postrun);
 
        if (exitall_on_terminate)
-               terminate_threads(td->groupid);
+               fio_terminate_threads(td->groupid);
 
 err:
        if (td->error)
@@ -1415,7 +1481,7 @@ reaped:
        }
 
        if (*nr_running == cputhreads && !pending && realthreads)
-               terminate_threads(TERMINATE_ALL);
+               fio_terminate_threads(TERMINATE_ALL);
 }
 
 static void *gtod_thread_main(void *data)
@@ -1477,6 +1543,8 @@ static void run_threads(void)
        if (fio_gtod_offload && fio_start_gtod_thread())
                return;
 
+       set_sig_handlers();
+
        if (!terse_output) {
                log_info("Starting ");
                if (nr_thread)
@@ -1492,8 +1560,6 @@ static void run_threads(void)
                fflush(stdout);
        }
 
-       set_sig_handlers();
-
        todo = thread_number;
        nr_running = 0;
        nr_started = 0;
@@ -1609,7 +1675,7 @@ static void run_threads(void)
                        dprint(FD_MUTEX, "wait on startup_mutex\n");
                        if (fio_mutex_down_timeout(startup_mutex, 10)) {
                                log_err("fio: job startup hung? exiting.\n");
-                               terminate_threads(TERMINATE_ALL);
+                               fio_terminate_threads(TERMINATE_ALL);
                                fio_abort = 1;
                                nr_started--;
                                break;
@@ -1677,48 +1743,31 @@ static void run_threads(void)
 
                reap_threads(&nr_running, &t_rate, &m_rate);
 
-               if (todo)
-                       usleep(100000);
+               if (todo) {
+                       if (is_backend)
+                               fio_server_idle_loop();
+                       else
+                               usleep(100000);
+               }
        }
 
        while (nr_running) {
                reap_threads(&nr_running, &t_rate, &m_rate);
-               usleep(10000);
+
+               if (is_backend)
+                       fio_server_idle_loop();
+               else
+                       usleep(10000);
        }
 
        update_io_ticks();
        fio_unpin_memory();
 }
 
-int main(int argc, char *argv[], char *envp[])
+int exec_run(void)
 {
-       long ps;
-
-       arch_init(envp);
-
-       sinit();
-
-       /*
-        * We need locale for number printing, if it isn't set then just
-        * go with the US format.
-        */
-       if (!getenv("LC_NUMERIC"))
-               setlocale(LC_NUMERIC, "en_US");
-
-       ps = sysconf(_SC_PAGESIZE);
-       if (ps < 0) {
-               log_err("Failed to get page size\n");
-               return 1;
-       }
-
-       page_size = ps;
-       page_mask = ps - 1;
-
-       fio_keywords_init();
-
-       if (parse_options(argc, argv))
-               return 1;
-
+       if (nr_clients)
+               return fio_handle_clients();
        if (exec_profile && load_profile(exec_profile))
                return 1;
 
@@ -1762,3 +1811,80 @@ int main(int argc, char *argv[], char *envp[])
        fio_mutex_remove(writeout_mutex);
        return exit_value;
 }
+
+void reset_fio_state(void)
+{
+       groupid = 0;
+       thread_number = 0;
+       nr_process = 0;
+       nr_thread = 0;
+       done_secs = 0;
+}
+
+static int endian_check(void)
+{
+       union {
+               uint8_t c[8];
+               uint64_t v;
+       } u;
+       int le = 0, be = 0;
+
+       u.v = 0x12;
+       if (u.c[7] == 0x12)
+               be = 1;
+       else if (u.c[0] == 0x12)
+               le = 1;
+
+#if defined(FIO_LITTLE_ENDIAN)
+       if (be)
+               return 1;
+#elif defined(FIO_BIG_ENDIAN)
+       if (le)
+               return 1;
+#else
+       return 1;
+#endif
+
+       if (!le && !be)
+               return 1;
+
+       return 0;
+}
+
+int main(int argc, char *argv[], char *envp[])
+{
+       long ps;
+
+       if (endian_check()) {
+               log_err("fio: endianness settings appear wrong.\n");
+               log_err("fio: please report this to fio@vger.kernel.org\n");
+               return 1;
+       }
+
+       arch_init(envp);
+
+       sinit();
+
+       /*
+        * We need locale for number printing, if it isn't set then just
+        * go with the US format.
+        */
+       if (!getenv("LC_NUMERIC"))
+               setlocale(LC_NUMERIC, "en_US");
+
+       ps = sysconf(_SC_PAGESIZE);
+       if (ps < 0) {
+               log_err("Failed to get page size\n");
+               return 1;
+       }
+
+       page_size = ps;
+       page_mask = ps - 1;
+
+       fio_keywords_init();
+
+       if (parse_options(argc, argv))
+               return 1;
+
+       return exec_run();
+}
diff --git a/fio.h b/fio.h
index 022ba57c2eb0e7b5992ddc133bd947bc10bb2b86..04963cd8b1680e6cb7062e8eb0696eba579310a9 100644 (file)
--- a/fio.h
+++ b/fio.h
@@ -35,6 +35,8 @@ struct thread_data;
 #include "time.h"
 #include "lib/getopt.h"
 #include "lib/rand.h"
+#include "server.h"
+#include "stat.h"
 
 #ifdef FIO_HAVE_GUASI
 #include <guasi.h>
@@ -44,14 +46,6 @@ struct thread_data;
 #include <sys/asynch.h>
 #endif
 
-struct group_run_stats {
-       unsigned long long max_run[2], min_run[2];
-       unsigned long long max_bw[2], min_bw[2];
-       unsigned long long io_kb[2];
-       unsigned long long agg[2];
-       unsigned int kb_base;
-};
-
 /*
  * What type of allocation to use for io buffers
  */
@@ -71,172 +65,6 @@ enum {
        RW_SEQ_IDENT,
 };
 
-/*
- * How many depth levels to log
- */
-#define FIO_IO_U_MAP_NR        7
-#define FIO_IO_U_LAT_U_NR 10
-#define FIO_IO_U_LAT_M_NR 12
-
-/*
- * Aggregate clat samples to report percentile(s) of them.
- *
- * EXECUTIVE SUMMARY
- *
- * FIO_IO_U_PLAT_BITS determines the maximum statistical error on the
- * value of resulting percentiles. The error will be approximately
- * 1/2^(FIO_IO_U_PLAT_BITS+1) of the value.
- *
- * FIO_IO_U_PLAT_GROUP_NR and FIO_IO_U_PLAT_BITS determine the maximum
- * range being tracked for latency samples. The maximum value tracked
- * accurately will be 2^(GROUP_NR + PLAT_BITS -1) microseconds.
- *
- * FIO_IO_U_PLAT_GROUP_NR and FIO_IO_U_PLAT_BITS determine the memory
- * requirement of storing those aggregate counts. The memory used will
- * be (FIO_IO_U_PLAT_GROUP_NR * 2^FIO_IO_U_PLAT_BITS) * sizeof(int)
- * bytes.
- *
- * FIO_IO_U_PLAT_NR is the total number of buckets.
- *
- * DETAILS
- *
- * Suppose the clat varies from 0 to 999 (usec), the straightforward
- * method is to keep an array of (999 + 1) buckets, in which a counter
- * keeps the count of samples which fall in the bucket, e.g.,
- * {[0],[1],...,[999]}. However this consumes a huge amount of space,
- * and can be avoided if an approximation is acceptable.
- *
- * One such method is to let the range of the bucket to be greater
- * than one. This method has low accuracy when the value is small. For
- * example, let the buckets be {[0,99],[100,199],...,[900,999]}, and
- * the represented value of each bucket be the mean of the range. Then
- * a value 0 has an round-off error of 49.5. To improve on this, we
- * use buckets with non-uniform ranges, while bounding the error of
- * each bucket within a ratio of the sample value. A simple example
- * would be when error_bound = 0.005, buckets are {
- * {[0],[1],...,[99]}, {[100,101],[102,103],...,[198,199]},..,
- * {[900,909],[910,919]...}  }. The total range is partitioned into
- * groups with different ranges, then buckets with uniform ranges. An
- * upper bound of the error is (range_of_bucket/2)/value_of_bucket
- *
- * For better efficiency, we implement this using base two. We group
- * samples by their Most Significant Bit (MSB), extract the next M bit
- * of them as an index within the group, and discard the rest of the
- * bits.
- *
- * E.g., assume a sample 'x' whose MSB is bit n (starting from bit 0),
- * and use M bit for indexing
- *
- *        | n |    M bits   | bit (n-M-1) ... bit 0 |
- *
- * Because x is at least 2^n, and bit 0 to bit (n-M-1) is at most
- * (2^(n-M) - 1), discarding bit 0 to (n-M-1) makes the round-off
- * error
- *
- *           2^(n-M)-1    2^(n-M)    1
- *      e <= --------- <= ------- = ---
- *             2^n          2^n     2^M
- *
- * Furthermore, we use "mean" of the range to represent the bucket,
- * the error e can be lowered by half to 1 / 2^(M+1). By using M bits
- * as the index, each group must contains 2^M buckets.
- *
- * E.g. Let M (FIO_IO_U_PLAT_BITS) be 6
- *      Error bound is 1/2^(6+1) = 0.0078125 (< 1%)
- *
- *     Group   MSB     #discarded      range of                #buckets
- *                     error_bits      value
- *     ----------------------------------------------------------------
- *     0*      0~5     0               [0,63]                  64
- *     1*      6       0               [64,127]                64
- *     2       7       1               [128,255]               64
- *     3       8       2               [256,511]               64
- *     4       9       3               [512,1023]              64
- *     ...     ...     ...             [...,...]               ...
- *     18      23      17              [8838608,+inf]**        64
- *
- *  * Special cases: when n < (M-1) or when n == (M-1), in both cases,
- *    the value cannot be rounded off. Use all bits of the sample as
- *    index.
- *
- *  ** If a sample's MSB is greater than 23, it will be counted as 23.
- */
-
-#define FIO_IO_U_PLAT_BITS 6
-#define FIO_IO_U_PLAT_VAL (1 << FIO_IO_U_PLAT_BITS)
-#define FIO_IO_U_PLAT_GROUP_NR 19
-#define FIO_IO_U_PLAT_NR (FIO_IO_U_PLAT_GROUP_NR * FIO_IO_U_PLAT_VAL)
-#define FIO_IO_U_LIST_MAX_LEN 20 /* The size of the default and user-specified
-                                       list of percentiles */
-
-#define MAX_PATTERN_SIZE 512
-
-struct thread_stat {
-       char *name;
-       char *verror;
-       int error;
-       int groupid;
-       pid_t pid;
-       char *description;
-       int members;
-
-       struct io_log *slat_log;
-       struct io_log *clat_log;
-       struct io_log *lat_log;
-       struct io_log *bw_log;
-
-       /*
-        * bandwidth and latency stats
-        */
-       struct io_stat clat_stat[2];            /* completion latency */
-       struct io_stat slat_stat[2];            /* submission latency */
-       struct io_stat lat_stat[2];             /* total latency */
-       struct io_stat bw_stat[2];              /* bandwidth stats */
-
-       unsigned long long stat_io_bytes[2];
-       struct timeval stat_sample_time[2];
-
-       /*
-        * fio system usage accounting
-        */
-       struct rusage ru_start;
-       struct rusage ru_end;
-       unsigned long usr_time;
-       unsigned long sys_time;
-       unsigned long ctx;
-       unsigned long minf, majf;
-
-       /*
-        * IO depth and latency stats
-        */
-       unsigned int clat_percentiles;
-       double* percentile_list;
-
-       unsigned int io_u_map[FIO_IO_U_MAP_NR];
-       unsigned int io_u_submit[FIO_IO_U_MAP_NR];
-       unsigned int io_u_complete[FIO_IO_U_MAP_NR];
-       unsigned int io_u_lat_u[FIO_IO_U_LAT_U_NR];
-       unsigned int io_u_lat_m[FIO_IO_U_LAT_M_NR];
-       unsigned int io_u_plat[2][FIO_IO_U_PLAT_NR];
-       unsigned long total_io_u[3];
-       unsigned long short_io_u[3];
-       unsigned long total_submit;
-       unsigned long total_complete;
-
-       unsigned long long io_bytes[2];
-       unsigned long long runtime[2];
-       unsigned long total_run_time;
-
-       /*
-        * IO Error related stats
-        */
-       unsigned continue_on_error;
-       unsigned long total_err_count;
-       int first_error;
-
-       unsigned int kb_base;
-};
-
 struct bssplit {
        unsigned int bs;
        unsigned char perc;
@@ -307,6 +135,7 @@ struct thread_options {
        unsigned int use_os_rand;
        unsigned int write_lat_log;
        unsigned int write_bw_log;
+       unsigned int write_iops_log;
        unsigned int norandommap;
        unsigned int softrandommap;
        unsigned int bs_unaligned;
@@ -325,6 +154,7 @@ struct thread_options {
        unsigned long long ramp_time;
        unsigned int overwrite;
        unsigned int bw_avg_time;
+       unsigned int iops_avg_time;
        unsigned int loops;
        unsigned long long zone_size;
        unsigned long long zone_skip;
@@ -365,12 +195,13 @@ struct thread_options {
        unsigned long long trim_backlog;
        unsigned int clat_percentiles;
        unsigned int overwrite_plist;
-       double percentile_list[FIO_IO_U_LIST_MAX_LEN];
+       fio_fp64_t percentile_list[FIO_IO_U_LIST_MAX_LEN];
 
        char *read_iolog_file;
        char *write_iolog_file;
        char *bw_log_file;
        char *lat_log_file;
+       char *iops_log_file;
        char *replay_redirect;
 
        /*
@@ -418,8 +249,6 @@ struct thread_options {
        unsigned int userspace_libaio_reap;
 };
 
-#define FIO_VERROR_SIZE        128
-
 /*
  * This describes a single thread/process executing a fio job.
  */
@@ -430,6 +259,22 @@ struct thread_data {
        int thread_number;
        int groupid;
        struct thread_stat ts;
+
+       struct io_log *slat_log;
+       struct io_log *clat_log;
+       struct io_log *lat_log;
+       struct io_log *bw_log;
+       struct io_log *iops_log;
+
+       uint64_t stat_io_bytes[2];
+       struct timeval bw_sample_time;
+
+       uint64_t stat_io_blocks[2];
+       struct timeval iops_sample_time;
+
+       struct rusage ru_start;
+       struct rusage ru_end;
+
        struct fio_file **files;
        unsigned int files_size;
        unsigned int files_index;
@@ -523,6 +368,7 @@ struct thread_data {
 
        unsigned long io_issues[2];
        unsigned long long io_blocks[2];
+       unsigned long long this_io_blocks[2];
        unsigned long long io_bytes[2];
        unsigned long long io_skip_bytes;
        unsigned long long this_io_bytes[2];
@@ -632,6 +478,9 @@ enum {
 #define td_vmsg(td, err, msg, func)    \
        __td_verror((td), (err), (msg), (func))
 
+#define __fio_stringify_1(x)   #x
+#define __fio_stringify(x)     __fio_stringify_1(x)
+
 extern int exitall_on_terminate;
 extern int thread_number;
 extern int nr_process, nr_thread;
@@ -650,6 +499,10 @@ extern int fio_gtod_cpu;
 extern enum fio_cs fio_clock_source;
 extern int warnings_fatal;
 extern int terse_version;
+extern int is_backend;
+extern int nr_clients;
+extern int log_syslog;
+extern const fio_fp64_t def_percentile_list[FIO_IO_U_LIST_MAX_LEN];
 
 extern struct thread_data *threads;
 
@@ -690,6 +543,10 @@ static inline int should_fsync(struct thread_data *td)
  * Init/option functions
  */
 extern int __must_check parse_options(int, char **);
+extern int parse_jobs_ini(char *, int, int);
+extern int parse_cmd_line(int, char **);
+extern int exec_run(void);
+extern void reset_fio_state(void);
 extern int fio_options_parse(struct thread_data *, char **, int);
 extern void fio_keywords_init(void);
 extern int fio_cmd_option_parse(struct thread_data *, const char *, char *);
@@ -731,6 +588,8 @@ enum {
 };
 
 extern void td_set_runstate(struct thread_data *, int);
+#define TERMINATE_ALL          (-1)
+extern void fio_terminate_threads(int);
 
 /*
  * Memory helpers
@@ -842,4 +701,7 @@ static inline void td_io_u_free_notify(struct thread_data *td)
                pthread_cond_signal(&td->free_cond);
 }
 
+extern const char *fio_get_arch_string(int);
+extern const char *fio_get_os_string(int);
+
 #endif
index 21d7c6a9a2fd77f8731146b2434f0a1947dc1382..4285415eac2879da04850369a6aab7a067c5518f 100755 (executable)
@@ -42,6 +42,24 @@ if [ "$PLOT_LINE"x != "x" ]; then
        echo "set title 'Bandwidth - $TITLE'; set xlabel 'time (msec)'; set ylabel 'KB/sec'; set terminal png size $XRES,$YRES; set output '$TITLE-bw.png'; plot " $PLOT_LINE | $GNUPLOT -
 fi
 
+PLOT_LINE=""
+for i in *iops.log; do
+       if [ ! -r $i ]; then
+               continue
+       fi
+       PT=$(echo $i | sed s/_iops.log//g)
+       if [ "$PLOT_LINE"x != "x" ]; then
+               PLOT_LINE=$PLOT_LINE", "
+       fi
+
+       PLOT_LINE=$PLOT_LINE"'$i' title '$PT' with lines"
+done
+
+if [ "$PLOT_LINE"x != "x" ]; then
+       echo Making bw logs
+       echo "set title 'IOPS - $TITLE'; set xlabel 'time (msec)'; set ylabel 'IOPS'; set terminal png size $XRES,$YRES; set output '$TITLE-IOPS.png'; plot " $PLOT_LINE | $GNUPLOT -
+fi
+
 PLOT_LINE=""
 for i in *slat.log; do
        if [ ! -r $i ]; then
diff --git a/fio_version.h b/fio_version.h
new file mode 100644 (file)
index 0000000..d947496
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef FIO_VERSION_H
+#define FIO_VERSION_H
+
+#define FIO_MAJOR      1
+#define FIO_MINOR      99
+#define FIO_PATCH      4
+
+#endif
diff --git a/hash.h b/hash.h
index 73f961b73ac77975bd7a86eb8c7b49d995e05408..0c3cdda77be642842337f6fe949da7c24581e661 100644 (file)
--- a/hash.h
+++ b/hash.h
@@ -1,6 +1,7 @@
 #ifndef _LINUX_HASH_H
 #define _LINUX_HASH_H
 
+#include <inttypes.h>
 #include "arch/arch.h"
 
 /* Fast hashing routine for a long.
@@ -59,4 +60,80 @@ static inline unsigned long hash_ptr(void *ptr, unsigned int bits)
 {
        return hash_long((unsigned long)ptr, bits);
 }
+
+/*
+ * Bob Jenkins jhash
+ */
+
+#define JHASH_INITVAL  GOLDEN_RATIO_PRIME
+
+static inline uint32_t rol32(uint32_t word, uint32_t shift)
+{
+       return (word << shift) | (word >> (32 - shift));
+}
+
+/* __jhash_mix -- mix 3 32-bit values reversibly. */
+#define __jhash_mix(a, b, c)                   \
+{                                              \
+       a -= c;  a ^= rol32(c, 4);  c += b;     \
+       b -= a;  b ^= rol32(a, 6);  a += c;     \
+       c -= b;  c ^= rol32(b, 8);  b += a;     \
+       a -= c;  a ^= rol32(c, 16); c += b;     \
+       b -= a;  b ^= rol32(a, 19); a += c;     \
+       c -= b;  c ^= rol32(b, 4);  b += a;     \
+}
+
+/* __jhash_final - final mixing of 3 32-bit values (a,b,c) into c */
+#define __jhash_final(a, b, c)                 \
+{                                              \
+       c ^= b; c -= rol32(b, 14);              \
+       a ^= c; a -= rol32(c, 11);              \
+       b ^= a; b -= rol32(a, 25);              \
+       c ^= b; c -= rol32(b, 16);              \
+       a ^= c; a -= rol32(c, 4);               \
+       b ^= a; b -= rol32(a, 14);              \
+       c ^= b; c -= rol32(b, 24);              \
+}
+
+static inline uint32_t jhash(const void *key, uint32_t length, uint32_t initval)
+{
+       const uint8_t *k = key;
+       uint32_t a, b, c;
+
+       /* Set up the internal state */
+       a = b = c = JHASH_INITVAL + length + initval;
+
+       /* All but the last block: affect some 32 bits of (a,b,c) */
+       while (length > 12) {
+               a += *k;
+               b += *(k + 4);
+               c += *(k + 8);
+               __jhash_mix(a, b, c);
+               length -= 12;
+               k += 12;
+       }
+
+       /* Last block: affect all 32 bits of (c) */
+       /* All the case statements fall through */
+       switch (length) {
+       case 12: c += (uint32_t) k[11] << 24;
+       case 11: c += (uint32_t) k[10] << 16;
+       case 10: c += (uint32_t) k[9] << 8;
+       case 9:  c += k[8];
+       case 8:  b += (uint32_t) k[7] << 24;
+       case 7:  b += (uint32_t) k[6] << 16;
+       case 6:  b += (uint32_t) k[5] << 8;
+       case 5:  b += k[4];
+       case 4:  a += (uint32_t) k[3] << 24;
+       case 3:  a += (uint32_t) k[2] << 16;
+       case 2:  a += (uint32_t) k[1] << 8;
+       case 1:  a += k[0];
+                __jhash_final(a, b, c);
+       case 0: /* Nothing left to add */
+               break;
+       }
+
+       return c;
+}
+
 #endif /* _LINUX_HASH_H */
diff --git a/ieee754.c b/ieee754.c
new file mode 100644 (file)
index 0000000..c7742a2
--- /dev/null
+++ b/ieee754.c
@@ -0,0 +1,84 @@
+/*
+ * Shamelessly lifted from Beej's Guide to Network Programming, found here:
+ *
+ * http://beej.us/guide/bgnet/output/html/singlepage/bgnet.html#serialization
+ *
+ * Below code was granted to the public domain.
+ */
+#include <inttypes.h>
+#include "ieee754.h"
+
+uint64_t pack754(long double f, unsigned bits, unsigned expbits)
+{
+       long double fnorm;
+       int shift;
+       long long sign, exp, significand;
+       unsigned significandbits = bits - expbits - 1; // -1 for sign bit
+
+       // get this special case out of the way
+       if (f == 0.0)
+               return 0;
+
+       // check sign and begin normalization
+       if (f < 0) {
+               sign = 1;
+               fnorm = -f;
+       } else {
+               sign = 0;
+               fnorm = f;
+       }
+
+       // get the normalized form of f and track the exponent
+       shift = 0;
+       while (fnorm >= 2.0) {
+               fnorm /= 2.0;
+               shift++;
+       }
+       while (fnorm < 1.0) {
+               fnorm *= 2.0;
+               shift--;
+       }
+       fnorm = fnorm - 1.0;
+
+       // calculate the binary form (non-float) of the significand data
+       significand = fnorm * ((1LL << significandbits) + 0.5f);
+
+       // get the biased exponent
+       exp = shift + ((1 << (expbits - 1)) - 1); // shift + bias
+
+       // return the final answer
+       return (sign << (bits - 1)) | (exp << (bits-expbits - 1)) | significand;
+}
+
+long double unpack754(uint64_t i, unsigned bits, unsigned expbits)
+{
+       long double result;
+       long long shift;
+       unsigned bias;
+       unsigned significandbits = bits - expbits - 1; // -1 for sign bit
+
+       if (i == 0)
+               return 0.0;
+
+       // pull the significand
+       result = (i & ((1LL << significandbits) - 1)); // mask
+       result /= (1LL << significandbits); // convert back to float
+       result += 1.0f; // add the one back on
+
+       // deal with the exponent
+       bias = (1 << (expbits - 1)) - 1;
+       shift = ((i >> significandbits) & ((1LL << expbits) - 1)) - bias;
+       while (shift > 0) {
+               result *= 2.0;
+               shift--;
+       }
+       while (shift < 0) {
+               result /= 2.0;
+               shift++;
+       }
+
+       // sign it
+       result *= (i >> (bits - 1)) & 1 ? -1.0 : 1.0;
+
+       return result;
+}
diff --git a/ieee754.h b/ieee754.h
new file mode 100644 (file)
index 0000000..5af9518
--- /dev/null
+++ b/ieee754.h
@@ -0,0 +1,20 @@
+#ifndef FIO_IEEE754_H
+#define FIO_IEEE754_H
+
+#include <inttypes.h>
+
+extern uint64_t pack754(long double f, unsigned bits, unsigned expbits);
+extern long double unpack754(uint64_t i, unsigned bits, unsigned expbits);
+
+#define fio_double_to_uint64(val)      pack754((val), 64, 11)
+#define fio_uint64_to_double(val)      unpack754((val), 64, 11)
+
+typedef struct fio_fp64 {
+       union {
+               uint64_t i;
+               double f;
+               uint8_t filler[16];
+       } u;
+} fio_fp64_t;
+
+#endif
diff --git a/init.c b/init.c
index 0435dd02fa197a9731d5c123432bba54cd606152..e8fef21edf567e645fef1d95a5cc83c532ba9d7b 100644 (file)
--- a/init.c
+++ b/init.c
 #include "filehash.h"
 #include "verify.h"
 #include "profile.h"
+#include "server.h"
 
 #include "lib/getopt.h"
 
-static char fio_version_string[] = "fio 1.59";
+#include "fio_version.h"
+
+#if FIO_PATCH > 0
+static char fio_version_string[] =     __fio_stringify(FIO_MAJOR) "."  \
+                                       __fio_stringify(FIO_MINOR) "."  \
+                                       __fio_stringify(FIO_PATCH);
+#else
+static char fio_version_string[] =     __fio_stringify(FIO_MAJOR) "."  \
+                                       __fio_stringify(FIO_MINOR);
+#endif
 
 #define FIO_RANDSEED           (0xb1899bedUL)
 
@@ -45,6 +55,9 @@ int nr_job_sections = 0;
 char *exec_profile = NULL;
 int warnings_fatal = 0;
 int terse_version = 2;
+int is_backend = 0;
+int nr_clients = 0;
+int log_syslog = 0;
 
 int write_bw_log = 0;
 int read_only = 0;
@@ -58,6 +71,27 @@ unsigned int fio_debug_jobno = -1;
 unsigned int *fio_debug_jobp = NULL;
 
 static char cmd_optstr[256];
+static int did_arg;
+
+const fio_fp64_t def_percentile_list[FIO_IO_U_LIST_MAX_LEN] = {
+       { .u.f  =  1.0 },
+       { .u.f  =  5.0 },
+       { .u.f  = 10.0 },
+       { .u.f  = 20.0 },
+       { .u.f  = 30.0 },
+       { .u.f  = 40.0 },
+       { .u.f  = 50.0 },
+       { .u.f  = 60.0 },
+       { .u.f  = 70.0 },
+       { .u.f  = 80.0 },
+       { .u.f  = 90.0 },
+       { .u.f  = 95.0 },
+       { .u.f  = 99.0 },
+       { .u.f  = 99.5 },
+       { .u.f  = 99.9 },
+};
+
+#define FIO_CLIENT_FLAG                (1 << 16)
 
 /*
  * Command line options. These will contain the above, plus a few
@@ -67,106 +101,178 @@ static struct option l_opts[FIO_NR_OPTIONS] = {
        {
                .name           = (char *) "output",
                .has_arg        = required_argument,
-               .val            = 'o',
+               .val            = 'o' | FIO_CLIENT_FLAG,
        },
        {
                .name           = (char *) "timeout",
                .has_arg        = required_argument,
-               .val            = 't',
+               .val            = 't' | FIO_CLIENT_FLAG,
        },
        {
                .name           = (char *) "latency-log",
                .has_arg        = required_argument,
-               .val            = 'l',
+               .val            = 'l' | FIO_CLIENT_FLAG,
        },
        {
                .name           = (char *) "bandwidth-log",
                .has_arg        = required_argument,
-               .val            = 'b',
+               .val            = 'b' | FIO_CLIENT_FLAG,
        },
        {
                .name           = (char *) "minimal",
                .has_arg        = optional_argument,
-               .val            = 'm',
+               .val            = 'm' | FIO_CLIENT_FLAG,
        },
        {
                .name           = (char *) "version",
                .has_arg        = no_argument,
-               .val            = 'v',
+               .val            = 'v' | FIO_CLIENT_FLAG,
        },
        {
                .name           = (char *) "help",
                .has_arg        = no_argument,
-               .val            = 'h',
+               .val            = 'h' | FIO_CLIENT_FLAG,
        },
        {
                .name           = (char *) "cmdhelp",
                .has_arg        = optional_argument,
-               .val            = 'c',
+               .val            = 'c' | FIO_CLIENT_FLAG,
        },
        {
                .name           = (char *) "showcmd",
                .has_arg        = no_argument,
-               .val            = 's',
+               .val            = 's' | FIO_CLIENT_FLAG,
        },
        {
                .name           = (char *) "readonly",
                .has_arg        = no_argument,
-               .val            = 'r',
+               .val            = 'r' | FIO_CLIENT_FLAG,
        },
        {
                .name           = (char *) "eta",
                .has_arg        = required_argument,
-               .val            = 'e',
+               .val            = 'e' | FIO_CLIENT_FLAG,
        },
        {
                .name           = (char *) "debug",
                .has_arg        = required_argument,
-               .val            = 'd',
+               .val            = 'd' | FIO_CLIENT_FLAG,
        },
        {
                .name           = (char *) "section",
                .has_arg        = required_argument,
-               .val            = 'x',
+               .val            = 'x' | FIO_CLIENT_FLAG,
        },
        {
                .name           = (char *) "alloc-size",
                .has_arg        = required_argument,
-               .val            = 'a',
+               .val            = 'a' | FIO_CLIENT_FLAG,
        },
        {
                .name           = (char *) "profile",
                .has_arg        = required_argument,
-               .val            = 'p',
+               .val            = 'p' | FIO_CLIENT_FLAG,
        },
        {
                .name           = (char *) "warnings-fatal",
                .has_arg        = no_argument,
-               .val            = 'w',
+               .val            = 'w' | FIO_CLIENT_FLAG,
        },
        {
                .name           = (char *) "max-jobs",
                .has_arg        = required_argument,
-               .val            = 'j',
+               .val            = 'j' | FIO_CLIENT_FLAG,
        },
        {
                .name           = (char *) "terse-version",
                .has_arg        = required_argument,
-               .val            = 'V',
+               .val            = 'V' | FIO_CLIENT_FLAG,
+       },
+       {
+               .name           = (char *) "server",
+               .has_arg        = optional_argument,
+               .val            = 'S',
+       },
+       {       .name           = (char *) "daemonize",
+               .has_arg        = required_argument,
+               .val            = 'D',
+       },
+       {
+               .name           = (char *) "client",
+               .has_arg        = required_argument,
+               .val            = 'C',
        },
        {
                .name           = NULL,
        },
 };
 
-FILE *get_f_out()
+static void free_shm(void)
 {
-       return f_out;
+       struct shmid_ds sbuf;
+
+       if (threads) {
+               void *tp = threads;
+
+               threads = NULL;
+               file_hash_exit();
+               fio_debug_jobp = NULL;
+               shmdt(tp);
+               shmctl(shm_id, IPC_RMID, &sbuf);
+       }
+
+       scleanup();
 }
 
-FILE *get_f_err()
+/*
+ * The thread area is shared between the main process and the job
+ * threads/processes. So setup a shared memory segment that will hold
+ * all the job info. We use the end of the region for keeping track of
+ * open files across jobs, for file sharing.
+ */
+static int setup_thread_area(void)
 {
-       return f_err;
+       void *hash;
+
+       if (threads)
+               return 0;
+
+       /*
+        * 1024 is too much on some machines, scale max_jobs if
+        * we get a failure that looks like too large a shm segment
+        */
+       do {
+               size_t size = max_jobs * sizeof(struct thread_data);
+
+               size += file_hash_size;
+               size += sizeof(unsigned int);
+
+               shm_id = shmget(0, size, IPC_CREAT | 0600);
+               if (shm_id != -1)
+                       break;
+               if (errno != EINVAL) {
+                       perror("shmget");
+                       break;
+               }
+
+               max_jobs >>= 1;
+       } while (max_jobs);
+
+       if (shm_id == -1)
+               return 1;
+
+       threads = shmat(shm_id, NULL, 0);
+       if (threads == (void *) -1) {
+               perror("shmat");
+               return 1;
+       }
+
+       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 = -1;
+       file_hash_init(hash);
+       return 0;
 }
 
 /*
@@ -178,6 +284,10 @@ static struct thread_data *get_new_job(int global, struct thread_data *parent)
 
        if (global)
                return &def_thread;
+       if (setup_thread_area()) {
+               log_err("error: failed to setup shm segment\n");
+               return NULL;
+       }
        if (thread_number >= max_jobs) {
                log_err("error: maximum number of jobs (%d) reached.\n",
                                max_jobs);
@@ -627,9 +737,9 @@ static int add_job(struct thread_data *td, const char *jobname, int job_add_num)
 
        td->ts.clat_percentiles = td->o.clat_percentiles;
        if (td->o.overwrite_plist)
-               td->ts.percentile_list = td->o.percentile_list;
+               memcpy(td->ts.percentile_list, td->o.percentile_list, sizeof(td->o.percentile_list));
        else
-               td->ts.percentile_list = NULL;
+               memcpy(td->ts.percentile_list, def_percentile_list, sizeof(def_percentile_list));
 
        td->ts.clat_stat[0].min_val = td->ts.clat_stat[1].min_val = ULONG_MAX;
        td->ts.slat_stat[0].min_val = td->ts.slat_stat[1].min_val = ULONG_MAX;
@@ -652,12 +762,14 @@ static int add_job(struct thread_data *td, const char *jobname, int job_add_num)
                goto err;
 
        if (td->o.write_lat_log) {
-               setup_log(&td->ts.lat_log);
-               setup_log(&td->ts.slat_log);
-               setup_log(&td->ts.clat_log);
+               setup_log(&td->lat_log);
+               setup_log(&td->slat_log);
+               setup_log(&td->clat_log);
        }
        if (td->o.write_bw_log)
-               setup_log(&td->ts.bw_log);
+               setup_log(&td->bw_log);
+       if (td->o.write_iops_log)
+               setup_log(&td->iops_log);
 
        if (!td->o.name)
                td->o.name = strdup(jobname);
@@ -800,7 +912,7 @@ static int is_empty_or_comment(char *line)
 /*
  * This is our [ini] type file parser.
  */
-static int parse_jobs_ini(char *file, int stonewall_flag)
+int parse_jobs_ini(char *file, int is_buf, int stonewall_flag)
 {
        unsigned int global;
        struct thread_data *td;
@@ -814,14 +926,18 @@ static int parse_jobs_ini(char *file, int stonewall_flag)
        char **opts;
        int i, alloc_opts, num_opts;
 
-       if (!strcmp(file, "-"))
-               f = stdin;
-       else
-               f = fopen(file, "r");
+       if (is_buf)
+               f = NULL;
+       else {
+               if (!strcmp(file, "-"))
+                       f = stdin;
+               else
+                       f = fopen(file, "r");
 
-       if (!f) {
-               perror("fopen job file");
-               return 1;
+               if (!f) {
+                       perror("fopen job file");
+                       return 1;
+               }
        }
 
        string = malloc(4096);
@@ -843,7 +959,10 @@ static int parse_jobs_ini(char *file, int stonewall_flag)
                 * haven't handled.
                 */
                if (!skip_fgets) {
-                       p = fgets(string, 4095, f);
+                       if (is_buf)
+                               p = strsep(&file, "\n");
+                       else
+                               p = fgets(string, 4095, f);
                        if (!p)
                                break;
                }
@@ -897,7 +1016,14 @@ static int parse_jobs_ini(char *file, int stonewall_flag)
                num_opts = 0;
                memset(opts, 0, alloc_opts * sizeof(char *));
 
-               while ((p = fgets(string, 4096, f)) != NULL) {
+               while (1) {
+                       if (is_buf)
+                               p = strsep(&file, "\n");
+                       else
+                               p = fgets(string, 4096, f);
+                       if (!p)
+                               break;
+
                        if (is_empty_or_comment(p))
                                continue;
 
@@ -950,7 +1076,7 @@ static int parse_jobs_ini(char *file, int stonewall_flag)
        free(string);
        free(name);
        free(opts);
-       if (f != stdin)
+       if (!is_buf && f != stdin)
                fclose(f);
        return ret;
 }
@@ -969,75 +1095,9 @@ static int fill_def_thread(void)
        return 0;
 }
 
-static void free_shm(void)
-{
-       struct shmid_ds sbuf;
-
-       if (threads) {
-               void *tp = threads;
-
-               threads = NULL;
-               file_hash_exit();
-               fio_debug_jobp = NULL;
-               shmdt(tp);
-               shmctl(shm_id, IPC_RMID, &sbuf);
-       }
-
-       scleanup();
-}
-
-/*
- * The thread area is shared between the main process and the job
- * threads/processes. So setup a shared memory segment that will hold
- * all the job info. We use the end of the region for keeping track of
- * open files across jobs, for file sharing.
- */
-static int setup_thread_area(void)
-{
-       void *hash;
-
-       /*
-        * 1024 is too much on some machines, scale max_jobs if
-        * we get a failure that looks like too large a shm segment
-        */
-       do {
-               size_t size = max_jobs * sizeof(struct thread_data);
-
-               size += file_hash_size;
-               size += sizeof(unsigned int);
-
-               shm_id = shmget(0, size, IPC_CREAT | 0600);
-               if (shm_id != -1)
-                       break;
-               if (errno != EINVAL) {
-                       perror("shmget");
-                       break;
-               }
-
-               max_jobs >>= 1;
-       } while (max_jobs);
-
-       if (shm_id == -1)
-               return 1;
-
-       threads = shmat(shm_id, NULL, 0);
-       if (threads == (void *) -1) {
-               perror("shmat");
-               return 1;
-       }
-
-       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 = -1;
-       file_hash_init(hash);
-       atexit(free_shm);
-       return 0;
-}
-
 static void usage(const char *name)
 {
-       printf("%s\n", fio_version_string);
+       printf("fio %s\n", fio_version_string);
        printf("%s [options] [job options] <job file(s)>\n", name);
        printf("\t--debug=options\tEnable debug logging\n");
        printf("\t--output\tWrite output to file\n");
@@ -1060,6 +1120,9 @@ static void usage(const char *name)
                " (def 1024)\n");
        printf("\t--warnings-fatal Fio parser warnings are fatal\n");
        printf("\t--max-jobs\tMaximum number of threads/processes to support\n");
+       printf("\t--server=args\tStart a backend fio server\n");
+       printf("\t--daemonize=pidfile Background fio server, write pid to file\n");
+       printf("\t--client=hostname Talk to remote backend fio server at hostname\n");
        printf("\nFio was written by Jens Axboe <jens.axboe@oracle.com>");
        printf("\n                   Jens Axboe <jaxboe@fusionio.com>\n");
 }
@@ -1079,6 +1142,7 @@ struct debug_level debug_levels[] = {
        { .name = "mutex",      .shift = FD_MUTEX },
        { .name = "profile",    .shift = FD_PROFILE },
        { .name = "time",       .shift = FD_TIME },
+       { .name = "net",        .shift = FD_NET },
        { .name = NULL, },
 };
 
@@ -1163,13 +1227,51 @@ static void fio_options_fill_optstring(void)
        ostr[c] = '\0';
 }
 
-static int parse_cmd_line(int argc, char *argv[])
+static int client_flag_set(char c)
+{
+       int i;
+
+       i = 0;
+       while (l_opts[i].name) {
+               int val = l_opts[i].val;
+
+               if (c == (val & 0xff))
+                       return (val & FIO_CLIENT_FLAG);
+
+               i++;
+       }
+
+       return 0;
+}
+
+void parse_cmd_client(void *client, char *opt)
+{
+       fio_client_add_cmd_option(client, opt);
+}
+
+int parse_cmd_line(int argc, char *argv[])
 {
        struct thread_data *td = NULL;
        int c, ini_idx = 0, lidx, ret = 0, do_exit = 0, exit_val = 0;
        char *ostr = cmd_optstr;
+       void *pid_file = NULL;
+       void *cur_client = NULL;
+       int backend = 0;
+
+       /*
+        * Reset optind handling, since we may call this multiple times
+        * for the backend.
+        */
+       optind = 1;
 
        while ((c = getopt_long_only(argc, argv, ostr, l_opts, &lidx)) != -1) {
+               did_arg = 1;
+
+               if ((c & FIO_CLIENT_FLAG) || client_flag_set(c)) {
+                       parse_cmd_client(cur_client, argv[optind - 1]);
+                       c &= ~FIO_CLIENT_FLAG;
+               }
+
                switch (c) {
                case 'a':
                        smalloc_pool_size = atoi(optarg);
@@ -1195,10 +1297,17 @@ static int parse_cmd_line(int argc, char *argv[])
                        terse_output = 1;
                        break;
                case 'h':
-                       usage(argv[0]);
-                       exit(0);
+                       if (!cur_client) {
+                               usage(argv[0]);
+                               do_exit++;
+                       }
+                       break;
                case 'c':
-                       exit(fio_show_option_help(optarg));
+                       if (!cur_client) {
+                               fio_show_option_help(optarg);
+                               do_exit++;
+                       }
+                       break;
                case 's':
                        dump_cmdline = 1;
                        break;
@@ -1206,8 +1315,11 @@ static int parse_cmd_line(int argc, char *argv[])
                        read_only = 1;
                        break;
                case 'v':
-                       log_info("%s\n", fio_version_string);
-                       exit(0);
+                       if (!cur_client) {
+                               log_info("fio %s\n", fio_version_string);
+                               do_exit++;
+                       }
+                       break;
                case 'V':
                        terse_version = atoi(optarg);
                        if (terse_version != 2) {
@@ -1284,22 +1396,64 @@ static int parse_cmd_line(int argc, char *argv[])
                                exit_val = 1;
                        }
                        break;
+               case 'S':
+                       if (nr_clients) {
+                               log_err("fio: can't be both client and server\n");
+                               do_exit++;
+                               exit_val = 1;
+                               break;
+                       }
+                       if (optarg)
+                               fio_server_set_arg(optarg);
+                       is_backend = 1;
+                       backend = 1;
+                       break;
+               case 'D':
+                       pid_file = strdup(optarg);
+                       break;
+               case 'C':
+                       if (is_backend) {
+                               log_err("fio: can't be both client and server\n");
+                               do_exit++;
+                               exit_val = 1;
+                               break;
+                       }
+                       if (fio_client_add(optarg, &cur_client)) {
+                               log_err("fio: failed adding client %s\n", optarg);
+                               do_exit++;
+                               exit_val = 1;
+                               break;
+                       }
+                       break;
                default:
                        do_exit++;
                        exit_val = 1;
                        break;
                }
+               if (do_exit)
+                       break;
+       }
+
+       if (do_exit) {
+               if (exit_val && !(is_backend || nr_clients))
+                       exit(exit_val);
+       }
+
+       if (nr_clients && fio_clients_connect()) {
+               do_exit++;
+               exit_val = 1;
+               return -1;
        }
 
-       if (do_exit)
-               exit(exit_val);
+       if (is_backend && backend)
+               return fio_start_server(pid_file);
 
        if (td) {
                if (!ret)
                        ret = add_job(td, td->o.name ?: "fio", 0);
        }
 
-       while (optind < argc) {
+       while (!ret && optind < argc) {
                ini_idx++;
                ini_file = realloc(ini_file, ini_idx * sizeof(char *));
                ini_file[ini_idx - 1] = strdup(argv[optind]);
@@ -1319,19 +1473,27 @@ int parse_options(int argc, char *argv[])
        fio_options_fill_optstring();
        fio_options_dup_and_init(l_opts);
 
-       if (setup_thread_area())
-               return 1;
+       atexit(free_shm);
+
        if (fill_def_thread())
                return 1;
 
        job_files = parse_cmd_line(argc, argv);
 
-       for (i = 0; i < job_files; i++) {
-               if (fill_def_thread())
-                       return 1;
-               if (parse_jobs_ini(ini_file[i], i))
-                       return 1;
-               free(ini_file[i]);
+       if (job_files > 0) {
+               for (i = 0; i < job_files; i++) {
+                       if (fill_def_thread())
+                               return 1;
+                       if (nr_clients) {
+                               if (fio_clients_send_ini(ini_file[i]))
+                                       return 1;
+                               free(ini_file[i]);
+                       } else if (!is_backend) {
+                               if (parse_jobs_ini(ini_file[i], 0, i))
+                                       return 1;
+                               free(ini_file[i]);
+                       }
+               }
        }
 
        free(ini_file);
@@ -1342,10 +1504,19 @@ int parse_options(int argc, char *argv[])
                        return 0;
                if (exec_profile)
                        return 0;
+               if (is_backend || nr_clients)
+                       return 0;
+               if (did_arg)
+                       return 0;
 
                log_err("No jobs(s) defined\n\n");
-               usage(argv[0]);
-               return 1;
+
+               if (!did_arg) {
+                       usage(argv[0]);
+                       return 1;
+               }
+
+               return 0;
        }
 
        if (def_thread.o.gtod_offload) {
@@ -1354,6 +1525,6 @@ int parse_options(int argc, char *argv[])
                fio_gtod_cpu = def_thread.o.gtod_cpu;
        }
 
-       log_info("%s\n", fio_version_string);
+       log_info("fio %s\n", fio_version_string);
        return 0;
 }
diff --git a/io_u.c b/io_u.c
index 4dcb1fce44dbbeb4478de8154e502af4eb9f7d32..d1f66a949293310f166690089438e2a883f9feec 100644 (file)
--- a/io_u.c
+++ b/io_u.c
@@ -1293,6 +1293,8 @@ static void account_io_completion(struct thread_data *td, struct io_u *io_u,
 
        if (!td->o.disable_bw)
                add_bw_sample(td, idx, bytes, &icd->time);
+
+       add_iops_sample(td, idx, &icd->time);
 }
 
 static void io_completed(struct thread_data *td, struct io_u *io_u,
@@ -1332,6 +1334,7 @@ static void io_completed(struct thread_data *td, struct io_u *io_u,
                int ret;
 
                td->io_blocks[idx]++;
+               td->this_io_blocks[idx]++;
                td->io_bytes[idx] += bytes;
                td->this_io_bytes[idx] += bytes;
 
@@ -1347,7 +1350,7 @@ static void io_completed(struct thread_data *td, struct io_u *io_u,
                        }
                }
 
-               if (ramp_time_over(td)) {
+               if (ramp_time_over(td) && td->runstate == TD_RUNNING) {
                        account_io_completion(td, io_u, icd, idx, bytes);
 
                        if (__should_check_rate(td, idx)) {
diff --git a/iolog.c b/iolog.c
new file mode 100644 (file)
index 0000000..f962864
--- /dev/null
+++ b/iolog.c
@@ -0,0 +1,541 @@
+/*
+ * Code related to writing an iolog of what a thread is doing, and to
+ * later read that back and replay
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <libgen.h>
+#include <assert.h>
+#include "flist.h"
+#include "fio.h"
+#include "verify.h"
+#include "trim.h"
+
+static const char iolog_ver2[] = "fio version 2 iolog";
+
+void queue_io_piece(struct thread_data *td, struct io_piece *ipo)
+{
+       flist_add_tail(&ipo->list, &td->io_log_list);
+       td->total_io_size += ipo->len;
+}
+
+void log_io_u(struct thread_data *td, struct io_u *io_u)
+{
+       const char *act[] = { "read", "write", "sync", "datasync",
+                               "sync_file_range", "wait", "trim" };
+
+       assert(io_u->ddir <= 6);
+
+       if (!td->o.write_iolog_file)
+               return;
+
+       fprintf(td->iolog_f, "%s %s %llu %lu\n", io_u->file->file_name,
+                                               act[io_u->ddir], io_u->offset,
+                                               io_u->buflen);
+}
+
+void log_file(struct thread_data *td, struct fio_file *f,
+             enum file_log_act what)
+{
+       const char *act[] = { "add", "open", "close" };
+
+       assert(what < 3);
+
+       if (!td->o.write_iolog_file)
+               return;
+
+
+       /*
+        * this happens on the pre-open/close done before the job starts
+        */
+       if (!td->iolog_f)
+               return;
+
+       fprintf(td->iolog_f, "%s %s\n", f->file_name, act[what]);
+}
+
+static void iolog_delay(struct thread_data *td, unsigned long delay)
+{
+       unsigned long usec = utime_since_now(&td->last_issue);
+
+       if (delay < usec)
+               return;
+
+       delay -= usec;
+
+       /*
+        * less than 100 usec delay, just regard it as noise
+        */
+       if (delay < 100)
+               return;
+
+       usec_sleep(td, delay);
+}
+
+static int ipo_special(struct thread_data *td, struct io_piece *ipo)
+{
+       struct fio_file *f;
+       int ret;
+
+       /*
+        * Not a special ipo
+        */
+       if (ipo->ddir != DDIR_INVAL)
+               return 0;
+
+       f = td->files[ipo->fileno];
+
+       switch (ipo->file_action) {
+       case FIO_LOG_OPEN_FILE:
+               ret = td_io_open_file(td, f);
+               if (!ret)
+                       break;
+               td_verror(td, ret, "iolog open file");
+               return -1;
+       case FIO_LOG_CLOSE_FILE:
+               td_io_close_file(td, f);
+               break;
+       case FIO_LOG_UNLINK_FILE:
+               unlink(f->file_name);
+               break;
+       default:
+               log_err("fio: bad file action %d\n", ipo->file_action);
+               break;
+       }
+
+       return 1;
+}
+
+int read_iolog_get(struct thread_data *td, struct io_u *io_u)
+{
+       struct io_piece *ipo;
+       unsigned long elapsed;
+       
+       while (!flist_empty(&td->io_log_list)) {
+               int ret;
+
+               ipo = flist_entry(td->io_log_list.next, struct io_piece, list);
+               flist_del(&ipo->list);
+               remove_trim_entry(td, ipo);
+
+               ret = ipo_special(td, ipo);
+               if (ret < 0) {
+                       free(ipo);
+                       break;
+               } else if (ret > 0) {
+                       free(ipo);
+                       continue;
+               }
+
+               io_u->ddir = ipo->ddir;
+               if (ipo->ddir != DDIR_WAIT) {
+                       io_u->offset = ipo->offset;
+                       io_u->buflen = ipo->len;
+                       io_u->file = td->files[ipo->fileno];
+                       get_file(io_u->file);
+                       dprint(FD_IO, "iolog: get %llu/%lu/%s\n", io_u->offset,
+                                               io_u->buflen, io_u->file->file_name);
+                       if (ipo->delay)
+                               iolog_delay(td, ipo->delay);
+               } else {
+                       elapsed = mtime_since_genesis();
+                       if (ipo->delay > elapsed)
+                               usec_sleep(td, (ipo->delay - elapsed) * 1000);
+                               
+               }
+
+               free(ipo);
+               
+               if (io_u->ddir != DDIR_WAIT)
+                       return 0;
+       }
+
+       td->done = 1;
+       return 1;
+}
+
+void prune_io_piece_log(struct thread_data *td)
+{
+       struct io_piece *ipo;
+       struct rb_node *n;
+
+       while ((n = rb_first(&td->io_hist_tree)) != NULL) {
+               ipo = rb_entry(n, struct io_piece, rb_node);
+               rb_erase(n, &td->io_hist_tree);
+               remove_trim_entry(td, ipo);
+               td->io_hist_len--;
+               free(ipo);
+       }
+
+       while (!flist_empty(&td->io_hist_list)) {
+               ipo = flist_entry(td->io_hist_list.next, struct io_piece, list);
+               flist_del(&ipo->list);
+               remove_trim_entry(td, ipo);
+               td->io_hist_len--;
+               free(ipo);
+       }
+}
+
+/*
+ * log a successful write, so we can unwind the log for verify
+ */
+void log_io_piece(struct thread_data *td, struct io_u *io_u)
+{
+       struct rb_node **p, *parent;
+       struct io_piece *ipo, *__ipo;
+
+       ipo = malloc(sizeof(struct io_piece));
+       init_ipo(ipo);
+       ipo->file = io_u->file;
+       ipo->offset = io_u->offset;
+       ipo->len = io_u->buflen;
+
+       if (io_u_should_trim(td, io_u)) {
+               flist_add_tail(&ipo->trim_list, &td->trim_list);
+               td->trim_entries++;
+       }
+
+       /*
+        * We don't need to sort the entries, if:
+        *
+        *      Sequential writes, or
+        *      Random writes that lay out the file as it goes along
+        *
+        * For both these cases, just reading back data in the order we
+        * wrote it out is the fastest.
+        *
+        * One exception is if we don't have a random map AND we are doing
+        * verifies, in that case we need to check for duplicate blocks and
+        * drop the old one, which we rely on the rb insert/lookup for
+        * handling.
+        */
+       if ((!td_random(td) || !td->o.overwrite) &&
+             (file_randommap(td, ipo->file) || td->o.verify == VERIFY_NONE)) {
+               INIT_FLIST_HEAD(&ipo->list);
+               flist_add_tail(&ipo->list, &td->io_hist_list);
+               ipo->flags |= IP_F_ONLIST;
+               td->io_hist_len++;
+               return;
+       }
+
+       RB_CLEAR_NODE(&ipo->rb_node);
+
+       /*
+        * Sort the entry into the verification list
+        */
+restart:
+       p = &td->io_hist_tree.rb_node;
+       parent = NULL;
+       while (*p) {
+               parent = *p;
+
+               __ipo = rb_entry(parent, struct io_piece, rb_node);
+               if (ipo->file < __ipo->file)
+                       p = &(*p)->rb_left;
+               else if (ipo->file > __ipo->file)
+                       p = &(*p)->rb_right;
+               else if (ipo->offset < __ipo->offset)
+                       p = &(*p)->rb_left;
+               else if (ipo->offset > __ipo->offset)
+                       p = &(*p)->rb_right;
+               else {
+                       assert(ipo->len == __ipo->len);
+                       td->io_hist_len--;
+                       rb_erase(parent, &td->io_hist_tree);
+                       remove_trim_entry(td, __ipo);
+                       free(__ipo);
+                       goto restart;
+               }
+       }
+
+       rb_link_node(&ipo->rb_node, parent, p);
+       rb_insert_color(&ipo->rb_node, &td->io_hist_tree);
+       ipo->flags |= IP_F_ONRB;
+       td->io_hist_len++;
+}
+
+void write_iolog_close(struct thread_data *td)
+{
+       fflush(td->iolog_f);
+       fclose(td->iolog_f);
+       free(td->iolog_buf);
+       td->iolog_f = NULL;
+       td->iolog_buf = NULL;
+}
+
+/*
+ * Read version 2 iolog data. It is enhanced to include per-file logging,
+ * syncs, etc.
+ */
+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 *str, *p;
+       enum fio_ddir rw;
+
+       free_release_files(td);
+
+       /*
+        * Read in the read iolog and store it, reuse the infrastructure
+        * for doing verifications.
+        */
+       str = malloc(4096);
+       fname = malloc(256+16);
+       act = malloc(256+16);
+
+       reads = writes = waits = 0;
+       while ((p = fgets(str, 4096, f)) != NULL) {
+               struct io_piece *ipo;
+               int r;
+
+               r = sscanf(p, "%256s %256s %llu %u", fname, act, &offset,
+                                                                       &bytes);
+               if (r == 4) {
+                       /*
+                        * Check action first
+                        */
+                       if (!strcmp(act, "wait"))
+                               rw = DDIR_WAIT;
+                       else if (!strcmp(act, "read"))
+                               rw = DDIR_READ;
+                       else if (!strcmp(act, "write"))
+                               rw = DDIR_WRITE;
+                       else if (!strcmp(act, "sync"))
+                               rw = DDIR_SYNC;
+                       else if (!strcmp(act, "datasync"))
+                               rw = DDIR_DATASYNC;
+                       else if (!strcmp(act, "trim"))
+                               rw = DDIR_TRIM;
+                       else {
+                               log_err("fio: bad iolog file action: %s\n",
+                                                                       act);
+                               continue;
+                       }
+               } else if (r == 2) {
+                       rw = DDIR_INVAL;
+                       if (!strcmp(act, "add")) {
+                               td->o.nr_files++;
+                               fileno = add_file(td, fname);
+                               file_action = FIO_LOG_ADD_FILE;
+                               continue;
+                       } else if (!strcmp(act, "open")) {
+                               fileno = get_fileno(td, fname);
+                               file_action = FIO_LOG_OPEN_FILE;
+                       } else if (!strcmp(act, "close")) {
+                               fileno = get_fileno(td, fname);
+                               file_action = FIO_LOG_CLOSE_FILE;
+                       } else {
+                               log_err("fio: bad iolog file action: %s\n",
+                                                                       act);
+                               continue;
+                       }
+               } else {
+                       log_err("bad iolog2: %s", p);
+                       continue;
+               }
+
+               if (rw == DDIR_READ)
+                       reads++;
+               else if (rw == DDIR_WRITE) {
+                       /*
+                        * Don't add a write for ro mode
+                        */
+                       if (read_only)
+                               continue;
+                       writes++;
+               } else if (rw == DDIR_WAIT) {
+                       waits++;
+               } else if (rw == DDIR_INVAL) {
+               } else if (!ddir_sync(rw)) {
+                       log_err("bad ddir: %d\n", rw);
+                       continue;
+               }
+
+               /*
+                * Make note of file
+                */
+               ipo = malloc(sizeof(*ipo));
+               init_ipo(ipo);
+               ipo->ddir = rw;
+               if (rw == DDIR_WAIT) {
+                       ipo->delay = offset;
+               } else {
+                       ipo->offset = offset;
+                       ipo->len = bytes;
+                       if (bytes > td->o.max_bs[rw])
+                               td->o.max_bs[rw] = bytes;
+                       ipo->fileno = fileno;
+                       ipo->file_action = file_action;
+               }
+                       
+               queue_io_piece(td, ipo);
+       }
+
+       free(str);
+       free(act);
+       free(fname);
+
+       if (writes && read_only) {
+               log_err("fio: <%s> skips replay of %d writes due to"
+                       " read-only\n", td->o.name, writes);
+               writes = 0;
+       }
+
+       if (!reads && !writes && !waits)
+               return 1;
+       else if (reads && !writes)
+               td->o.td_ddir = TD_DDIR_READ;
+       else if (!reads && writes)
+               td->o.td_ddir = TD_DDIR_WRITE;
+       else
+               td->o.td_ddir = TD_DDIR_RW;
+
+       return 0;
+}
+
+/*
+ * open iolog, check version, and call appropriate parser
+ */
+static int init_iolog_read(struct thread_data *td)
+{
+       char buffer[256], *p;
+       FILE *f;
+       int ret;
+
+       f = fopen(td->o.read_iolog_file, "r");
+       if (!f) {
+               perror("fopen read iolog");
+               return 1;
+       }
+
+       p = fgets(buffer, sizeof(buffer), f);
+       if (!p) {
+               td_verror(td, errno, "iolog read");
+               log_err("fio: unable to read iolog\n");
+               fclose(f);
+               return 1;
+       }
+
+       /*
+        * version 2 of the iolog stores a specific string as the
+        * first line, check for that
+        */
+       if (!strncmp(iolog_ver2, buffer, strlen(iolog_ver2)))
+               ret = read_iolog2(td, f);
+       else {
+               log_err("fio: iolog version 1 is no longer supported\n");
+               ret = 1;
+       }
+
+       fclose(f);
+       return ret;
+}
+
+/*
+ * Set up a log for storing io patterns.
+ */
+static int init_iolog_write(struct thread_data *td)
+{
+       struct fio_file *ff;
+       FILE *f;
+       unsigned int i;
+
+       f = fopen(td->o.write_iolog_file, "a");
+       if (!f) {
+               perror("fopen write iolog");
+               return 1;
+       }
+
+       /*
+        * That's it for writing, setup a log buffer and we're done.
+         */
+       td->iolog_f = f;
+       td->iolog_buf = malloc(8192);
+       setvbuf(f, td->iolog_buf, _IOFBF, 8192);
+
+       /*
+        * write our version line
+        */
+       if (fprintf(f, "%s\n", iolog_ver2) < 0) {
+               perror("iolog init\n");
+               return 1;
+       }
+
+       /*
+        * add all known files
+        */
+       for_each_file(td, ff, i)
+               log_file(td, ff, FIO_LOG_ADD_FILE);
+
+       return 0;
+}
+
+int init_iolog(struct thread_data *td)
+{
+       int ret = 0;
+
+       if (td->o.read_iolog_file) {
+               /*
+                * Check if it's a blktrace file and load that if possible.
+                * Otherwise assume it's a normal log file and load that.
+                */
+               if (is_blktrace(td->o.read_iolog_file))
+                       ret = load_blktrace(td, td->o.read_iolog_file);
+               else
+                       ret = init_iolog_read(td);
+       } else if (td->o.write_iolog_file)
+               ret = init_iolog_write(td);
+
+       return ret;
+}
+
+void setup_log(struct io_log **log)
+{
+       struct io_log *l = malloc(sizeof(*l));
+
+       l->nr_samples = 0;
+       l->max_samples = 1024;
+       l->log = malloc(l->max_samples * sizeof(struct io_sample));
+       *log = l;
+}
+
+void __finish_log(struct io_log *log, const char *name)
+{
+       unsigned int i;
+       FILE *f;
+
+       f = fopen(name, "a");
+       if (!f) {
+               perror("fopen log");
+               return;
+       }
+
+       for (i = 0; i < log->nr_samples; i++) {
+               fprintf(f, "%lu, %lu, %u, %u\n", log->log[i].time,
+                                               log->log[i].val,
+                                               log->log[i].ddir,
+                                               log->log[i].bs);
+       }
+
+       fclose(f);
+       free(log->log);
+       free(log);
+}
+
+void finish_log_named(struct thread_data *td, struct io_log *log,
+                      const char *prefix, const char *postfix)
+{
+       char file_name[256], *p;
+
+       snprintf(file_name, 200, "%s_%s.log", prefix, postfix);
+       p = basename(file_name);
+       __finish_log(log, p);
+}
+
+void finish_log(struct thread_data *td, struct io_log *log, const char *name)
+{
+       finish_log_named(td, log, td->o.name, name);
+}
diff --git a/iolog.h b/iolog.h
index c35ce1e694511b2eb08300e8b3744410d3f7a298..a523d4d22767ec8cd80cd022d9ba96dad3e4c012 100644 (file)
--- a/iolog.h
+++ b/iolog.h
@@ -1,16 +1,18 @@
 #ifndef FIO_IOLOG_H
 #define FIO_IOLOG_H
 
+#include "ieee754.h"
+
 /*
  * Use for maintaining statistics
  */
 struct io_stat {
-       unsigned long max_val;
-       unsigned long min_val;
-       unsigned long samples;
+       uint64_t max_val;
+       uint64_t min_val;
+       uint64_t samples;
 
-       double mean;
-       double S;
+       fio_fp64_t mean;
+       fio_fp64_t S;
 };
 
 /*
@@ -91,7 +93,7 @@ extern void add_slat_sample(struct thread_data *, enum fio_ddir, unsigned long,
                                unsigned int);
 extern void add_bw_sample(struct thread_data *, enum fio_ddir, unsigned int,
                                struct timeval *);
-extern void show_run_stats(void);
+extern void add_iops_sample(struct thread_data *, enum fio_ddir, struct timeval *);
 extern void init_disk_util(struct thread_data *);
 extern void update_rusage_stat(struct thread_data *);
 extern void update_io_ticks(void);
diff --git a/log.c b/log.c
index f962864d912d9b5cdc3bfbc39286241fdd717665..af974f85b594abc1951b4bfb77ad625f4887b529 100644 (file)
--- a/log.c
+++ b/log.c
-/*
- * Code related to writing an iolog of what a thread is doing, and to
- * later read that back and replay
- */
-#include <stdio.h>
-#include <stdlib.h>
-#include <libgen.h>
-#include <assert.h>
-#include "flist.h"
-#include "fio.h"
-#include "verify.h"
-#include "trim.h"
-
-static const char iolog_ver2[] = "fio version 2 iolog";
-
-void queue_io_piece(struct thread_data *td, struct io_piece *ipo)
-{
-       flist_add_tail(&ipo->list, &td->io_log_list);
-       td->total_io_size += ipo->len;
-}
-
-void log_io_u(struct thread_data *td, struct io_u *io_u)
-{
-       const char *act[] = { "read", "write", "sync", "datasync",
-                               "sync_file_range", "wait", "trim" };
-
-       assert(io_u->ddir <= 6);
-
-       if (!td->o.write_iolog_file)
-               return;
-
-       fprintf(td->iolog_f, "%s %s %llu %lu\n", io_u->file->file_name,
-                                               act[io_u->ddir], io_u->offset,
-                                               io_u->buflen);
-}
-
-void log_file(struct thread_data *td, struct fio_file *f,
-             enum file_log_act what)
-{
-       const char *act[] = { "add", "open", "close" };
-
-       assert(what < 3);
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdarg.h>
+#include <syslog.h>
 
-       if (!td->o.write_iolog_file)
-               return;
-
-
-       /*
-        * this happens on the pre-open/close done before the job starts
-        */
-       if (!td->iolog_f)
-               return;
-
-       fprintf(td->iolog_f, "%s %s\n", f->file_name, act[what]);
-}
-
-static void iolog_delay(struct thread_data *td, unsigned long delay)
-{
-       unsigned long usec = utime_since_now(&td->last_issue);
-
-       if (delay < usec)
-               return;
-
-       delay -= usec;
-
-       /*
-        * less than 100 usec delay, just regard it as noise
-        */
-       if (delay < 100)
-               return;
-
-       usec_sleep(td, delay);
-}
-
-static int ipo_special(struct thread_data *td, struct io_piece *ipo)
-{
-       struct fio_file *f;
-       int ret;
-
-       /*
-        * Not a special ipo
-        */
-       if (ipo->ddir != DDIR_INVAL)
-               return 0;
-
-       f = td->files[ipo->fileno];
-
-       switch (ipo->file_action) {
-       case FIO_LOG_OPEN_FILE:
-               ret = td_io_open_file(td, f);
-               if (!ret)
-                       break;
-               td_verror(td, ret, "iolog open file");
-               return -1;
-       case FIO_LOG_CLOSE_FILE:
-               td_io_close_file(td, f);
-               break;
-       case FIO_LOG_UNLINK_FILE:
-               unlink(f->file_name);
-               break;
-       default:
-               log_err("fio: bad file action %d\n", ipo->file_action);
-               break;
-       }
-
-       return 1;
-}
-
-int read_iolog_get(struct thread_data *td, struct io_u *io_u)
-{
-       struct io_piece *ipo;
-       unsigned long elapsed;
-       
-       while (!flist_empty(&td->io_log_list)) {
-               int ret;
-
-               ipo = flist_entry(td->io_log_list.next, struct io_piece, list);
-               flist_del(&ipo->list);
-               remove_trim_entry(td, ipo);
-
-               ret = ipo_special(td, ipo);
-               if (ret < 0) {
-                       free(ipo);
-                       break;
-               } else if (ret > 0) {
-                       free(ipo);
-                       continue;
-               }
-
-               io_u->ddir = ipo->ddir;
-               if (ipo->ddir != DDIR_WAIT) {
-                       io_u->offset = ipo->offset;
-                       io_u->buflen = ipo->len;
-                       io_u->file = td->files[ipo->fileno];
-                       get_file(io_u->file);
-                       dprint(FD_IO, "iolog: get %llu/%lu/%s\n", io_u->offset,
-                                               io_u->buflen, io_u->file->file_name);
-                       if (ipo->delay)
-                               iolog_delay(td, ipo->delay);
-               } else {
-                       elapsed = mtime_since_genesis();
-                       if (ipo->delay > elapsed)
-                               usec_sleep(td, (ipo->delay - elapsed) * 1000);
-                               
-               }
-
-               free(ipo);
-               
-               if (io_u->ddir != DDIR_WAIT)
-                       return 0;
-       }
-
-       td->done = 1;
-       return 1;
-}
-
-void prune_io_piece_log(struct thread_data *td)
-{
-       struct io_piece *ipo;
-       struct rb_node *n;
-
-       while ((n = rb_first(&td->io_hist_tree)) != NULL) {
-               ipo = rb_entry(n, struct io_piece, rb_node);
-               rb_erase(n, &td->io_hist_tree);
-               remove_trim_entry(td, ipo);
-               td->io_hist_len--;
-               free(ipo);
-       }
-
-       while (!flist_empty(&td->io_hist_list)) {
-               ipo = flist_entry(td->io_hist_list.next, struct io_piece, list);
-               flist_del(&ipo->list);
-               remove_trim_entry(td, ipo);
-               td->io_hist_len--;
-               free(ipo);
-       }
-}
-
-/*
- * log a successful write, so we can unwind the log for verify
- */
-void log_io_piece(struct thread_data *td, struct io_u *io_u)
-{
-       struct rb_node **p, *parent;
-       struct io_piece *ipo, *__ipo;
-
-       ipo = malloc(sizeof(struct io_piece));
-       init_ipo(ipo);
-       ipo->file = io_u->file;
-       ipo->offset = io_u->offset;
-       ipo->len = io_u->buflen;
-
-       if (io_u_should_trim(td, io_u)) {
-               flist_add_tail(&ipo->trim_list, &td->trim_list);
-               td->trim_entries++;
-       }
-
-       /*
-        * We don't need to sort the entries, if:
-        *
-        *      Sequential writes, or
-        *      Random writes that lay out the file as it goes along
-        *
-        * For both these cases, just reading back data in the order we
-        * wrote it out is the fastest.
-        *
-        * One exception is if we don't have a random map AND we are doing
-        * verifies, in that case we need to check for duplicate blocks and
-        * drop the old one, which we rely on the rb insert/lookup for
-        * handling.
-        */
-       if ((!td_random(td) || !td->o.overwrite) &&
-             (file_randommap(td, ipo->file) || td->o.verify == VERIFY_NONE)) {
-               INIT_FLIST_HEAD(&ipo->list);
-               flist_add_tail(&ipo->list, &td->io_hist_list);
-               ipo->flags |= IP_F_ONLIST;
-               td->io_hist_len++;
-               return;
-       }
-
-       RB_CLEAR_NODE(&ipo->rb_node);
-
-       /*
-        * Sort the entry into the verification list
-        */
-restart:
-       p = &td->io_hist_tree.rb_node;
-       parent = NULL;
-       while (*p) {
-               parent = *p;
-
-               __ipo = rb_entry(parent, struct io_piece, rb_node);
-               if (ipo->file < __ipo->file)
-                       p = &(*p)->rb_left;
-               else if (ipo->file > __ipo->file)
-                       p = &(*p)->rb_right;
-               else if (ipo->offset < __ipo->offset)
-                       p = &(*p)->rb_left;
-               else if (ipo->offset > __ipo->offset)
-                       p = &(*p)->rb_right;
-               else {
-                       assert(ipo->len == __ipo->len);
-                       td->io_hist_len--;
-                       rb_erase(parent, &td->io_hist_tree);
-                       remove_trim_entry(td, __ipo);
-                       free(__ipo);
-                       goto restart;
-               }
-       }
-
-       rb_link_node(&ipo->rb_node, parent, p);
-       rb_insert_color(&ipo->rb_node, &td->io_hist_tree);
-       ipo->flags |= IP_F_ONRB;
-       td->io_hist_len++;
-}
-
-void write_iolog_close(struct thread_data *td)
-{
-       fflush(td->iolog_f);
-       fclose(td->iolog_f);
-       free(td->iolog_buf);
-       td->iolog_f = NULL;
-       td->iolog_buf = NULL;
-}
+#include "fio.h"
 
-/*
- * Read version 2 iolog data. It is enhanced to include per-file logging,
- * syncs, etc.
- */
-static int read_iolog2(struct thread_data *td, FILE *f)
+int log_valist(const char *str, va_list args)
 {
-       unsigned long long offset;
-       unsigned int bytes;
-       int reads, writes, waits, fileno = 0, file_action = 0; /* stupid gcc */
-       char *fname, *act;
-       char *str, *p;
-       enum fio_ddir rw;
-
-       free_release_files(td);
-
-       /*
-        * Read in the read iolog and store it, reuse the infrastructure
-        * for doing verifications.
-        */
-       str = malloc(4096);
-       fname = malloc(256+16);
-       act = malloc(256+16);
-
-       reads = writes = waits = 0;
-       while ((p = fgets(str, 4096, f)) != NULL) {
-               struct io_piece *ipo;
-               int r;
-
-               r = sscanf(p, "%256s %256s %llu %u", fname, act, &offset,
-                                                                       &bytes);
-               if (r == 4) {
-                       /*
-                        * Check action first
-                        */
-                       if (!strcmp(act, "wait"))
-                               rw = DDIR_WAIT;
-                       else if (!strcmp(act, "read"))
-                               rw = DDIR_READ;
-                       else if (!strcmp(act, "write"))
-                               rw = DDIR_WRITE;
-                       else if (!strcmp(act, "sync"))
-                               rw = DDIR_SYNC;
-                       else if (!strcmp(act, "datasync"))
-                               rw = DDIR_DATASYNC;
-                       else if (!strcmp(act, "trim"))
-                               rw = DDIR_TRIM;
-                       else {
-                               log_err("fio: bad iolog file action: %s\n",
-                                                                       act);
-                               continue;
-                       }
-               } else if (r == 2) {
-                       rw = DDIR_INVAL;
-                       if (!strcmp(act, "add")) {
-                               td->o.nr_files++;
-                               fileno = add_file(td, fname);
-                               file_action = FIO_LOG_ADD_FILE;
-                               continue;
-                       } else if (!strcmp(act, "open")) {
-                               fileno = get_fileno(td, fname);
-                               file_action = FIO_LOG_OPEN_FILE;
-                       } else if (!strcmp(act, "close")) {
-                               fileno = get_fileno(td, fname);
-                               file_action = FIO_LOG_CLOSE_FILE;
-                       } else {
-                               log_err("fio: bad iolog file action: %s\n",
-                                                                       act);
-                               continue;
-                       }
-               } else {
-                       log_err("bad iolog2: %s", p);
-                       continue;
-               }
-
-               if (rw == DDIR_READ)
-                       reads++;
-               else if (rw == DDIR_WRITE) {
-                       /*
-                        * Don't add a write for ro mode
-                        */
-                       if (read_only)
-                               continue;
-                       writes++;
-               } else if (rw == DDIR_WAIT) {
-                       waits++;
-               } else if (rw == DDIR_INVAL) {
-               } else if (!ddir_sync(rw)) {
-                       log_err("bad ddir: %d\n", rw);
-                       continue;
-               }
-
-               /*
-                * Make note of file
-                */
-               ipo = malloc(sizeof(*ipo));
-               init_ipo(ipo);
-               ipo->ddir = rw;
-               if (rw == DDIR_WAIT) {
-                       ipo->delay = offset;
-               } else {
-                       ipo->offset = offset;
-                       ipo->len = bytes;
-                       if (bytes > td->o.max_bs[rw])
-                               td->o.max_bs[rw] = bytes;
-                       ipo->fileno = fileno;
-                       ipo->file_action = file_action;
-               }
-                       
-               queue_io_piece(td, ipo);
-       }
+       char buffer[1024];
+       size_t len;
 
-       free(str);
-       free(act);
-       free(fname);
+       len = vsnprintf(buffer, sizeof(buffer), str, args);
 
-       if (writes && read_only) {
-               log_err("fio: <%s> skips replay of %d writes due to"
-                       " read-only\n", td->o.name, writes);
-               writes = 0;
-       }
-
-       if (!reads && !writes && !waits)
-               return 1;
-       else if (reads && !writes)
-               td->o.td_ddir = TD_DDIR_READ;
-       else if (!reads && writes)
-               td->o.td_ddir = TD_DDIR_WRITE;
+       if (log_syslog)
+               syslog(LOG_INFO, "%s", buffer);
        else
-               td->o.td_ddir = TD_DDIR_RW;
+               len = fwrite(buffer, len, 1, f_out);
 
-       return 0;
+       return len;
 }
 
-/*
- * open iolog, check version, and call appropriate parser
- */
-static int init_iolog_read(struct thread_data *td)
+int log_local_buf(const char *buf, size_t len)
 {
-       char buffer[256], *p;
-       FILE *f;
-       int ret;
-
-       f = fopen(td->o.read_iolog_file, "r");
-       if (!f) {
-               perror("fopen read iolog");
-               return 1;
-       }
-
-       p = fgets(buffer, sizeof(buffer), f);
-       if (!p) {
-               td_verror(td, errno, "iolog read");
-               log_err("fio: unable to read iolog\n");
-               fclose(f);
-               return 1;
-       }
-
-       /*
-        * version 2 of the iolog stores a specific string as the
-        * first line, check for that
-        */
-       if (!strncmp(iolog_ver2, buffer, strlen(iolog_ver2)))
-               ret = read_iolog2(td, f);
-       else {
-               log_err("fio: iolog version 1 is no longer supported\n");
-               ret = 1;
-       }
+       if (log_syslog)
+               syslog(LOG_INFO, "%s", buf);
+       else
+               len = fwrite(buf, len, 1, f_out);
 
-       fclose(f);
-       return ret;
+       return len;
 }
 
-/*
- * Set up a log for storing io patterns.
- */
-static int init_iolog_write(struct thread_data *td)
+int log_local(const char *format, ...)
 {
-       struct fio_file *ff;
-       FILE *f;
-       unsigned int i;
-
-       f = fopen(td->o.write_iolog_file, "a");
-       if (!f) {
-               perror("fopen write iolog");
-               return 1;
-       }
-
-       /*
-        * That's it for writing, setup a log buffer and we're done.
-         */
-       td->iolog_f = f;
-       td->iolog_buf = malloc(8192);
-       setvbuf(f, td->iolog_buf, _IOFBF, 8192);
+       char buffer[1024];
+       va_list args;
+       size_t len;
 
-       /*
-        * write our version line
-        */
-       if (fprintf(f, "%s\n", iolog_ver2) < 0) {
-               perror("iolog init\n");
-               return 1;
-       }
+       va_start(args, format);
+       len = vsnprintf(buffer, sizeof(buffer), format, args);
+       va_end(args);
 
-       /*
-        * add all known files
-        */
-       for_each_file(td, ff, i)
-               log_file(td, ff, FIO_LOG_ADD_FILE);
+       if (log_syslog)
+               syslog(LOG_INFO, "%s", buffer);
+       else
+               len = fwrite(buffer, len, 1, f_out);
 
-       return 0;
+       return len;
 }
 
-int init_iolog(struct thread_data *td)
+int log_info(const char *format, ...)
 {
-       int ret = 0;
+       char buffer[1024];
+       va_list args;
+       size_t len;
 
-       if (td->o.read_iolog_file) {
-               /*
-                * Check if it's a blktrace file and load that if possible.
-                * Otherwise assume it's a normal log file and load that.
-                */
-               if (is_blktrace(td->o.read_iolog_file))
-                       ret = load_blktrace(td, td->o.read_iolog_file);
-               else
-                       ret = init_iolog_read(td);
-       } else if (td->o.write_iolog_file)
-               ret = init_iolog_write(td);
+       va_start(args, format);
+       len = vsnprintf(buffer, sizeof(buffer), format, args);
+       va_end(args);
 
-       return ret;
+       if (is_backend)
+               return fio_server_text_output(buffer, len);
+       else if (log_syslog) {
+               syslog(LOG_INFO, "%s", buffer);
+               return len;
+       } else
+               return fwrite(buffer, len, 1, f_out);
 }
 
-void setup_log(struct io_log **log)
+int log_err(const char *format, ...)
 {
-       struct io_log *l = malloc(sizeof(*l));
+       char buffer[1024];
+       va_list args;
+       size_t len;
 
-       l->nr_samples = 0;
-       l->max_samples = 1024;
-       l->log = malloc(l->max_samples * sizeof(struct io_sample));
-       *log = l;
-}
+       va_start(args, format);
+       len = vsnprintf(buffer, sizeof(buffer), format, args);
+       va_end(args);
 
-void __finish_log(struct io_log *log, const char *name)
-{
-       unsigned int i;
-       FILE *f;
+       if (is_backend)
+               return fio_server_text_output(buffer, len);
+       else if (log_syslog) {
+               syslog(LOG_INFO, "%s", buffer);
+               return len;
+       } else {
+               if (f_err != stderr) {
+                       int fio_unused ret;
 
-       f = fopen(name, "a");
-       if (!f) {
-               perror("fopen log");
-               return;
-       }
+                       ret = fwrite(buffer, len, 1, stderr);
+               }
 
-       for (i = 0; i < log->nr_samples; i++) {
-               fprintf(f, "%lu, %lu, %u, %u\n", log->log[i].time,
-                                               log->log[i].val,
-                                               log->log[i].ddir,
-                                               log->log[i].bs);
+               return fwrite(buffer, len, 1, f_err);
        }
-
-       fclose(f);
-       free(log->log);
-       free(log);
-}
-
-void finish_log_named(struct thread_data *td, struct io_log *log,
-                      const char *prefix, const char *postfix)
-{
-       char file_name[256], *p;
-
-       snprintf(file_name, 200, "%s_%s.log", prefix, postfix);
-       p = basename(file_name);
-       __finish_log(log, p);
-}
-
-void finish_log(struct thread_data *td, struct io_log *log, const char *name)
-{
-       finish_log_named(td, log, td->o.name, name);
 }
diff --git a/log.h b/log.h
index eea1129ba4ff3756fcd969545b6340e6fcc34032..fdf3d7b19aed3d3981b166c32339fbd7bb596f79 100644 (file)
--- a/log.h
+++ b/log.h
@@ -2,23 +2,15 @@
 #define FIO_LOG_H
 
 #include <stdio.h>
+#include <stdarg.h>
 
 extern FILE *f_out;
 extern FILE *f_err;
 
-/*
- * If logging output to a file, stderr should go to both stderr and f_err
- */
-#define log_err(args, ...)     do {                            \
-       fprintf(f_err, args,  ##__VA_ARGS__);           \
-       if (f_err != stderr)                                            \
-               fprintf(stderr, args,  ##__VA_ARGS__);  \
-       } while (0)
-
-#define log_info(args, ...)    fprintf(f_out, args, ##__VA_ARGS__)
-#define log_valist(str, args)  vfprintf(f_out, (str), (args))
-
-FILE *get_f_out(void);
-FILE *get_f_err(void);
+extern int log_err(const char *format, ...);
+extern int log_info(const char *format, ...);
+extern int log_local(const char *format, ...);
+extern int log_valist(const char *str, va_list);
+extern int log_local_buf(const char *buf, size_t);
 
 #endif
index 5252477b6e0756888d6cc3bd42b8dcdc5b492e4b..580d5aa5b95800d707c3fff92a719cc4ac4b1c76 100644 (file)
--- a/options.c
+++ b/options.c
@@ -595,6 +595,14 @@ static char *get_next_file_name(char **ptr)
        return start;
 }
 
+static int str_hostname_cb(void *data, const char *input)
+{
+       struct thread_data *td = data;
+
+       td->o.filename = strdup(input);
+       return 0;
+}
+
 static int str_filename_cb(void *data, const char *input)
 {
        struct thread_data *td = data;
@@ -749,6 +757,17 @@ static int str_write_lat_log_cb(void *data, const char *str)
        return 0;
 }
 
+static int str_write_iops_log_cb(void *data, const char *str)
+{
+       struct thread_data *td = data;
+
+       if (str)
+               td->o.iops_log_file = strdup(str);
+
+       td->o.write_iops_log = 1;
+       return 0;
+}
+
 static int str_gtod_reduce_cb(void *data, int *il)
 {
        struct thread_data *td = data;
@@ -829,9 +848,6 @@ static int kb_base_verify(struct fio_option *o, void *data)
        return 0;
 }
 
-#define __stringify_1(x)       #x
-#define __stringify(x)         __stringify_1(x)
-
 /*
  * Map of job/command line options
  */
@@ -863,6 +879,12 @@ static struct fio_option options[FIO_MAX_OPTS] = {
                .prio   = -1, /* must come after "directory" */
                .help   = "File(s) to use for the workload",
        },
+       {
+               .name   = "hostname",
+               .type   = FIO_OPT_STR_STORE,
+               .cb     = str_hostname_cb,
+               .help   = "Hostname for net IO engine",
+       },
        {
                .name   = "kb_base",
                .type   = FIO_OPT_INT,
@@ -1824,6 +1846,15 @@ static struct fio_option options[FIO_MAX_OPTS] = {
                .help   = "Time window over which to calculate bandwidth"
                          " (msec)",
                .def    = "500",
+               .parent = "write_bw_log",
+       },
+       {
+               .name   = "iopsavgtime",
+               .type   = FIO_OPT_INT,
+               .off1   = td_var_offset(iops_avg_time),
+               .help   = "Time window over which to calculate IOPS (msec)",
+               .def    = "500",
+               .parent = "write_iops_log",
        },
        {
                .name   = "create_serialize",
@@ -1941,12 +1972,19 @@ static struct fio_option options[FIO_MAX_OPTS] = {
                .cb     = str_write_lat_log_cb,
                .help   = "Write log of latency during run",
        },
+       {
+               .name   = "write_iops_log",
+               .type   = FIO_OPT_STR,
+               .off1   = td_var_offset(write_iops_log),
+               .cb     = str_write_iops_log_cb,
+               .help   = "Write log of IOPS during run",
+       },
        {
                .name   = "hugepage-size",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(hugepage_size),
                .help   = "When using hugepages, specify size of each page",
-               .def    = __stringify(FIO_HUGE_PAGE),
+               .def    = __fio_stringify(FIO_HUGE_PAGE),
        },
        {
                .name   = "group_reporting",
index 91c8bcda84d572f76b04fe5f12589430ca4252cd..2f75bf8f3f45fec7f0bdcc1f0234cd3b43172a2c 100644 (file)
@@ -1,6 +1,8 @@
 #ifndef FIO_OS_AIX_H
 #define FIO_OS_AIX_H
 
+#define        FIO_OS  os_aix
+
 #include <errno.h>
 #include <unistd.h>
 #include <sys/devinfo.h>
 #define OS_MAP_ANON            MAP_ANON
 #define OS_MSG_DONTWAIT                0
 
+#if BYTE_ORDER == BIG_ENDIAN
+#define FIO_BIG_ENDIAN
+#else
+#define FIO_LITTLE_ENDIAN
+#endif
+
+#define FIO_USE_GENERIC_SWAP
+
+#define FIO_OS_HAVE_SOCKLEN_T
+#define fio_socklen_t socklen_t
+
 static inline int blockdev_invalidate_cache(struct fio_file *f)
 {
        return EINVAL;
index 317d403f3bd3f228deeaf7d45f00312986c65be3..93205c32280d282d36175f13217aa8f80ce733f7 100644 (file)
@@ -1,10 +1,14 @@
 #ifndef FIO_OS_FREEBSD_H
 #define FIO_OS_FREEBSD_H
 
+#define        FIO_OS  os_freebsd
+
 #include <errno.h>
 #include <sys/sysctl.h>
 #include <sys/disk.h>
 #include <sys/thr.h>
+#include <sys/endian.h>
+#include <sys/socket.h>
 
 #include "../file.h"
 
 
 #define OS_MAP_ANON            MAP_ANON
 
+#if BYTE_ORDER == LITTLE_ENDIAN
+#define FIO_LITTLE_ENDIAN
+#else
+#define FIO_BIG_ENDIAN
+#endif
+
+#define fio_swap16(x)  bswap16(x)
+#define fio_swap32(x)  bswap32(x)
+#define fio_swap64(x)  bswap64(x)
+
 typedef off_t off64_t;
 
 static inline int blockdev_size(struct fio_file *f, unsigned long long *bytes)
index 4353a0199aa64c5c857613d9bc4b1c7331e7f45f..5943938194cc71027556ae6d59f037e9ca0a5f6d 100644 (file)
@@ -1,6 +1,8 @@
 #ifndef FIO_OS_HPUX_H
 #define FIO_OS_HPUX_H
 
+#define        FIO_OS  os_hpux
+
 #include <errno.h>
 #include <unistd.h>
 #include <sys/ioctl.h>
@@ -13,6 +15,7 @@
 #include <sys/pstat.h>
 #include <time.h>
 #include <aio.h>
+#include <arm.h>
 
 #include "../file.h"
 
 #define MSG_WAITALL    0x40
 #endif
 
+#ifdef LITTLE_ENDIAN
+#define FIO_LITTLE_ENDIAN
+#else
+#define FIO_BIG_ENDIAN
+#endif
+
+#define FIO_USE_GENERIC_SWAP
+
 #define FIO_OS_HAVE_AIOCB_TYPEDEF
 typedef struct aiocb64 os_aiocb_t;
 
+#define FIO_OS_HAVE_SOCKLEN_T
+typedef int fio_socklen_t;
+
 static inline int blockdev_invalidate_cache(struct fio_file *f)
 {
        return EINVAL;
index a36552ba68ba93caf6ca58e5fc9761fc53c0b88d..9f547fffe635ad0f652b1baa6a579032bdacc801 100644 (file)
@@ -1,6 +1,8 @@
 #ifndef FIO_OS_LINUX_H
 #define FIO_OS_LINUX_H
 
+#define        FIO_OS  os_linux
+
 #include <sys/ioctl.h>
 #include <sys/uio.h>
 #include <sys/syscall.h>
@@ -12,6 +14,7 @@
 #include <linux/unistd.h>
 #include <linux/raw.h>
 #include <linux/major.h>
+#include <endian.h>
 
 #include "indirect.h"
 #include "binject.h"
@@ -89,8 +92,8 @@ typedef struct drand48_data os_random_state_t;
        sched_getaffinity((pid), (ptr))
 #endif
 
-#define fio_cpu_clear(mask, cpu)       CPU_CLR((cpu), (mask))
-#define fio_cpu_set(mask, cpu)         CPU_SET((cpu), (mask))
+#define fio_cpu_clear(mask, cpu)       (void) CPU_CLR((cpu), (mask))
+#define fio_cpu_set(mask, cpu)         (void) CPU_SET((cpu), (mask))
 
 static inline int fio_cpuset_init(os_cpu_mask_t *mask)
 {
@@ -286,6 +289,18 @@ static inline int fio_lookup_raw(dev_t dev, int *majdev, int *mindev)
 #define FIO_MADV_FREE  MADV_REMOVE
 #endif
 
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define FIO_LITTLE_ENDIAN
+#elif __BYTE_ORDER == __BIG_ENDIAN
+#define FIO_BIG_ENDIAN
+#else
+#error "Unknown endianness"
+#endif
+
+#define fio_swap16(x)  __bswap_16(x)
+#define fio_swap32(x)  __bswap_32(x)
+#define fio_swap64(x)  __bswap_64(x)
+
 #define CACHE_LINE_FILE        \
        "/sys/devices/system/cpu/cpu0/cache/index0/coherency_line_size"
 
index eb55cd7742fa4a7dee86e5122273f2a33362608c..80c49f474e007ea461bd22eb30d16d5b646125de 100644 (file)
@@ -1,6 +1,8 @@
 #ifndef FIO_OS_APPLE_H
 #define FIO_OS_APPLE_H
 
+#define        FIO_OS  os_mac
+
 #include <errno.h>
 #include <fcntl.h>
 #include <sys/disk.h>
@@ -9,6 +11,8 @@
 #include <unistd.h>
 #include <signal.h>
 #include <mach/mach_init.h>
+#include <machine/endian.h>
+#include <libkern/OSByteOrder.h>
 
 #include "../file.h"
 
 
 #define OS_MAP_ANON            MAP_ANON
 
+#if defined(__LITTLE_ENDIAN__)
+#define FIO_LITTLE_ENDIAN
+#elif defined(__BIG_ENDIAN__)
+#define FIO_BIG_ENDIAN
+#else
+#error "Undefined byte order"
+#endif
+
+#define fio_swap16(x)  OSSwapInt16(x)
+#define fio_swap32(x)  OSSwapInt32(x)
+#define fio_swap64(x)  OSSwapInt64(x)
+
 /*
  * OSX has a pitifully small shared memory segment by default,
  * so default to a lower number of max jobs supported
index e03866dfc86c131ea6fa9cf32f4786b45042baf8..78ac135f9ea035eddb02fb01e34e29a6e0e6eae6 100644 (file)
@@ -1,9 +1,12 @@
 #ifndef FIO_OS_NETBSD_H
 #define FIO_OS_NETBSD_H
 
+#define        FIO_OS  os_netbsd
+
 #include <errno.h>
 #include <sys/param.h>
 #include <sys/thr.h>
+#include <sys/endian.h>
 /* XXX hack to avoid confilcts between rbtree.h and <sys/rb.h> */
 #define        rb_node _rb_node
 #include <sys/sysctl.h>
 #define PTHREAD_STACK_MIN 4096
 #endif
 
+#if BYTE_ORDER == LITTLE_ENDIAN
+#define FIO_LITTLE_ENDIAN
+#else
+#define FIO_BIG_ENDIAN
+#endif
+
+#define fio_swap16(x)  bswap16(x)
+#define fio_swap32(x)  bswap32(x)
+#define fio_swap64(x)  bswap64(x)
+
 typedef off_t off64_t;
 
 static inline int blockdev_invalidate_cache(struct fio_file *f)
index c0d3c30f82ba2e93b458d96bb1c5d9ef238eafd8..5bf868ac8f527c3ed56c130ee633d12b5ab3b693 100644 (file)
@@ -1,6 +1,8 @@
 #ifndef FIO_OS_SOLARIS_H
 #define FIO_OS_SOLARIS_H
 
+#define        FIO_OS  os_solaris
+
 #include <errno.h>
 #include <malloc.h>
 #include <sys/types.h>
@@ -8,6 +10,7 @@
 #include <sys/pset.h>
 #include <sys/mman.h>
 #include <sys/dkio.h>
+#include <sys/byteorder.h>
 
 #include "../file.h"
 
 #define OS_MAP_ANON            MAP_ANON
 #define OS_RAND_MAX            2147483648UL
 
+#if defined(_BIG_ENDIAN)
+#define FIO_BIG_ENDIAN
+#else
+#define FIO_LITTLE_ENDIAN
+#endif
+
+#define fio_swap16(x)  BSWAP_16(x)
+#define fio_swap32(x)  BSWAP_32(x)
+#define fio_swap64(x)  BSWAP_64(x)
+
 struct solaris_rand_seed {
        unsigned short r[3];
 };
index db4127b3a2e35eed0fa796eec671e7334ad2f21e..8812cfa2e0d76943d398acc11e5b67787a7d9b50 100644 (file)
@@ -1,10 +1,13 @@
 #ifndef FIO_OS_WINDOWS_H\r
 #define FIO_OS_WINDOWS_H\r
 \r
+#define FIO_OS os_windows\r
+\r
 #include <sys/types.h>\r
 #include <errno.h>\r
 #include <windows.h>\r
 #include <psapi.h>\r
+#include <stdlib.h>\r
 \r
 #include "../smalloc.h"\r
 #include "../file.h"\r
@@ -18,6 +21,9 @@
 #define FIO_HAVE_WINDOWSAIO\r
 #define FIO_HAVE_GETTID\r
 \r
+#define FIO_OS_HAVE_SOCKLEN_T\r
+typedef int fio_socklen_t;\r
+\r
 #define FIO_USE_GENERIC_RAND\r
 \r
 #define OS_MAP_ANON            MAP_ANON\r
 \r
 #define FIO_PREFERRED_ENGINE   "windowsaio"\r
 \r
+#define FIO_LITTLE_ENDIAN\r
+#define fio_swap16(x)  _byteswap_ushort(x)\r
+#define fio_swap32(x)  _byteswap_ulong(x)\r
+#define fio_swap64(x)  _byteswap_uint64(x)\r
+\r
 typedef off_t off64_t;\r
 \r
 typedef struct {\r
diff --git a/os/os.h b/os/os.h
index 2eb38e87afda20dae2b52ebd65d1dfc146247e23..12188156cf280afea5ca29935d74ee794058228b 100644 (file)
--- a/os/os.h
+++ b/os/os.h
@@ -6,6 +6,19 @@
 #include <unistd.h>
 #include <stdlib.h>
 
+enum {
+       os_linux = 1,
+       os_aix,
+       os_freebsd,
+       os_hpux,
+       os_mac,
+       os_netbsd,
+       os_solaris,
+       os_windows,
+
+       os_nr,
+};
+
 #if defined(__linux__)
 #include "os-linux.h"
 #elif defined(__FreeBSD__)
@@ -115,6 +128,75 @@ typedef unsigned long os_cpu_mask_t;
 #define FIO_MAX_JOBS           2048
 #endif
 
+#ifndef FIO_OS_HAVE_SOCKLEN_T
+typedef socklen_t fio_socklen_t;
+#endif
+
+#ifdef FIO_USE_GENERIC_SWAP
+static inline uint16_t fio_swap16(uint16_t val)
+{
+       return (val << 8) | (val >> 8);
+}
+
+static inline uint32_t fio_swap32(uint32_t val)
+{
+       val = ((val & 0xff00ff00UL) >> 8) | ((val & 0x00ff00ffUL) << 8);
+
+       return (val >> 16) | (val << 16);
+}
+
+static inline uint64_t fio_swap64(uint64_t val)
+{
+       val = ((val & 0xff00ff00ff00ff00ULL) >> 8) |
+             ((val & 0x00ff00ff00ff00ffULL) << 8);
+       val = ((val & 0xffff0000ffff0000ULL) >> 16) |
+             ((val & 0x0000ffff0000ffffULL) << 16);
+
+       return (val >> 32) | (val << 32);
+}
+#endif
+
+#ifdef FIO_LITTLE_ENDIAN
+#define __le16_to_cpu(x)               (x)
+#define __le32_to_cpu(x)               (x)
+#define __le64_to_cpu(x)               (x)
+#define __cpu_to_le16(x)               (x)
+#define __cpu_to_le32(x)               (x)
+#define __cpu_to_le64(x)               (x)
+#else
+#define __le16_to_cpu(x)               fio_swap16(x)
+#define __le32_to_cpu(x)               fio_swap32(x)
+#define __le64_to_cpu(x)               fio_swap64(x)
+#define __cpu_to_le16(x)               fio_swap16(x)
+#define __cpu_to_le32(x)               fio_swap32(x)
+#define __cpu_to_le64(x)               fio_swap64(x)
+#endif
+
+#define le16_to_cpu(val) ({                    \
+       uint16_t *__val = &(val);               \
+       __le16_to_cpu(*__val);                  \
+})
+#define le32_to_cpu(val) ({                    \
+       uint32_t *__val = &(val);               \
+       __le32_to_cpu(*__val);                  \
+})
+#define le64_to_cpu(val) ({                    \
+       uint64_t *__val = &(val);               \
+       __le64_to_cpu(*__val);                  \
+})
+#define cpu_to_le16(val) ({                    \
+       uint16_t *__val = &(val);               \
+       __cpu_to_le16(*__val);                  \
+})
+#define cpu_to_le32(val) ({                    \
+       uint32_t *__val = &(val);               \
+       __cpu_to_le32(*__val);                  \
+})
+#define cpu_to_le64(val) ({                    \
+       uint64_t *__val = &(val);               \
+       __cpu_to_le64(*__val);                  \
+})
+
 #ifndef FIO_HAVE_BLKTRACE
 static inline int is_blktrace(const char *fname)
 {
index 028c4cc32f23ba51298dbcf434ae05ec5a4b2ddd..1bf3ea6b8f0ca0c9337a9d9ac3aee6ebcd87f3c1 100755 (executable)
@@ -2,8 +2,8 @@
 <Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">\r
 \r
 <?define VersionMajor = 1?>\r
-<?define VersionMinor = 59?>\r
-<?define VersionBuild = 0?>\r
+<?define VersionMinor = 99?>\r
+<?define VersionBuild = 4?>\r
 \r
        <Product Id="*"\r
          Codepage="1252" Language="1033"\r
index 6c8a228c0d804a1a12afc4107dd34b0ada465a57..d8f4602f964dd3d4cb9fc9ddb4c3afd88ecad9d4 100644 (file)
@@ -1,4 +1,6 @@
-#define FIO_VERSION_MAJOR 1\r
-#define FIO_VERSION_MINOR 59\r
-#define FIO_VERSION_BUILD 0\r
-#define FIO_VERSION_STRING "1.59"\r
+#include "../../fio_version.h"\r
+\r
+#define FIO_VERSION_MAJOR FIO_MAJOR\r
+#define FIO_VERSION_MINOR FIO_MINOR\r
+#define FIO_VERSION_BUILD FIO_PATCH\r
+#define FIO_VERSION_STRING "1.99.1"\r
diff --git a/parse.c b/parse.c
index 239e37141c0fb5be468d9f841844c0ca4c64ba19..27e7336b49af99720886905928a69058e42f3bf1 100644 (file)
--- a/parse.c
+++ b/parse.c
@@ -44,24 +44,25 @@ static void posval_sort(struct fio_option *o, struct value_pair *vpmap)
        qsort(vpmap, entries, sizeof(struct value_pair), vp_cmp);
 }
 
-static void show_option_range(struct fio_option *o, FILE *out)
+static void show_option_range(struct fio_option *o,
+                               int (*logger)(const char *format, ...))
 {
        if (o->type == FIO_OPT_FLOAT_LIST){
                if (isnan(o->minfp) && isnan(o->maxfp))
                        return;
 
-               fprintf(out, "%20s: min=%f", "range", o->minfp);
+               logger("%20s: min=%f", "range", o->minfp);
                if (!isnan(o->maxfp))
-                       fprintf(out, ", max=%f", o->maxfp);
-               fprintf(out, "\n");
+                       logger(", max=%f", o->maxfp);
+               logger("\n");
        } else {
                if (!o->minval && !o->maxval)
                        return;
 
-               fprintf(out, "%20s: min=%d", "range", o->minval);
+               logger("%20s: min=%d", "range", o->minval);
                if (o->maxval)
-                       fprintf(out, ", max=%d", o->maxval);
-               fprintf(out, "\n");
+                       logger(", max=%d", o->maxval);
+               logger("\n");
        }
 }
 
@@ -75,17 +76,17 @@ static void show_option_values(struct fio_option *o)
                if (!vp->ival)
                        continue;
 
-               printf("%20s: %-10s", i == 0 ? "valid values" : "", vp->ival);
+               log_info("%20s: %-10s", i == 0 ? "valid values" : "", vp->ival);
                if (vp->help)
-                       printf(" %s", vp->help);
-               printf("\n");
+                       log_info(" %s", vp->help);
+               log_info("\n");
        }
 
        if (i)
-               printf("\n");
+               log_info("\n");
 }
 
-static void show_option_help(struct fio_option *o, FILE *out)
+static void show_option_help(struct fio_option *o, int is_err)
 {
        const char *typehelp[] = {
                "invalid",
@@ -101,15 +102,21 @@ static void show_option_help(struct fio_option *o, FILE *out)
                "no argument (opt)",
                "deprecated",
        };
+       int (*logger)(const char *format, ...);
+
+       if (is_err)
+               logger = log_err;
+       else
+               logger = log_info;
 
        if (o->alias)
-               fprintf(out, "%20s: %s\n", "alias", o->alias);
+               logger("%20s: %s\n", "alias", o->alias);
 
-       fprintf(out, "%20s: %s\n", "type", typehelp[o->type]);
-       fprintf(out, "%20s: %s\n", "default", o->def ? o->def : "no default");
+       logger("%20s: %s\n", "type", typehelp[o->type]);
+       logger("%20s: %s\n", "default", o->def ? o->def : "no default");
        if (o->prof_name)
-               fprintf(out, "%20s: only for profile '%s'\n", "valid", o->prof_name);
-       show_option_range(o, stdout);
+               logger("%20s: only for profile '%s'\n", "valid", o->prof_name);
+       show_option_range(o, logger);
        show_option_values(o);
 }
 
@@ -357,7 +364,7 @@ static int __handle_option(struct fio_option *o, const char *ptr, void *data,
                                                        o->type, ptr);
 
        if (!ptr && o->type != FIO_OPT_STR_SET && o->type != FIO_OPT_STR) {
-               fprintf(stderr, "Option %s requires an argument\n", o->name);
+               log_err("Option %s requires an argument\n", o->name);
                return 1;
        }
 
@@ -411,12 +418,12 @@ static int __handle_option(struct fio_option *o, const char *ptr, void *data,
                        break;
 
                if (o->maxval && ull > o->maxval) {
-                       fprintf(stderr, "max value out of range: %lld"
+                       log_err("max value out of range: %lld"
                                        " (%d max)\n", ull, o->maxval);
                        return 1;
                }
                if (o->minval && ull < o->minval) {
-                       fprintf(stderr, "min value out of range: %lld"
+                       log_err("min value out of range: %lld"
                                        " (%d min)\n", ull, o->minval);
                        return 1;
                }
@@ -462,22 +469,21 @@ static int __handle_option(struct fio_option *o, const char *ptr, void *data,
                        *ilp = ul2;
                }
                if (curr >= o->maxlen) {
-                       fprintf(stderr, "the list exceeding max length %d\n",
+                       log_err("the list exceeding max length %d\n",
                                        o->maxlen);
                        return 1;
                }
                if(!str_to_float(ptr, &uf)){
-                       fprintf(stderr, "not a floating point value: %s\n",
-                                       ptr);
+                       log_err("not a floating point value: %s\n", ptr);
                        return 1;
                }
                if (!isnan(o->maxfp) && uf > o->maxfp) {
-                       fprintf(stderr, "value out of range: %f"
+                       log_err("value out of range: %f"
                                " (range max: %f)\n", uf, o->maxfp);
                        return 1;
                }
                if (!isnan(o->minfp) && uf < o->minfp) {
-                       fprintf(stderr, "value out of range: %f"
+                       log_err("value out of range: %f"
                                " (range min: %f)\n", uf, o->minfp);
                        return 1;
                }
@@ -603,12 +609,12 @@ match:
                        break;
 
                if (o->maxval && il > (int) o->maxval) {
-                       fprintf(stderr, "max value out of range: %d (%d max)\n",
+                       log_err("max value out of range: %d (%d max)\n",
                                                                il, o->maxval);
                        return 1;
                }
                if (o->minval && il < o->minval) {
-                       fprintf(stderr, "min value out of range: %d (%d min)\n",
+                       log_err("min value out of range: %d (%d min)\n",
                                                                il, o->minval);
                        return 1;
                }
@@ -635,10 +641,10 @@ match:
                break;
        }
        case FIO_OPT_DEPRECATED:
-               fprintf(stdout, "Option %s is deprecated\n", o->name);
+               log_info("Option %s is deprecated\n", o->name);
                break;
        default:
-               fprintf(stderr, "Bad option type %u\n", o->type);
+               log_err("Bad option type %u\n", o->type);
                ret = 1;
        }
 
@@ -648,9 +654,9 @@ match:
        if (o->verify) {
                ret = o->verify(o, data);
                if (ret) {
-                       fprintf(stderr,"Correct format for offending option\n");
-                       fprintf(stderr, "%20s: %s\n", o->name, o->help);
-                       show_option_help(o, stderr);
+                       log_err("Correct format for offending option\n");
+                       log_err("%20s: %s\n", o->name, o->help);
+                       show_option_help(o, 1);
                }
        }
 
@@ -776,14 +782,14 @@ int parse_cmd_option(const char *opt, const char *val,
 
        o = find_option(options, opt);
        if (!o) {
-               fprintf(stderr, "Bad option <%s>\n", opt);
+               log_err("Bad option <%s>\n", opt);
                return 1;
        }
 
        if (!handle_option(o, val, data))
                return 0;
 
-       fprintf(stderr, "fio: failed parsing %s=%s\n", opt, val);
+       log_err("fio: failed parsing %s=%s\n", opt, val);
        return 1;
 }
 
@@ -804,7 +810,7 @@ static char *option_dup_subs(const char *opt)
        size_t envlen;
 
        if (strlen(opt) + 1 > OPT_LEN_MAX) {
-               fprintf(stderr, "OPT_LEN_MAX (%d) is too small\n", OPT_LEN_MAX);
+               log_err("OPT_LEN_MAX (%d) is too small\n", OPT_LEN_MAX);
                return NULL;
        }
 
@@ -852,7 +858,7 @@ int parse_option(const char *opt, struct fio_option *options, void *data)
 
        o = get_option(tmp, options, &post);
        if (!o) {
-               fprintf(stderr, "Bad option <%s>\n", tmp);
+               log_err("Bad option <%s>\n", tmp);
                free(tmp);
                return 1;
        }
@@ -862,7 +868,7 @@ int parse_option(const char *opt, struct fio_option *options, void *data)
                return 0;
        }
 
-       fprintf(stderr, "fio: failed parsing %s\n", opt);
+       log_err("fio: failed parsing %s\n", opt);
        free(tmp);
        return 1;
 }
@@ -936,7 +942,7 @@ static void __print_option(struct fio_option *o, struct fio_option *org,
 
        sprintf(p, "%s", o->name);
 
-       printf("%-24s: %s\n", name, o->help);
+       log_info("%-24s: %s\n", name, o->help);
 }
 
 static void print_option(struct fio_option *o)
@@ -1001,7 +1007,7 @@ int show_cmd_help(struct fio_option *options, const char *name)
                if (show_all || match) {
                        found = 1;
                        if (match)
-                               printf("%20s: %s\n", o->name, o->help);
+                               log_info("%20s: %s\n", o->name, o->help);
                        if (show_all) {
                                if (!o->parent)
                                        print_option(o);
@@ -1012,24 +1018,24 @@ int show_cmd_help(struct fio_option *options, const char *name)
                if (!match)
                        continue;
 
-               show_option_help(o, stdout);
+               show_option_help(o, 0);
        }
 
        if (found)
                return 0;
 
-       printf("No such command: %s", name);
+       log_err("No such command: %s", name);
 
        /*
         * Only print an appropriately close option, one where the edit
         * distance isn't too big. Otherwise we get crazy matches.
         */
        if (closest && best_dist < 3) {
-               printf(" - showing closest match\n");
-               printf("%20s: %s\n", closest->name, closest->help);
-               show_option_help(closest, stdout);
+               log_info(" - showing closest match\n");
+               log_info("%20s: %s\n", closest->name, closest->help);
+               show_option_help(closest, 0);
        } else
-               printf("\n");
+               log_info("\n");
 
        return 1;
 }
@@ -1061,20 +1067,17 @@ void option_init(struct fio_option *o)
                o->maxfp = NAN;
        }
        if (o->type == FIO_OPT_STR_SET && o->def) {
-               fprintf(stderr, "Option %s: string set option with"
+               log_err("Option %s: string set option with"
                                " default will always be true\n", o->name);
        }
-       if (!o->cb && (!o->off1 && !o->roff1)) {
-               fprintf(stderr, "Option %s: neither cb nor offset given\n",
-                                                       o->name);
-       }
+       if (!o->cb && (!o->off1 && !o->roff1))
+               log_err("Option %s: neither cb nor offset given\n", o->name);
        if (o->type == FIO_OPT_STR || o->type == FIO_OPT_STR_STORE ||
            o->type == FIO_OPT_STR_MULTI)
                return;
        if (o->cb && ((o->off1 || o->off2 || o->off3 || o->off4) ||
                      (o->roff1 || o->roff2 || o->roff3 || o->roff4))) {
-               fprintf(stderr, "Option %s: both cb and offset given\n",
-                                                        o->name);
+               log_err("Option %s: both cb and offset given\n", o->name);
        }
 }
 
diff --git a/server.c b/server.c
new file mode 100644 (file)
index 0000000..2c20e37
--- /dev/null
+++ b/server.c
@@ -0,0 +1,1077 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <limits.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/poll.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <syslog.h>
+#include <signal.h>
+
+#include "fio.h"
+#include "server.h"
+#include "crc/crc16.h"
+#include "ieee754.h"
+
+#include "fio_version.h"
+
+int fio_net_port = 8765;
+
+int exit_backend = 0;
+
+static int server_fd = -1;
+static char *fio_server_arg;
+static char *bind_sock;
+static struct sockaddr_in saddr_in;
+
+static const char *fio_server_ops[FIO_NET_CMD_NR] = {
+       "",
+       "QUIT",
+       "EXIT",
+       "JOB",
+       "JOBLINE",
+       "TEXT",
+       "TS",
+       "GS",
+       "SEND_ETA",
+       "ETA",
+       "PROBE",
+       "START",
+       "STOP"
+};
+
+const char *fio_server_op(unsigned int op)
+{
+       static char buf[32];
+
+       if (op < FIO_NET_CMD_NR)
+               return fio_server_ops[op];
+
+       sprintf(buf, "UNKNOWN/%d", op);
+       return buf;
+}
+
+int fio_send_data(int sk, const void *p, unsigned int len)
+{
+       assert(len <= sizeof(struct fio_net_cmd) + FIO_SERVER_MAX_PDU);
+
+       do {
+               int ret = send(sk, p, len, 0);
+
+               if (ret > 0) {
+                       len -= ret;
+                       if (!len)
+                               break;
+                       p += ret;
+                       continue;
+               } else if (!ret)
+                       break;
+               else if (errno == EAGAIN || errno == EINTR)
+                       continue;
+               else
+                       break;
+       } while (!exit_backend);
+
+       if (!len)
+               return 0;
+
+       return 1;
+}
+
+int fio_recv_data(int sk, void *p, unsigned int len)
+{
+       do {
+               int ret = recv(sk, p, len, MSG_WAITALL);
+
+               if (ret > 0) {
+                       len -= ret;
+                       if (!len)
+                               break;
+                       p += ret;
+                       continue;
+               } else if (!ret)
+                       break;
+               else if (errno == EAGAIN || errno == EINTR)
+                       continue;
+               else
+                       break;
+       } while (!exit_backend);
+
+       if (!len)
+               return 0;
+
+       return -1;
+}
+
+static int verify_convert_cmd(struct fio_net_cmd *cmd)
+{
+       uint16_t crc;
+
+       cmd->cmd_crc16 = le16_to_cpu(cmd->cmd_crc16);
+       cmd->pdu_crc16 = le16_to_cpu(cmd->pdu_crc16);
+
+       crc = crc16(cmd, FIO_NET_CMD_CRC_SZ);
+       if (crc != cmd->cmd_crc16) {
+               log_err("fio: server bad crc on command (got %x, wanted %x)\n",
+                               cmd->cmd_crc16, crc);
+               return 1;
+       }
+
+       cmd->version    = le16_to_cpu(cmd->version);
+       cmd->opcode     = le16_to_cpu(cmd->opcode);
+       cmd->flags      = le32_to_cpu(cmd->flags);
+       cmd->tag        = le64_to_cpu(cmd->tag);
+       cmd->pdu_len    = le32_to_cpu(cmd->pdu_len);
+
+       switch (cmd->version) {
+       case FIO_SERVER_VER:
+               break;
+       default:
+               log_err("fio: bad server cmd version %d\n", cmd->version);
+               return 1;
+       }
+
+       if (cmd->pdu_len > FIO_SERVER_MAX_PDU) {
+               log_err("fio: command payload too large: %u\n", cmd->pdu_len);
+               return 1;
+       }
+
+       return 0;
+}
+
+/*
+ * Read (and defragment, if necessary) incoming commands
+ */
+struct fio_net_cmd *fio_net_recv_cmd(int sk)
+{
+       struct fio_net_cmd cmd, *cmdret = NULL;
+       size_t cmd_size = 0, pdu_offset = 0;
+       uint16_t crc;
+       int ret, first = 1;
+       void *pdu = NULL;
+
+       do {
+               ret = fio_recv_data(sk, &cmd, sizeof(cmd));
+               if (ret)
+                       break;
+
+               /* We have a command, verify it and swap if need be */
+               ret = verify_convert_cmd(&cmd);
+               if (ret)
+                       break;
+
+               if (first) {
+                       /* if this is text, add room for \0 at the end */
+                       cmd_size = sizeof(cmd) + cmd.pdu_len + 1;
+                       assert(!cmdret);
+               } else
+                       cmd_size += cmd.pdu_len;
+
+               cmdret = realloc(cmdret, cmd_size);
+
+               if (first)
+                       memcpy(cmdret, &cmd, sizeof(cmd));
+               else
+                       assert(cmdret->opcode == cmd.opcode);
+
+               if (!cmd.pdu_len)
+                       break;
+
+               /* There's payload, get it */
+               pdu = (void *) cmdret->payload + pdu_offset;
+               ret = fio_recv_data(sk, pdu, cmd.pdu_len);
+               if (ret)
+                       break;
+
+               /* Verify payload crc */
+               crc = crc16(pdu, cmd.pdu_len);
+               if (crc != cmd.pdu_crc16) {
+                       log_err("fio: server bad crc on payload ");
+                       log_err("(got %x, wanted %x)\n", cmd.pdu_crc16, crc);
+                       ret = 1;
+                       break;
+               }
+
+               pdu_offset += cmd.pdu_len;
+               if (!first)
+                       cmdret->pdu_len += cmd.pdu_len;
+               first = 0;
+       } while (cmd.flags & FIO_NET_CMD_F_MORE);
+
+       if (ret) {
+               free(cmdret);
+               cmdret = NULL;
+       } else if (cmdret) {
+               /* zero-terminate text input */
+               if (cmdret->pdu_len && (cmdret->opcode == FIO_NET_CMD_TEXT ||
+                   cmdret->opcode == FIO_NET_CMD_JOB)) {
+                       char *buf = (char *) cmdret->payload;
+
+                       buf[cmdret->pdu_len ] = '\0';
+               }
+               /* frag flag is internal */
+               cmdret->flags &= ~FIO_NET_CMD_F_MORE;
+       }
+
+       return cmdret;
+}
+
+void fio_net_cmd_crc(struct fio_net_cmd *cmd)
+{
+       uint32_t pdu_len;
+
+       cmd->cmd_crc16 = __cpu_to_le16(crc16(cmd, FIO_NET_CMD_CRC_SZ));
+
+       pdu_len = le32_to_cpu(cmd->pdu_len);
+       if (pdu_len)
+               cmd->pdu_crc16 = __cpu_to_le16(crc16(cmd->payload, pdu_len));
+}
+
+int fio_net_send_cmd(int fd, uint16_t opcode, const void *buf, off_t size,
+                    uint64_t tag)
+{
+       struct fio_net_cmd *cmd = NULL;
+       size_t this_len, cur_len = 0;
+       int ret;
+
+       do {
+               this_len = size;
+               if (this_len > FIO_SERVER_MAX_PDU)
+                       this_len = FIO_SERVER_MAX_PDU;
+
+               if (!cmd || cur_len < sizeof(*cmd) + this_len) {
+                       if (cmd)
+                               free(cmd);
+
+                       cur_len = sizeof(*cmd) + this_len;
+                       cmd = malloc(cur_len);
+               }
+
+               fio_init_net_cmd(cmd, opcode, buf, this_len, tag);
+
+               if (this_len < size)
+                       cmd->flags = __cpu_to_le32(FIO_NET_CMD_F_MORE);
+
+               fio_net_cmd_crc(cmd);
+
+               ret = fio_send_data(fd, cmd, sizeof(*cmd) + this_len);
+               size -= this_len;
+               buf += this_len;
+       } while (!ret && size);
+
+       if (cmd)
+               free(cmd);
+
+       return ret;
+}
+
+static int fio_net_send_simple_stack_cmd(int sk, uint16_t opcode, uint64_t tag)
+{
+       struct fio_net_cmd cmd;
+
+       fio_init_net_cmd(&cmd, opcode, NULL, 0, tag);
+       fio_net_cmd_crc(&cmd);
+
+       return fio_send_data(sk, &cmd, sizeof(cmd));
+}
+
+/*
+ * If 'list' is non-NULL, then allocate and store the sent command for
+ * later verification.
+ */
+int fio_net_send_simple_cmd(int sk, uint16_t opcode, uint64_t tag,
+                           struct flist_head *list)
+{
+       struct fio_net_int_cmd *cmd;
+       int ret;
+
+       if (!list)
+               return fio_net_send_simple_stack_cmd(sk, opcode, tag);
+
+       cmd = malloc(sizeof(*cmd));
+
+       fio_init_net_cmd(&cmd->cmd, opcode, NULL, 0, (uintptr_t) cmd);
+       fio_net_cmd_crc(&cmd->cmd);
+
+       INIT_FLIST_HEAD(&cmd->list);
+       gettimeofday(&cmd->tv, NULL);
+       cmd->saved_tag = tag;
+
+       ret = fio_send_data(sk, &cmd->cmd, sizeof(cmd->cmd));
+       if (ret) {
+               free(cmd);
+               return ret;
+       }
+
+       flist_add_tail(&cmd->list, list);
+       return 0;
+}
+
+static int fio_server_send_quit_cmd(void)
+{
+       dprint(FD_NET, "server: sending quit\n");
+       return fio_net_send_simple_cmd(server_fd, FIO_NET_CMD_QUIT, 0, NULL);
+}
+
+static int handle_job_cmd(struct fio_net_cmd *cmd)
+{
+       char *buf = (char *) cmd->payload;
+       int ret;
+
+       if (parse_jobs_ini(buf, 1, 0)) {
+               fio_server_send_quit_cmd();
+               return -1;
+       }
+
+       fio_net_send_simple_cmd(server_fd, FIO_NET_CMD_START, 0, NULL);
+
+       ret = exec_run();
+       fio_server_send_quit_cmd();
+       reset_fio_state();
+       return ret;
+}
+
+static int handle_jobline_cmd(struct fio_net_cmd *cmd)
+{
+       void *pdu = cmd->payload;
+       struct cmd_single_line_pdu *cslp;
+       struct cmd_line_pdu *clp;
+       unsigned long offset;
+       char **argv;
+       int ret, i;
+
+       clp = pdu;
+       clp->lines = le16_to_cpu(clp->lines);
+       argv = malloc(clp->lines * sizeof(char *));
+       offset = sizeof(*clp);
+
+       dprint(FD_NET, "server: %d command line args\n", clp->lines);
+
+       for (i = 0; i < clp->lines; i++) {
+               cslp = pdu + offset;
+               argv[i] = (char *) cslp->text;
+
+               offset += sizeof(*cslp) + le16_to_cpu(cslp->len);
+               dprint(FD_NET, "server: %d: %s\n", i, argv[i]);
+       }
+
+       if (parse_cmd_line(clp->lines, argv)) {
+               fio_server_send_quit_cmd();
+               free(argv);
+               return -1;
+       }
+
+       free(argv);
+
+       fio_net_send_simple_cmd(server_fd, FIO_NET_CMD_START, 0, NULL);
+
+       ret = exec_run();
+       fio_server_send_quit_cmd();
+       reset_fio_state();
+       return ret;
+}
+
+static int handle_probe_cmd(struct fio_net_cmd *cmd)
+{
+       struct cmd_probe_pdu probe;
+
+       dprint(FD_NET, "server: sending probe reply\n");
+
+       memset(&probe, 0, sizeof(probe));
+       gethostname((char *) probe.hostname, sizeof(probe.hostname));
+#ifdef FIO_BIG_ENDIAN
+       probe.bigendian = 1;
+#endif
+       probe.fio_major = FIO_MAJOR;
+       probe.fio_minor = FIO_MINOR;
+       probe.fio_patch = FIO_PATCH;
+
+       probe.os        = FIO_OS;
+       probe.arch      = FIO_ARCH;
+
+       probe.bpp       = sizeof(void *);
+
+       return fio_net_send_cmd(server_fd, FIO_NET_CMD_PROBE, &probe, sizeof(probe), cmd->tag);
+}
+
+static int handle_send_eta_cmd(struct fio_net_cmd *cmd)
+{
+       struct jobs_eta *je;
+       size_t size;
+       int i;
+
+       if (!thread_number)
+               return 0;
+
+       size = sizeof(*je) + thread_number * sizeof(char) + 1;
+       je = malloc(size);
+       memset(je, 0, size);
+
+       if (!calc_thread_status(je, 1)) {
+               free(je);
+               return 0;
+       }
+
+       dprint(FD_NET, "server sending status\n");
+
+       je->nr_running          = cpu_to_le32(je->nr_running);
+       je->nr_ramp             = cpu_to_le32(je->nr_ramp);
+       je->nr_pending          = cpu_to_le32(je->nr_pending);
+       je->files_open          = cpu_to_le32(je->files_open);
+       je->m_rate              = cpu_to_le32(je->m_rate);
+       je->t_rate              = cpu_to_le32(je->t_rate);
+       je->m_iops              = cpu_to_le32(je->m_iops);
+       je->t_iops              = cpu_to_le32(je->t_iops);
+
+       for (i = 0; i < 2; i++) {
+               je->rate[i]     = cpu_to_le32(je->rate[i]);
+               je->iops[i]     = cpu_to_le32(je->iops[i]);
+       }
+
+       je->elapsed_sec         = cpu_to_le32(je->nr_running);
+       je->eta_sec             = cpu_to_le64(je->eta_sec);
+
+       fio_net_send_cmd(server_fd, FIO_NET_CMD_ETA, je, size, cmd->tag);
+       free(je);
+       return 0;
+}
+
+static int handle_command(struct fio_net_cmd *cmd)
+{
+       int ret;
+
+       dprint(FD_NET, "server: got op [%s], pdu=%u, tag=%lx\n",
+                       fio_server_op(cmd->opcode), cmd->pdu_len, cmd->tag);
+
+       switch (cmd->opcode) {
+       case FIO_NET_CMD_QUIT:
+               fio_terminate_threads(TERMINATE_ALL);
+               return -1;
+       case FIO_NET_CMD_EXIT:
+               exit_backend = 1;
+               return -1;
+       case FIO_NET_CMD_JOB:
+               ret = handle_job_cmd(cmd);
+               break;
+       case FIO_NET_CMD_JOBLINE:
+               ret = handle_jobline_cmd(cmd);
+               break;
+       case FIO_NET_CMD_PROBE:
+               ret = handle_probe_cmd(cmd);
+               break;
+       case FIO_NET_CMD_SEND_ETA:
+               ret = handle_send_eta_cmd(cmd);
+               break;
+       default:
+               log_err("fio: unknown opcode: %s\n",fio_server_op(cmd->opcode));
+               ret = 1;
+       }
+
+       return ret;
+}
+
+static int handle_connection(int sk, int block)
+{
+       struct fio_net_cmd *cmd = NULL;
+       int ret = 0;
+
+       /* read forever */
+       while (!exit_backend) {
+               struct pollfd pfd = {
+                       .fd     = sk,
+                       .events = POLLIN,
+               };
+
+               ret = 0;
+               do {
+                       ret = poll(&pfd, 1, 100);
+                       if (ret < 0) {
+                               if (errno == EINTR)
+                                       break;
+                               log_err("fio: poll: %s\n", strerror(errno));
+                               break;
+                       } else if (!ret) {
+                               if (!block)
+                                       return 0;
+                               continue;
+                       }
+
+                       if (pfd.revents & POLLIN)
+                               break;
+                       if (pfd.revents & (POLLERR|POLLHUP)) {
+                               ret = 1;
+                               break;
+                       }
+               } while (!exit_backend);
+
+               if (ret < 0)
+                       break;
+
+               cmd = fio_net_recv_cmd(sk);
+               if (!cmd) {
+                       ret = -1;
+                       break;
+               }
+
+               ret = handle_command(cmd);
+               if (ret)
+                       break;
+
+               free(cmd);
+               cmd = NULL;
+       }
+
+       if (cmd)
+               free(cmd);
+
+       return ret;
+}
+
+void fio_server_idle_loop(void)
+{
+       if (server_fd != -1)
+               handle_connection(server_fd, 0);
+}
+
+static int accept_loop(int listen_sk)
+{
+       struct sockaddr_in addr;
+       fio_socklen_t len = sizeof(addr);
+       struct pollfd pfd;
+       int ret, sk, flags, exitval = 0;
+
+       dprint(FD_NET, "server enter accept loop\n");
+
+       flags = fcntl(listen_sk, F_GETFL);
+       flags |= O_NONBLOCK;
+       fcntl(listen_sk, F_SETFL, flags);
+again:
+       pfd.fd = listen_sk;
+       pfd.events = POLLIN;
+       do {
+               ret = poll(&pfd, 1, 100);
+               if (ret < 0) {
+                       if (errno == EINTR)
+                               break;
+                       log_err("fio: poll: %s\n", strerror(errno));
+                       goto out;
+               } else if (!ret)
+                       continue;
+
+               if (pfd.revents & POLLIN)
+                       break;
+       } while (!exit_backend);
+
+       if (exit_backend)
+               goto out;
+
+       sk = accept(listen_sk, (struct sockaddr *) &addr, &len);
+       if (sk < 0) {
+               log_err("fio: accept: %s\n", strerror(errno));
+               return -1;
+       }
+
+       dprint(FD_NET, "server: connect from %s\n", inet_ntoa(addr.sin_addr));
+
+       server_fd = sk;
+
+       exitval = handle_connection(sk, 1);
+
+       server_fd = -1;
+       close(sk);
+
+       if (!exit_backend)
+               goto again;
+
+out:
+       return exitval;
+}
+
+int fio_server_text_output(const char *buf, size_t len)
+{
+       if (server_fd != -1)
+               return fio_net_send_cmd(server_fd, FIO_NET_CMD_TEXT, buf, len, 0);
+
+       return log_local_buf(buf, len);
+}
+
+static void convert_io_stat(struct io_stat *dst, struct io_stat *src)
+{
+       dst->max_val    = cpu_to_le64(src->max_val);
+       dst->min_val    = cpu_to_le64(src->min_val);
+       dst->samples    = cpu_to_le64(src->samples);
+
+       /*
+        * Encode to IEEE 754 for network transfer
+        */
+       dst->mean.u.i   = __cpu_to_le64(fio_double_to_uint64(src->mean.u.f));
+       dst->S.u.i      = __cpu_to_le64(fio_double_to_uint64(src->S.u.f));
+}
+
+static void convert_gs(struct group_run_stats *dst, struct group_run_stats *src)
+{
+       int i;
+
+       for (i = 0; i < 2; i++) {
+               dst->max_run[i]         = cpu_to_le64(src->max_run[i]);
+               dst->min_run[i]         = cpu_to_le64(src->min_run[i]);
+               dst->max_bw[i]          = cpu_to_le64(src->max_bw[i]);
+               dst->min_bw[i]          = cpu_to_le64(src->min_bw[i]);
+               dst->io_kb[i]           = cpu_to_le64(src->io_kb[i]);
+               dst->agg[i]             = cpu_to_le64(src->agg[i]);
+       }
+
+       dst->kb_base    = cpu_to_le32(src->kb_base);
+       dst->groupid    = cpu_to_le32(src->groupid);
+}
+
+/*
+ * Send a CMD_TS, which packs struct thread_stat and group_run_stats
+ * into a single payload.
+ */
+void fio_server_send_ts(struct thread_stat *ts, struct group_run_stats *rs)
+{
+       struct cmd_ts_pdu p;
+       int i, j;
+
+       dprint(FD_NET, "server sending end stats\n");
+
+       memset(&p, 0, sizeof(p));
+
+       strcpy(p.ts.name, ts->name);
+       strcpy(p.ts.verror, ts->verror);
+       strcpy(p.ts.description, ts->description);
+
+       p.ts.error      = cpu_to_le32(ts->error);
+       p.ts.groupid    = cpu_to_le32(ts->groupid);
+       p.ts.pid        = cpu_to_le32(ts->pid);
+       p.ts.members    = cpu_to_le32(ts->members);
+
+       for (i = 0; i < 2; i++) {
+               convert_io_stat(&p.ts.clat_stat[i], &ts->clat_stat[i]);
+               convert_io_stat(&p.ts.slat_stat[i], &ts->slat_stat[i]);
+               convert_io_stat(&p.ts.lat_stat[i], &ts->lat_stat[i]);
+               convert_io_stat(&p.ts.bw_stat[i], &ts->bw_stat[i]);
+       }
+
+       p.ts.usr_time           = cpu_to_le64(ts->usr_time);
+       p.ts.sys_time           = cpu_to_le64(ts->sys_time);
+       p.ts.ctx                = cpu_to_le64(ts->ctx);
+       p.ts.minf               = cpu_to_le64(ts->minf);
+       p.ts.majf               = cpu_to_le64(ts->majf);
+       p.ts.clat_percentiles   = cpu_to_le64(ts->clat_percentiles);
+
+       for (i = 0; i < FIO_IO_U_LIST_MAX_LEN; i++) {
+               fio_fp64_t *fp = &p.ts.percentile_list[i];
+
+               fp->u.i = __cpu_to_le64(fio_double_to_uint64(fp->u.f));
+       }
+
+       for (i = 0; i < FIO_IO_U_MAP_NR; i++) {
+               p.ts.io_u_map[i]        = cpu_to_le32(ts->io_u_map[i]);
+               p.ts.io_u_submit[i]     = cpu_to_le32(ts->io_u_submit[i]);
+               p.ts.io_u_complete[i]   = cpu_to_le32(ts->io_u_complete[i]);
+       }
+
+       for (i = 0; i < FIO_IO_U_LAT_U_NR; i++) {
+               p.ts.io_u_lat_u[i]      = cpu_to_le32(ts->io_u_lat_u[i]);
+               p.ts.io_u_lat_m[i]      = cpu_to_le32(ts->io_u_lat_m[i]);
+       }
+
+       for (i = 0; i < 2; i++)
+               for (j = 0; j < FIO_IO_U_PLAT_NR; j++)
+                       p.ts.io_u_plat[i][j] = cpu_to_le32(ts->io_u_plat[i][j]);
+
+       for (i = 0; i < 3; i++) {
+               p.ts.total_io_u[i]      = cpu_to_le64(ts->total_io_u[i]);
+               p.ts.short_io_u[i]      = cpu_to_le64(ts->short_io_u[i]);
+       }
+
+       p.ts.total_submit       = cpu_to_le64(ts->total_submit);
+       p.ts.total_complete     = cpu_to_le64(ts->total_complete);
+
+       for (i = 0; i < 2; i++) {
+               p.ts.io_bytes[i]        = cpu_to_le64(ts->io_bytes[i]);
+               p.ts.runtime[i]         = cpu_to_le64(ts->runtime[i]);
+       }
+
+       p.ts.total_run_time     = cpu_to_le64(ts->total_run_time);
+       p.ts.continue_on_error  = cpu_to_le16(ts->continue_on_error);
+       p.ts.total_err_count    = cpu_to_le64(ts->total_err_count);
+       p.ts.first_error        = cpu_to_le32(ts->first_error);
+       p.ts.kb_base            = cpu_to_le32(ts->kb_base);
+
+       convert_gs(&p.rs, rs);
+
+       fio_net_send_cmd(server_fd, FIO_NET_CMD_TS, &p, sizeof(p), 0);
+}
+
+void fio_server_send_gs(struct group_run_stats *rs)
+{
+       struct group_run_stats gs;
+
+       dprint(FD_NET, "server sending group run stats\n");
+
+       convert_gs(&gs, rs);
+       fio_net_send_cmd(server_fd, FIO_NET_CMD_GS, &gs, sizeof(gs), 0);
+}
+
+int fio_server_log(const char *format, ...)
+{
+       char buffer[1024];
+       va_list args;
+       size_t len;
+
+       dprint(FD_NET, "server log\n");
+
+       va_start(args, format);
+       len = vsnprintf(buffer, sizeof(buffer), format, args);
+       va_end(args);
+
+       return fio_server_text_output(buffer, len);
+}
+
+static int fio_init_server_ip(void)
+{
+       int sk, opt;
+
+       sk = socket(AF_INET, SOCK_STREAM, 0);
+       if (sk < 0) {
+               log_err("fio: socket: %s\n", strerror(errno));
+               return -1;
+       }
+
+       opt = 1;
+       if (setsockopt(sk, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
+               log_err("fio: setsockopt: %s\n", strerror(errno));
+               close(sk);
+               return -1;
+       }
+#ifdef SO_REUSEPORT
+       if (setsockopt(sk, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt)) < 0) {
+               log_err("fio: setsockopt: %s\n", strerror(errno));
+               close(sk);
+               return -1;
+       }
+#endif
+
+       saddr_in.sin_family = AF_INET;
+
+       if (bind(sk, (struct sockaddr *) &saddr_in, sizeof(saddr_in)) < 0) {
+               log_err("fio: bind: %s\n", strerror(errno));
+               close(sk);
+               return -1;
+       }
+
+       return sk;
+}
+
+static int fio_init_server_sock(void)
+{
+       struct sockaddr_un addr;
+       fio_socklen_t len;
+       mode_t mode;
+       int sk;
+
+       sk = socket(AF_UNIX, SOCK_STREAM, 0);
+       if (sk < 0) {
+               log_err("fio: socket: %s\n", strerror(errno));
+               return -1;
+       }
+
+       mode = umask(000);
+
+       memset(&addr, 0, sizeof(addr));
+       addr.sun_family = AF_UNIX;
+       strcpy(addr.sun_path, bind_sock);
+       unlink(bind_sock);
+
+       len = sizeof(addr.sun_family) + strlen(bind_sock) + 1;
+
+       if (bind(sk, (struct sockaddr *) &addr, len) < 0) {
+               log_err("fio: bind: %s\n", strerror(errno));
+               close(sk);
+               return -1;
+       }
+
+       umask(mode);
+       return sk;
+}
+
+static int fio_init_server_connection(void)
+{
+       char bind_str[128];
+       int sk;
+
+       dprint(FD_NET, "starting server\n");
+
+       if (!bind_sock)
+               sk = fio_init_server_ip();
+       else
+               sk = fio_init_server_sock();
+
+       if (sk < 0)
+               return sk;
+
+       if (!bind_sock)
+               sprintf(bind_str, "%s:%u", inet_ntoa(saddr_in.sin_addr), fio_net_port);
+       else
+               strcpy(bind_str, bind_sock);
+
+       log_info("fio: server listening on %s\n", bind_str);
+
+       if (listen(sk, 0) < 0) {
+               log_err("fio: listen: %s\n", strerror(errno));
+               return -1;
+       }
+
+       return sk;
+}
+
+int fio_server_parse_string(const char *str, char **ptr, int *is_sock,
+                           int *port, struct in_addr *inp)
+{
+       *ptr = NULL;
+       *is_sock = 0;
+       *port = fio_net_port;
+
+       if (!strncmp(str, "sock:", 5)) {
+               *ptr = strdup(str + 5);
+               *is_sock = 1;
+       } else {
+               const char *host = str;
+               char *portp;
+               int lport = 0;
+
+               /*
+                * Is it ip:<ip or host>:port
+                */
+               if (!strncmp(host, "ip:", 3))
+                       host += 3;
+               else if (host[0] == ':') {
+                       /* String is :port */
+                       host++;
+                       lport = atoi(host);
+                       if (!lport || lport > 65535) {
+                               log_err("fio: bad server port %u\n", port);
+                               return 1;
+                       }
+                       /* no hostname given, we are done */
+                       *port = lport;
+                       return 0;
+               }
+
+               /*
+                * If no port seen yet, check if there's a last ':' at the end
+                */
+               if (!lport) {
+                       portp = strchr(host, ':');
+                       if (portp) {
+                               *portp = '\0';
+                               portp++;
+                               lport = atoi(portp);
+                               if (!lport || lport > 65535) {
+                                       log_err("fio: bad server port %u\n", port);
+                                       return 1;
+                               }
+                       }
+               }
+
+               if (lport)
+                       *port = lport;
+
+               *ptr = strdup(host);
+
+               if (inet_aton(host, inp) != 1) {
+                       struct hostent *hent;
+
+                       hent = gethostbyname(host);
+                       if (!hent) {
+                               free(*ptr);
+                               *ptr = NULL;
+                               return 1;
+                       }
+
+                       memcpy(inp, hent->h_addr, 4);
+               }
+       }
+
+       if (*port == 0)
+               *port = fio_net_port;
+
+       return 0;
+}
+
+/*
+ * Server arg should be one of:
+ *
+ * sock:/path/to/socket
+ *   ip:1.2.3.4
+ *      1.2.3.4
+ *
+ * Where sock uses unix domain sockets, and ip binds the server to
+ * a specific interface. If no arguments are given to the server, it
+ * uses IP and binds to 0.0.0.0.
+ *
+ */
+static int fio_handle_server_arg(void)
+{
+       int port = fio_net_port;
+       int is_sock, ret = 0;
+
+       saddr_in.sin_addr.s_addr = htonl(INADDR_ANY);
+
+       if (!fio_server_arg)
+               goto out;
+
+       ret = fio_server_parse_string(fio_server_arg, &bind_sock, &is_sock,
+                                       &port, &saddr_in.sin_addr);
+
+       if (!is_sock && bind_sock) {
+               free(bind_sock);
+               bind_sock = NULL;
+       }
+
+out:
+       fio_net_port = port;
+       saddr_in.sin_port = htons(port);
+       return ret;
+}
+
+static int fio_server(void)
+{
+       int sk, ret;
+
+       dprint(FD_NET, "starting server\n");
+
+       if (fio_handle_server_arg())
+               return -1;
+
+       sk = fio_init_server_connection();
+       if (sk < 0)
+               return -1;
+
+       ret = accept_loop(sk);
+
+       close(sk);
+
+       if (fio_server_arg) {
+               free(fio_server_arg);
+               fio_server_arg = NULL;
+       }
+       if (bind_sock)
+               free(bind_sock);
+
+       return ret;
+}
+
+void fio_server_got_signal(int signal)
+{
+       if (signal == SIGPIPE)
+               server_fd = -1;
+       else {
+               log_info("\nfio: terminating on signal %d\n", signal);
+               exit_backend = 1;
+       }
+}
+
+static int check_existing_pidfile(const char *pidfile)
+{
+       struct stat sb;
+       char buf[16];
+       pid_t pid;
+       FILE *f;
+
+       if (stat(pidfile, &sb))
+               return 0;
+
+       f = fopen(pidfile, "r");
+       if (!f)
+               return 0;
+
+       if (fread(buf, sb.st_size, 1, f) <= 0) {
+               fclose(f);
+               return 1;
+       }
+       fclose(f);
+
+       pid = atoi(buf);
+       if (kill(pid, SIGCONT) < 0)
+               return errno != ESRCH;
+
+       return 1;
+}
+
+static int write_pid(pid_t pid, const char *pidfile)
+{
+       FILE *fpid;
+
+       fpid = fopen(pidfile, "w");
+       if (!fpid) {
+               log_err("fio: failed opening pid file %s\n", pidfile);
+               return 1;
+       }
+
+       fprintf(fpid, "%u\n", (unsigned int) pid);
+       fclose(fpid);
+       return 0;
+}
+
+/*
+ * If pidfile is specified, background us.
+ */
+int fio_start_server(char *pidfile)
+{
+       pid_t pid;
+       int ret;
+
+       if (!pidfile)
+               return fio_server();
+
+       if (check_existing_pidfile(pidfile)) {
+               log_err("fio: pidfile %s exists and server appears alive\n",
+                                                               pidfile);
+               return -1;
+       }
+
+       pid = fork();
+       if (pid < 0) {
+               log_err("fio: failed server fork: %s", strerror(errno));
+               free(pidfile);
+               return -1;
+       } else if (pid) {
+               int ret = write_pid(pid, pidfile);
+
+               exit(ret);
+       }
+
+       setsid();
+       openlog("fio", LOG_NDELAY|LOG_NOWAIT|LOG_PID, LOG_USER);
+       log_syslog = 1;
+       close(STDIN_FILENO);
+       close(STDOUT_FILENO);
+       close(STDERR_FILENO);
+       f_out = NULL;
+       f_err = NULL;
+
+       ret = fio_server();
+
+       closelog();
+       unlink(pidfile);
+       free(pidfile);
+       return ret;
+}
+
+void fio_server_set_arg(const char *arg)
+{
+       fio_server_arg = strdup(arg);
+}
diff --git a/server.h b/server.h
new file mode 100644 (file)
index 0000000..46555ce
--- /dev/null
+++ b/server.h
@@ -0,0 +1,136 @@
+#ifndef FIO_SERVER_H
+#define FIO_SERVER_H
+
+#include <inttypes.h>
+#include <string.h>
+#include <sys/time.h>
+
+#include "stat.h"
+#include "os/os.h"
+
+/*
+ * On-wire encoding is little endian
+ */
+struct fio_net_cmd {
+       uint16_t version;       /* protocol version */
+       uint16_t opcode;        /* command opcode */
+       uint32_t flags;         /* modifier flags */
+       uint64_t tag;           /* passed back on reply */
+       uint32_t pdu_len;       /* length of post-cmd layload */
+       /*
+        * These must be immediately before the payload, anything before
+        * these fields are checksummed.
+        */
+       uint16_t cmd_crc16;     /* cmd checksum */
+       uint16_t pdu_crc16;     /* payload checksum */
+       uint8_t payload[0];     /* payload */
+};
+
+struct fio_net_int_cmd {
+       struct fio_net_cmd cmd;
+       struct flist_head list;
+       struct timeval tv;
+       uint64_t saved_tag;
+};
+
+enum {
+       FIO_SERVER_VER          = 5,
+
+       FIO_SERVER_MAX_PDU      = 1024,
+
+       FIO_NET_CMD_QUIT        = 1,
+       FIO_NET_CMD_EXIT        = 2,
+       FIO_NET_CMD_JOB         = 3,
+       FIO_NET_CMD_JOBLINE     = 4,
+       FIO_NET_CMD_TEXT        = 5,
+       FIO_NET_CMD_TS          = 6,
+       FIO_NET_CMD_GS          = 7,
+       FIO_NET_CMD_SEND_ETA    = 8,
+       FIO_NET_CMD_ETA         = 9,
+       FIO_NET_CMD_PROBE       = 10,
+       FIO_NET_CMD_START       = 11,
+       FIO_NET_CMD_STOP        = 12,
+       FIO_NET_CMD_NR          = 13,
+
+       FIO_NET_CMD_F_MORE      = 1UL << 0,
+
+       /* crc does not include the crc fields */
+       FIO_NET_CMD_CRC_SZ      = sizeof(struct fio_net_cmd) -
+                                       2 * sizeof(uint16_t),
+
+       FIO_NET_CLIENT_TIMEOUT  = 5000,
+};
+
+struct cmd_ts_pdu {
+       struct thread_stat ts;
+       struct group_run_stats rs;
+};
+
+struct cmd_probe_pdu {
+       uint8_t hostname[64];
+       uint8_t bigendian;
+       uint8_t fio_major;
+       uint8_t fio_minor;
+       uint8_t fio_patch;
+       uint8_t os;
+       uint8_t arch;
+       uint8_t bpp;
+};
+
+struct cmd_single_line_pdu {
+       uint16_t len;
+       uint8_t text[0];
+};
+
+struct cmd_line_pdu {
+       uint16_t lines;
+       struct cmd_single_line_pdu options[0];
+};
+
+extern int fio_start_server(char *);
+extern int fio_server_text_output(const char *, size_t);
+extern int fio_server_log(const char *format, ...);
+extern int fio_net_send_cmd(int, uint16_t, const void *, off_t, uint64_t);
+extern int fio_net_send_simple_cmd(int, uint16_t, uint64_t, struct flist_head *);
+extern void fio_server_set_arg(const char *);
+extern int fio_server_parse_string(const char *, char **, int *, int *, struct in_addr *);
+extern const char *fio_server_op(unsigned int);
+extern void fio_server_got_signal(int);
+
+struct thread_stat;
+struct group_run_stats;
+extern void fio_server_send_ts(struct thread_stat *, struct group_run_stats *);
+extern void fio_server_send_gs(struct group_run_stats *);
+extern void fio_server_idle_loop(void);
+
+extern int fio_clients_connect(void);
+extern int fio_clients_send_ini(const char *);
+extern int fio_handle_clients(void);
+extern int fio_client_add(const char *, void **);
+extern void fio_client_add_cmd_option(void *, const char *);
+
+extern int fio_recv_data(int sk, void *p, unsigned int len);
+extern int fio_send_data(int sk, const void *p, unsigned int len);
+extern void fio_net_cmd_crc(struct fio_net_cmd *);
+extern struct fio_net_cmd *fio_net_recv_cmd(int sk);
+
+extern int exit_backend;
+extern int fio_net_port;
+
+static inline void fio_init_net_cmd(struct fio_net_cmd *cmd, uint16_t opcode,
+                                   const void *pdu, uint32_t pdu_len,
+                                   uint64_t tag)
+{
+       memset(cmd, 0, sizeof(*cmd));
+
+       cmd->version    = __cpu_to_le16(FIO_SERVER_VER);
+       cmd->opcode     = cpu_to_le16(opcode);
+       cmd->tag        = cpu_to_le64(tag);
+
+       if (pdu) {
+               cmd->pdu_len    = cpu_to_le32(pdu_len);
+               memcpy(&cmd->payload, pdu, pdu_len);
+       }
+}
+
+#endif
diff --git a/stat.c b/stat.c
index 3662fd9eee2bdd4fbbedf249cb850dbee4ab334c..c2705d26f38bbd82f25b82882aa1f5910901a7d0 100644 (file)
--- a/stat.c
+++ b/stat.c
@@ -9,23 +9,24 @@
 
 #include "fio.h"
 #include "diskutil.h"
+#include "ieee754.h"
 
 void update_rusage_stat(struct thread_data *td)
 {
        struct thread_stat *ts = &td->ts;
 
-       getrusage(RUSAGE_SELF, &ts->ru_end);
+       getrusage(RUSAGE_SELF, &td->ru_end);
 
-       ts->usr_time += mtime_since(&ts->ru_start.ru_utime,
-                                       &ts->ru_end.ru_utime);
-       ts->sys_time += mtime_since(&ts->ru_start.ru_stime,
-                                       &ts->ru_end.ru_stime);
-       ts->ctx += ts->ru_end.ru_nvcsw + ts->ru_end.ru_nivcsw
-                       - (ts->ru_start.ru_nvcsw + ts->ru_start.ru_nivcsw);
-       ts->minf += ts->ru_end.ru_minflt - ts->ru_start.ru_minflt;
-       ts->majf += ts->ru_end.ru_majflt - ts->ru_start.ru_majflt;
+       ts->usr_time += mtime_since(&td->ru_start.ru_utime,
+                                       &td->ru_end.ru_utime);
+       ts->sys_time += mtime_since(&td->ru_start.ru_stime,
+                                       &td->ru_end.ru_stime);
+       ts->ctx += td->ru_end.ru_nvcsw + td->ru_end.ru_nivcsw
+                       - (td->ru_start.ru_nvcsw + td->ru_start.ru_nivcsw);
+       ts->minf += td->ru_end.ru_minflt - td->ru_start.ru_minflt;
+       ts->majf += td->ru_end.ru_majflt - td->ru_start.ru_majflt;
 
-       memcpy(&ts->ru_start, &ts->ru_end, sizeof(ts->ru_end));
+       memcpy(&td->ru_start, &td->ru_end, sizeof(td->ru_end));
 }
 
 /*
@@ -101,13 +102,13 @@ static unsigned int plat_idx_to_val(unsigned int idx)
 
 static int double_cmp(const void *a, const void *b)
 {
-       const double fa = *(const double *)a;
-       const double fb = *(const double *)b;
+       const fio_fp64_t fa = *(const fio_fp64_t *) a;
+       const fio_fp64_t fb = *(const fio_fp64_t *) b;
        int cmp = 0;
 
-       if (fa > fb)
+       if (fa.u.f > fb.u.f)
                cmp = 1;
-       else if (fa < fb)
+       else if (fa.u.f < fb.u.f)
                cmp = -1;
 
        return cmp;
@@ -116,38 +117,31 @@ static int double_cmp(const void *a, const void *b)
 /*
  * Find and display the p-th percentile of clat
  */
-static void show_clat_percentiles(unsigned intio_u_plat, unsigned long nr,
-                                double* user_list)
+static void show_clat_percentiles(unsigned int *io_u_plat, unsigned long nr,
+                                 fio_fp64_t *plist)
 {
        unsigned long sum = 0;
        unsigned int len, i, j = 0;
-       const double *plist;
        int is_last = 0;
-       static const double def_list[FIO_IO_U_LIST_MAX_LEN] = {
-                       1.0, 5.0, 10.0, 20.0, 30.0,
-                       40.0, 50.0, 60.0, 70.0, 80.0,
-                       90.0, 95.0, 99.0, 99.5, 99.9};
 
-       plist = user_list;
-       if (!plist)
-               plist = def_list;
-
-       for (len = 0; len <FIO_IO_U_LIST_MAX_LEN && plist[len] != 0; len++)
-               ;
+       len = 0;
+       while (len < FIO_IO_U_LIST_MAX_LEN && plist[len].u.f != 0.0)
+               len++;
 
        /*
-        * Sort the user-specified list. Note that this does not work
-        * for NaN values
+        * Sort the percentile list. Note that it may already be sorted if
+        * we are using the default values, but since it's a short list this
+        * isn't a worry. Also note that this does not work for NaN values.
         */
-       if (user_list && len > 1)
-               qsort((void*)user_list, len, sizeof(user_list[0]), double_cmp);
+       if (len > 1)
+               qsort((void*)plist, len, sizeof(plist[0]), double_cmp);
 
        log_info("    clat percentiles (usec) :");
 
        for (i = 0; i < FIO_IO_U_PLAT_NR && !is_last; i++) {
                sum += io_u_plat[i];
-               while (sum >= (plist[j] / 100 * nr)) {
-                       assert(plist[j] <= 100.0);
+               while (sum >= (plist[j].u.f / 100.0 * nr)) {
+                       assert(plist[j].u.f <= 100.0);
 
                        /* for formatting */
                        if (j != 0 && (j % 4) == 0)
@@ -181,23 +175,23 @@ static int calc_lat(struct io_stat *is, unsigned long *min, unsigned long *max,
        *max = is->max_val;
 
        n = (double) is->samples;
-       *mean = is->mean;
+       *mean = is->mean.u.f;
 
        if (n > 1.0)
-               *dev = sqrt(is->S / (n - 1.0));
+               *dev = sqrt(is->S.u.f / (n - 1.0));
        else
                *dev = 0;
 
        return 1;
 }
 
-static void show_group_stats(struct group_run_stats *rs, int id)
+void show_group_stats(struct group_run_stats *rs)
 {
        char *p1, *p2, *p3, *p4;
        const char *ddir_str[] = { "   READ", "  WRITE" };
        int i;
 
-       log_info("\nRun status group %d (all jobs):\n", id);
+       log_info("\nRun status group %d (all jobs):\n", rs->groupid);
 
        for (i = 0; i <= DDIR_WRITE; i++) {
                const int i2p = is_power_of_2(rs->kb_base);
@@ -378,7 +372,7 @@ static void show_ddir_status(struct group_run_stats *rs, struct thread_stat *ts,
                double p_of_agg;
 
                p_of_agg = mean * 100 / (double) rs->agg[ddir];
-               log_info("    bw (KB/s) : min=%5lu, max=%5lu, per=%3.2f%%,"
+               log_info("     bw (KB/s) : min=%5lu, max=%5lu, per=%3.2f%%,"
                         " avg=%5.02f, stdev=%5.02f\n", min, max, p_of_agg,
                                                        mean, dev);
        }
@@ -433,8 +427,7 @@ static void show_latencies(double *io_u_lat_u, double *io_u_lat_m)
        log_info("\n");
 }
 
-static void show_thread_status(struct thread_stat *ts,
-                              struct group_run_stats *rs)
+void show_thread_status(struct thread_stat *ts, struct group_run_stats *rs)
 {
        double usr_cpu, sys_cpu;
        unsigned long runtime;
@@ -630,23 +623,114 @@ static void sum_stat(struct io_stat *dst, struct io_stat *src, int nr)
         *  #Parallel_algorithm>
         */
        if (nr == 1) {
-               mean = src->mean;
-               S = src->S;
+               mean = src->mean.u.f;
+               S = src->S.u.f;
        } else {
-               double delta = src->mean - dst->mean;
+               double delta = src->mean.u.f - dst->mean.u.f;
 
-               mean = ((src->mean * src->samples) +
-                       (dst->mean * dst->samples)) /
+               mean = ((src->mean.u.f * src->samples) +
+                       (dst->mean.u.f * dst->samples)) /
                        (dst->samples + src->samples);
 
-               S =  src->S + dst->S + pow(delta, 2.0) *
+               S =  src->S.u.f + dst->S.u.f + pow(delta, 2.0) *
                        (dst->samples * src->samples) /
                        (dst->samples + src->samples);
        }
 
        dst->samples += src->samples;
-       dst->mean = mean;
-       dst->S = S;
+       dst->mean.u.f = mean;
+       dst->S.u.f = S;
+}
+
+void sum_group_stats(struct group_run_stats *dst, struct group_run_stats *src)
+{
+       int i;
+
+       for (i = 0; i < 2; i++) {
+               if (dst->max_run[i] < src->max_run[i])
+                       dst->max_run[i] = src->max_run[i];
+               if (dst->min_run[i] && dst->min_run[i] > src->min_run[i])
+                       dst->min_run[i] = src->min_run[i];
+               if (dst->max_bw[i] < src->max_bw[i])
+                       dst->max_bw[i] = src->max_bw[i];
+               if (dst->min_bw[i] && dst->min_bw[i] > src->min_bw[i])
+                       dst->min_bw[i] = src->min_bw[i];
+
+               dst->io_kb[i] += src->io_kb[i];
+               dst->agg[i] += src->agg[i];
+       }
+
+}
+
+void sum_thread_stats(struct thread_stat *dst, struct thread_stat *src, int nr)
+{
+       int l, k;
+
+       for (l = 0; l <= DDIR_WRITE; l++) {
+               sum_stat(&dst->clat_stat[l], &src->clat_stat[l], nr);
+               sum_stat(&dst->slat_stat[l], &src->slat_stat[l], nr);
+               sum_stat(&dst->lat_stat[l], &src->lat_stat[l], nr);
+               sum_stat(&dst->bw_stat[l], &src->bw_stat[l], nr);
+
+               dst->io_bytes[l] += src->io_bytes[l];
+
+               if (dst->runtime[l] < src->runtime[l])
+                       dst->runtime[l] = src->runtime[l];
+       }
+
+       dst->usr_time += src->usr_time;
+       dst->sys_time += src->sys_time;
+       dst->ctx += src->ctx;
+       dst->majf += src->majf;
+       dst->minf += src->minf;
+
+       for (k = 0; k < FIO_IO_U_MAP_NR; k++)
+               dst->io_u_map[k] += src->io_u_map[k];
+       for (k = 0; k < FIO_IO_U_MAP_NR; k++)
+               dst->io_u_submit[k] += src->io_u_submit[k];
+       for (k = 0; k < FIO_IO_U_MAP_NR; k++)
+               dst->io_u_complete[k] += src->io_u_complete[k];
+       for (k = 0; k < FIO_IO_U_LAT_U_NR; k++)
+               dst->io_u_lat_u[k] += src->io_u_lat_u[k];
+       for (k = 0; k < FIO_IO_U_LAT_M_NR; k++)
+               dst->io_u_lat_m[k] += src->io_u_lat_m[k];
+
+       for (k = 0; k <= 2; k++) {
+               dst->total_io_u[k] += src->total_io_u[k];
+               dst->short_io_u[k] += src->short_io_u[k];
+       }
+
+       for (k = 0; k <= DDIR_WRITE; k++) {
+               int m;
+               for (m = 0; m < FIO_IO_U_PLAT_NR; m++)
+                       dst->io_u_plat[k][m] += src->io_u_plat[k][m];
+       }
+
+       dst->total_run_time += src->total_run_time;
+       dst->total_submit += src->total_submit;
+       dst->total_complete += src->total_complete;
+}
+
+void init_group_run_stat(struct group_run_stats *gs)
+{
+       memset(gs, 0, sizeof(*gs));
+       gs->min_bw[0] = gs->min_run[0] = ~0UL;
+       gs->min_bw[1] = gs->min_run[1] = ~0UL;
+}
+
+void init_thread_stat(struct thread_stat *ts)
+{
+       int j;
+
+       memset(ts, 0, sizeof(*ts));
+
+       for (j = 0; j <= DDIR_WRITE; j++) {
+               ts->lat_stat[j].min_val = -1UL;
+               ts->clat_stat[j].min_val = -1UL;
+               ts->slat_stat[j].min_val = -1UL;
+               ts->bw_stat[j].min_val = -1UL;
+       }
+       ts->groupid = -1;
 }
 
 void show_run_stats(void)
@@ -654,18 +738,13 @@ void show_run_stats(void)
        struct group_run_stats *runstats, *rs;
        struct thread_data *td;
        struct thread_stat *threadstats, *ts;
-       int i, j, k, l, nr_ts, last_ts, idx;
+       int i, j, nr_ts, last_ts, idx;
        int kb_base_warned = 0;
 
        runstats = malloc(sizeof(struct group_run_stats) * (groupid + 1));
 
-       for (i = 0; i < groupid + 1; i++) {
-               rs = &runstats[i];
-
-               memset(rs, 0, sizeof(*rs));
-               rs->min_bw[0] = rs->min_run[0] = ~0UL;
-               rs->min_bw[1] = rs->min_run[1] = ~0UL;
-       }
+       for (i = 0; i < groupid + 1; i++)
+               init_group_run_stat(&runstats[i]);
 
        /*
         * find out how many threads stats we need. if group reporting isn't
@@ -687,18 +766,8 @@ void show_run_stats(void)
 
        threadstats = malloc(nr_ts * sizeof(struct thread_stat));
 
-       for (i = 0; i < nr_ts; i++) {
-               ts = &threadstats[i];
-
-               memset(ts, 0, sizeof(*ts));
-               for (j = 0; j <= DDIR_WRITE; j++) {
-                       ts->lat_stat[j].min_val = -1UL;
-                       ts->clat_stat[j].min_val = -1UL;
-                       ts->slat_stat[j].min_val = -1UL;
-                       ts->bw_stat[j].min_val = -1UL;
-               }
-               ts->groupid = -1;
-       }
+       for (i = 0; i < nr_ts; i++)
+               init_thread_stat(&threadstats[i]);
 
        j = 0;
        last_ts = -1;
@@ -716,9 +785,9 @@ void show_run_stats(void)
 
                ts->clat_percentiles = td->o.clat_percentiles;
                if (td->o.overwrite_plist)
-                       ts->percentile_list = td->o.percentile_list;
+                       memcpy(ts->percentile_list, td->o.percentile_list, sizeof(td->o.percentile_list));
                else
-                       ts->percentile_list = NULL;
+                       memcpy(ts->percentile_list, def_percentile_list, sizeof(def_percentile_list));
 
                idx++;
                ts->members++;
@@ -727,8 +796,13 @@ void show_run_stats(void)
                        /*
                         * These are per-group shared already
                         */
-                       ts->name = td->o.name;
-                       ts->description = td->o.description;
+                       strncpy(ts->name, td->o.name, FIO_JOBNAME_SIZE);
+                       if (td->o.description)
+                               strncpy(ts->description, td->o.description,
+                                               FIO_JOBNAME_SIZE);
+                       else
+                               memset(ts->description, 0, FIO_JOBNAME_SIZE);
+
                        ts->groupid = td->groupid;
 
                        /*
@@ -750,58 +824,14 @@ void show_run_stats(void)
                        if (!td->error && td->o.continue_on_error &&
                            td->first_error) {
                                ts->error = td->first_error;
-                               ts->verror = td->verror;
+                               strcpy(ts->verror, td->verror);
                        } else  if (td->error) {
                                ts->error = td->error;
-                               ts->verror = td->verror;
+                               strcpy(ts->verror, td->verror);
                        }
                }
 
-               for (l = 0; l <= DDIR_WRITE; l++) {
-                       sum_stat(&ts->clat_stat[l], &td->ts.clat_stat[l], idx);
-                       sum_stat(&ts->slat_stat[l], &td->ts.slat_stat[l], idx);
-                       sum_stat(&ts->lat_stat[l], &td->ts.lat_stat[l], idx);
-                       sum_stat(&ts->bw_stat[l], &td->ts.bw_stat[l], idx);
-
-                       ts->stat_io_bytes[l] += td->ts.stat_io_bytes[l];
-                       ts->io_bytes[l] += td->ts.io_bytes[l];
-
-                       if (ts->runtime[l] < td->ts.runtime[l])
-                               ts->runtime[l] = td->ts.runtime[l];
-               }
-
-               ts->usr_time += td->ts.usr_time;
-               ts->sys_time += td->ts.sys_time;
-               ts->ctx += td->ts.ctx;
-               ts->majf += td->ts.majf;
-               ts->minf += td->ts.minf;
-
-               for (k = 0; k < FIO_IO_U_MAP_NR; k++)
-                       ts->io_u_map[k] += td->ts.io_u_map[k];
-               for (k = 0; k < FIO_IO_U_MAP_NR; k++)
-                       ts->io_u_submit[k] += td->ts.io_u_submit[k];
-               for (k = 0; k < FIO_IO_U_MAP_NR; k++)
-                       ts->io_u_complete[k] += td->ts.io_u_complete[k];
-               for (k = 0; k < FIO_IO_U_LAT_U_NR; k++)
-                       ts->io_u_lat_u[k] += td->ts.io_u_lat_u[k];
-               for (k = 0; k < FIO_IO_U_LAT_M_NR; k++)
-                       ts->io_u_lat_m[k] += td->ts.io_u_lat_m[k];
-
-
-               for (k = 0; k <= 2; k++) {
-                       ts->total_io_u[k] += td->ts.total_io_u[k];
-                       ts->short_io_u[k] += td->ts.short_io_u[k];
-               }
-
-               for (k = 0; k <= DDIR_WRITE; k++) {
-                       int m;
-                       for (m = 0; m < FIO_IO_U_PLAT_NR; m++)
-                               ts->io_u_plat[k][m] += td->ts.io_u_plat[k][m];
-               }
-
-               ts->total_run_time += td->ts.total_run_time;
-               ts->total_submit += td->ts.total_submit;
-               ts->total_complete += td->ts.total_complete;
+               sum_thread_stats(ts, &td->ts, idx);
        }
 
        for (i = 0; i < nr_ts; i++) {
@@ -858,15 +888,24 @@ void show_run_stats(void)
                ts = &threadstats[i];
                rs = &runstats[ts->groupid];
 
-               if (terse_output)
+               if (is_backend)
+                       fio_server_send_ts(ts, rs);
+               else if (terse_output)
                        show_thread_status_terse(ts, rs);
                else
                        show_thread_status(ts, rs);
        }
 
        if (!terse_output) {
-               for (i = 0; i < groupid + 1; i++)
-                       show_group_stats(&runstats[i], i);
+               for (i = 0; i < groupid + 1; i++) {
+                       rs = &runstats[i];
+
+                       rs->groupid = i;
+                       if (is_backend)
+                               fio_server_send_gs(rs);
+                       else
+                               show_group_stats(rs);
+               }
 
                show_disk_util();
        }
@@ -885,10 +924,10 @@ static inline void add_stat_sample(struct io_stat *is, unsigned long data)
        if (data < is->min_val)
                is->min_val = data;
 
-       delta = val - is->mean;
+       delta = val - is->mean.u.f;
        if (delta) {
-               is->mean += delta / (is->samples + 1.0);
-               is->S += delta * (val - is->mean);
+               is->mean.u.f += delta / (is->samples + 1.0);
+               is->S.u.f += delta * (val - is->mean.u.f);
        }
 
        is->samples++;
@@ -954,8 +993,8 @@ void add_clat_sample(struct thread_data *td, enum fio_ddir ddir,
 
        add_stat_sample(&ts->clat_stat[ddir], usec);
 
-       if (ts->clat_log)
-               add_log_sample(td, ts->clat_log, usec, ddir, bs);
+       if (td->clat_log)
+               add_log_sample(td, td->clat_log, usec, ddir, bs);
 
        if (ts->clat_percentiles)
                add_clat_percentile_sample(ts, usec, ddir);
@@ -971,8 +1010,8 @@ void add_slat_sample(struct thread_data *td, enum fio_ddir ddir,
 
        add_stat_sample(&ts->slat_stat[ddir], usec);
 
-       if (ts->slat_log)
-               add_log_sample(td, ts->slat_log, usec, ddir, bs);
+       if (td->slat_log)
+               add_log_sample(td, td->slat_log, usec, ddir, bs);
 }
 
 void add_lat_sample(struct thread_data *td, enum fio_ddir ddir,
@@ -985,8 +1024,8 @@ void add_lat_sample(struct thread_data *td, enum fio_ddir ddir,
 
        add_stat_sample(&ts->lat_stat[ddir], usec);
 
-       if (ts->lat_log)
-               add_log_sample(td, ts->lat_log, usec, ddir, bs);
+       if (td->lat_log)
+               add_log_sample(td, td->lat_log, usec, ddir, bs);
 }
 
 void add_bw_sample(struct thread_data *td, enum fio_ddir ddir, unsigned int bs,
@@ -998,17 +1037,43 @@ void add_bw_sample(struct thread_data *td, enum fio_ddir ddir, unsigned int bs,
        if (!ddir_rw(ddir))
                return;
 
-       spent = mtime_since(&ts->stat_sample_time[ddir], t);
+       spent = mtime_since(&td->bw_sample_time, t);
        if (spent < td->o.bw_avg_time)
                return;
 
-       rate = (td->this_io_bytes[ddir] - ts->stat_io_bytes[ddir]) *
+       rate = (td->this_io_bytes[ddir] - td->stat_io_bytes[ddir]) *
                        1000 / spent / 1024;
        add_stat_sample(&ts->bw_stat[ddir], rate);
 
-       if (ts->bw_log)
-               add_log_sample(td, ts->bw_log, rate, ddir, bs);
+       if (td->bw_log)
+               add_log_sample(td, td->bw_log, rate, ddir, bs);
+
+       fio_gettime(&td->bw_sample_time, NULL);
+       td->stat_io_bytes[ddir] = td->this_io_bytes[ddir];
+}
+
+void add_iops_sample(struct thread_data *td, enum fio_ddir ddir,
+                    struct timeval *t)
+{
+       struct thread_stat *ts = &td->ts;
+       unsigned long spent, iops;
+
+       if (!ddir_rw(ddir))
+               return;
+
+       spent = mtime_since(&td->iops_sample_time, t);
+       if (spent < td->o.iops_avg_time)
+               return;
+
+       iops = ((td->this_io_blocks[ddir] - td->stat_io_blocks[ddir]) * 1000) / spent;
+
+       add_stat_sample(&ts->iops_stat[ddir], iops);
+
+       if (td->iops_log) {
+               assert(iops);
+               add_log_sample(td, td->iops_log, iops, ddir, 0);
+       }
 
-       fio_gettime(&ts->stat_sample_time[ddir], NULL);
-       ts->stat_io_bytes[ddir] = td->this_io_bytes[ddir];
+       fio_gettime(&td->iops_sample_time, NULL);
+       td->stat_io_blocks[ddir] = td->this_io_blocks[ddir];
 }
diff --git a/stat.h b/stat.h
new file mode 100644 (file)
index 0000000..3115539
--- /dev/null
+++ b/stat.h
@@ -0,0 +1,201 @@
+#ifndef FIO_STAT_H
+#define FIO_STAT_H
+
+struct group_run_stats {
+       uint64_t max_run[2], min_run[2];
+       uint64_t max_bw[2], min_bw[2];
+       uint64_t io_kb[2];
+       uint64_t agg[2];
+       uint32_t kb_base;
+       uint32_t groupid;
+};
+
+/*
+ * How many depth levels to log
+ */
+#define FIO_IO_U_MAP_NR        7
+#define FIO_IO_U_LAT_U_NR 10
+#define FIO_IO_U_LAT_M_NR 12
+
+/*
+ * Aggregate clat samples to report percentile(s) of them.
+ *
+ * EXECUTIVE SUMMARY
+ *
+ * FIO_IO_U_PLAT_BITS determines the maximum statistical error on the
+ * value of resulting percentiles. The error will be approximately
+ * 1/2^(FIO_IO_U_PLAT_BITS+1) of the value.
+ *
+ * FIO_IO_U_PLAT_GROUP_NR and FIO_IO_U_PLAT_BITS determine the maximum
+ * range being tracked for latency samples. The maximum value tracked
+ * accurately will be 2^(GROUP_NR + PLAT_BITS -1) microseconds.
+ *
+ * FIO_IO_U_PLAT_GROUP_NR and FIO_IO_U_PLAT_BITS determine the memory
+ * requirement of storing those aggregate counts. The memory used will
+ * be (FIO_IO_U_PLAT_GROUP_NR * 2^FIO_IO_U_PLAT_BITS) * sizeof(int)
+ * bytes.
+ *
+ * FIO_IO_U_PLAT_NR is the total number of buckets.
+ *
+ * DETAILS
+ *
+ * Suppose the clat varies from 0 to 999 (usec), the straightforward
+ * method is to keep an array of (999 + 1) buckets, in which a counter
+ * keeps the count of samples which fall in the bucket, e.g.,
+ * {[0],[1],...,[999]}. However this consumes a huge amount of space,
+ * and can be avoided if an approximation is acceptable.
+ *
+ * One such method is to let the range of the bucket to be greater
+ * than one. This method has low accuracy when the value is small. For
+ * example, let the buckets be {[0,99],[100,199],...,[900,999]}, and
+ * the represented value of each bucket be the mean of the range. Then
+ * a value 0 has an round-off error of 49.5. To improve on this, we
+ * use buckets with non-uniform ranges, while bounding the error of
+ * each bucket within a ratio of the sample value. A simple example
+ * would be when error_bound = 0.005, buckets are {
+ * {[0],[1],...,[99]}, {[100,101],[102,103],...,[198,199]},..,
+ * {[900,909],[910,919]...}  }. The total range is partitioned into
+ * groups with different ranges, then buckets with uniform ranges. An
+ * upper bound of the error is (range_of_bucket/2)/value_of_bucket
+ *
+ * For better efficiency, we implement this using base two. We group
+ * samples by their Most Significant Bit (MSB), extract the next M bit
+ * of them as an index within the group, and discard the rest of the
+ * bits.
+ *
+ * E.g., assume a sample 'x' whose MSB is bit n (starting from bit 0),
+ * and use M bit for indexing
+ *
+ *        | n |    M bits   | bit (n-M-1) ... bit 0 |
+ *
+ * Because x is at least 2^n, and bit 0 to bit (n-M-1) is at most
+ * (2^(n-M) - 1), discarding bit 0 to (n-M-1) makes the round-off
+ * error
+ *
+ *           2^(n-M)-1    2^(n-M)    1
+ *      e <= --------- <= ------- = ---
+ *             2^n          2^n     2^M
+ *
+ * Furthermore, we use "mean" of the range to represent the bucket,
+ * the error e can be lowered by half to 1 / 2^(M+1). By using M bits
+ * as the index, each group must contains 2^M buckets.
+ *
+ * E.g. Let M (FIO_IO_U_PLAT_BITS) be 6
+ *      Error bound is 1/2^(6+1) = 0.0078125 (< 1%)
+ *
+ *     Group   MSB     #discarded      range of                #buckets
+ *                     error_bits      value
+ *     ----------------------------------------------------------------
+ *     0*      0~5     0               [0,63]                  64
+ *     1*      6       0               [64,127]                64
+ *     2       7       1               [128,255]               64
+ *     3       8       2               [256,511]               64
+ *     4       9       3               [512,1023]              64
+ *     ...     ...     ...             [...,...]               ...
+ *     18      23      17              [8838608,+inf]**        64
+ *
+ *  * Special cases: when n < (M-1) or when n == (M-1), in both cases,
+ *    the value cannot be rounded off. Use all bits of the sample as
+ *    index.
+ *
+ *  ** If a sample's MSB is greater than 23, it will be counted as 23.
+ */
+
+#define FIO_IO_U_PLAT_BITS 6
+#define FIO_IO_U_PLAT_VAL (1 << FIO_IO_U_PLAT_BITS)
+#define FIO_IO_U_PLAT_GROUP_NR 19
+#define FIO_IO_U_PLAT_NR (FIO_IO_U_PLAT_GROUP_NR * FIO_IO_U_PLAT_VAL)
+#define FIO_IO_U_LIST_MAX_LEN 20 /* The size of the default and user-specified
+                                       list of percentiles */
+
+#define MAX_PATTERN_SIZE       512
+#define FIO_JOBNAME_SIZE       128
+#define FIO_VERROR_SIZE                128
+
+struct thread_stat {
+       char name[FIO_JOBNAME_SIZE];
+       char verror[FIO_VERROR_SIZE];
+       uint32_t error;
+       uint32_t groupid;
+       uint32_t pid;
+       char description[FIO_JOBNAME_SIZE];
+       uint32_t members;
+
+       /*
+        * bandwidth and latency stats
+        */
+       struct io_stat clat_stat[2];            /* completion latency */
+       struct io_stat slat_stat[2];            /* submission latency */
+       struct io_stat lat_stat[2];             /* total latency */
+       struct io_stat bw_stat[2];              /* bandwidth stats */
+       struct io_stat iops_stat[2];            /* IOPS stats */
+
+       /*
+        * fio system usage accounting
+        */
+       uint64_t usr_time;
+       uint64_t sys_time;
+       uint64_t ctx;
+       uint64_t minf, majf;
+
+       /*
+        * IO depth and latency stats
+        */
+       uint64_t clat_percentiles;
+       fio_fp64_t percentile_list[FIO_IO_U_LIST_MAX_LEN];
+
+       uint32_t io_u_map[FIO_IO_U_MAP_NR];
+       uint32_t io_u_submit[FIO_IO_U_MAP_NR];
+       uint32_t io_u_complete[FIO_IO_U_MAP_NR];
+       uint32_t io_u_lat_u[FIO_IO_U_LAT_U_NR];
+       uint32_t io_u_lat_m[FIO_IO_U_LAT_M_NR];
+       uint32_t io_u_plat[2][FIO_IO_U_PLAT_NR];
+       uint64_t total_io_u[3];
+       uint64_t short_io_u[3];
+       uint64_t total_submit;
+       uint64_t total_complete;
+
+       uint64_t io_bytes[2];
+       uint64_t runtime[2];
+       uint64_t total_run_time;
+
+       /*
+        * IO Error related stats
+        */
+       uint16_t continue_on_error;
+       uint64_t total_err_count;
+       uint32_t first_error;
+
+       uint32_t kb_base;
+};
+
+struct jobs_eta {
+       uint32_t nr_running;
+       uint32_t nr_ramp;
+       uint32_t nr_pending;
+       uint32_t files_open;
+       uint32_t m_rate, t_rate;
+       uint32_t m_iops, t_iops;
+       uint32_t rate[2];
+       uint32_t iops[2];
+       uint64_t elapsed_sec;
+       uint64_t eta_sec;
+
+       /*
+        * Network 'copy' of run_str[]
+        */
+       uint32_t nr_threads;
+       uint8_t run_str[0];
+};
+
+extern void show_thread_status(struct thread_stat *ts, struct group_run_stats *rs);
+extern void show_group_stats(struct group_run_stats *rs);
+extern int 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 sum_thread_stats(struct thread_stat *dst, struct thread_stat *src, int nr);
+extern void sum_group_stats(struct group_run_stats *dst, struct group_run_stats *src);
+extern void init_thread_stat(struct thread_stat *ts);
+extern void init_group_run_stat(struct group_run_stats *gs);
+
+#endif
diff --git a/t/ieee754.c b/t/ieee754.c
new file mode 100644 (file)
index 0000000..afc25f3
--- /dev/null
@@ -0,0 +1,21 @@
+#include <stdio.h>
+#include "../ieee754.h"
+
+static double values[] = { -17.23, 17.23, 123.4567, 98765.4321, 0.0 };
+
+int main(int argc, char *argv[])
+{
+       uint64_t i;
+       double f;
+       int j;
+
+       j = 0;
+       do {
+               i = fio_double_to_uint64(values[j]);
+               f = fio_uint64_to_double(i);
+               printf("%f -> %f\n", values[j], f);
+               j++;
+       } while (values[j] != 0.0);
+
+       return 0;
+}
diff --git a/t/log.c b/t/log.c
new file mode 100644 (file)
index 0000000..7f1de27
--- /dev/null
+++ b/t/log.c
@@ -0,0 +1,15 @@
+#include <stdio.h>
+#include <stdarg.h>
+
+int log_err(const char *format, ...)
+{
+       char buffer[1024];
+       va_list args;
+       size_t len;
+
+       va_start(args, format);
+       len = vsnprintf(buffer, sizeof(buffer), format, args);
+       va_end(args);
+
+       return fwrite(buffer, len, 1, stderr);
+}