Avoid opening files until they are used

Fio still opens and creates all files, just to check the size of them. Add
a specialized IO engine op for getting the size of a file and use that
instead.

This also cleans a lot of things up. Note that the IO engine version is now
bumped to 10, meaning that external engines will have to separate the
file open from the size checking.

Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
diff --git a/engines/guasi.c b/engines/guasi.c
index 4ae0143..3802f2c 100644
--- a/engines/guasi.c
+++ b/engines/guasi.c
@@ -261,6 +261,7 @@
 	.cleanup	= fio_guasi_cleanup,
 	.open_file	= generic_open_file,
 	.close_file	= generic_close_file,
+	.get_file_size	= generic_get_file_size,
 };
 
 #else /* FIO_HAVE_GUASI */
diff --git a/engines/libaio.c b/engines/libaio.c
index 99170b5..4f1e0ed 100644
--- a/engines/libaio.c
+++ b/engines/libaio.c
@@ -223,6 +223,7 @@
 	.cleanup	= fio_libaio_cleanup,
 	.open_file	= generic_open_file,
 	.close_file	= generic_close_file,
+	.get_file_size	= generic_get_file_size,
 };
 
 #else /* FIO_HAVE_LIBAIO */
diff --git a/engines/mmap.c b/engines/mmap.c
index 5b55a81..dffad90 100644
--- a/engines/mmap.c
+++ b/engines/mmap.c
@@ -131,6 +131,7 @@
 	.queue		= fio_mmapio_queue,
 	.open_file	= fio_mmapio_open,
 	.close_file	= fio_mmapio_close,
+	.get_file_size	= generic_get_file_size,
 	.flags		= FIO_SYNCIO | FIO_NOEXTEND,
 };
 
diff --git a/engines/posixaio.c b/engines/posixaio.c
index db11edb..3ffdcb6 100644
--- a/engines/posixaio.c
+++ b/engines/posixaio.c
@@ -226,6 +226,7 @@
 	.cleanup	= fio_posixaio_cleanup,
 	.open_file	= generic_open_file,
 	.close_file	= generic_close_file,
+	.get_file_size	= generic_get_file_size,
 };
 
 #else /* FIO_HAVE_POSIXAIO */
diff --git a/engines/sg.c b/engines/sg.c
index d7c0a6d..39f99d8 100644
--- a/engines/sg.c
+++ b/engines/sg.c
@@ -404,6 +404,7 @@
 	.cleanup	= fio_sgio_cleanup,
 	.open_file	= fio_sgio_open,
 	.close_file	= generic_close_file,
+	.get_file_size	= generic_get_file_size,
 	.flags		= FIO_SYNCIO | FIO_RAWIO,
 };
 
diff --git a/engines/solarisaio.c b/engines/solarisaio.c
index d499f57..a48ec41 100644
--- a/engines/solarisaio.c
+++ b/engines/solarisaio.c
@@ -213,6 +213,7 @@
 	.cleanup	= fio_solarisaio_cleanup,
 	.open_file	= generic_open_file,
 	.close_file	= generic_close_file,
+	.get_file_size	= generic_get_file_size,
 };
 
 #else /* FIO_HAVE_SOLARISAIO */
diff --git a/engines/splice.c b/engines/splice.c
index a847f9c..26c5c9a 100644
--- a/engines/splice.c
+++ b/engines/splice.c
@@ -293,6 +293,7 @@
 	.cleanup	= fio_spliceio_cleanup,
 	.open_file	= generic_open_file,
 	.close_file	= generic_close_file,
+	.get_file_size	= generic_get_file_size,
 	.flags		= FIO_SYNCIO,
 };
 
diff --git a/engines/sync.c b/engines/sync.c
index 4ca04d5..842c6c0 100644
--- a/engines/sync.c
+++ b/engines/sync.c
@@ -283,6 +283,7 @@
 	.queue		= fio_syncio_queue,
 	.open_file	= generic_open_file,
 	.close_file	= generic_close_file,
