Merge branch 'master' into gfio

Conflicts:
	Makefile
	configure
	fio.h

Signed-off-by: Jens Axboe <axboe@kernel.dk>
diff --git a/Makefile b/Makefile
index a14d1f3..aff4fba 100644
--- a/Makefile
+++ b/Makefile
@@ -164,6 +164,7 @@
 ifneq ($(findstring $(MAKEFLAGS),s),s)
 ifndef V
 	QUIET_CC	= @echo '   ' CC $@;
+	QUIET_LINK	= @echo '   ' LINK $@;
 	QUIET_DEP	= @echo '   ' DEP $@;
 endif
 endif
@@ -189,7 +190,7 @@
 
 override CFLAGS += -DFIO_VERSION='"$(FIO_VERSION)"'
 
-.c.o: FORCE
+.c.o: FORCE FIO-VERSION-FILE
 	$(QUIET_CC)$(CC) -o $@ $(CFLAGS) $(CPPFLAGS) -c $<
 	@$(CC) -MM $(CFLAGS) $(CPPFLAGS) $*.c > $*.d
 	@mv -f $*.d $*.d.tmp
@@ -198,7 +199,7 @@
 		sed -e 's/^ *//' -e 's/$$/:/' >> $*.d
 	@rm -f $*.d.tmp
 
-init.o: FIO-VERSION-FILE
+init.o: FIO-VERSION-FILE init.c
 	$(QUIET_CC)$(CC) -o init.o $(CFLAGS) $(CPPFLAGS) -c init.c
 
 gcompat.o: gcompat.c gcompat.h
@@ -229,22 +230,22 @@
 	$(QUIET_CC)$(CC) $(CFLAGS) $(GTK_CFLAGS) $(CPPFLAGS) -c printing.c
 
 t/stest: $(T_SMALLOC_OBJS)
-	$(QUIET_CC)$(CC) $(LDFLAGS) $(CFLAGS) -o $@ $(T_SMALLOC_OBJS) $(LIBS) $(LDFLAGS)
+	$(QUIET_LINK)$(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)
+	$(QUIET_LINK)$(CC) $(LDFLAGS) $(CFLAGS) -o $@ $(T_IEEE_OBJS) $(LIBS) $(LDFLAGS)
 
 fio: $(FIO_OBJS)
-	$(QUIET_CC)$(CC) $(LDFLAGS) $(CFLAGS) -o $@ $(FIO_OBJS) $(LIBS) $(LDFLAGS)
+	$(QUIET_LINK)$(CC) $(LDFLAGS) $(CFLAGS) -o $@ $(FIO_OBJS) $(LIBS) $(LDFLAGS)
 
 gfio: $(GFIO_OBJS)
-	$(QUIET_CC)$(CC) $(LIBS) -o gfio $(GFIO_OBJS) $(LIBS) $(GTK_LDFLAGS)
+	$(QUIET_LINK)$(CC) $(LIBS) -o gfio $(GFIO_OBJS) $(LIBS) $(GTK_LDFLAGS)
 
 t/genzipf: $(T_ZIPF_OBJS)
-	$(QUIET_CC)$(CC) $(LDFLAGS) $(CFLAGS) -o $@ $(T_ZIPF_OBJS) $(LIBS) $(LDFLAGS)
+	$(QUIET_LINK)$(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)
+	$(QUIET_LINK)$(CC) $(LDFLAGS) $(CFLAGS) -o $@ $(T_AXMAP_OBJS) $(LIBS) $(LDFLAGS)
 
 clean: FORCE
 	-rm -f .depend $(GFIO_OBJS) $(OBJS) $(T_OBJS) $(PROGS) $(T_PROGS) core.* core gfio FIO-VERSION-FILE config-host.mak cscope.out *.d
diff --git a/README b/README
index 4c7b542..4173237 100644
--- a/README
+++ b/README
@@ -125,6 +125,9 @@
  5. Run 'make clean'.
  6. Run 'make'.
 
+To build fio on 32-bit Windows, download x86/pthreadGC2.dll instead and do
+'./configure --build-32bit-win=yes' before 'make'.
+
 
 Command line
 ------------
diff --git a/cconv.c b/cconv.c
index e2548a8..5d575d3 100644
--- a/cconv.c
+++ b/cconv.c
@@ -182,6 +182,7 @@
 	o->trim_batch = le32_to_cpu(top->trim_batch);
 	o->trim_zero = le32_to_cpu(top->trim_zero);
 	o->clat_percentiles = le32_to_cpu(top->clat_percentiles);
+	o->percentile_precision = le32_to_cpu(top->percentile_precision);
 	o->continue_on_error = le32_to_cpu(top->continue_on_error);
 	o->cgroup_weight = le32_to_cpu(top->cgroup_weight);
 	o->cgroup_nodelete = le32_to_cpu(top->cgroup_nodelete);
@@ -327,6 +328,7 @@
 	top->trim_batch = cpu_to_le32(o->trim_batch);
 	top->trim_zero = cpu_to_le32(o->trim_zero);
 	top->clat_percentiles = cpu_to_le32(o->clat_percentiles);
