Improve ETA for fill_device based job files

Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
diff --git a/eta.c b/eta.c
index 53dcc93..81dbfa7 100644
--- a/eta.c
+++ b/eta.c
@@ -110,6 +110,13 @@
 
 	bytes_total = td->total_io_size;
 
+	if (td->o.fill_device && td->o.size  == -1ULL) {
+		if (!td->fill_device_size || td->fill_device_size == -1ULL)
+			return 0;
+
+		bytes_total = td->fill_device_size;
+	}
+
 	/*
 	 * if writing, bytes_total will be twice the size. If mixing,
 	 * assume a 50/50 split and thus bytes_total will be 50% larger.
@@ -124,9 +131,6 @@
 	if (td->o.zone_size && td->o.zone_skip)
 		bytes_total /= (td->o.zone_skip / td->o.zone_size);
 
-	if (td->o.fill_device && td->o.size  == -1ULL)
-		return 0;
-
 	if (td->runstate == TD_RUNNING || td->runstate == TD_VERIFYING) {
 		double perc, perc_t;
 
diff --git a/filesetup.c b/filesetup.c
index 32b8b2e..ec5d781 100644
--- a/filesetup.c
+++ b/filesetup.c
@@ -503,6 +503,75 @@
 	return err;
 }
 
+struct fio_mount {
+	struct flist_head list;
+	const char *base;
+	char __base[256];
+	unsigned int key;
+};
+
+/*
+ * Get free number of bytes for each file on each unique mount.
+ */
+static unsigned long long get_fs_free_counts(struct thread_data *td)
+{
+	struct flist_head *n, *tmp;
+	unsigned long long ret;
+	struct fio_mount *fm;
+	FLIST_HEAD(list);
+	struct fio_file *f;
+	unsigned int i;
+
+	for_each_file(td, f, i) {
+		struct stat sb;
+		char buf[256];
+
+		strcpy(buf, f->file_name);
+
+		if (stat(buf, &sb) < 0) {
+			if (errno != ENOENT)
+				break;
+			strcpy(buf, ".");
+			if (stat(buf, &sb) < 0)
+				break;
+		}
+
+		fm = NULL;
+		flist_for_each(n, &list) {
+			fm = flist_entry(n, struct fio_mount, list);
+			if (fm->key == sb.st_dev)
+				break;
+
+			fm = NULL;
+		}
+
+		if (fm)
+			continue;
+
+		fm = malloc(sizeof(*fm));
+		strcpy(fm->__base, buf);
+		fm->base = basename(fm->__base);
+		fm->key = sb.st_dev;
+		flist_add(&fm->list, &list);
+	}
+
+	ret = 0;
+	flist_for_each_safe(n, tmp, &list) {
+		unsigned long long sz;
+
+		fm = flist_entry(n, struct fio_mount, list);
+		flist_del(&fm->list);
+
+		sz = get_fs_size(fm->base);
+		if (sz && sz != -1ULL)
+			ret += sz;
+
+		free(fm);
+	}
+
+	return ret;
+}
+
 /*
  * Open the files and setup files sizes, creating files if necessary.
  */
@@ -543,6 +612,9 @@
 			total_size += f->real_file_size;
 	}
 
+	if (td->o.fill_device)
+		td->fill_device_size = get_fs_free_counts(td);
+
 	/*
 	 * device/file sizes are zero and no size given, punt
 	 */
diff --git a/fio.h b/fio.h
index 5dc0352..56aa9b6 100644
--- a/fio.h
+++ b/fio.h
@@ -373,6 +373,7 @@
 	struct timeval lastrate[2];
 
 	unsigned long long total_io_size;
+	unsigned long long fill_device_size;
 
 	unsigned long io_issues[2];
 	unsigned long long io_blocks[2];
diff --git a/os/os-linux.h b/os/os-linux.h
index 8c61cc0..b6ba569 100644
--- a/os/os-linux.h
+++ b/os/os-linux.h
@@ -4,6 +4,7 @@
 #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>
@@ -35,6 +36,7 @@
 #define FIO_HAVE_CGROUPS
 #define FIO_HAVE_FDATASYNC
 #define FIO_HAVE_SYNC_FILE_RANGE
+#define FIO_HAVE_FS_STAT
 
 #define OS_MAP_ANON		MAP_ANONYMOUS
 
@@ -280,4 +282,17 @@
 		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;
+}
+
 #endif
diff --git a/os/os.h b/os/os.h
index b7fce80..218766a 100644
--- a/os/os.h
+++ b/os/os.h
@@ -147,4 +147,11 @@
 }
 #endif
 
+#ifndef FIO_HAVE_FS_STAT
+static inline unsigned long long get_fs_size(const char *path)
+{
+	return 0;
+}
+#endif
+
 #endif