+	.get_file_size	= generic_get_file_size,
 	.flags		= FIO_SYNCIO,
 };
 
@@ -292,6 +293,7 @@
 	.queue		= fio_psyncio_queue,
 	.open_file	= generic_open_file,
 	.close_file	= generic_close_file,
+	.get_file_size	= generic_get_file_size,
 	.flags		= FIO_SYNCIO,
 };
 
@@ -306,6 +308,7 @@
 	.getevents	= fio_vsyncio_getevents,
 	.open_file	= generic_open_file,
 	.close_file	= generic_close_file,
+	.get_file_size	= generic_get_file_size,
 	.flags		= FIO_SYNCIO,
 };
 
diff --git a/engines/syslet-rw.c b/engines/syslet-rw.c
index 2eab207..ad9cb35 100644
--- a/engines/syslet-rw.c
+++ b/engines/syslet-rw.c
@@ -284,6 +284,7 @@
 	.cleanup	= fio_syslet_cleanup,
 	.open_file	= generic_open_file,
 	.close_file	= generic_close_file,
+	.get_file_size	= generic_get_file_size,
 };
 
 #else /* FIO_HAVE_SYSLET */
diff --git a/filesetup.c b/filesetup.c
index 9862c7d..68516b9 100644
--- a/filesetup.c
+++ b/filesetup.c
@@ -136,7 +136,7 @@
 {
 	struct stat st;
 
-	if (fstat(f->fd, &st) == -1) {
+	if (stat(f->file_name, &st) == -1) {
 		td_verror(td, errno, "fstat");
 		return 1;
 	}
@@ -150,19 +150,28 @@
 	unsigned long long bytes;
 	int r;
 
+	if (td->io_ops->open_file(td, f)) {
+		log_err("fio: failed opening blockdev %s for size check\n",
+			f->file_name);
+		return 1;
+	}
+
 	r = blockdev_size(f->fd, &bytes);
 	if (r) {
 		td_verror(td, r, "blockdev_size");
-		return 1;
+		goto err;
 	}
 
 	if (!bytes) {
 		log_err("%s: zero sized block device?\n", f->file_name);
-		return 1;
+		goto err;
 	}
 
 	f->real_file_size = bytes;
 	return 0;
+err:
+	td->io_ops->close_file(td, f);
+	return 1;
 }
 
 static int get_file_size(struct thread_data *td, struct fio_file *f)
@@ -355,9 +364,6 @@
 		td_verror(td, __e, buf);
 	}
 
-	if (get_file_size(td, f))
-		goto err;
-
 	if (!from_hash && f->fd != -1) {
 		if (add_file_hash(f)) {
 			int ret;
@@ -371,43 +377,11 @@
 	}
 
 	return 0;
-err:
-	close(f->fd);
-	return 1;
 }
 