+	top->percentile_precision = cpu_to_le32(o->percentile_precision);
 	top->continue_on_error = cpu_to_le32(o->continue_on_error);
 	top->cgroup_weight = cpu_to_le32(o->cgroup_weight);
 	top->cgroup_nodelete = cpu_to_le32(o->cgroup_nodelete);
diff --git a/configure b/configure
index cab3da8..1c8b2bb 100755
--- a/configure
+++ b/configure
@@ -137,9 +137,11 @@
   --extra-cflags=*)
   CFLAGS="$CFLAGS $optarg"
   ;;
+  --build-32bit-win=*) build_32bit_win="$optarg"
+  ;;
   --enable-gfio)
-    gfio="yes"
-    ;;
+  gfio="yes"
+  ;;
   --help)
     show_help="yes"
     ;;
@@ -153,6 +155,7 @@
 if test "$show_help" = "yes" ; then
   echo "--cc=                  Specify compiler to use"
   echo "--extra-cflags=        Specify extra CFLAGS to pass to compiler"
+  echo "--build-32bit-win=     Specify yes for a 32-bit build on Windows"
   echo "--enable-gfio          Enable building of gtk gfio"
   exit $exit_val
 fi
@@ -191,13 +194,20 @@
 CYGWIN*)
   echo "Forcing known good options on Windows"
   if test -z "$CC" ; then
-    CC="x86_64-w64-mingw32-gcc"
+    if test ! -z "$build_32bit_win" && test "$build_32bit_win" = "yes"; then
+      CC="i686-w64-mingw32-gcc"
+    else
+      CC="x86_64-w64-mingw32-gcc"
+    fi
   fi
   output_sym "CONFIG_LITTLE_ENDIAN"
-  output_sym "CONFIG_64BIT_LLP64"
+  if test ! -z "$build_32bit_win" && test "$build_32bit_win" = "yes"; then
+    output_sym "CONFIG_32BIT"
+  else
+    output_sym "CONFIG_64BIT_LLP64"
+  fi
   output_sym "CONFIG_FADVISE"
   output_sym "CONFIG_SOCKLEN_T"
-  output_sym "CONFIG_POSIX_FALLOCATE"
   output_sym "CONFIG_FADVISE"
   output_sym "CONFIG_SFAA"
   output_sym "CONFIG_RUSAGE_THREAD"
diff --git a/engines/windowsaio.c b/engines/windowsaio.c
index 773f027..3a24fa7 100644
--- a/engines/windowsaio.c
+++ b/engines/windowsaio.c
@@ -1,6 +1,7 @@
 /*
- * Native Windows async IO engine
- * Copyright (C) 2012 Bruce Cran <bruce@cran.org.uk>
+ * windowsaio engine
+ *
+ * IO engine using Windows IO Completion Ports.
  */
 
 #include <stdio.h>
@@ -37,94 +38,18 @@
 };
 
 static int fio_windowsaio_cancel(struct thread_data *td,
-			       struct io_u *io_u);
+				   struct io_u *io_u);
 static BOOL timeout_expired(DWORD start_count, DWORD end_count);
 static int fio_windowsaio_getevents(struct thread_data *td, unsigned int min,
-				    unsigned int max, struct timespec *t);
+					unsigned int max, struct timespec *t);
 static struct io_u *fio_windowsaio_event(struct thread_data *td, int event);
 static int fio_windowsaio_queue(struct thread_data *td,
-			      struct io_u *io_u);
+				  struct io_u *io_u);
 static void fio_windowsaio_cleanup(struct thread_data *td);
 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_posix_error(DWORD winerr);
