Recent changes (gfio)

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



The following changes since commit 7956f141c058cdd95f14491ee646b7f7951ab2ab:

  Merge branch 'master' into gfio (2012-11-15 13:57:29 -0700)

are available in the git repository at:

  git://git.kernel.dk/fio.git gfio

Aaron Carroll (1):
      Add support for Android

Bruce Cran (3):
      Add more examples to the Windows installer.
      Generate a new Windows installer product code for 2.0.11.
      Fix windowsaio IO error handling and document device write access issue on Windows and FreeBSD.

Jens Axboe (40):
      Get rid of -fno-omit-frame-pointer
      genzipf: add help/usage screen (-h)
      Disable random map automatically if a non-uniform random distribution is given
      Document random distribution
      Fio 2.0.11
      Use unique seed for zipf/pareto init if rand_repeat is not set
      memory: wire up mmap huge page support
      Rework file random map
      bitmap: fix bit_masks[] for 32-bit compiles
      bitmap: kill debug code
      Kill now unused bitmap defines from legacy code
      Restore BUSY_OK bypassing of bitmap
      Add LFSR generator
      Ensure that lfsr_init() is always called
      lfsr: crank it 128 times before using the sequence
      Fix compile for older Linux without mmap MAP_HUGETLB
      bitmap: fix off-by-8 allocation error
      Rename the bitmap to axmap
      Add t/axmap tester
      libfio: don't sent KILL signal to own process from signal handler
      net: fix segfault with receiver, tcp, and no hostname set
      net: exit on WAITALL and !ret
      net: set runstate to SETTING_UP while waiting for a connection
      Wire up _weak posix_fadvise()
      parse: fix wrong "might be used unitialized" warning on some compilers
      Fix broken 'norandommap'
      solaris: enable fadvise()
      Merge branch 'rand-map'
      net: sent udp open messages
      verify: treat as failure if given verify type is different from media
      net: add basic ping/pong type workload support
      net: fix receiver start time
      Cache layout improvements
      Change preferred default clocksource to gettimeofday()
      Wire up SIGUSR2 to kill blocking threads
      Revert "Change preferred default clocksource to gettimeofday()"
      Merge branch 'master' into gfio
      gfio: bad merge in Makefile
      gfio: fix Makefile typo for cleaning gfio objs
      gfio: add check for unnecessary g_thread_init() call

Steven Noonan (1):
      engines/net.c: fix compilation error due to missing signal.h include

 FIO-VERSION-GEN         |    2 +-
 HOWTO                   |   56 +++++--
 Makefile                |   26 +++-
 backend.c               |    4 +-
 cconv.c                 |    2 +
 engines/net.c           |  189 ++++++++++++++++++-----
 engines/windowsaio.c    |   86 ++++++++++-
 file.h                  |   15 +-
 filesetup.c             |   30 ++--
 fio.1                   |   26 +++
 fio.h                   |   24 +++-
 gfio.c                  |    3 +
 helpers.c               |    7 +
 helpers.h               |    1 +
 init.c                  |   29 ++++
 io_ddir.h               |    2 +-
 io_u.c                  |  230 +++++++++-------------------
 ioengine.h              |   22 ++--
 lib/axmap.c             |  390 +++++++++++++++++++++++++++++++++++++++++++++++
 lib/axmap.h             |   18 +++
 lib/lfsr.c              |  269 ++++++++++++++++++++++++++++++++
 lib/lfsr.h              |   24 +++
 libfio.c                |    7 +-
 memory.c                |    9 +
 options.c               |   17 ++
 os/os-android.h         |  234 ++++++++++++++++++++++++++++
 os/os-linux.h           |    5 +
 os/os-solaris.h         |    1 +
 os/os.h                 |   16 ++-
 os/windows/examples.wxs |   24 +++-
 os/windows/install.wxs  |   10 +-
 parse.c                 |    4 +-
 profile.c               |    4 +-
 t/axmap.c               |   46 ++++++
 t/genzipf.c             |   18 ++-
 thread_options.h        |    4 +
 verify.c                |   12 ++-
 37 files changed, 1580 insertions(+), 286 deletions(-)
 create mode 100644 lib/axmap.c
 create mode 100644 lib/axmap.h
 create mode 100644 lib/lfsr.c
 create mode 100644 lib/lfsr.h
 create mode 100644 os/os-android.h
 create mode 100644 t/axmap.c

---

Diff of recent changes:

diff --git a/FIO-VERSION-GEN b/FIO-VERSION-GEN
index cc7eb83..035ddaf 100755
--- a/FIO-VERSION-GEN
+++ b/FIO-VERSION-GEN
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=FIO-VERSION-FILE
-DEF_VER=fio-2.0.10
+DEF_VER=fio-2.0.11
 
 LF='
 '
diff --git a/HOWTO b/HOWTO
index 5611814..a8a3d95 100644
--- a/HOWTO
+++ b/HOWTO
@@ -61,7 +61,7 @@ bottom, it contains the following basic parameters:
 
 	Num threads	How many threads or processes should we spread
 			this workload over.
-	
+
 The above are the basic parameters defined for a workload, in addition
 there's a multitude of parameters that modify other aspects of how this
 job behaves.
@@ -274,11 +274,14 @@ filename=str	Fio normally makes up a filename based on the job name,
 		as the two working files, you would use
 		filename=/dev/sda:/dev/sdb. On Windows, disk devices are accessed
 		as \\.\PhysicalDrive0 for the first device, \\.\PhysicalDrive1
-		for the second etc.  If the wanted filename does need to 
-		include a colon, then escape that with a '\' character. 
-		For instance, if the filename is "/dev/dsk/foo@3,0:c", 
-		then you would use filename="/dev/dsk/foo@3,0\:c". 
-		'-' is a reserved name, meaning stdin or stdout. Which of the 
+		for the second etc.
+		Note: Windows and FreeBSD prevent write access to areas of the disk
+		containing in-use data (e.g. filesystems).
+		If the wanted filename does need to include a colon, then escape that
+ 		with a '\' character.
+		For instance, if the filename is "/dev/dsk/foo@3,0:c",
+		then you would use filename="/dev/dsk/foo@3,0\:c".
+		'-' is a reserved name, meaning stdin or stdout. Which of the
 		two depends on the read/write direction set.
 
 opendir=str	Tell fio to recursively add any file it can find in this
@@ -716,6 +719,25 @@ rwmixwrite=int	How large a percentage of the mix should be writes. If both
 		if fio is asked to limit reads or writes to a certain rate.
 		If that is the case, then the distribution may be skewed.
 
+random_distribution=str:float	By default, fio will use a completely uniform
+		random distribution when asked to perform random IO. Sometimes
+		it is useful to skew the distribution in specific ways,
+		ensuring that some parts of the data is more hot than others.
+		fio includes the following distribution models:
+
+		random		Uniform random distribution
+		zipf		Zipf distribution
+		pareto		Pareto distribution
+
+		When using a zipf or pareto distribution, an input value
+		is also needed to define the access pattern. For zipf, this
+		is the zipf theta. For pareto, it's the pareto power. Fio
+		includes a test program, genzipf, that can be used visualize
+		what the given input values will yield in terms of hit rates.
+		If you wanted to use zipf with a theta of 1.2, you would use
+		random_distribution=zipf:1.2 as the option. If a non-uniform
+		model is used, fio will disable use of the random map.
+
 norandommap	Normally fio will cover every block of the file when doing
 		random IO. If this option is given, fio will just get a
 		new random offset without looking at past io history. This
@@ -1067,7 +1089,7 @@ verify_backlog_batch=int	Control how many blocks fio will verify
 		less than verify_backlog then not all blocks will be verified,
 		if verify_backlog_batch is larger than verify_backlog, some
 		blocks will be verified more than once.
-		
+
 stonewall
 wait_for_previous Wait for preceeding jobs in the job file to exit, before
 		starting this one. Can be used to insert serialization
@@ -1113,7 +1135,7 @@ read_iolog=str	Open an iolog with the specified file name and replay the
 		for how to capture such logging data. For blktrace replay,
 		the file needs to be turned into a blkparse binary data
 		file first (blkparse <device> -o /dev/null -d file_for_fio.bin).
-		
+
 replay_no_stall=int When replaying I/O with read_iolog the default behavior
 		is to attempt to respect the time stamps within the log and
 		replay them with the appropriate delay between IOPS.  By
@@ -1270,12 +1292,12 @@ ignore_error=str Sometimes you want to ignore some errors during test
 		 may be symbol ('ENOSPC', 'ENOMEM') or integer.
 		 Example:
 			ignore_error=EAGAIN,ENOSPC:122
-		 This option will ignore EAGAIN from READ, and ENOSPC and 
-		 122(EDQUOT) from WRITE. 
+		 This option will ignore EAGAIN from READ, and ENOSPC and
+		 122(EDQUOT) from WRITE.
 
 error_dump=bool If set dump every error even if it is non fatal, true
 		by default. If disabled only fatal error will be dumped
-				 
+
 cgroup=str	Add job to this control group. If it doesn't exist, it will
 		be created. The system must have a mounted cgroup blkio
 		mount point for this to work. If your system doesn't have it
@@ -1362,7 +1384,7 @@ that defines them is selected.
 [e4defrag] donorname=str
 	        File will be used as a block donor(swap extents between files)
 [e4defrag] inplace=int
-		Configure donor file blocks allocation strategy		
+		Configure donor file blocks allocation strategy
 		0(default): Preallocate donor's file on init
 		1 	  : allocate space immidietly inside defragment event,
 			    and free right after event
@@ -1550,8 +1572,8 @@ Split up, the format is as follows:
 			  Read merges, write merges,
 			  Read ticks, write ticks,
 			  Time spent in queue, disk utilization percentage
-	Additional Info (dependant on continue_on_error, default off): total # errors, first error code 
-	
+	Additional Info (dependant on continue_on_error, default off): total # errors, first error code
+
 	Additional Info (dependant on description being set): Text description
 
 Completion latency percentiles can be a grouping of up to 20 sets, so
@@ -1567,7 +1589,7 @@ there will be a disk utilization section.
 
 8.0 Trace file format
 ---------------------
-There are two trace file format that you can encounter. The older (v1) format 
+There are two trace file format that you can encounter. The older (v1) format
 is unsupported since version 1.20-rc3 (March 2008). It will still be described
 below in case that you get an old trace and want to understand it.
 
@@ -1604,7 +1626,7 @@ filename action
 The filename is given as an absolute path. The action can be one of these:
 
 add          Add the given filename to the trace
-open         Open the file with the given filename. The filename has to have 
+open         Open the file with the given filename. The filename has to have
              been added with the add action before.
 close        Close the file with the given filename. The file has to have been
              opened before.
@@ -1615,7 +1637,7 @@ The file io action format:
 filename action offset length
 
 The filename is given as an absolute path, and has to have been added and opened
-before it can be used with this format. The offset and length are given in 
+before it can be used with this format. The offset and length are given in
 bytes. The action can be one of these:
 
 wait       Wait for 'offset' microseconds. Everything below 100 is discarded.
diff --git a/Makefile b/Makefile
index b0b6857..bc0a2f8 100644
--- a/Makefile
+++ b/Makefile
@@ -1,10 +1,10 @@
 ifneq ($(origin CC), environment)
-CC	= gcc
+CC	= $(CROSS_COMPILE)gcc
 endif
 DEBUGFLAGS = -D_FORTIFY_SOURCE=2 -DFIO_INC_DEBUG
 CPPFLAGS= -D_GNU_SOURCE -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 \
 	$(DEBUGFLAGS)
-OPTFLAGS= -O3 -fno-omit-frame-pointer -g $(EXTFLAGS)
+OPTFLAGS= -O3 -g $(EXTFLAGS)
 CFLAGS	= -std=gnu99 -Wwrite-strings -Wall $(OPTFLAGS)
 LIBS	= -lm -lz $(EXTLIBS)
 PROGS	= fio