-int open_files(struct thread_data *td)
+int generic_get_file_size(struct thread_data *td, struct fio_file *f)
 {
-	struct fio_file *f;
-	unsigned int i;
-	int err = 0;
-
-	dprint(FD_FILE, "open files\n");
-
-	for_each_file(td, f, i) {
-		err = td_io_open_file(td, f);
-		if (err) {
-			if (td->error == EMFILE) {
-				log_err("fio: limited open files to: %d\n",
-							td->nr_open_files);
-				td->o.open_files = td->nr_open_files;
-				err = 0;
-				clear_error(td);
-			}
-			break;
-		}
-
-		if (td->o.open_files == td->nr_open_files)
-			break;
-	}
-
-	if (!err)
-		return 0;
-
-	for_each_file(td, f, i)
-		td_io_close_file(td, f);
-
-	return err;
+	return get_file_size(td, f);
 }
 
 /*
@@ -423,15 +397,12 @@
 		dprint(FD_FILE, "get file size for %p/%d/%p\n", f, i,
 								f->file_name);
 
-		if (td->io_ops->open_file(td, f)) {
+		if (td->io_ops->get_file_size(td, f)) {
 			if (td->error != ENOENT) {
 				log_err("%s\n", td->verror);
 				err = 1;
 			}
 			clear_error(td);
-		} else {
-			if (td->io_ops->close_file)
-				td->io_ops->close_file(td, f);
 		}
 
 		if (f->real_file_size == -1ULL && td->o.size)
diff --git a/fio.c b/fio.c
index b4415fa..f699951 100644
--- a/fio.c
+++ b/fio.c
@@ -897,25 +897,16 @@
 	memcpy(&td->start, &tv, sizeof(tv));
 }
 
-static int clear_io_state(struct thread_data *td)
+static void clear_io_state(struct thread_data *td)
 {
 	struct fio_file *f;
 	unsigned int i;
-	int ret;
 
 	reset_io_counters(td);
 
 	close_files(td);
-
-	ret = 0;
-	for_each_file(td, f, i) {
+	for_each_file(td, f, i)
 		f->flags &= ~FIO_FILE_DONE;
-		ret = td_io_open_file(td, f);
-		if (ret)
-			break;
-	}
-
-	return ret;
 }
 
 /*
@@ -1003,9 +994,6 @@
 	if (td_io_init(td))
 		goto err;
 
-	if (open_files(td))
-		goto err;
-
 	if (init_random_map(td))
 		goto err;
 
@@ -1028,8 +1016,8 @@
 			memcpy(&td->lastrate, &td->ts.stat_sample_time,
 							sizeof(td->lastrate));
 
-		if (clear_state && clear_io_state(td))
-			break;
+		if (clear_state)
+			clear_io_state(td);
 
 		prune_io_piece_log(td);
 
@@ -1064,8 +1052,7 @@
 		    (td->io_ops->flags & FIO_UNIDIR))
 			continue;
 
-		if (clear_io_state(td))
-			break;
+		clear_io_state(td);
 
 		fio_gettime(&td->start, NULL);
 
diff --git a/fio.h b/fio.h
index 22fecd9..b6ffe60 100644
--- a/fio.h
+++ b/fio.h
@@ -862,10 +862,10 @@
 extern void close_files(struct thread_data *);
 extern void close_and_free_files(struct thread_data *);
 extern int __must_check setup_files(struct thread_data *);
-extern int __must_check open_files(struct thread_data *);
 extern int __must_check file_invalidate_cache(struct thread_data *, struct fio_file *);
 extern int __must_check generic_open_file(struct thread_data *, struct fio_file *);
 extern int __must_check generic_close_file(struct thread_data *, struct fio_file *);
+extern int __must_check generic_get_file_size(struct thread_data *, struct fio_file *);
 extern int add_file(struct thread_data *, const char *);
 extern void get_file(struct fio_file *);
 extern int __must_check put_file(struct thread_data *, struct fio_file *);
@@ -965,6 +965,7 @@
 extern int __must_check td_io_commit(struct thread_data *);
 extern int __must_check td_io_open_file(struct thread_data *, struct fio_file *);
 extern int td_io_close_file(struct thread_data *, struct fio_file *);
+extern int __must_check td_io_get_file_size(struct thread_data *, struct fio_file *);
 
 /*
  * blktrace support
@@ -990,11 +991,12 @@
 	void (*cleanup)(struct thread_data *);
 	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 *data;
 	void *dlhandle;
 };
 
-#define FIO_IOOPS_VERSION	9
+#define FIO_IOOPS_VERSION	10
 
 extern struct ioengine_ops *load_ioengine(struct thread_data *, const char *);
 extern void register_ioengine(struct ioengine_ops *);
diff --git a/ioengines.c b/ioengines.c
index 8073d1b..2969840 100644
--- a/ioengines.c
+++ b/ioengines.c
@@ -405,3 +405,11 @@
 
 	return put_file(td, f);
 }
+
+int td_io_get_file_size(struct thread_data *td, struct fio_file *f)
+{
+	if (!td->io_ops->get_file_size)
+		return 0;
+
+	return td->io_ops->get_file_size(td, f);
+}