-
-static int win_to_posix_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;
-}
 
 static int fio_windowsaio_init(struct thread_data *td)
 {
@@ -132,23 +57,27 @@
 	HANDLE hKernel32Dll;
 	int rc = 0;
 
-	wd = malloc(sizeof(struct windowsaio_data));
-	if (wd != NULL)
-		ZeroMemory(wd, sizeof(struct windowsaio_data));
-	else
+	wd = calloc(1, sizeof(struct windowsaio_data));
+	if (wd == NULL) {
+		 log_err("windowsaio: failed to allocate memory for engine data\n");
 		rc = 1;
+	}
 
 	if (!rc) {
 		wd->aio_events = malloc(td->o.iodepth * sizeof(struct io_u*));
-		if (wd->aio_events == NULL)
+		if (wd->aio_events == NULL) {
+			log_err("windowsaio: failed to allocate memory for aio events list\n");
 			rc = 1;
+		}
 	}
 
 	if (!rc) {
 		/* Create an auto-reset event */
 		wd->iocomplete_event = CreateEvent(NULL, FALSE, FALSE, NULL);
-		if (wd->iocomplete_event == NULL)
+		if (wd->iocomplete_event == NULL) {
+			log_err("windowsaio: failed to create io complete event handle\n");
 			rc = 1;
+		}
 	}
 
 	if (rc) {
@@ -164,15 +93,16 @@
 	wd->pCancelIoEx = (CANCELIOEX)GetProcAddress(hKernel32Dll, "CancelIoEx");
 	td->io_ops->data = wd;
 
-
 	if (!rc) {
 		struct thread_ctx *ctx;
 		struct windowsaio_data *wd;
 		HANDLE hFile;
 
 		hFile = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
-		if (hFile == INVALID_HANDLE_VALUE)
+		if (hFile == INVALID_HANDLE_VALUE) {
+			log_err("windowsaio: failed to create io completion port\n");
 			rc = 1;
+		}
 
 		wd = td->io_ops->data;
 		wd->iothread_running = TRUE;
@@ -183,7 +113,7 @@
 
 		if (!rc && ctx == NULL)
 		{
-			log_err("fio: out of memory in windowsaio\n");
+			log_err("windowsaio: failed to allocate memory for thread context structure\n");
 			CloseHandle(hFile);
 			rc = 1;
 		}
@@ -193,6 +123,8 @@
 			ctx->iocp = hFile;
 			ctx->wd = wd;
 			wd->iothread = CreateThread(NULL, 0, IoCompletionRoutine, ctx, 0, NULL);
+			if (wd->iothread == NULL)
+				log_err("windowsaio: failed to create io completion thread\n");
 		}
 
 		if (rc || wd->iothread == NULL)
@@ -234,12 +166,12 @@
 	dprint(FD_FILE, "fd open %s\n", f->file_name);
 
 	if (f->filetype == FIO_TYPE_PIPE) {
-		log_err("fio: windowsaio doesn't support pipes\n");
+		log_err("windowsaio: pipes are not supported\n");
 		return 1;
 	}
 
 	if (!strcmp(f->file_name, "-")) {
-		log_err("fio: can't read/write to stdin/out\n");
+		log_err("windowsaio: can't read/write to stdin/out\n");
 		return 1;
 	}
 
@@ -271,8 +203,10 @@
 	f->hFile = CreateFile(f->file_name, access, sharemode,
 		NULL, openmode, flags, NULL);
 
-	if (f->hFile == INVALID_HANDLE_VALUE)
+	if (f->hFile == INVALID_HANDLE_VALUE) {
+		log_err("windowsaio: failed to open file \"%s\"\n", f->file_name);
 		rc = 1;
+	}
 
 	/* Only set up the completion port and thread if we're not just
 	 * querying the device size */
@@ -281,8 +215,10 @@
 
 		wd = td->io_ops->data;
 
-		if (CreateIoCompletionPort(f->hFile, wd->iocp, 0, 0) == NULL)
+		if (CreateIoCompletionPort(f->hFile, wd->iocp, 0, 0) == NULL) {
+			log_err("windowsaio: failed to create io completion port\n");
 			rc = 1;
+		}
 	}
 
 	return rc;
@@ -295,8 +231,10 @@
 	dprint(FD_FILE, "fd close %s\n", f->file_name);
 
 	if (f->hFile != INVALID_HANDLE_VALUE) {
-		if (!CloseHandle(f->hFile))
+		if (!CloseHandle(f->hFile)) {
+			log_info("windowsaio: failed to close file handle for \"%s\"\n", f->file_name);
 			rc = 1;
+		}
 	}
 
 	f->hFile = INVALID_HANDLE_VALUE;
@@ -325,7 +263,7 @@
 }
 
 static int fio_windowsaio_getevents(struct thread_data *td, unsigned int min,
-				    unsigned int max, struct timespec *t)
+					unsigned int max, struct timespec *t)
 {
 	struct windowsaio_data *wd = td->io_ops->data;
 	struct flist_head *entry;
@@ -362,7 +300,7 @@
 		if (dequeued < min) {
 			status = WaitForSingleObject(wd->iocomplete_event, mswait);
 			if (status != WAIT_OBJECT_0 && dequeued >= min)
-			    break;
+				break;
 		}
 
 		if (dequeued >= min || (t != NULL && timeout_expired(start_count, end_count)))
@@ -398,13 +336,15 @@
 	case DDIR_DATASYNC:
 	case DDIR_SYNC_FILE_RANGE:
 		success = FlushFileBuffers(io_u->file->hFile);
-		if (!success)
-		    io_u->error = win_to_posix_error(GetLastError());
+		if (!success) {
+			log_err("windowsaio: failed to flush file buffers\n");
+			io_u->error = win_to_posix_error(GetLastError());
+		}
 
 		return FIO_Q_COMPLETED;
 		break;
 	case DDIR_TRIM:
-		log_err("manual TRIM isn't supported on Windows");
+		log_err("windowsaio: manual TRIM isn't supported on Windows\n");
 		io_u->error = 1;
 		io_u->resid = io_u->xfer_buflen;
 		return FIO_Q_COMPLETED;
@@ -463,7 +403,7 @@
 }
 
 static int fio_windowsaio_cancel(struct thread_data *td,
-			       struct io_u *io_u)
+				   struct io_u *io_u)
 {
 	int rc = 0;
 
@@ -473,8 +413,10 @@
 	if (wd->pCancelIoEx != NULL) {
 		struct fio_overlapped *ovl = io_u->engine_data;
 
-		if (!wd->pCancelIoEx(io_u->file->hFile, &ovl->o))
+		if (!wd->pCancelIoEx(io_u->file->hFile, &ovl->o)) {
+			log_err("windowsaio: failed to cancel io\n");
 			rc = 1;
+		}
 	} else
 		rc = 1;
 
@@ -500,7 +442,8 @@
 	o->io_complete = FALSE;
 	o->io_u = io_u;
 	o->o.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
-	if (!o->o.hEvent) {
+	if (o->o.hEvent == NULL) {
+		log_err("windowsaio: failed to create event handle\n");
 		free(o);
 		return 1;
 	}
@@ -525,12 +468,12 @@
 	.io_u_free	= fio_windowsaio_io_u_free,
 };
 
-static void fio_init fio_posixaio_register(void)
+static void fio_init fio_windowsaio_register(void)
 {
 	register_ioengine(&ioengine);
 }
 
-static void fio_exit fio_posixaio_unregister(void)
+static void fio_exit fio_windowsaio_unregister(void)
 {
 	unregister_ioengine(&ioengine);
 }
diff --git a/eta.c b/eta.c
index 6f897a4..a724fe6 100644
--- a/eta.c
+++ b/eta.c
@@ -139,6 +139,15 @@
 		bytes_total = td->fill_device_size;
 	}
 