@@ -20,7 +20,8 @@ SOURCE := gettime.c ioengines.c init.c stat.c log.c time.c filesetup.c \
 		lib/num2str.c lib/ieee754.c $(wildcard crc/*.c) engines/cpu.c \
 		engines/mmap.c engines/sync.c engines/null.c engines/net.c \
 		memalign.c server.c client.c iolog.c backend.c libfio.c flow.c \
-		cconv.c lib/prio_tree.c lib/zipf.c json.c gettime-thread.c
+		cconv.c lib/prio_tree.c json.c lib/zipf.c lib/axmap.c \
+		lib/lfsr.c gettime-thread.c
 
 ifeq ($(UNAME), Linux)
   SOURCE += diskutil.c fifo.c blktrace.c helpers.c cgroup.c trim.c \
@@ -31,6 +32,14 @@ ifeq ($(UNAME), Linux)
   LIBS += -lpthread -ldl -lrt -laio
   LDFLAGS += -rdynamic
 endif
+ifeq ($(UNAME), Android)
+  SOURCE += diskutil.c fifo.c blktrace.c helpers.c trim.c \
+		engines/splice.c profiles/tiobench.c engines/falloc.c \
+		engines/e4defrag.c
+  LIBS += -ldl
+  LDFLAGS += -rdynamic
+  CPPFLAGS += -DFIO_NO_HAVE_SHM_H
+endif
 ifeq ($(UNAME), SunOS)
   SOURCE += fifo.c lib/strsep.c helpers.c engines/posixaio.c \
 		engines/solarisaio.c
@@ -88,13 +97,19 @@ T_ZIPF_OBS = t/genzipf.o
 T_ZIPF_OBJS += t/log.o lib/ieee754.o lib/rand.o lib/zipf.o t/genzipf.o
 T_ZIPF_PROGS = t/genzipf
 
+T_AXMAP_OBJS = t/axmap.o
+T_AXMAP_OBJS += lib/lfsr.o lib/axmap.o
+T_AXMAP_PROGS = t/axmap
+
 T_OBJS = $(T_SMALLOC_OBJS)
 T_OBJS += $(T_IEEE_OBJS)
 T_OBJS += $(T_ZIPF_OBJS)
+T_OBJS += $(T_AXMAP_OBJS)
 
 T_PROGS = $(T_SMALLOC_PROGS)
 T_PROGS += $(T_IEEE_PROGS)
 T_PROGS += $(T_ZIPF_PROGS)
+T_PROGS += $(T_AXMAP_PROGS)
 
 ifneq ($(findstring $(MAKEFLAGS),s),s)
 ifndef V
@@ -172,13 +187,16 @@ gfio: $(GFIO_OBJS)
 t/genzipf: $(T_ZIPF_OBJS)
 	$(QUIET_CC)$(CC) $(LDFLAGS) $(CFLAGS) -o $@ $(T_ZIPF_OBJS) $(LIBS) $(LDFLAGS)
 
+t/axmap: $(T_AXMAP_OBJS)
+	$(QUIET_CC)$(CC) $(LDFLAGS) $(CFLAGS) -o $@ $(T_AXMAP_OBJS) $(LIBS) $(LDFLAGS)
+
 .depend: $(SOURCE)
 	$(QUIET_DEP)$(CC) -MM $(CFLAGS) $(CPPFLAGS) $(SOURCE) 1> .depend
 
 $(PROGS): .depend
 
 clean: FORCE
-	-rm -f .depend $(GFIO_OBJS )$(OBJS) $(T_OBJS) $(PROGS) $(T_PROGS) core.* core gfio FIO-VERSION-FILE
+	-rm -f .depend $(GFIO_OBJS) $(OBJS) $(T_OBJS) $(PROGS) $(T_PROGS) core.* core gfio FIO-VERSION-FILE
 
 cscope:
 	@cscope -b -R
diff --git a/backend.c b/backend.c
index 39ef759..d56c7d0 100644
--- a/backend.c
+++ b/backend.c
@@ -34,7 +34,9 @@
 #include <sys/stat.h>
 #include <sys/wait.h>
 #include <sys/ipc.h>
+#ifndef FIO_NO_HAVE_SHM_H
 #include <sys/shm.h>
+#endif
 #include <sys/mman.h>
 
 #include "fio.h"
@@ -1398,7 +1400,7 @@ static void reap_threads(unsigned int *nr_running, unsigned int *t_rate,
 			if (WIFSIGNALED(status)) {
 				int sig = WTERMSIG(status);
 
-				if (sig != SIGTERM)
+				if (sig != SIGTERM && sig != SIGUSR2)
 					log_err("fio: pid=%d, got signal=%d\n",
 							(int) td->pid, sig);
 				td->sig = sig;
diff --git a/cconv.c b/cconv.c
index ca97c73..1733123 100644
--- a/cconv.c
+++ b/cconv.c
@@ -121,6 +121,7 @@ void convert_thread_options_to_cpu(struct thread_options *o,
 	o->random_distribution = le32_to_cpu(top->random_distribution);
 	o->zipf_theta.u.f = fio_uint64_to_double(le64_to_cpu(top->zipf_theta.u.i));
 	o->pareto_h.u.f = fio_uint64_to_double(le64_to_cpu(top->pareto_h.u.i));
+	o->random_generator = le32_to_cpu(top->random_generator);
 	o->hugepage_size = le32_to_cpu(top->hugepage_size);
 	o->rw_min_bs = le32_to_cpu(top->rw_min_bs);
 	o->thinktime = le32_to_cpu(top->thinktime);
@@ -274,6 +275,7 @@ void convert_thread_options_to_net(struct thread_options_pack *top,
 	top->random_distribution = cpu_to_le32(o->random_distribution);
 	top->zipf_theta.u.i = __cpu_to_le64(fio_double_to_uint64(o->zipf_theta.u.f));
 	top->pareto_h.u.i = __cpu_to_le64(fio_double_to_uint64(o->pareto_h.u.f));
+	top->random_generator = cpu_to_le32(o->random_generator);
 	top->hugepage_size = cpu_to_le32(o->hugepage_size);
 	top->rw_min_bs = cpu_to_le32(o->rw_min_bs);
 	top->thinktime = cpu_to_le32(o->thinktime);
diff --git a/engines/net.c b/engines/net.c
index 468bc7c..bbfce81 100644
--- a/engines/net.c
+++ b/engines/net.c
@@ -7,6 +7,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
+#include <signal.h>
 #include <errno.h>
 #include <assert.h>
 #include <netinet/in.h>
@@ -33,6 +34,7 @@ struct netio_options {
 	unsigned int port;
 	unsigned int proto;
 	unsigned int listen;
+	unsigned int pingpong;
 };
 
 struct udp_close_msg {
@@ -42,7 +44,8 @@ struct udp_close_msg {
 
 enum {
 	FIO_LINK_CLOSE = 0x89,
-	FIO_LINK_CLOSE_MAGIC = 0x6c696e6b,
+	FIO_LINK_OPEN_CLOSE_MAGIC = 0x6c696e6b,
+	FIO_LINK_OPEN = 0x98,
 
 	FIO_TYPE_TCP	= 1,
 	FIO_TYPE_UDP	= 2,
@@ -102,6 +105,12 @@ static struct fio_option options[] = {
 		.category = FIO_OPT_C_IO,
 	},
 	{
+		.name	= "pingpong",
+		.type	= FIO_OPT_STR_SET,
+		.off1	= offsetof(struct netio_options, pingpong),
+		.help	= "Ping-pong IO requests",
+	},
+	{
 		.name	= NULL,
 	},
 };
@@ -295,7 +304,7 @@ static int fio_netio_send(struct thread_data *td, struct io_u *io_u)
 {
 	struct netio_data *nd = td->io_ops->data;
 	struct netio_options *o = td->eo;
-	int ret, flags = OS_MSG_DONTWAIT;
+	int ret, flags = 0;
 
 	do {
 		if (o->proto == FIO_TYPE_UDP) {
@@ -309,8 +318,8 @@ static int fio_netio_send(struct thread_data *td, struct io_u *io_u)
 			 * if we are going to write more, set MSG_MORE
 			 */
 #ifdef MSG_MORE
-			if (td->this_io_bytes[DDIR_WRITE] + io_u->xfer_buflen <
-			    td->o.size)
+			if ((td->this_io_bytes[DDIR_WRITE] + io_u->xfer_buflen <
+			    td->o.size) && !o->pingpong)
 				flags |= MSG_MORE;
 #endif
 			ret = send(io_u->file->fd, io_u->xfer_buf,
@@ -322,8 +331,6 @@ static int fio_netio_send(struct thread_data *td, struct io_u *io_u)
 		ret = poll_wait(td, io_u->file->fd, POLLOUT);
 		if (ret <= 0)
 			break;
-
-		flags &= ~OS_MSG_DONTWAIT;
 	} while (1);
 
 	return ret;
@@ -337,7 +344,7 @@ static int is_udp_close(struct io_u *io_u, int len)
 		return 0;
 
 	msg = io_u->xfer_buf;
-	if (ntohl(msg->magic) != FIO_LINK_CLOSE_MAGIC)
+	if (ntohl(msg->magic) != FIO_LINK_OPEN_CLOSE_MAGIC)
 		return 0;
 	if (ntohl(msg->cmd) != FIO_LINK_CLOSE)
 		return 0;
@@ -349,7 +356,7 @@ static int fio_netio_recv(struct thread_data *td, struct io_u *io_u)
 {
 	struct netio_data *nd = td->io_ops->data;
 	struct netio_options *o = td->eo;
-	int ret, flags = OS_MSG_DONTWAIT;
+	int ret, flags = 0;
 
 	do {
 		if (o->proto == FIO_TYPE_UDP) {
@@ -368,32 +375,32 @@ static int fio_netio_recv(struct thread_data *td, struct io_u *io_u)
 		}
 		if (ret > 0)
 			break;
+		else if (!ret && (flags & MSG_WAITALL))
+			break;
 
 		ret = poll_wait(td, io_u->file->fd, POLLIN);
 		if (ret <= 0)
 			break;
-		flags &= ~OS_MSG_DONTWAIT;
 		flags |= MSG_WAITALL;
 	} while (1);
 
 	return ret;
 }
 
-static int fio_netio_queue(struct thread_data *td, struct io_u *io_u)
+static int __fio_netio_queue(struct thread_data *td, struct io_u *io_u,
+			     enum fio_ddir ddir)
 {
 	struct netio_data *nd = td->io_ops->data;
 	struct netio_options *o = td->eo;
 	int ret;
 
-	fio_ro_check(td, io_u);
-
-	if (io_u->ddir == DDIR_WRITE) {
+	if (ddir == DDIR_WRITE) {
 		if (!nd->use_splice || o->proto == FIO_TYPE_UDP ||
 		    o->proto == 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) {
+	} else if (ddir == DDIR_READ) {
 		if (!nd->use_splice || o->proto == FIO_TYPE_UDP ||
 		    o->proto == FIO_TYPE_UNIX)
 			ret = fio_netio_recv(td, io_u);
@@ -410,7 +417,7 @@ static int fio_netio_queue(struct thread_data *td, struct io_u *io_u)
 		} else {
 			int err = errno;
 
-			if (io_u->ddir == DDIR_WRITE && err == EMSGSIZE)
+			if (ddir == DDIR_WRITE && err == EMSGSIZE)
 				return FIO_Q_BUSY;
 
 			io_u->error = err;
@@ -423,6 +430,28 @@ static int fio_netio_queue(struct thread_data *td, struct io_u *io_u)
 	return FIO_Q_COMPLETED;
 }
 
+static int fio_netio_queue(struct thread_data *td, struct io_u *io_u)
+{
+	struct netio_options *o = td->eo;
+	int ret;
+
+	fio_ro_check(td, io_u);
+
+	ret = __fio_netio_queue(td, io_u, io_u->ddir);
+	if (!o->pingpong || ret != FIO_Q_COMPLETED)
+		return ret;
+
+	/*
+	 * For ping-pong mode, receive or send reply as needed
+	 */
+	if (td_read(td) && io_u->ddir == DDIR_READ)
+		ret = __fio_netio_queue(td, io_u, DDIR_WRITE);
+	else if (td_write(td) && io_u->ddir == DDIR_WRITE)
+		ret = __fio_netio_queue(td, io_u, DDIR_READ);
+
+	return ret;
+}
+
 static int fio_netio_connect(struct thread_data *td, struct fio_file *f)
 {
 	struct netio_data *nd = td->io_ops->data;
@@ -481,39 +510,33 @@ static int fio_netio_accept(struct thread_data *td, struct fio_file *f)
 	struct netio_data *nd = td->io_ops->data;
 	struct netio_options *o = td->eo;
 	fio_socklen_t socklen = sizeof(nd->addr);
+	int state;
 
 	if (o->proto == FIO_TYPE_UDP) {
 		f->fd = nd->listenfd;
 		return 0;
 	}
 
+	state = td->runstate;
+	td_set_runstate(td, TD_SETTING_UP);
+
 	log_info("fio: waiting for connection\n");
 
 	if (poll_wait(td, nd->listenfd, POLLIN) < 0)
-		return 1;
+		goto err;
 
 	f->fd = accept(nd->listenfd, (struct sockaddr *) &nd->addr, &socklen);
 	if (f->fd < 0) {
 		td_verror(td, errno, "accept");
-		return 1;
+		goto err;
 	}
 
+	reset_all_stats(td);
+	td_set_runstate(td, state);
 	return 0;
-}
-
-static int fio_netio_open_file(struct thread_data *td, struct fio_file *f)
-{
-	int ret;
-	struct netio_options *o = td->eo;
-
-	if (o->listen)
-		ret = fio_netio_accept(td, f);
-	else
-		ret = fio_netio_connect(td, f);
-
-	if (ret)
-		f->fd = -1;
-	return ret;
+err:
+	td_set_runstate(td, state);
+	return 1;
 }
 
 static void fio_netio_udp_close(struct thread_data *td, struct fio_file *f)
@@ -523,7 +546,7 @@ static void fio_netio_udp_close(struct thread_data *td, struct fio_file *f)
 	struct sockaddr *to = (struct sockaddr *) &nd->addr;
 	int ret;
 
-	msg.magic = htonl(FIO_LINK_CLOSE_MAGIC);
+	msg.magic = htonl(FIO_LINK_OPEN_CLOSE_MAGIC);
 	msg.cmd = htonl(FIO_LINK_CLOSE);
 
 	ret = sendto(f->fd, &msg, sizeof(msg), MSG_WAITALL, to,
@@ -546,11 +569,98 @@ static int fio_netio_close_file(struct thread_data *td, struct fio_file *f)
 	return generic_close_file(td, f);
 }
 
+static int fio_netio_udp_recv_open(struct thread_data *td, struct fio_file *f)
+{
+	struct netio_data *nd = td->io_ops->data;
+	struct udp_close_msg msg;
+	struct sockaddr *to = (struct sockaddr *) &nd->addr;
+	fio_socklen_t len = sizeof(nd->addr);
+	int ret;
+
+	ret = recvfrom(f->fd, &msg, sizeof(msg), MSG_WAITALL, to, &len);
+	if (ret < 0) {
+		td_verror(td, errno, "sendto udp link open");
+		return ret;
+	}
+
+	if (ntohl(msg.magic) != FIO_LINK_OPEN_CLOSE_MAGIC ||
+	    ntohl(msg.cmd) != FIO_LINK_OPEN) {
+		log_err("fio: bad udp open magic %x/%x\n", ntohl(msg.magic),
+								ntohl(msg.cmd));
+		return -1;
+	}
+
+	return 0;
+}
+
+static int fio_netio_udp_send_open(struct thread_data *td, struct fio_file *f)
+{
+	struct netio_data *nd = td->io_ops->data;
+	struct udp_close_msg msg;
+	struct sockaddr *to = (struct sockaddr *) &nd->addr;
+	int ret;
+
+	msg.magic = htonl(FIO_LINK_OPEN_CLOSE_MAGIC);
+	msg.cmd = htonl(FIO_LINK_OPEN);
+
+	ret = sendto(f->fd, &msg, sizeof(msg), MSG_WAITALL, to,
+			sizeof(nd->addr));
+	if (ret < 0) {
+		td_verror(td, errno, "sendto udp link open");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int fio_netio_open_file(struct thread_data *td, struct fio_file *f)
+{
+	int ret;
+	struct netio_options *o = td->eo;
+
+	if (o->listen)
+		ret = fio_netio_accept(td, f);
+	else
+		ret = fio_netio_connect(td, f);
+
+	if (ret) {
+		f->fd = -1;
+		return ret;
+	}
+
+	if (o->proto == FIO_TYPE_UDP) {
+		if (td_write(td))
+			ret = fio_netio_udp_send_open(td, f);
+		else {
+			int state;
+
+			state = td->runstate;
+			td_set_runstate(td, TD_SETTING_UP);
+			ret = fio_netio_udp_recv_open(td, f);
+			td_set_runstate(td, state);
+		}
+	}
+
+	if (ret)
+		fio_netio_close_file(td, f);
+
+	return ret;
+}
+
 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;
 
+	if (!host) {
+		log_err("fio: connect with no host to connect to.\n");
+		if (td_read(td))
+			log_err("fio: did you forget to set 'listen'?\n");
+
+		td_verror(td, EINVAL, "no hostname= set");
+		return 1;
+	}
+
 	nd->addr.sin_family = AF_INET;
 	nd->addr.sin_port = htons(port);
 
@@ -780,6 +890,11 @@ static int fio_netio_setup(struct thread_data *td)
 	return 0;
 }
 
+static void fio_netio_terminate(struct thread_data *td)
+{
+	kill(td->pid, SIGUSR2);
+}
+
 #ifdef FIO_HAVE_SPLICE
 static int fio_netio_setup_splice(struct thread_data *td)
 {
@@ -808,11 +923,12 @@ static struct ioengine_ops ioengine_splice = {
 	.init			= fio_netio_init,
 	.cleanup		= fio_netio_cleanup,
 	.open_file		= fio_netio_open_file,
-	.close_file		= generic_close_file,
+	.close_file		= fio_netio_close_file,
+	.terminate		= fio_netio_terminate,
 	.options		= options,
 	.option_struct_size	= sizeof(struct netio_options),
 	.flags			= FIO_SYNCIO | FIO_DISKLESSIO | FIO_UNIDIR |
-				  FIO_SIGTERM | FIO_PIPEIO,
+				  FIO_PIPEIO,
 };
 #endif
 
@@ -826,10 +942,11 @@ static struct ioengine_ops ioengine_rw = {
 	.cleanup		= fio_netio_cleanup,
 	.open_file		= fio_netio_open_file,
 	.close_file		= fio_netio_close_file,
+	.terminate		= fio_netio_terminate,
 	.options		= options,
 	.option_struct_size	= sizeof(struct netio_options),
 	.flags			= FIO_SYNCIO | FIO_DISKLESSIO | FIO_UNIDIR |
-				  FIO_SIGTERM | FIO_PIPEIO,
+				  FIO_PIPEIO,
 };
 
 static int str_hostname_cb(void *data, const char *input)
diff --git a/engines/windowsaio.c b/engines/windowsaio.c
index ea89969..db75730 100644
--- a/engines/windowsaio.c
+++ b/engines/windowsaio.c
@@ -14,6 +14,8 @@
 
 typedef BOOL (WINAPI *CANCELIOEX)(HANDLE hFile, LPOVERLAPPED lpOverlapped);
 
+int geterrno_from_win_error (DWORD code, int deferrno);
+
 struct fio_overlapped {
 	OVERLAPPED o;
 	struct io_u *io_u;
@@ -48,6 +50,82 @@ static DWORD WINAPI IoCompletionRoutine(LPVOID lpParameter);
 static int fio_windowsaio_init(struct thread_data *td);
 static int fio_windowsaio_open_file(struct thread_data *td, struct fio_file *f);
 static int fio_windowsaio_close_file(struct thread_data fio_unused *td, struct fio_file *f);
+static int win_to_poxix_error(DWORD winerr);
+
+static int win_to_poxix_error(DWORD winerr)
+{
+	switch (winerr)
+	{
+	case ERROR_FILE_NOT_FOUND:		return ENOENT;
+	case ERROR_PATH_NOT_FOUND:		return ENOENT;
+	case ERROR_ACCESS_DENIED:		return EACCES;
+	case ERROR_INVALID_HANDLE:		return EBADF;
+	case ERROR_NOT_ENOUGH_MEMORY:	return ENOMEM;
+	case ERROR_INVALID_DATA:		return EINVAL;
+	case ERROR_OUTOFMEMORY:			return ENOMEM;
+	case ERROR_INVALID_DRIVE:		return ENODEV;
+	case ERROR_NOT_SAME_DEVICE:		return EXDEV;
+	case ERROR_WRITE_PROTECT:		return EROFS;
+	case ERROR_BAD_UNIT:			return ENODEV;
+	case ERROR_SHARING_VIOLATION:	return EACCES;
+	case ERROR_LOCK_VIOLATION:		return EACCES;
+	case ERROR_SHARING_BUFFER_EXCEEDED:	return ENOLCK;
+	case ERROR_HANDLE_DISK_FULL:	return ENOSPC;
+	case ERROR_NOT_SUPPORTED:		return ENOSYS;
+	case ERROR_FILE_EXISTS:			return EEXIST;
+	case ERROR_CANNOT_MAKE:			return EPERM;
+	case ERROR_INVALID_PARAMETER:	return EINVAL;
+	case ERROR_NO_PROC_SLOTS:		return EAGAIN;
+	case ERROR_BROKEN_PIPE:			return EPIPE;
+	case ERROR_OPEN_FAILED:			return EIO;
+	case ERROR_NO_MORE_SEARCH_HANDLES:	return ENFILE;
+	case ERROR_CALL_NOT_IMPLEMENTED:	return ENOSYS;
+	case ERROR_INVALID_NAME:		return ENOENT;
+	case ERROR_WAIT_NO_CHILDREN:	return ECHILD;
+	case ERROR_CHILD_NOT_COMPLETE:	return EBUSY;
+	case ERROR_DIR_NOT_EMPTY:		return ENOTEMPTY;
+	case ERROR_SIGNAL_REFUSED:		return EIO;
+	case ERROR_BAD_PATHNAME:		return ENOENT;
+	case ERROR_SIGNAL_PENDING:		return EBUSY;
+	case ERROR_MAX_THRDS_REACHED:	return EAGAIN;
+	case ERROR_BUSY:				return EBUSY;
+	case ERROR_ALREADY_EXISTS:		return EEXIST;
+	case ERROR_NO_SIGNAL_SENT:		return EIO;
+	case ERROR_FILENAME_EXCED_RANGE:	return EINVAL;
+	case ERROR_META_EXPANSION_TOO_LONG:	return EINVAL;
+	case ERROR_INVALID_SIGNAL_NUMBER:	return EINVAL;
+	case ERROR_THREAD_1_INACTIVE:	return EINVAL;
+	case ERROR_BAD_PIPE:			return EINVAL;
+	case ERROR_PIPE_BUSY:			return EBUSY;
+	case ERROR_NO_DATA:				return EPIPE;
+	case ERROR_MORE_DATA:			return EAGAIN;
+	case ERROR_DIRECTORY:			return ENOTDIR;
+	case ERROR_PIPE_CONNECTED:		return EBUSY;
+	case ERROR_NO_TOKEN:			return EINVAL;
+	case ERROR_PROCESS_ABORTED:		return EFAULT;
+	case ERROR_BAD_DEVICE:			return ENODEV;
+	case ERROR_BAD_USERNAME:		return EINVAL;
+	case ERROR_OPEN_FILES:			return EAGAIN;
+	case ERROR_ACTIVE_CONNECTIONS:	return EAGAIN;
+	case ERROR_DEVICE_IN_USE:		return EAGAIN;
+	case ERROR_INVALID_AT_INTERRUPT_TIME:	return EINTR;
+	case ERROR_IO_DEVICE:			return EIO;
+	case ERROR_NOT_OWNER:			return EPERM;
+	case ERROR_END_OF_MEDIA:		return ENOSPC;
+	case ERROR_EOM_OVERFLOW:		return ENOSPC;
+	case ERROR_BEGINNING_OF_MEDIA:	return ESPIPE;
+	case ERROR_SETMARK_DETECTED:	return ESPIPE;
+	case ERROR_NO_DATA_DETECTED:	return ENOSPC;
+	case ERROR_POSSIBLE_DEADLOCK:	return EDEADLOCK;
+	case ERROR_CRC:					return EIO;
+	case ERROR_NEGATIVE_SEEK:		return EINVAL;
+	case ERROR_DISK_FULL:			return ENOSPC;
+	case ERROR_NOACCESS:			return EFAULT;
+	case ERROR_FILE_INVALID:		return ENXIO;
+	}
+
+	return winerr;
+}
 
 int sync_file_range(int fd, off64_t offset, off64_t nbytes,
 			   unsigned int flags)
@@ -350,7 +428,7 @@ static int fio_windowsaio_queue(struct thread_data *td, struct io_u *io_u)
 	case DDIR_SYNC_FILE_RANGE:
 		success = FlushFileBuffers(io_u->file->hFile);
 		if (!success)
-		    io_u->error = GetLastError();
+		    io_u->error = win_to_poxix_error(GetLastError());
 
 		return FIO_Q_COMPLETED;
 		break;
@@ -368,7 +446,7 @@ static int fio_windowsaio_queue(struct thread_data *td, struct io_u *io_u)
 	if (success || GetLastError() == ERROR_IO_PENDING)
 		rc = FIO_Q_QUEUED;
 	else {
-		io_u->error = GetLastError();
+		io_u->error = win_to_poxix_error(GetLastError());
 		io_u->resid = io_u->xfer_buflen;
 	}
 
@@ -390,7 +468,7 @@ static DWORD WINAPI IoCompletionRoutine(LPVOID lpParameter)
 	wd = ctx->wd;
 
 	do {
-		if (!GetQueuedCompletionStatus(ctx->iocp, &bytes, &ulKey, &ovl, 250))
+		if (!GetQueuedCompletionStatus(ctx->iocp, &bytes, &ulKey, &ovl, 250) && ovl == NULL)
 			continue;
 
 		fov = CONTAINING_RECORD(ovl, struct fio_overlapped, o);
@@ -401,7 +479,7 @@ static DWORD WINAPI IoCompletionRoutine(LPVOID lpParameter)
 			io_u->error = 0;
 		} else {
 			io_u->resid = io_u->xfer_buflen;
-			io_u->error = ovl->Internal;
+			io_u->error = win_to_poxix_error(GetLastError());
 		}
 
 		fov->io_complete = TRUE;
diff --git a/file.h b/file.h
index 38e9d0d..3024c54 100644
--- a/file.h
+++ b/file.h
@@ -6,6 +6,8 @@
 #include "io_ddir.h"
 #include "flist.h"
 #include "lib/zipf.h"
+#include "lib/axmap.h"
+#include "lib/lfsr.h"
 
 /*
  * The type of object we are working on
@@ -108,10 +110,9 @@ struct fio_file {
 	/*
 	 * block map for random io
 	 */
-	unsigned long *file_map;
-	unsigned long num_maps;
-	unsigned long last_free_lookup;
-	unsigned failed_rands;
+	struct axmap *io_axmap;
+
+	struct fio_lfsr lfsr;
 
 	/*
 	 * Used for zipf random distribution
@@ -177,13 +178,11 @@ extern void free_release_files(struct thread_data *);
 
 static inline void fio_file_reset(struct fio_file *f)
 {
-	f->last_free_lookup = 0;
-	f->failed_rands = 0;
 	f->last_pos = f->file_offset;
 	f->last_start = -1ULL;
 	f->file_pos = -1ULL;
-	if (f->file_map)
-		memset(f->file_map, 0, f->num_maps * sizeof(unsigned long));
+	if (f->io_axmap)
+		axmap_reset(f->io_axmap);
 }
 
 #endif
diff --git a/filesetup.c b/filesetup.c
index 4a2383f..ee58a7b 100644
--- a/filesetup.c
+++ b/filesetup.c
@@ -13,6 +13,7 @@
 #include "filehash.h"
 #include "os/os.h"
 #include "hash.h"
+#include "lib/axmap.h"
 
 #ifdef FIO_HAVE_LINUX_FALLOCATE
 #include <linux/falloc.h>
@@ -873,6 +874,9 @@ static int __init_rand_distribution(struct thread_data *td, struct fio_file *f)
 	nranges = (f->real_file_size + range_size - 1) / range_size;
 
 	seed = jhash(f->file_name, strlen(f->file_name), 0) * td->thread_number;
+	if (!td->o.rand_repeatable)
+		seed = td->rand_seeds[4];
+
 	if (td->o.random_distribution == FIO_RAND_DIST_ZIPF)
 		zipf_init(&f->zipf, nranges, td->o.zipf_theta.u.f, seed);
 	else
@@ -901,28 +905,27 @@ static int init_rand_distribution(struct thread_data *td)
 
 int init_random_map(struct thread_data *td)
 {
-	unsigned long long blocks, num_maps;
+	unsigned long long blocks;
 	struct fio_file *f;
 	unsigned int i;
 
 	if (init_rand_distribution(td))
 		return 0;
-	if (td->o.norandommap || !td_random(td))
+	if (!td_random(td))
 		return 0;
 
 	for_each_file(td, f, i) {
 		blocks = (f->real_file_size + td->o.rw_min_bs - 1) /
 				(unsigned long long) td->o.rw_min_bs;
-		num_maps = (blocks + BLOCKS_PER_MAP - 1) /
-				(unsigned long long) BLOCKS_PER_MAP;
-		if (num_maps == (unsigned long) num_maps) {
-			f->file_map = smalloc(num_maps * sizeof(unsigned long));
-			if (f->file_map) {
-				f->num_maps = num_maps;
+		if (td->o.random_generator == FIO_RAND_GEN_LFSR) {
+			if (!lfsr_init(&f->lfsr, blocks))
 				continue;
-			}
-		} else
-			f->file_map = NULL;
+		} else if (!td->o.norandommap) {
+			f->io_axmap = axmap_new(blocks);
+			if (f->io_axmap)
+				continue;
+		} else if (td->o.norandommap)
+			continue;
 
 		if (!td->o.softrandommap) {
 			log_err("fio: failed allocating random map. If running"
@@ -934,7 +937,6 @@ int init_random_map(struct thread_data *td)
 
 		log_info("fio: file %s failed allocating random map. Running "
 			 "job without.\n", f->file_name);
-		f->num_maps = 0;
 	}
 
 	return 0;
@@ -971,8 +973,8 @@ void close_and_free_files(struct thread_data *td)
 
 		sfree(f->file_name);
 		f->file_name = NULL;
-		sfree(f->file_map);
-		f->file_map = NULL;
+		axmap_free(f->io_axmap);
+		f->io_axmap = NULL;
 		sfree(f);
 	}
 
diff --git a/fio.1 b/fio.1
index 8d3fedf..02eafae 100644
--- a/fio.1
+++ b/fio.1
@@ -580,6 +580,32 @@ overrides the first. This may interfere with a given rate setting, if fio is
 asked to limit reads or writes to a certain rate. If that is the case, then
 the distribution may be skewed. Default: 50.
 .TP
+.BI random_distribution \fR=\fPstr:float
+By default, fio will use a completely uniform random distribution when asked
+to perform random IO. Sometimes it is useful to skew the distribution in
+specific ways, ensuring that some parts of the data is more hot than others.
+Fio includes the following distribution models:
+.RS
+.TP
+.B random
+Uniform random distribution
+.TP
+.B zipf
+Zipf distribution
+.TP
+.B pareto
+Pareto distribution
+.TP
+.RE
+.P
+When using a zipf or pareto distribution, an input value is also needed to
+define the access pattern. For zipf, this is the zipf theta. For pareto,
+it's the pareto power. Fio includes a test program, genzipf, that can be
+used visualize what the given input values will yield in terms of hit rates.
+If you wanted to use zipf with a theta of 1.2, you would use
+random_distribution=zipf:1.2 as the option. If a non-uniform model is used,
+fio will disable use of the random map.
+.TP
 .B norandommap
 Normally \fBfio\fR will cover every block of the file when doing random I/O. If
 this parameter is given, a new offset will be chosen without looking at past
diff --git a/fio.h b/fio.h
index 5022cdf..43ad765 100644
--- a/fio.h
+++ b/fio.h
@@ -68,11 +68,23 @@ enum {
 	RW_SEQ_IDENT,
 };
 
+
+enum {
+	TD_F_VER_BACKLOG	= 1,
+	TD_F_TRIM_BACKLOG	= 2,
+	TD_F_READ_IOLOG		= 4,
+	TD_F_REFILL_BUFFERS	= 8,
+	TD_F_SCRAMBLE_BUFFERS	= 16,
+	TD_F_VER_NONE		= 32,
+	TD_F_PROFILE_OPS	= 64,
+};
+
 /*
  * This describes a single thread/process executing a fio job.
  */
 struct thread_data {
 	struct thread_options o;
+	unsigned long flags;
 	void *eo;
 	char verror[FIO_VERROR_SIZE];
 	pthread_t thread;
@@ -348,11 +360,6 @@ static inline void fio_ro_check(struct thread_data *td, struct io_u *io_u)
 	assert(!(io_u->ddir == DDIR_WRITE && !td_write(td)));
 }
 
-#define BLOCKS_PER_MAP		(8 * sizeof(unsigned long))
-#define TO_MAP_BLOCK(f, b)	(b)
-#define RAND_MAP_IDX(f, b)	(TO_MAP_BLOCK(f, b) / BLOCKS_PER_MAP)
-#define RAND_MAP_BIT(f, b)	(TO_MAP_BLOCK(f, b) & (BLOCKS_PER_MAP - 1))
-
 #define REAL_MAX_JOBS		2048
 
 static inline enum error_type_bit td_error_type(enum fio_ddir ddir, int err)
@@ -452,8 +459,8 @@ enum {
 	TD_NOT_CREATED = 0,
 	TD_CREATED,
 	TD_INITIALIZED,
-	TD_SETTING_UP,
 	TD_RAMP,
+	TD_SETTING_UP,
 	TD_RUNNING,
 	TD_PRE_READING,
 	TD_VERIFYING,
@@ -596,4 +603,9 @@ enum {
 	FIO_RAND_DIST_PARETO,
 };
 
+enum {
+	FIO_RAND_GEN_TAUSWORTHE = 0,
+	FIO_RAND_GEN_LFSR,
+};
+
 #endif
diff --git a/gfio.c b/gfio.c
index 185dbd5..65302e6 100644
--- a/gfio.c
+++ b/gfio.c
@@ -1677,8 +1677,11 @@ static void init_ui(int *argc, char **argv[], struct gui *ui)
 	 * Without it, the update that happens in gfio_update_thread_status
 	 * doesn't really happen in a timely fashion, you need expose events
 	 */
+#if !GTK_CHECK_VERSION(2, 24, 0)
 	if (!g_thread_supported())
 		g_thread_init(NULL);
+#endif
+
 	gdk_threads_init();
 
 	gtk_init(argc, argv);
diff --git a/helpers.c b/helpers.c
index 5be45cc..1b4e1d0 100644
--- a/helpers.c
+++ b/helpers.c
@@ -50,3 +50,10 @@ int _weak sync_file_range(int fd, off64_t offset, off64_t nbytes,
 	return -1;
 }
 #endif
+
+#ifndef FIO_HAVE_FADVISE
+int _weak posix_fadvise(int fd, off_t offset, off_t len, int advice)
+{
+	return 0;
+}
+#endif
diff --git a/helpers.h b/helpers.h
index ed2086d..191096b 100644
--- a/helpers.h
+++ b/helpers.h
@@ -15,5 +15,6 @@ extern int _weak inet_aton(const char *cp, struct in_addr *inp);
 extern int _weak clock_gettime(clockid_t clk_id, struct timespec *ts);
 extern int _weak sync_file_range(int fd, off64_t offset, off64_t nbytes,
 					unsigned int flags);
+extern int _weak posix_fadvise(int fd, off_t offset, off_t len, int advice);
 
 #endif /* FIO_HELPERS_H_ */
diff --git a/init.c b/init.c
index bdee8a2..59b4727 100644
--- a/init.c
+++ b/init.c
@@ -9,7 +9,9 @@
 #include <string.h>
 #include <errno.h>
 #include <sys/ipc.h>
+#ifndef FIO_NO_HAVE_SHM_H
 #include <sys/shm.h>
+#endif
 #include <sys/types.h>
 #include <sys/stat.h>
 
@@ -603,6 +605,13 @@ static int fixup_options(struct thread_data *td)
 		td->o.compress_percentage = 0;
 	}
 
+	/*
+	 * Using a non-uniform random distribution excludes usage of
+	 * a random map
+	 */
+	if (td->o.random_distribution != FIO_RAND_DIST_RANDOM)
+		td->o.norandommap = 1;
+
 	return ret;
 }
 
@@ -765,6 +774,24 @@ int ioengine_load(struct thread_data *td)
 	return 0;
 }
 
+static void init_flags(struct thread_data *td)
+{
+	struct thread_options *o = &td->o;
+
+	if (o->verify_backlog)
+		td->flags |= TD_F_VER_BACKLOG;
+	if (o->trim_backlog)
+		td->flags |= TD_F_TRIM_BACKLOG;
+	if (o->read_iolog_file)
+		td->flags |= TD_F_READ_IOLOG;
+	if (o->refill_buffers)
+		td->flags |= TD_F_REFILL_BUFFERS;
+	if (o->scramble_buffers)
+		td->flags |= TD_F_SCRAMBLE_BUFFERS;
+	if (o->verify != VERIFY_NONE)
+		td->flags |= TD_F_VER_NONE;
+}
+
 /*
  * Adds a job to the list of things todo. Sanitizes the various options
  * to make sure we don't have conflicts, and initializes various
@@ -783,6 +810,8 @@ static int add_job(struct thread_data *td, const char *jobname, int job_add_num,
 	if (td == &def_thread)
 		return 0;
 
+	init_flags(td);
+
 	/*
 	 * if we are just dumping the output command line, don't add the job
 	 */
diff --git a/io_ddir.h b/io_ddir.h
index f28f755..8df24b8 100644
--- a/io_ddir.h
+++ b/io_ddir.h
@@ -30,7 +30,7 @@ enum td_ddir {
 #define td_trim(td)		((td)->o.td_ddir & TD_DDIR_TRIM)
 #define td_rw(td)		(((td)->o.td_ddir & TD_DDIR_RW) == TD_DDIR_RW)
 #define td_random(td)		((td)->o.td_ddir & TD_DDIR_RAND)
-#define file_randommap(td, f)	(!(td)->o.norandommap && (f)->file_map)
+#define file_randommap(td, f)	(!(td)->o.norandommap && (f)->io_axmap)
 
 static inline int ddir_sync(enum fio_ddir ddir)
 {
diff --git a/io_u.c b/io_u.c
index dcb56f1..6f1db18 100644
--- a/io_u.c
+++ b/io_u.c
@@ -10,6 +10,7 @@
 #include "verify.h"
 #include "trim.h"
 #include "lib/rand.h"
+#include "lib/axmap.h"
 
 struct io_completion_data {
 	int nr;				/* input */
@@ -20,17 +21,12 @@ struct io_completion_data {
 };
 
 /*
- * The ->file_map[] contains a map of blocks we have or have not done io
+ * The ->io_axmap contains a map of blocks we have or have not done io
  * to yet. Used to make sure we cover the entire range in a fair fashion.
  */
 static int random_map_free(struct fio_file *f, const unsigned long long block)
 {
-	unsigned int idx = RAND_MAP_IDX(f, block);
-	unsigned int bit = RAND_MAP_BIT(f, block);
-
-	dprint(FD_RANDOM, "free: b=%llu, idx=%u, bit=%u\n", block, idx, bit);
-
-	return (f->file_map[idx] & (1UL << bit)) == 0;
+	return !axmap_isset(f->io_axmap, block);
 }
 
 /*
@@ -41,61 +37,16 @@ static void mark_random_map(struct thread_data *td, struct io_u *io_u)
 	unsigned int min_bs = td->o.rw_min_bs;
 	struct fio_file *f = io_u->file;
 	unsigned long long block;
-	unsigned int blocks, nr_blocks;
-	int busy_check;
+	unsigned int nr_blocks;
 
 	block = (io_u->offset - f->file_offset) / (unsigned long long) min_bs;
 	nr_blocks = (io_u->buflen + min_bs - 1) / min_bs;
-	blocks = 0;
-	busy_check = !(io_u->flags & IO_U_F_BUSY_OK);
-
-	while (nr_blocks) {
-		unsigned int idx, bit;
-		unsigned long mask, this_blocks;
-
-		/*
-		 * If we have a mixed random workload, we may
-		 * encounter blocks we already did IO to.
-		 */
-		if (!busy_check) {
-			blocks = nr_blocks;
-			break;
-		}
-		if ((td->o.ddir_seq_nr == 1) && !random_map_free(f, block))
-			break;
-
-		idx = RAND_MAP_IDX(f, block);
-		bit = RAND_MAP_BIT(f, block);
 
-		fio_assert(td, idx < f->num_maps);
-
-		this_blocks = nr_blocks;
-		if (this_blocks + bit > BLOCKS_PER_MAP)
-			this_blocks = BLOCKS_PER_MAP - bit;
-
-		do {
-			if (this_blocks == BLOCKS_PER_MAP)
-				mask = -1UL;
-			else
-				mask = ((1UL << this_blocks) - 1) << bit;
-
-			if (!(f->file_map[idx] & mask))
-				break;
-
-			this_blocks--;
-		} while (this_blocks);
-
-		if (!this_blocks)
-			break;
-
-		f->file_map[idx] |= mask;
-		nr_blocks -= this_blocks;
-		blocks += this_blocks;
-		block += this_blocks;
-	}
+	if (!(io_u->flags & IO_U_F_BUSY_OK))
+		nr_blocks = axmap_set_nr(f->io_axmap, block, nr_blocks);
 
-	if ((blocks * min_bs) < io_u->buflen)
-		io_u->buflen = blocks * min_bs;
+	if ((nr_blocks * min_bs) < io_u->buflen)
+		io_u->buflen = nr_blocks * min_bs;
 }
 
 static unsigned long long last_block(struct thread_data *td, struct fio_file *f,
@@ -123,113 +74,57 @@ static unsigned long long last_block(struct thread_data *td, struct fio_file *f,
 	return max_blocks;
 }
 
-/*
- * Return the next free block in the map.
- */
-static int get_next_free_block(struct thread_data *td, struct fio_file *f,
-			       enum fio_ddir ddir, unsigned long long *b)
-{
-	unsigned long long block, min_bs = td->o.rw_min_bs, lastb;
-	int i;
-
-	lastb = last_block(td, f, ddir);
-	if (!lastb)
-		return 1;
-
-	i = f->last_free_lookup;
-	block = i * BLOCKS_PER_MAP;
-	while (block * min_bs < f->real_file_size &&
-		block * min_bs < f->io_size) {
-		if (f->file_map[i] != -1UL) {
-			block += ffz(f->file_map[i]);
-			if (block > lastb)
-				break;
-			f->last_free_lookup = i;
-			*b = block;
-			return 0;
-		}
-
-		block += BLOCKS_PER_MAP;
-		i++;
-	}
-
-	dprint(FD_IO, "failed finding a free block\n");
-	return 1;
-}
-
 static int __get_next_rand_offset(struct thread_data *td, struct fio_file *f,
 				  enum fio_ddir ddir, unsigned long long *b)
 {
-	unsigned long long rmax, r, lastb;
-	int loops = 5;
+	unsigned long long r;
 
-	lastb = last_block(td, f, ddir);
-	if (!lastb)
-		return 1;
+	if (td->o.random_generator == FIO_RAND_GEN_TAUSWORTHE) {
+		unsigned long long rmax, lastb;
 
-	if (f->failed_rands >= 200)
-		goto ffz;
+		lastb = last_block(td, f, ddir);
+		if (!lastb)
+			return 1;
 
-	rmax = td->o.use_os_rand ? OS_RAND_MAX : FRAND_MAX;
-	do {
-		if (td->o.use_os_rand)
+		rmax = td->o.use_os_rand ? OS_RAND_MAX : FRAND_MAX;
+
+		if (td->o.use_os_rand) {
+			rmax = OS_RAND_MAX;
 			r = os_random_long(&td->random_state);
-		else
+		} else {
+			rmax = FRAND_MAX;
 			r = __rand(&td->__random_state);
-
-		*b = (lastb - 1) * (r / ((unsigned long long) rmax + 1.0));
+		}
 
 		dprint(FD_RANDOM, "off rand %llu\n", r);
 
+		*b = (lastb - 1) * (r / ((unsigned long long) rmax + 1.0));
+	} else {
+		uint64_t off = 0;
 
-		/*
-		 * if we are not maintaining a random map, we are done.
-		 */
-		if (!file_randommap(td, f))
-			goto ret_good;
-
-		/*
-		 * calculate map offset and check if it's free
-		 */
-		if (random_map_free(f, *b))
-			goto ret_good;
-
-		dprint(FD_RANDOM, "get_next_rand_offset: offset %llu busy\n",
-									*b);
-	} while (--loops);
+		if (lfsr_next(&f->lfsr, &off))
+			return 1;
 
-	if (!f->failed_rands++)
-		f->last_free_lookup = 0;
+		*b = off;
+	}
 
 	/*
-	 * we get here, if we didn't suceed in looking up a block. generate
-	 * a random start offset into the filemap, and find the first free
-	 * block from there.
+	 * if we are not maintaining a random map, we are done.
 	 */
-	loops = 10;
-	do {
-		f->last_free_lookup = (f->num_maps - 1) *
-					(r / ((unsigned long long) rmax + 1.0));
-		if (!get_next_free_block(td, f, ddir, b))
-			goto ret;
-
-		if (td->o.use_os_rand)
-			r = os_random_long(&td->random_state);
-		else
-			r = __rand(&td->__random_state);
-	} while (--loops);
+	if (!file_randommap(td, f))
+		goto ret;
 
 	/*
-	 * that didn't work either, try exhaustive search from the start
+	 * calculate map offset and check if it's free
 	 */
-	f->last_free_lookup = 0;
-ffz:
-	if (!get_next_free_block(td, f, ddir, b))
-		return 0;
-	f->last_free_lookup = 0;
-	return get_next_free_block(td, f, ddir, b);
-ret_good:
-	f->failed_rands = 0;
+	if (random_map_free(f, *b))
+		goto ret;
+
+	dprint(FD_RANDOM, "get_next_rand_offset: offset %llu busy\n", *b);
+
+	*b = axmap_next_free(f->io_axmap, *b);
+	if (*b == (uint64_t) -1ULL)
+		return 1;
 ret:
 	return 0;
 }
@@ -347,7 +242,8 @@ static int get_next_block(struct thread_data *td, struct io_u *io_u,
 		else if (b != -1ULL)
 			io_u->offset = b * td->o.ba[ddir];
 		else {
-			log_err("fio: bug in offset generation\n");
+			log_err("fio: bug in offset generation: offset=%llu, b=%llu\n",
+								offset, b);
 			ret = 1;
 		}
 	}
@@ -394,10 +290,12 @@ static int __get_next_offset(struct thread_data *td, struct io_u *io_u)
 
 static int get_next_offset(struct thread_data *td, struct io_u *io_u)
 {
-	struct prof_io_ops *ops = &td->prof_io_ops;
+	if (td->flags & TD_F_PROFILE_OPS) {
+		struct prof_io_ops *ops = &td->prof_io_ops;
 
-	if (ops->fill_io_u_off)
-		return ops->fill_io_u_off(td, io_u);
+		if (ops->fill_io_u_off)
+			return ops->fill_io_u_off(td, io_u);
+	}
 
 	return __get_next_offset(td, io_u);
 }
@@ -472,10 +370,12 @@ static unsigned int __get_next_buflen(struct thread_data *td, struct io_u *io_u)
 
 static unsigned int get_next_buflen(struct thread_data *td, struct io_u *io_u)
 {
-	struct prof_io_ops *ops = &td->prof_io_ops;
+	if (td->flags & TD_F_PROFILE_OPS) {
+		struct prof_io_ops *ops = &td->prof_io_ops;
 
-	if (ops->fill_io_u_size)
-		return ops->fill_io_u_size(td, io_u);
+		if (ops->fill_io_u_size)
+			return ops->fill_io_u_size(td, io_u);
+	}
 
 	return __get_next_buflen(td, io_u);
 }
@@ -1064,10 +964,12 @@ out:
 
 static struct fio_file *get_next_file(struct thread_data *td)
 {
-	struct prof_io_ops *ops = &td->prof_io_ops;
+	if (!(td->flags & TD_F_PROFILE_OPS)) {
+		struct prof_io_ops *ops = &td->prof_io_ops;
 
-	if (ops->get_next_file)
-		return ops->get_next_file(td);
+		if (ops->get_next_file)
+			return ops->get_next_file(td);
+	}
 
 	return __get_next_file(td);
 }
@@ -1144,7 +1046,10 @@ again:
 
 static int check_get_trim(struct thread_data *td, struct io_u *io_u)
 {
-	if (td->o.trim_backlog && td->trim_entries) {
+	if (!(td->flags & TD_F_TRIM_BACKLOG))
+		return 0;
+
+	if (td->trim_entries) {
 		int get_trim = 0;
 
 		if (td->trim_batch) {
@@ -1167,7 +1072,10 @@ static int check_get_trim(struct thread_data *td, struct io_u *io_u)
 
 static int check_get_verify(struct thread_data *td, struct io_u *io_u)
 {
-	if (td->o.verify_backlog && td->io_hist_len) {
+	if (!(td->flags & TD_F_VER_BACKLOG))
+		return 0;
+
+	if (td->io_hist_len) {
 		int get_verify = 0;
 
 		if (td->verify_batch)
@@ -1258,7 +1166,7 @@ struct io_u *get_io_u(struct thread_data *td)
 	/*
 	 * If using an iolog, grab next piece if any available.
 	 */
-	if (td->o.read_iolog_file) {
+	if (td->flags & TD_F_READ_IOLOG) {
 		if (read_iolog_get(td, io_u))
 			goto err_put;
 	} else if (set_io_u_file(td, io_u)) {
@@ -1279,12 +1187,12 @@ struct io_u *get_io_u(struct thread_data *td)
 		f->last_pos = io_u->offset + io_u->buflen;
 
 		if (io_u->ddir == DDIR_WRITE) {
-			if (td->o.refill_buffers) {
+			if (td->flags & TD_F_REFILL_BUFFERS) {
 				io_u_fill_buffer(td, io_u,
 					io_u->xfer_buflen, io_u->xfer_buflen);
-			} else if (td->o.scramble_buffers)
+			} else if (td->flags & TD_F_SCRAMBLE_BUFFERS)
 				do_scramble = 1;
-			if (td->o.verify != VERIFY_NONE) {
+			if (td->flags & TD_F_VER_NONE) {
 				populate_verify_io_u(td, io_u);
 				do_scramble = 0;
 			}
diff --git a/ioengine.h b/ioengine.h
index b4bc22b..1cd08af 100644
--- a/ioengine.h
+++ b/ioengine.h
@@ -6,7 +6,7 @@
 #include "debug.h"
 #include "file.h"
 
-#define FIO_IOOPS_VERSION	13
+#define FIO_IOOPS_VERSION	14
 
 enum {
 	IO_U_F_FREE		= 1 << 0,
@@ -52,12 +52,16 @@ struct io_u {
 	struct timeval start_time;
 	struct timeval issue_time;
 
+	struct fio_file *file;
+	unsigned int flags;
+	enum fio_ddir ddir;
+
 	/*
 	 * Allocated/set buffer and length
 	 */
-	void *buf;
 	unsigned long buflen;
 	unsigned long long offset;
+	void *buf;
 
 	/*
 	 * Initial seed for generating the buffer contents
@@ -80,8 +84,6 @@ struct io_u {
 	unsigned int resid;
 	unsigned int error;
 
-	enum fio_ddir ddir;
-
 	/*
 	 * io engine private data
 	 */
@@ -91,10 +93,6 @@ struct io_u {
 		void *engine_data;
 	};
 
-	unsigned int flags;
-
-	struct fio_file *file;
-
 	struct flist_head list;
 
 	/*
@@ -129,6 +127,7 @@ struct ioengine_ops {
 	int (*open_file)(struct thread_data *, struct fio_file *);
 	int (*close_file)(struct thread_data *, struct fio_file *);
 	int (*get_file_size)(struct thread_data *, struct fio_file *);
+	void (*terminate)(struct thread_data *);
 	int option_struct_size;
 	struct fio_option *options;
 	void *data;
@@ -143,10 +142,9 @@ enum fio_ioengine_flags {
 	FIO_NODISKUTIL  = 1 << 4,	/* diskutil can't handle filename */
 	FIO_UNIDIR	= 1 << 5,	/* engine is uni-directional */
 	FIO_NOIO	= 1 << 6,	/* thread does only pseudo IO */
-	FIO_SIGTERM	= 1 << 7,	/* needs SIGTERM to exit */
-	FIO_PIPEIO	= 1 << 8,	/* input/output no seekable */
-	FIO_BARRIER	= 1 << 9,	/* engine supports barriers */
-	FIO_MEMALIGN	= 1 << 10,	/* engine wants aligned memory */
+	FIO_PIPEIO	= 1 << 7,	/* input/output no seekable */
+	FIO_BARRIER	= 1 << 8,	/* engine supports barriers */
+	FIO_MEMALIGN	= 1 << 9,	/* engine wants aligned memory */
 };
 
 /*
diff --git a/lib/axmap.c b/lib/axmap.c
new file mode 100644
index 0000000..a44e0ec
--- /dev/null
+++ b/lib/axmap.c
@@ -0,0 +1,390 @@
+/*
+ * Bitmap of bitmaps, where each layer is number-of-bits-per-word smaller than
+ * the previous. Hence an 'axmap', since we axe each previous layer into a
+ * much smaller piece. I swear, that is why it's named like that. It has
+ * nothing to do with anything remotely narcissistic.
+ *
+ * A set bit at layer N indicates a full word at layer N-1, and so forth. As
+ * the bitmap becomes progressively more full, checking for existance
+ * becomes cheaper (since fewer layers are walked, making it a lot more
+ * cache friendly) and locating the next free space likewise.
+ *
+ * Axmaps get pretty close to optimal (1 bit per block) space usage, since
+ * layers quickly diminish in size. Doing the size math is straight forward,
+ * since we have log64(blocks) layers of maps. For 20000 blocks, overhead
+ * is roughly 1.9%, or 1.019 bits per block. The number quickly converges
+ * towards 1.0158, or 1.58% of overhead.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "../arch/arch.h"
+#include "axmap.h"
+#include "../smalloc.h"
+#include "../minmax.h"
+
+#if BITS_PER_LONG == 64
+#define UNIT_SHIFT		6
+#elif BITS_PER_LONG == 32
+#define UNIT_SHIFT		5
+#else
+#error "Number of arch bits unknown"
+#endif
+
+#define BLOCKS_PER_UNIT		(1UL << UNIT_SHIFT)
+#define BLOCKS_PER_UNIT_MASK	(BLOCKS_PER_UNIT - 1)
+
+#define firstfree_valid(b)	((b)->first_free != (uint64_t) -1)
+
+struct axmap_level {
+	int level;
+	unsigned long map_size;
+	unsigned long *map;
+};
+
+struct axmap {
+	unsigned int nr_levels;
+	struct axmap_level *levels;
+	uint64_t first_free;
+};
+
+static unsigned long ulog64(unsigned long val, unsigned int log)
+{
+	while (log-- && val)
+		val >>= UNIT_SHIFT;
+
+	return val;
+}
+
+void axmap_reset(struct axmap *axmap)
+{
+	int i;
+
+	for (i = 0; i < axmap->nr_levels; i++) {
+		struct axmap_level *al = &axmap->levels[i];
+
+		memset(al->map, 0, al->map_size * sizeof(unsigned long));
+	}
+}
+
+void axmap_free(struct axmap *axmap)
+{
+	unsigned int i;
+
+	if (!axmap)
+		return;
+
+	for (i = 0; i < axmap->nr_levels; i++)
+		sfree(axmap->levels[i].map);
+
+	sfree(axmap->levels);
+	sfree(axmap);
+}
+
+struct axmap *axmap_new(unsigned long nr_bits)
+{
+	struct axmap *axmap;
+	unsigned int i, levels;
+
+	axmap = smalloc(sizeof(*axmap));
+	if (!axmap)
+		return NULL;
+
+	levels = 1;
+	i = (nr_bits + BLOCKS_PER_UNIT - 1) >> UNIT_SHIFT;
+	while (i > 1) {
+		i = (i + BLOCKS_PER_UNIT - 1) >> UNIT_SHIFT;
+		levels++;
+	}
+
+	axmap->nr_levels = levels;
+	axmap->levels = smalloc(axmap->nr_levels * sizeof(struct axmap_level));
+	axmap->first_free = 0;
+
+	for (i = 0; i < axmap->nr_levels; i++) {
+		struct axmap_level *al = &axmap->levels[i];
+
+		al->level = i;
+		al->map_size = (nr_bits + BLOCKS_PER_UNIT - 1) >> UNIT_SHIFT;
+		al->map = smalloc(al->map_size * sizeof(unsigned long));
+		if (!al->map)
+			goto err;
+
+		nr_bits = (nr_bits + BLOCKS_PER_UNIT - 1) >> UNIT_SHIFT;
+	}
+
+	axmap_reset(axmap);
+	return axmap;
+err:
+	for (i = 0; i < axmap->nr_levels; i++)
+		if (axmap->levels[i].map)
+			sfree(axmap->levels[i].map);
+
+	sfree(axmap->levels);
+	return NULL;
+}
+
+static int axmap_handler(struct axmap *axmap, uint64_t bit_nr,
+			  int (*func)(struct axmap_level *, unsigned long, unsigned int,
+			  void *), void *data)
+{
+	struct axmap_level *al;
+	int i;
+
+	for (i = 0; i < axmap->nr_levels; i++) {
+		unsigned long index = ulog64(bit_nr, i);
+		unsigned long offset = index >> UNIT_SHIFT;
+		unsigned int bit = index & BLOCKS_PER_UNIT_MASK;
+
+		al = &axmap->levels[i];
+
+		if (func(al, offset, bit, data))
+			return 1;
+	}
+
+	return 0;
+}
+
+static int axmap_handler_topdown(struct axmap *axmap, uint64_t bit_nr,
+	int (*func)(struct axmap_level *, unsigned long, unsigned int, void *),
+	void *data)
+{
+	struct axmap_level *al;
+	int i, level = axmap->nr_levels;
+
+	for (i = axmap->nr_levels - 1; i >= 0; i--) {
+		unsigned long index = ulog64(bit_nr, --level);
+		unsigned long offset = index >> UNIT_SHIFT;
+		unsigned int bit = index & BLOCKS_PER_UNIT_MASK;
+
+		al = &axmap->levels[i];
+
+		if (func(al, offset, bit, data))
+			return 1;
+	}
+
+	return 0;
+}
+
+static int axmap_clear_fn(struct axmap_level *al, unsigned long offset,
+			   unsigned int bit, void *unused)
+{
+	if (!(al->map[offset] & (1UL << bit)))
+		return 1;
+
+	al->map[offset] &= ~(1UL << bit);
+	return 0;
+}
+
+void axmap_clear(struct axmap *axmap, uint64_t bit_nr)
+{
+	axmap_handler(axmap, bit_nr, axmap_clear_fn, NULL);
+}
+
+struct axmap_set_data {
+	unsigned int nr_bits;
+	unsigned int set_bits;
+	unsigned int fail_ok;
+};
+
+static unsigned long bit_masks[] = {
+	0x0000000000000000, 0x0000000000000001, 0x0000000000000003, 0x0000000000000007,
+	0x000000000000000f, 0x000000000000001f, 0x000000000000003f, 0x000000000000007f,
+	0x00000000000000ff, 0x00000000000001ff, 0x00000000000003ff, 0x00000000000007ff,
+	0x0000000000000fff, 0x0000000000001fff, 0x0000000000003fff, 0x0000000000007fff,
+	0x000000000000ffff, 0x000000000001ffff, 0x000000000003ffff, 0x000000000007ffff,
+	0x00000000000fffff, 0x00000000001fffff, 0x00000000003fffff, 0x00000000007fffff,
+	0x0000000000ffffff, 0x0000000001ffffff, 0x0000000003ffffff, 0x0000000007ffffff,
+	0x000000000fffffff, 0x000000001fffffff, 0x000000003fffffff, 0x000000007fffffff,
+	0x00000000ffffffff,
+#if BITS_PER_LONG == 64
+	0x00000001ffffffff, 0x00000003ffffffff, 0x00000007ffffffff, 0x0000000fffffffff,
+	0x0000001fffffffff, 0x0000003fffffffff, 0x0000007fffffffff, 0x000000ffffffffff,
+	0x000001ffffffffff, 0x000003ffffffffff, 0x000007ffffffffff, 0x00000fffffffffff,
+	0x00001fffffffffff, 0x00003fffffffffff, 0x00007fffffffffff, 0x0000ffffffffffff,
+	0x0001ffffffffffff, 0x0003ffffffffffff, 0x0007ffffffffffff, 0x000fffffffffffff,
+	0x001fffffffffffff, 0x003fffffffffffff, 0x007fffffffffffff, 0x00ffffffffffffff,
+	0x01ffffffffffffff, 0x03ffffffffffffff, 0x07ffffffffffffff, 0x0fffffffffffffff,
+	0x1fffffffffffffff, 0x3fffffffffffffff, 0x7fffffffffffffff, 0xffffffffffffffff
+#endif
+};
+
+static int axmap_set_fn(struct axmap_level *al, unsigned long offset,
+			 unsigned int bit, void *__data)
+{
+	struct axmap_set_data *data = __data;
+	unsigned long mask, overlap;
+	unsigned int nr_bits;
+
+	nr_bits = min(data->nr_bits, BLOCKS_PER_UNIT - bit);
+
+	mask = bit_masks[nr_bits] << bit;
+
+	/*
+	 * Mask off any potential overlap, only sets contig regions
+	 */
+	overlap = al->map[offset] & mask;
+	if (overlap == mask) {
+		assert(data->fail_ok);
+		return 1;
+	}
+
+	while (overlap) {
+		unsigned long clear_mask = ~(1UL << ffz(~overlap));
+
+		mask &= clear_mask;
+		overlap &= clear_mask;
+		nr_bits--;
+	}
+
+	assert(mask);
+	assert(!(al->map[offset] & mask));
+		
+	al->map[offset] |= mask;
+
+	if (!al->level)
+		data->set_bits = nr_bits;
+
+	data->nr_bits = 1;
+	return al->map[offset] != -1UL;
+}
+
+static void __axmap_set(struct axmap *axmap, uint64_t bit_nr,
+			 struct axmap_set_data *data)
+{
+	unsigned int set_bits, nr_bits = data->nr_bits;
+
+	if (axmap->first_free >= bit_nr &&
+	    axmap->first_free < bit_nr + data->nr_bits)
+		axmap->first_free = -1ULL;
+
+	set_bits = 0;
+	while (nr_bits) {
+		axmap_handler(axmap, bit_nr, axmap_set_fn, data);
+		set_bits += data->set_bits;
+
+		if (data->set_bits != (BLOCKS_PER_UNIT - nr_bits))
+			break;
+
+		nr_bits -= data->set_bits;
+		bit_nr += data->set_bits;
+
+		data->nr_bits = nr_bits;
+		data->fail_ok = 1;
+	}
+
+	data->set_bits = set_bits;
+}
+
+void axmap_set(struct axmap *axmap, uint64_t bit_nr)
+{
+	struct axmap_set_data data = { .nr_bits = 1, };
+
+	__axmap_set(axmap, bit_nr, &data);
+}
+
+unsigned int axmap_set_nr(struct axmap *axmap, uint64_t bit_nr, unsigned int nr_bits)
+{
+	struct axmap_set_data data = { .nr_bits = nr_bits, };
+
+	__axmap_set(axmap, bit_nr, &data);
+	return data.set_bits;
+}
+
+static int axmap_isset_fn(struct axmap_level *al, unsigned long offset,
+			    unsigned int bit, void *unused)
+{
+	return (al->map[offset] & (1UL << bit)) != 0;
+}
+
+int axmap_isset(struct axmap *axmap, uint64_t bit_nr)
+{
+	return axmap_handler_topdown(axmap, bit_nr, axmap_isset_fn, NULL);
+}
+
+static uint64_t axmap_find_first_free(struct axmap *axmap, unsigned int level,
+				       uint64_t index)
+{
+	unsigned long j;
+	int i;
+
+	/*
+	 * Start at the bottom, then converge towards first free bit at the top
+	 */
+	for (i = level; i >= 0; i--) {
+		struct axmap_level *al = &axmap->levels[i];
+
+		if (index >= al->map_size) {
+			index = -1ULL;
+			break;
+		}
+
+		for (j = index; j < al->map_size; j++) {
+			if (al->map[j] == -1UL)
+				continue;
+
+			/*
+			 * First free bit here is our index into the first
+			 * free bit at the next higher level
+			 */
+			index = (j << UNIT_SHIFT) + ffz(al->map[j]);
+			break;
+		}
+	}
+
+	return index;
+}
+
+uint64_t axmap_first_free(struct axmap *axmap)
+{
+	if (firstfree_valid(axmap))
+		return axmap->first_free;
+
+	axmap->first_free = axmap_find_first_free(axmap, axmap->nr_levels - 1, 0);
+	return axmap->first_free;
+}
+
+struct axmap_next_free_data {
+	unsigned int level;
+	unsigned long offset;
+	uint64_t bit;
+};
+
+static int axmap_next_free_fn(struct axmap_level *al, unsigned long offset,
+			       unsigned int bit, void *__data)
+{
+	struct axmap_next_free_data *data = __data;
+	uint64_t mask = ~((1UL << ((data->bit & BLOCKS_PER_UNIT_MASK) + 1)) - 1);
+
+	if (!(mask & al->map[offset]))
+		return 0;
+
+	if (al->map[offset] != -1UL) {
+		data->level = al->level;
+		data->offset = offset;
+		return 1;
+	}
+
+	data->bit = (data->bit + BLOCKS_PER_UNIT - 1) / BLOCKS_PER_UNIT;
+	return 0;
+}
+
+/*
+ * 'bit_nr' is already set. Find the next free bit after this one.
+ */
+uint64_t axmap_next_free(struct axmap *axmap, uint64_t bit_nr)
+{
+	struct axmap_next_free_data data = { .level = -1U, .bit = bit_nr, };
+
+	if (firstfree_valid(axmap) && bit_nr < axmap->first_free)
+		return axmap->first_free;
+
+	if (!axmap_handler(axmap, bit_nr, axmap_next_free_fn, &data))
+		return axmap_first_free(axmap);
+
+	assert(data.level != -1U);
+
+	return axmap_find_first_free(axmap, data.level, data.offset);
+}
diff --git a/lib/axmap.h b/lib/axmap.h
new file mode 100644
index 0000000..edfeba8
--- /dev/null
+++ b/lib/axmap.h
@@ -0,0 +1,18 @@
+#ifndef FIO_BITMAP_H
+#define FIO_BITMAP_H
+
+#include <inttypes.h>
+
+struct axmap;
+struct axmap *axmap_new(unsigned long nr_bits);
+void axmap_free(struct axmap *bm);
+
+void axmap_clear(struct axmap *axmap, uint64_t bit_nr);
+void axmap_set(struct axmap *axmap, uint64_t bit_nr);
+unsigned int axmap_set_nr(struct axmap *axmap, uint64_t bit_nr, unsigned int nr_bits);
+int axmap_isset(struct axmap *axmap, uint64_t bit_nr);
+uint64_t axmap_first_free(struct axmap *axmap);
+uint64_t axmap_next_free(struct axmap *axmap, uint64_t bit_nr);
+void axmap_reset(struct axmap *axmap);
+
+#endif
diff --git a/lib/lfsr.c b/lib/lfsr.c
new file mode 100644
index 0000000..01c97cb
--- /dev/null
+++ b/lib/lfsr.c
@@ -0,0 +1,269 @@
+#include <stdio.h>
+
+#include "lfsr.h"
+
+/*
+ * From table 3 of
+ *
+ * http://www.xilinx.com/support/documentation/application_notes/xapp052.pdf
+ */
+static struct lfsr_taps lfsr_taps[] = {
+	{
+		.length	= 16,
+		.taps	= { 16, 15, 13, 4, },
+	},
+	{
+		.length = 17,
+		.taps	= { 17, 14, },
+	},
+	{
+		.length = 18,
+		.taps	= { 18, 11, },
+	},
+	{
+		.length	= 19,
+		.taps	= { 19, 6, 2, 1, },
+	},
+	{
+		.length	= 20,
+		.taps	= { 20, 17, },
+	},
+	{
+		.length	= 21,
+		.taps	= { 21, 19, },
+	},
+	{
+		.length	= 22,
+		.taps	= { 22, 21, },
+	},
+	{
+		.length	= 23,
+		.taps	= { 23, 18, },
+	},
+	{
+		.length = 24,
+		.taps	= { 24, 23, 22, 17, },
+	},
+	{
+		.length	= 25,
+		.taps	= { 25, 22, },
+	},
+	{
+		.length	= 26,
+		.taps	= {26, 6, 2, 1, },
+	},
+	{
+		.length	= 27,
+		.taps	= { 27, 5, 2, 1, },
+	},
+	{
+		.length	= 28,
+		.taps	= { 28, 25, },
+	},
+	{
+		.length	= 29,
+		.taps	= {29, 27, },
+	},
+	{
+		.length	= 30,
+		.taps	= { 30, 6, 4, 1, },
+	},
+	{
+		.length	= 31,
+		.taps	= { 31, 28, },
+	},
+	{
+		.length	= 32,
+		.taps	= { 32, 22, 2, 1, },
+	},
+	{
+		.length	= 33,
+		.taps	= { 33, 20, },
+	},
+	{
+		.length	= 34,
+		.taps	= { 34, 27, 2, 1, },
+	},
+	{
+		.length	= 35,
+		.taps	= { 35, 33, },
+	},
+	{
+		.length	= 36,
+		.taps	= { 36, 25, },
+	},
+	{
+		.length	= 37,
+		.taps	= { 37, 5, 4, 3, 2, 1, },
+	},
+	{
+		.length	= 38,
+		.taps	= { 38, 6, 5, 1, },
+	},
+	{
+		.length	= 39,
+		.taps	= { 39, 35, },
+	},
+	{
+		.length	= 40,
+		.taps	= { 40, 38, 21, 19, },
+	},
+	{
+		.length	= 41,
+		.taps	= { 41, 38, },
+	},
+	{
+		.length	= 42,
+		.taps	= { 42, 41, 20, 19, },
+	},
+	{
+		.length	= 43,
+		.taps	= { 43, 42, 38, 37, },
+	},
+	{
+		.length	= 44,
+		.taps	= { 44, 43, 38, 37, },
+	},
+	{
+		.length	= 45,
+		.taps	= { 45, 44, 42, 41, },
+	},
+	{
+		.length	= 46,
+		.taps	= { 46, 45, 26, 25, },
+	},
+	{
+		.length	= 47,
+		.taps	= { 47, 42, },
+	},
+	{
+		.length	= 48,
+		.taps	= { 48, 47, 21, 20, },
+	},
+	{
+		.length	= 49,
+		.taps	= { 49, 40, },
+	},
+	{
+		.length	= 50,
+		.taps	= { 50, 49, 36, 35, },
+	},
+	{
+		.length	= 51,
+		.taps	= { 51, 50, 36, 35, },
+	},
+	{
+		.length	= 52,
+		.taps	= { 52, 49, },
+	},
+	{
+		.length	= 53,
+		.taps	= { 53, 52, 38, 37 },
+	},
+	{
+		.length	= 54,
+		.taps	= { 54, 53, 18, 17 },
+	},
+	{
+		.length	= 55,
+		.taps	= { 55, 31, },
+	},
+	{
+		.length	= 56,
+		.taps	= { 56, 55, 35, 34, },
+	},
+	{
+		.length	= 57,
+		.taps	= { 57, 50, },
+	},
+	{
+		.length = 58,
+		.taps	= { 58, 39, },
+	},
+	{
+		.length	= 59,
+		.taps	= { 59, 58, 38, 37, },
+	},
+	{
+		.length	= 60,
+		.taps	= { 60, 59, },
+	},
+	{
+		.length	= 61,
+		.taps	= { 61, 60, 46, 45, },
+	},
+	{
+		.length	= 62,
+		.taps	= { 62, 61, 6, 5, },
+	},
+	{
+		.length	= 63,
+		.taps	= { 63, 62, },
+	},
+};
+
+#define FIO_LFSR_CRANKS		128
+
+static uint64_t __lfsr_next(uint64_t v, struct lfsr_taps *lt)
+{
+	uint64_t xor_mask = 0;
+	int i;
+
+	for (i = 0; lt->taps[i]; i++)
+		xor_mask ^= (v << (lt->taps[i] - 1));
+
+	xor_mask &= ~(~0UL << 1) << (lt->length - 1);
+	return xor_mask | (v >> 1);
+}
+
+int lfsr_next(struct fio_lfsr *fl, uint64_t *off)
+{
+	if (fl->num_vals > fl->max_val)
+		return 1;
+
+	do {
+		fl->last_val = __lfsr_next(fl->last_val, &fl->taps);
+		if (fl->last_val - 1 <= fl->max_val)
+			break;
+	} while (1);
+
+	*off = fl->last_val - 1;
+	fl->num_vals++;
+	return 0;
+}
+
+static struct lfsr_taps *find_lfsr(uint64_t size)
+{
+	int i;
+
+	for (i = 0; lfsr_taps[i].length; i++)
+		if (((1UL << lfsr_taps[i].length) + FIO_LFSR_CRANKS) >= size)
+			return &lfsr_taps[i];
+
+	return NULL;
+}
+
+int lfsr_init(struct fio_lfsr *fl, uint64_t size)
+{
+	struct lfsr_taps *tap;
+	int i;
+
+	tap = find_lfsr(size);
+	if (!tap)
+		return 1;
+
+	fl->last_val = 1;
+	fl->max_val = size - 1;
+	fl->num_vals = 0;
+	fl->taps.length = tap->length;
+	for (i = 0; i < FIO_MAX_TAPS; i++) {
+		fl->taps.taps[i] = tap->taps[i];
+		if (!fl->taps.taps[i])
+			break;
+	}
+
+	for (i = 0; i < FIO_LFSR_CRANKS; i++)
+		fl->last_val = __lfsr_next(fl->last_val, &fl->taps);
+
+	return 0;
+}
diff --git a/lib/lfsr.h b/lib/lfsr.h
new file mode 100644
index 0000000..0de9ea8
--- /dev/null
+++ b/lib/lfsr.h
@@ -0,0 +1,24 @@
+#ifndef FIO_LFSR_H
+#define FIO_LFSR_H
+
+#include <inttypes.h>
+
+#define FIO_MAX_TAPS	8
+
+struct lfsr_taps {
+	unsigned int length;
+	unsigned int taps[FIO_MAX_TAPS];
+};
+
+
+struct fio_lfsr {
+	uint64_t last_val;
+	uint64_t max_val;
+	uint64_t num_vals;
+	struct lfsr_taps taps;
+};
+
+int lfsr_next(struct fio_lfsr *fl, uint64_t *off);
+int lfsr_init(struct fio_lfsr *fl, uint64_t size);
+
+#endif
diff --git a/libfio.c b/libfio.c
index b48d7c2..5395dd2 100644
--- a/libfio.c
+++ b/libfio.c
@@ -168,6 +168,7 @@ void td_set_runstate(struct thread_data *td, int runstate)
 void fio_terminate_threads(int group_id)
 {
 	struct thread_data *td;
+	pid_t pid = getpid();
 	int i;
 
 	dprint(FD_PROCESS, "terminate group_id=%d\n", group_id);
@@ -182,15 +183,15 @@ void fio_terminate_threads(int group_id)
 			/*
 			 * if the thread is running, just let it exit
 			 */
-			if (!td->pid)
+			if (!td->pid || pid == td->pid)
 				continue;
 			else if (td->runstate < TD_RAMP)
 				kill(td->pid, SIGTERM);
 			else {
 				struct ioengine_ops *ops = td->io_ops;
 
-				if (ops && (ops->flags & FIO_SIGTERM))
-					kill(td->pid, SIGTERM);
+				if (ops && ops->terminate)
+					ops->terminate(td);
 			}
 		}
 	}
diff --git a/memory.c b/memory.c
index f97749b..5293af9 100644
--- a/memory.c
+++ b/memory.c
@@ -5,7 +5,9 @@
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <unistd.h>
+#ifndef FIO_NO_HAVE_SHM_H
 #include <sys/shm.h>
+#endif
 #include <sys/mman.h>
 
 #include "fio.h"
@@ -119,6 +121,13 @@ static int alloc_mem_mmap(struct thread_data *td, size_t total_mem)
 
 	td->mmapfd = 1;
 
+	if (td->o.mem_type == MEM_MMAPHUGE) {
+		unsigned long mask = td->o.hugepage_size - 1;
+
+		flags |= MAP_HUGETLB;
+		total_mem = (total_mem + mask) & ~mask;
+	}
+
 	if (td->o.mmapfile) {
 		td->mmapfd = open(td->o.mmapfile, O_RDWR|O_CREAT, 0644);
 
diff --git a/options.c b/options.c
index 9ee6060..db02194 100644
--- a/options.c
+++ b/options.c
@@ -1615,6 +1615,23 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
 		.group	= FIO_OPT_G_RANDOM,
 	},
 	{
+		.name	= "random_generator",
+		.type	= FIO_OPT_STR,
+		.off1	= td_var_offset(random_generator),
+		.help	= "Type of random number generator to use",
+		.def	= "tausworthe",
+		.posval	= {
+			  { .ival = "tausworthe",
+			    .oval = FIO_RAND_GEN_TAUSWORTHE,
+			    .help = "Strong Tausworthe generator",
+			  },
+			  { .ival = "lfsr",
+			    .oval = FIO_RAND_GEN_LFSR,
+			    .help = "Variable length LFSR",
+			  },
+		},
+	},
+	{
 		.name	= "random_distribution",
 		.type	= FIO_OPT_STR,
 		.off1	= td_var_offset(random_distribution),
diff --git a/os/os-android.h b/os/os-android.h
new file mode 100644
index 0000000..3da3953
--- /dev/null
+++ b/os/os-android.h
@@ -0,0 +1,234 @@
+#ifndef FIO_OS_ANDROID_H
+#define FIO_OS_ANDROID_H
+
+#define	FIO_OS	os_android
+
+#include <sys/ioctl.h>
+#include <sys/uio.h>
+#include <sys/syscall.h>
+#include <sys/vfs.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sched.h>
+#include <linux/unistd.h>
+#include <linux/major.h>
+#include <endian.h>
+
+#include "indirect.h"
+#include "binject.h"
+#include "../file.h"
+
+#define FIO_HAVE_DISK_UTIL
+#define FIO_HAVE_SPLICE
+#define FIO_HAVE_IOSCHED_SWITCH
+#define FIO_HAVE_ODIRECT
+#define FIO_HAVE_HUGETLB
+#define FIO_HAVE_BLKTRACE
+#define FIO_HAVE_STRSEP
+#define FIO_HAVE_POSIXAIO_FSYNC
+#define FIO_HAVE_PSHARED_MUTEX
+#define FIO_HAVE_CL_SIZE
+#define FIO_HAVE_FDATASYNC
+#define FIO_HAVE_FS_STAT
+#define FIO_HAVE_TRIM
+#define FIO_HAVE_CLOCK_MONOTONIC
+#define FIO_HAVE_GETTID
+#define FIO_USE_GENERIC_INIT_RANDOM_STATE
+#define FIO_HAVE_E4_ENG
+#define FIO_HAVE_BYTEORDER_FUNCS
+
+#define OS_MAP_ANON		MAP_ANONYMOUS
+
+#define posix_madvise   madvise
+#define POSIX_MADV_DONTNEED MADV_DONTNEED
+#define POSIX_MADV_SEQUENTIAL	MADV_SEQUENTIAL
+#define POSIX_MADV_RANDOM	MADV_RANDOM
+#ifdef MADV_REMOVE
+#define FIO_MADV_FREE	MADV_REMOVE
+#endif
+
+
+/*
+ * The Android NDK doesn't currently export <sys/shm.h>, so define the
+ * necessary stuff here.
+ */
+
+#include <linux/shm.h>
+#define SHM_HUGETLB    04000
+
+static inline int shmctl (int __shmid, int __cmd, struct shmid_ds *__buf)
+{
+	return syscall(__NR_shmctl, __shmid, __cmd, __buf);
+}
+
+static inline int shmget (key_t __key, size_t __size, int __shmflg)
+{
+	return syscall(__NR_shmget, __key, __size, __shmflg);
+}
+
+static inline void *shmat (int __shmid, const void *__shmaddr, int __shmflg)
+{
+	return (void *)syscall(__NR_shmat, __shmid, __shmaddr, __shmflg);
+}
+
+static inline int shmdt (const void *__shmaddr)
+{
+	return syscall(__NR_shmctl, __shmaddr);
+}
+
+
+/*
+ * Just check for SPLICE_F_MOVE, if that isn't there, assume the others
+ * aren't either.
+ */
+#ifndef SPLICE_F_MOVE
+#define SPLICE_F_MOVE	(0x01)	/* move pages instead of copying */
+#define SPLICE_F_NONBLOCK (0x02) /* don't block on the pipe splicing (but */
+				 /* we may still block on the fd we splice */
+				 /* from/to, of course */
+#define SPLICE_F_MORE	(0x04)	/* expect more data */
+#define SPLICE_F_GIFT   (0x08)  /* pages passed in are a gift */
+
+static inline int splice(int fdin, loff_t *off_in, int fdout, loff_t *off_out,
+			 size_t len, unsigned int flags)
+{
+	return syscall(__NR_sys_splice, fdin, off_in, fdout, off_out, len, flags);
+}
+
+static inline int tee(int fdin, int fdout, size_t len, unsigned int flags)
+{
+	return syscall(__NR_sys_tee, fdin, fdout, len, flags);
+}
+
+static inline int vmsplice(int fd, const struct iovec *iov,
+			   unsigned long nr_segs, unsigned int flags)
+{
+	return syscall(__NR_sys_vmsplice, fd, iov, nr_segs, flags);
+}
+#endif
+
+#define SPLICE_DEF_SIZE	(64*1024)
+
+#ifndef BLKGETSIZE64
+#define BLKGETSIZE64	_IOR(0x12,114,size_t)
+#endif
+
+#ifndef BLKFLSBUF
+#define BLKFLSBUF	_IO(0x12,97)
+#endif
+
+#ifndef BLKDISCARD
+#define BLKDISCARD	_IO(0x12,119)
+#endif
+
+static inline int blockdev_invalidate_cache(struct fio_file *f)
+{
+	return ioctl(f->fd, BLKFLSBUF);
+}
+
+static inline int blockdev_size(struct fio_file *f, unsigned long long *bytes)
+{
+	if (!ioctl(f->fd, BLKGETSIZE64, bytes))
+		return 0;
+
+	return errno;
+}
+
+static inline unsigned long long os_phys_mem(void)
+{
+	long pagesize, pages;
+
+	pagesize = sysconf(_SC_PAGESIZE);
+	pages = sysconf(_SC_PHYS_PAGES);
+	if (pages == -1 || pagesize == -1)
+		return 0;
+
+	return (unsigned long long) pages * (unsigned long long) pagesize;
+}
+
+typedef struct { unsigned short r[3]; } os_random_state_t;
+
+static inline void os_random_seed(unsigned long seed, os_random_state_t *rs)
+{
+	rs->r[0] = seed & 0xffff;
+	seed >>= 16;
+	rs->r[1] = seed & 0xffff;
+	seed >>= 16;
+	rs->r[2] = seed & 0xffff;
+	seed48(rs->r);
+}
+
+static inline long os_random_long(os_random_state_t *rs)
+{
+	return nrand48(rs->r);
+}
+
+#ifdef O_NOATIME
+#define FIO_O_NOATIME	O_NOATIME
+#else
+#define FIO_O_NOATIME	0
+#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"
+
+static inline int arch_cache_line_size(void)
+{
+	char size[32];
+	int fd, ret;
+
+	fd = open(CACHE_LINE_FILE, O_RDONLY);
+	if (fd < 0)
+		return -1;
+
+	ret = read(fd, size, sizeof(size));
+
+	close(fd);
+
+	if (ret <= 0)
+		return -1;
+	else
+		return atoi(size);
+}
+
+static inline unsigned long long get_fs_size(const char *path)
+{
+	unsigned long long ret;
+	struct statfs s;
+
+	if (statfs(path, &s) < 0)
+		return -1ULL;
+
+	ret = s.f_bsize;
+	ret *= (unsigned long long) s.f_bfree;
+	return ret;
+}
+
+static inline int os_trim(int fd, unsigned long long start,
+			  unsigned long long len)
+{
+	uint64_t range[2];
+
+	range[0] = start;
+	range[1] = len;
+
+	if (!ioctl(fd, BLKDISCARD, range))
+		return 0;
+
+	return errno;
+}
+
+#endif
diff --git a/os/os-linux.h b/os/os-linux.h
index 081f5d6..0d98298 100644
--- a/os/os-linux.h
+++ b/os/os-linux.h
@@ -7,6 +7,7 @@
 #include <sys/uio.h>
 #include <sys/syscall.h>
 #include <sys/vfs.h>
+#include <sys/mman.h>
 #include <unistd.h>
 #include <fcntl.h>
 #include <errno.h>
@@ -47,6 +48,10 @@
 #define FIO_USE_GENERIC_INIT_RANDOM_STATE
 #define FIO_HAVE_E4_ENG
 
+#ifdef MAP_HUGETLB
+#define FIO_HAVE_MMAP_HUGE
+#endif
+
 /*
  * Can only enable this for newer glibcs, or the header and defines are
  * missing
diff --git a/os/os-solaris.h b/os/os-solaris.h
index e7a544e..5efd7ac 100644
--- a/os/os-solaris.h
+++ b/os/os-solaris.h
@@ -24,6 +24,7 @@
 #define FIO_USE_GENERIC_BDEV_SIZE
 #define FIO_USE_GENERIC_INIT_RANDOM_STATE
 #define FIO_HAVE_GETTID
+#define FIO_HAVE_FADVISE
 
 #define OS_MAP_ANON		MAP_ANON
 #define OS_RAND_MAX		2147483648UL
diff --git a/os/os.h b/os/os.h
index e2c188f..8ca507a 100644
--- a/os/os.h
+++ b/os/os.h
@@ -17,11 +17,14 @@ enum {
 	os_netbsd,
 	os_solaris,
 	os_windows,
+	os_android,
 
 	os_nr,
 };
 
-#if defined(__linux__)
+#if defined(__ANDROID__)
+#include "os-android.h"
+#elif defined(__linux__)
 #include "os-linux.h"
 #elif defined(__FreeBSD__)
 #include "os-freebsd.h"
@@ -65,15 +68,11 @@ typedef struct aiocb os_aiocb_t;
 #define OS_MSG_DONTWAIT	MSG_DONTWAIT
 #endif
 
-#ifndef FIO_HAVE_FADVISE
-#define posix_fadvise(fd, off, len, advice)	(0)
-
 #ifndef POSIX_FADV_DONTNEED
 #define POSIX_FADV_DONTNEED	(0)
 #define POSIX_FADV_SEQUENTIAL	(0)
 #define POSIX_FADV_RANDOM	(0)
 #endif
-#endif /* FIO_HAVE_FADVISE */
 
 #ifndef FIO_HAVE_CPU_AFFINITY
 #define fio_setaffinity(pid, mask)	(0)
@@ -95,6 +94,7 @@ typedef unsigned long os_cpu_mask_t;
 
 #ifndef FIO_HAVE_HUGETLB
 #define SHM_HUGETLB			0
+#define MAP_HUGETLB			0
 #ifndef FIO_HUGE_PAGE
 #define FIO_HUGE_PAGE			0
 #endif
@@ -104,6 +104,10 @@ typedef unsigned long os_cpu_mask_t;
 #endif
 #endif
 
+#ifndef FIO_HAVE_MMAP_HUGE
+#define MAP_HUGETLB			0
+#endif
+
 #ifndef FIO_O_NOATIME
 #define FIO_O_NOATIME			0
 #endif
@@ -170,6 +174,7 @@ static inline uint64_t fio_swap64(uint64_t val)
 }
 #endif
 
+#ifndef FIO_HAVE_BYTEORDER_FUNCS
 #ifdef FIO_LITTLE_ENDIAN
 #define __le16_to_cpu(x)		(x)
 #define __le32_to_cpu(x)		(x)
@@ -185,6 +190,7 @@ static inline uint64_t fio_swap64(uint64_t val)
 #define __cpu_to_le32(x)		fio_swap32(x)
 #define __cpu_to_le64(x)		fio_swap64(x)
 #endif
+#endif /* FIO_HAVE_BYTEORDER_FUNCS */
 
 #define le16_to_cpu(val) ({			\
 	uint16_t *__val = &(val);		\
diff --git a/os/windows/examples.wxs b/os/windows/examples.wxs
index f3d6bc3..84f2a86 100755
--- a/os/windows/examples.wxs
+++ b/os/windows/examples.wxs
@@ -14,7 +14,7 @@
                 <Component>
                     <File Source="..\..\examples\fsx" />
                 </Component>
-                <Component >
+                <Component>
                     <File Source="..\..\examples\iometer-file-access-server" />
                 </Component>
                 <Component>
@@ -35,6 +35,21 @@
                 <Component>
                   <File Source="..\..\examples\flow" />
                 </Component>
+                <Component>
+                  <File Source="..\..\examples\cpuio" />
+                </Component>
+                <Component>
+                  <File Source="..\..\examples\falloc" />
+                </Component>
+                <Component>
+                  <File Source="..\..\examples\fusion-aw-sync.ini" />
+                </Component>
+                <Component>
+                  <File Source="..\..\examples\ssd-steadystate.fio" />
+                </Component>
+                <Component>
+                  <File Source="..\..\examples\zipf" />
+                </Component>
         </DirectoryRef>
     </Fragment>
     <Fragment>
@@ -50,6 +65,11 @@
             <ComponentRef Id="tiobench_example" />
             <ComponentRef Id="null" />
             <ComponentRef Id="flow" />
+            <ComponentRef Id="cpuio" />
+            <ComponentRef Id="falloc" />
+            <ComponentRef Id="fusion_aw_sync.ini" />
+            <ComponentRef Id="ssd_steadystate.fio" />
+            <ComponentRef Id="zipf" />
         </ComponentGroup>
     </Fragment>
-</Wix>
\ No newline at end of file
+</Wix>
diff --git a/os/windows/install.wxs b/os/windows/install.wxs
index e02347e..86098b0 100755
--- a/os/windows/install.wxs
+++ b/os/windows/install.wxs
@@ -1,17 +1,17 @@
 <?xml version="1.0" encoding="utf-8"?>
 <Wix xmlns="http://schemas.microsoft.com/wix/2006/wi";>
-	
+
 	<?if $(env.FIO_ARCH) = x86 ?>
 		<?define ProgramDirectory = ProgramFilesFolder ?>
 	<?else?>
 		<?define ProgramDirectory = ProgramFiles64Folder ?>
 	<?endif?>
 
-	<Product Id="2BA394F9-0D9E-4597-BB9D-6B18097D64BB"
+    <Product Id="C3DC8A4F-1191-412F-8287-ACB6BA798F6A"
 	  Codepage="1252" Language="1033"
 	  Manufacturer="fio" Name="fio"
-	  UpgradeCode="2338A332-5511-43cf-b9BD-5C60496CCFCC" Version="2.0.10">
-		<Package 
+	  UpgradeCode="2338A332-5511-43cf-b9BD-5C60496CCFCC" Version="2.0.11">
+		<Package
 		  Comments="Contact: Your local administrator"
 		  Description="Flexible IO Tester"
 		  InstallerVersion="200" Keywords="Installer,MSI,Database"
@@ -57,7 +57,7 @@
 		<ComponentRef Id="COPYING"/>
 		<ComponentGroupRef Id="examples"/>
 	</Feature>
-		
+
 	<Property Id="ARPURLINFOABOUT" Value="http://git.kernel.dk/?p=fio.git"; />
 	<Property Id='ARPCONTACT'>fio@xxxxxxxxxxxxxxx</Property>
 	<Property Id='ARPHELPLINK'>http://www.spinics.net/lists/fio/</Property>
diff --git a/parse.c b/parse.c
index ffe2dc0..92adbe5 100644
--- a/parse.c
+++ b/parse.c
@@ -360,7 +360,7 @@ static int __handle_option(struct fio_option *o, const char *ptr, void *data,
 	long long ull, *ullp;
 	long ul1, ul2;
 	double uf;
-	char **cp;
+	char **cp = NULL;
 	int ret = 0, is_time = 0;
 	const struct value_pair *vp;
 	struct value_pair posval[PARSE_MAX_VP];
@@ -535,8 +535,6 @@ static int __handle_option(struct fio_option *o, const char *ptr, void *data,
 				cp = td_var(data, o->off1);
 
 			*cp = strdup(ptr);
-		} else {
-			cp = NULL;
 		}
 
 		if (fn)
diff --git a/profile.c b/profile.c
index 74f14fa..6a80dec 100644
--- a/profile.c
+++ b/profile.c
@@ -93,8 +93,10 @@ void profile_add_hooks(struct thread_data *td)
 	if (!ops)
 		return;
 
-	if (ops->io_ops)
+	if (ops->io_ops) {
 		td->prof_io_ops = *ops->io_ops;
+		td->flags |= TD_F_PROFILE_OPS;
+	}
 }
 
 int profile_td_init(struct thread_data *td)
diff --git a/t/axmap.c b/t/axmap.c
new file mode 100644
index 0000000..1f8c3e9
--- /dev/null
+++ b/t/axmap.c
@@ -0,0 +1,46 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+#include <inttypes.h>
+
+#include "../lib/lfsr.h"
+
+struct axmap;
+void axmap_set(struct axmap *, uint64_t);
+struct axmap *axmap_new(uint64_t size);
+
+void *smalloc(size_t size)
+{
+	return malloc(size);
+}
+
+void sfree(void *ptr)
+{
+	free(ptr);
+}
+
+int main(int argc, char *argv[])
+{
+	struct fio_lfsr lfsr;
+	size_t size = (1UL << 28) - 200;
+	struct axmap *map;
+
+	if (argc > 1)
+		size = strtoul(argv[1], NULL, 10);
+
+	printf("Using %llu entries\n", (unsigned long long) size);
+
+	lfsr_init(&lfsr, size);
+	map = axmap_new(size);
+
+	while (size--) {
+		uint64_t val;
+
+		lfsr_next(&lfsr, &val);
+		axmap_set(map, val);
+	}
+
+	return 0;
+}
diff --git a/t/genzipf.c b/t/genzipf.c
index 3cc93e6..c5f098c 100644
--- a/t/genzipf.c
+++ b/t/genzipf.c
@@ -78,13 +78,29 @@ static struct node *hash_insert(struct node *n, unsigned long long val)
 	return n;
 }
 
+static void usage(void)
+{
+	printf("genzipf: test zipf/pareto values for fio input\n");
+	printf("\t-h\tThis help screen\n");
+	printf("\t-p\tGenerate size of data set that are hit by this percentage\n");
+	printf("\t-t\tDistribution type (zipf or pareto)\n");
+	printf("\t-i\tDistribution algorithm input (zipf theta or pareto power)\n");
+	printf("\t-b\tBlock size of a given range (in bytes)\n");
+	printf("\t-g\tSize of data set (in gigabytes)\n");
+	printf("\t-o\tNumber of output columns\n");
+	printf("\t-c\tOutput ranges in CSV format\n");
+}
+
 static int parse_options(int argc, char *argv[])
 {
-	const char *optstring = "t:g:i:o:b:p:c";
+	const char *optstring = "t:g:i:o:b:p:ch";
 	int c, dist_val_set = 0;
 
 	while ((c = getopt(argc, argv, optstring)) != -1) {
 		switch (c) {
+		case 'h':
+			usage();
+			return 1;
 		case 'p':
 			percentage = atof(optarg);
 			break;
diff --git a/thread_options.h b/thread_options.h
index 9975af1..5354473 100644
--- a/thread_options.h
+++ b/thread_options.h
@@ -123,6 +123,8 @@ struct thread_options {
 	fio_fp64_t zipf_theta;
 	fio_fp64_t pareto_h;
 
+	unsigned int random_generator;
+
 	unsigned int hugepage_size;
 	unsigned int rw_min_bs;
 	unsigned int thinktime;
@@ -326,6 +328,8 @@ struct thread_options_pack {
 	fio_fp64_t zipf_theta;
 	fio_fp64_t pareto_h;
 
+	uint32_t random_generator;
+
 	uint32_t hugepage_size;
 	uint32_t rw_min_bs;
 	uint32_t thinktime;
diff --git a/verify.c b/verify.c
index 01b56fd..85fc448 100644
--- a/verify.c
+++ b/verify.c
@@ -690,6 +690,7 @@ int verify_io_u(struct thread_data *td, struct io_u *io_u)
 			.hdr_num	= hdr_num,
 			.td		= td,
 		};
+		unsigned int verify_type;
 
 		if (ret && td->o.verify_fatal)
 			break;
@@ -708,7 +709,12 @@ int verify_io_u(struct thread_data *td, struct io_u *io_u)
 			return EILSEQ;
 		}
 
-		switch (hdr->verify_type) {
+		if (td->o.verify != VERIFY_NONE)
+			verify_type = td->o.verify;
+		else
+			verify_type = hdr->verify_type;
+
+		switch (verify_type) {
 		case VERIFY_MD5:
 			ret = verify_io_u_md5(hdr, &vc);
 			break;
@@ -747,6 +753,10 @@ int verify_io_u(struct thread_data *td, struct io_u *io_u)
 			log_err("Bad verify type %u\n", hdr->verify_type);
 			ret = EINVAL;
 		}
+
+		if (ret && verify_type != hdr->verify_type)
+			log_err("fio: verify type mismatch (%u media, %u given)\n",
+					hdr->verify_type, verify_type);
 	}
 
 done:
--
To unsubscribe from this list: send the line "unsubscribe fio" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Kernel]     [Linux SCSI]     [Linux IDE]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux SCSI]

  Powered by Linux