+	if (td->o.zone_size && td->o.zone_skip && bytes_total) {
+		unsigned int nr_zones;
+		uint64_t zone_bytes;
+
+		zone_bytes = bytes_total + td->o.zone_size + td->o.zone_skip;
+		nr_zones = (zone_bytes - 1) / (td->o.zone_size + td->o.zone_skip);
+		bytes_total -= nr_zones * td->o.zone_skip;
+	}
+
 	/*
 	 * if writing and verifying afterwards, bytes_total will be twice the
 	 * size. In a mixed workload, verify phase will be the size of the
@@ -156,9 +165,6 @@
 			bytes_total <<= 1;
 	}
 
-	if (td->o.zone_size && td->o.zone_skip)
-		bytes_total /= (td->o.zone_skip / td->o.zone_size);
-
 	if (td->runstate == TD_RUNNING || td->runstate == TD_VERIFYING) {
 		double perc, perc_t;
 
diff --git a/init.c b/init.c
index 25c8d78..ce699df 100644
--- a/init.c
+++ b/init.c
@@ -415,7 +415,7 @@
 	/*
 	 * only really works with 1 file
 	 */
-	if (o->zone_size && o->open_files == 1)
+	if (o->zone_size && o->open_files > 1)
 		o->zone_size = 0;
 
 	/*
@@ -869,6 +869,7 @@
 	td->mutex = fio_mutex_init(FIO_MUTEX_LOCKED);
 
 	td->ts.clat_percentiles = td->o.clat_percentiles;
+	td->ts.percentile_precision = td->o.percentile_precision;
 	memcpy(td->ts.percentile_list, td->o.percentile_list, sizeof(td->o.percentile_list));
 
 	for (i = 0; i < DDIR_RWDIR_CNT; i++) {
diff --git a/options.c b/options.c
index 63293e0..fcf4270 100644
--- a/options.c
+++ b/options.c
@@ -2851,6 +2851,7 @@
 		.lname	= "Completion latency percentile list",
 		.type	= FIO_OPT_FLOAT_LIST,
 		.off1	= td_var_offset(percentile_list),
+		.off2	= td_var_offset(percentile_precision),
 		.help	= "Specify a custom list of percentiles to report",
 		.def    = "1:5:10:20:30:40:50:60:70:80:90:95:99:99.5:99.9:99.95:99.99",
 		.maxlen	= FIO_IO_U_LIST_MAX_LEN,
diff --git a/os/os-windows.h b/os/os-windows.h
index 98f9030..09f9c54 100644
--- a/os/os-windows.h
+++ b/os/os-windows.h
@@ -151,12 +151,14 @@
 
 static inline unsigned long long os_phys_mem(void)
 {
-	SYSTEM_INFO sysInfo;
-	uintptr_t addr;
+	long pagesize, pages;
 
-	GetSystemInfo(&sysInfo);
-	addr = (uintptr_t)sysInfo.lpMaximumApplicationAddress;
-	return (unsigned long long)addr;
+	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;
 }
 
 static inline void os_get_tmpdir(char *path, int len)
diff --git a/os/windows/eula.rtf b/os/windows/eula.rtf
index be37fad..cc7be7f 100755
--- a/os/windows/eula.rtf
+++ b/os/windows/eula.rtf
Binary files differ
diff --git a/os/windows/install.wxs b/os/windows/install.wxs
index 5845d5f..b43120a 100755
--- a/os/windows/install.wxs
+++ b/os/windows/install.wxs
@@ -7,14 +7,13 @@
 		<?define ProgramDirectory = ProgramFiles64Folder ?>

 	<?endif?>

 

-	<Product Id="F9883688-6AB3-4BD1-AB93-91D39F12F003"

+	<Product Id="*"

 	  Codepage="1252" Language="1033"

 	  Manufacturer="fio" Name="fio"

-	  UpgradeCode="2338A332-5511-43cf-b9BD-5C60496CCFCC" Version="2.0.13">

+	  UpgradeCode="2338A332-5511-43CF-B9BD-5C60496CCFCC" Version="2.0.13">

 		<Package

-		  Comments="Contact: Your local administrator"

 		  Description="Flexible IO Tester"

-		  InstallerVersion="200" Keywords="Installer,MSI,Database"

+		  InstallerVersion="301" Keywords="Installer,MSI,Database"

 		  Languages="1033" Manufacturer="fio"

 		  InstallScope="perMachine" InstallPrivileges="elevated" Compressed="yes"/>

 

@@ -67,11 +66,6 @@
 

 	<UIRef Id="WixUI_Minimal"/>

 

-	<Condition Message="Per-user installations are not supported">

-		Installed OR

-		ALLUSERS=1

-	</Condition>

-

 	<MajorUpgrade AllowDowngrades="no" DowngradeErrorMessage="A newer version of the application is already installed."/>

 </Product>

 </Wix>

diff --git a/os/windows/posix.c b/os/windows/posix.c
index 05fa5a9..6a7841d 100755
--- a/os/windows/posix.c
+++ b/os/windows/posix.c
@@ -43,6 +43,81 @@
   const char *format,
   va_list argptr);
 
+int win_to_posix_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 GetNumLogicalProcessors(void)
 {
 	SYSTEM_LOGICAL_PROCESSOR_INFORMATION *processor_info = NULL;
@@ -79,6 +154,7 @@
 long sysconf(int name)
 {
 	long val = -1;
+	long val2 = -1;
 	SYSTEM_INFO sysInfo;
 	MEMORYSTATUSEX status;
 
@@ -87,7 +163,7 @@
 	case _SC_NPROCESSORS_ONLN:
 		val = GetNumLogicalProcessors();
 		if (val == -1)
-			log_err("_SC_NPROCESSORS_ONLN failed\n");
+			log_err("sysconf(_SC_NPROCESSORS_ONLN) failed\n");
 
 		break;
 
@@ -98,8 +174,11 @@
 
 	case _SC_PHYS_PAGES:
 		status.dwLength = sizeof(status);
-		GlobalMemoryStatusEx(&status);
-		val = status.ullTotalPhys;
+		val2 = sysconf(_SC_PAGESIZE);
+		if (GlobalMemoryStatusEx(&status) && val2 != -1)
+			val = status.ullTotalPhys / val2;
+		else
+			log_err("sysconf(_SC_PHYS_PAGES) failed\n");
 		break;
 	default:
 		log_err("sysconf(%d) is not implemented\n", name);
@@ -210,6 +289,8 @@
 	if ((flags & MAP_ANON) | (flags & MAP_ANONYMOUS))
 	{
 		allocAddr = VirtualAlloc(addr, len, MEM_COMMIT, vaProt);
+		if (allocAddr == NULL)
+			errno = win_to_posix_error(GetLastError());
 	}
 
 	return allocAddr;
@@ -217,21 +298,26 @@
 
 int munmap(void *addr, size_t len)
 {
-	return !VirtualFree(addr, 0, MEM_RELEASE);
+	if (!VirtualFree(addr, 0, MEM_RELEASE)) {
+		errno = win_to_posix_error(GetLastError());
+		return -1;
+	}
+
+	return 0;
 }
 
 int fork(void)
 {
 	log_err("%s is not implemented\n", __func__);
 	errno = ENOSYS;
-	return (-1);
+	return -1;
 }
 
 pid_t setsid(void)
 {
 	log_err("%s is not implemented\n", __func__);
 	errno = ENOSYS;
-	return (-1);
+	return -1;
 }
 
 static HANDLE log_file = INVALID_HANDLE_VALUE;
@@ -276,7 +362,7 @@
 int kill(pid_t pid, int sig)
 {
 	errno = ESRCH;
-	return (-1);
+	return -1;
 }
 
 /*
@@ -297,7 +383,7 @@
 		return 0;
 	else if (cmd != F_SETFL) {
 		errno = EINVAL;
-		return (-1);
+		return -1;
 	}
 
 	va_start(ap, 1);
@@ -369,12 +455,42 @@
 
 int mlock(const void * addr, size_t len)
 {
-	return !VirtualLock((LPVOID)addr, len);
+	SIZE_T min, max;
+	BOOL success;
+	HANDLE process = GetCurrentProcess();
+
+	success = GetProcessWorkingSetSize(process, &min, &max);
+	if (!success) {
+		errno = win_to_posix_error(GetLastError());
+		return -1;
+	}
+
+	min += len;
+	max += len;
+	success = SetProcessWorkingSetSize(process, min, max);
+	if (!success) {
+		errno = win_to_posix_error(GetLastError());
+		return -1;
+	}
+
+	success = VirtualLock((LPVOID)addr, len);
+	if (!success) {
+		errno = win_to_posix_error(GetLastError());
+		return -1;
+	}
+
+	return 0;
 }
 
 int munlock(const void * addr, size_t len)
 {
-	return !VirtualUnlock((LPVOID)addr, len);
+	BOOL success = VirtualUnlock((LPVOID)addr, len);
+	if (!success) {
+		errno = win_to_posix_error(GetLastError());
+		return -1;
+	}
+
+	return 0;
 }
 
 pid_t waitpid(pid_t pid, int *stat_loc, int options)
@@ -408,68 +524,15 @@
 	return name;
 }
 
-int posix_fallocate(int fd, off_t offset, off_t len)
-{
-	const int BUFFER_SIZE = 256 * 1024;
-	int rc = 0;
-	char *buf;
-	unsigned int write_len;
-	unsigned int bytes_written;
-	off_t bytes_remaining = len;
-
-	if (len == 0 || offset < 0)
-		return EINVAL;
-
-	buf = malloc(BUFFER_SIZE);
-
-	if (buf == NULL)
-		return ENOMEM;
-
-	memset(buf, 0, BUFFER_SIZE);
-
-	int64_t prev_pos = _telli64(fd);
-
-	if (_lseeki64(fd, offset, SEEK_SET) == -1)
-		return errno;
-
-	while (bytes_remaining > 0) {
-		if (bytes_remaining < BUFFER_SIZE)
-			write_len = (unsigned int)bytes_remaining;
-		else
-			write_len = BUFFER_SIZE;
-
-		bytes_written = _write(fd, buf, write_len);
-		if (bytes_written == -1) {
-			rc = errno;
-			break;
-		}
-
-		/* Don't allow Windows to cache the write: flush it to disk */
-		_commit(fd);
-
-		bytes_remaining -= bytes_written;
-	}
-
-	free(buf);
-	_lseeki64(fd, prev_pos, SEEK_SET);
-	return rc;
-}
-
-int ftruncate(int fildes, off_t length)
-{
-	BOOL bSuccess;
-	int64_t prev_pos = _telli64(fildes);
-	_lseeki64(fildes, length, SEEK_SET);
-	HANDLE hFile = (HANDLE)_get_osfhandle(fildes);
-	bSuccess = SetEndOfFile(hFile);
-	_lseeki64(fildes, prev_pos, SEEK_SET);
-	return !bSuccess;
-}
-
 int fsync(int fildes)
 {
 	HANDLE hFile = (HANDLE)_get_osfhandle(fildes);
-	return !FlushFileBuffers(hFile);
+	if (!FlushFileBuffers(hFile)) {
+		errno = win_to_posix_error(GetLastError());
+		return -1;
+	}
+
+	return 0;
 }
 
 int nFileMappings = 0;
@@ -497,14 +560,33 @@
 	void* mapAddr;
 	MEMORY_BASIC_INFORMATION memInfo;
 	mapAddr = MapViewOfFile(fileMappings[shmid], FILE_MAP_ALL_ACCESS, 0, 0, 0);
-	VirtualQuery(mapAddr, &memInfo, sizeof(memInfo));
+	if (mapAddr == NULL) {
+		errno = win_to_posix_error(GetLastError());
+		return (void*)-1;
+	}
+
+	if (VirtualQuery(mapAddr, &memInfo, sizeof(memInfo)) == 0) {
+		errno = win_to_posix_error(GetLastError());
+		return (void*)-1;
+	}
+
 	mapAddr = VirtualAlloc(mapAddr, memInfo.RegionSize, MEM_COMMIT, PAGE_READWRITE);
+	if (mapAddr == NULL) {
+		errno = win_to_posix_error(GetLastError());
+		return (void*)-1;
+	}
+
 	return mapAddr;
 }
 
 int shmdt(const void *shmaddr)
 {
-	return !UnmapViewOfFile(shmaddr);
+	if (!UnmapViewOfFile(shmaddr)) {
+		errno = win_to_posix_error(GetLastError());
+		return -1;
+	}
+
+	return 0;
 }
 
 int shmctl(int shmid, int cmd, struct shmid_ds *buf)
@@ -515,21 +597,22 @@
 	} else {
 		log_err("%s is not implemented\n", __func__);
 	}
-	return (-1);
+	errno = ENOSYS;
+	return -1;
 }
 
 int setuid(uid_t uid)
 {
 	log_err("%s is not implemented\n", __func__);
 	errno = ENOSYS;
-	return (-1);
+	return -1;
 }
 
 int setgid(gid_t gid)
 {
 	log_err("%s is not implemented\n", __func__);
 	errno = ENOSYS;
-	return (-1);
+	return -1;
 }
 
 int nice(int incr)
@@ -622,14 +705,14 @@
 {
 	log_err("%s is not implemented\n", __func__);
 	errno = ENOSYS;
-	return (-1);
+	return -1;
 }
 
 ssize_t writev(int fildes, const struct iovec *iov, int iovcnt)
 {
 	log_err("%s is not implemented\n", __func__);
 	errno = ENOSYS;
-	return (-1);
+	return -1;
 }
 
 long long strtoll(const char *restrict str, char **restrict endptr,
@@ -725,40 +808,40 @@
 
 DIR *opendir(const char *dirname)
 {
-    struct dirent_ctx *dc = NULL;
+	struct dirent_ctx *dc = NULL;
 
-    /* See if we can open it. If not, we'll return an error here */
-    HANDLE file = CreateFileA(dirname, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
-    if (file != INVALID_HANDLE_VALUE) {
-        CloseHandle(file);
-        dc = (struct dirent_ctx*)malloc(sizeof(struct dirent_ctx));
-        StringCchCopyA(dc->dirname, MAX_PATH, dirname);
-        dc->find_handle = INVALID_HANDLE_VALUE;
-    } else {
-        DWORD error = GetLastError();
-        if (error == ERROR_FILE_NOT_FOUND)
-            errno = ENOENT;
+	/* See if we can open it. If not, we'll return an error here */
+	HANDLE file = CreateFileA(dirname, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+	if (file != INVALID_HANDLE_VALUE) {
+		CloseHandle(file);
+		dc = (struct dirent_ctx*)malloc(sizeof(struct dirent_ctx));
+		StringCchCopyA(dc->dirname, MAX_PATH, dirname);
+		dc->find_handle = INVALID_HANDLE_VALUE;
+	} else {
+		DWORD error = GetLastError();
+		if (error == ERROR_FILE_NOT_FOUND)
+			errno = ENOENT;
 
-        else if (error == ERROR_PATH_NOT_FOUND)
-            errno = ENOTDIR;
-        else if (error == ERROR_TOO_MANY_OPEN_FILES)
-            errno = ENFILE;
-        else if (error == ERROR_ACCESS_DENIED)
-            errno = EACCES;
-        else
-            errno = error;
-    }
+		else if (error == ERROR_PATH_NOT_FOUND)
+			errno = ENOTDIR;
+		else if (error == ERROR_TOO_MANY_OPEN_FILES)
+			errno = ENFILE;
+		else if (error == ERROR_ACCESS_DENIED)
+			errno = EACCES;
+		else
+			errno = error;
+	}
 
-    return dc;
+	return dc;
 }
 
 int closedir(DIR *dirp)
 {
-    if (dirp != NULL && dirp->find_handle != INVALID_HANDLE_VALUE)
-        FindClose(dirp->find_handle);
+	if (dirp != NULL && dirp->find_handle != INVALID_HANDLE_VALUE)
+		FindClose(dirp->find_handle);
 
-    free(dirp);
-    return 0;
+	free(dirp);
+	return 0;
 }
 
 struct dirent *readdir(DIR *dirp)
diff --git a/os/windows/posix.h b/os/windows/posix.h
index cb89cf6..85640a2 100644
--- a/os/windows/posix.h
+++ b/os/windows/posix.h
@@ -6,5 +6,6 @@
 
 extern int clock_gettime(clockid_t clock_id, struct timespec *tp);
 extern int inet_aton(const char *, struct in_addr *);
+extern int win_to_posix_error(DWORD winerr);
 
 #endif
diff --git a/parse.c b/parse.c
index 7652a4d..9250133 100644
--- a/parse.c
+++ b/parse.c
@@ -501,6 +501,21 @@
 		break;
 	}
 	case FIO_OPT_FLOAT_LIST: {
+		char *cp2;
+
+		if (first) {
+			/*
+			** Initialize precision to 0 and zero out list
+			** in case specified list is shorter than default
+			*/
+			ul2 = 0;
+			ilp = td_var(data, o->off2);
+			*ilp = ul2;
+
+			flp = td_var(data, o->off1);
+			for(i = 0; i < o->maxlen; i++)
+				flp[i].u.f = 0.0;
+		}
 		if (curr >= o->maxlen) {
 			log_err("the list exceeding max length %d\n",
 					o->maxlen);
@@ -524,6 +539,23 @@
 		flp = td_var(data, o->off1);
 		flp[curr].u.f = uf;
 
+		/*
+		** Calculate precision for output by counting
+		** number of digits after period. Find first
+		** period in entire remaining list each time
+		*/
+		cp2 = strchr(ptr, '.');
+		if (cp2 != NULL) {
+			int len = 0;
+
+			while (*++cp2 != '\0' && *cp2 >= '0' && *cp2 <= '9')
+				len++;
+
+			ilp = td_var(data, o->off2);
+			if (len > *ilp)
+				*ilp = len;
+		}
+
 		break;
 	}
 	case FIO_OPT_STR_STORE: {
diff --git a/stat.c b/stat.c
index b16c55c..8835f7f 100644
--- a/stat.c
+++ b/stat.c
@@ -180,11 +180,12 @@
  * Find and display the p-th percentile of clat
  */
 static void show_clat_percentiles(unsigned int *io_u_plat, unsigned long nr,
-				  fio_fp64_t *plist)
+				  fio_fp64_t *plist, unsigned int precision)
 {
 	unsigned int len, j = 0, minv, maxv;
 	unsigned int *ovals;
-	int is_last, scale_down;
+	int is_last, per_line, scale_down;
+	char fmt[32];
 
 	len = calc_clat_percentiles(io_u_plat, nr, plist, &ovals, &maxv, &minv);
 	if (!len)
@@ -202,20 +203,23 @@
 		log_info("    clat percentiles (usec):\n     |");
 	}
 
+	snprintf(fmt, sizeof(fmt), "%%1.%uf", precision);
+	per_line = (80 - 7) / (precision + 14);
+
 	for (j = 0; j < len; j++) {
-		char fbuf[8];
+		char fbuf[16], *ptr = fbuf;
 
 		/* for formatting */
-		if (j != 0 && (j % 4) == 0)
+		if (j != 0 && (j % per_line) == 0)
 			log_info("     |");
 
 		/* end of the list */
 		is_last = (j == len - 1);
 
 		if (plist[j].u.f < 10.0)
-			sprintf(fbuf, " %2.2f", plist[j].u.f);
-		else
-			sprintf(fbuf, "%2.2f", plist[j].u.f);
+			ptr += sprintf(fbuf, " ");
+
+		snprintf(ptr, sizeof(fbuf), fmt, plist[j].u.f);
 
 		if (scale_down)
 			ovals[j] = (ovals[j] + 999) / 1000;
@@ -225,7 +229,7 @@
 		if (is_last)
 			break;
 
-		if (j % 4 == 3)	/* for formatting */
+		if ((j % per_line) == per_line - 1)	/* for formatting */
 			log_info("\n");
 	}
 
@@ -397,7 +401,8 @@
 	if (ts->clat_percentiles) {
 		show_clat_percentiles(ts->io_u_plat[ddir],
 					ts->clat_stat[ddir].samples,
-					ts->percentile_list);
+					ts->percentile_list,
+					ts->percentile_precision);
 	}
 	if (calc_lat(&ts->bw_stat[ddir], &min, &max, &mean, &dev)) {
 		double p_of_agg = 100.0;
@@ -614,7 +619,7 @@
 			log_info(";0%%=0");
 			continue;
 		}
-		log_info(";%2.2f%%=%u", ts->percentile_list[i].u.f, ovals[i]);
+		log_info(";%f%%=%u", ts->percentile_list[i].u.f, ovals[i]);
 	}
 
 	if (calc_lat(&ts->lat_stat[ddir], &min, &max, &mean, &dev))
@@ -712,7 +717,7 @@
 			json_object_add_value_int(percentile_object, "0.00", 0);
 			continue;
 		}
-		snprintf(buf, sizeof(buf), "%2.2f", ts->percentile_list[i].u.f);
+		snprintf(buf, sizeof(buf), "%f", ts->percentile_list[i].u.f);
 		json_object_add_value_int(percentile_object, (const char *)buf, ovals[i]);
 	}
 
@@ -1169,6 +1174,7 @@
 		ts = &threadstats[j];
 
 		ts->clat_percentiles = td->o.clat_percentiles;
+		ts->percentile_precision = td->o.percentile_precision;
 		memcpy(ts->percentile_list, td->o.percentile_list, sizeof(td->o.percentile_list));
 
 		idx++;
diff --git a/stat.h b/stat.h
index ba4c2bf..a3b391c 100644
--- a/stat.h
+++ b/stat.h
@@ -147,6 +147,7 @@
 	 * IO depth and latency stats
 	 */
 	uint64_t clat_percentiles;
+	uint64_t percentile_precision;
 	fio_fp64_t percentile_list[FIO_IO_U_LIST_MAX_LEN];
 
 	uint32_t io_u_map[FIO_IO_U_MAP_NR];
diff --git a/t/log.c b/t/log.c
index 76ae68e..1ed3851 100644
--- a/t/log.c
+++ b/t/log.c
@@ -1,5 +1,6 @@
 #include <stdio.h>
 #include <stdarg.h>
+#include "../minmax.h"
 
 int log_err(const char *format, ...)
 {
diff --git a/thread_options.h b/thread_options.h
index 11e7af7..a28ccfe 100644
--- a/thread_options.h
+++ b/thread_options.h
@@ -197,6 +197,7 @@
 	unsigned int trim_zero;
 	unsigned long long trim_backlog;
 	unsigned int clat_percentiles;
+	unsigned int percentile_precision;	/* digits after decimal for percentiles */
 	fio_fp64_t percentile_list[FIO_IO_U_LIST_MAX_LEN];
 
 	char *read_iolog_file;
@@ -396,6 +397,7 @@
 	uint32_t trim_zero;
 	uint64_t trim_backlog;
 	uint32_t clat_percentiles;
+	uint32_t percentile_precision;
 	fio_fp64_t percentile_list[FIO_IO_U_LIST_MAX_LEN];
 
 	uint8_t read_iolog_file[FIO_TOP_STR_MAX];