Add file reference counting

We must not close a file, while io is in progress to it. That
will make the queuing engines barf.

Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
diff --git a/filesetup.c b/filesetup.c
index f6e9974..f3fccdb 100644
--- a/filesetup.c
+++ b/filesetup.c
@@ -445,3 +445,23 @@
 	td->open_files++;
 	td->nr_uniq_files = td->open_files;
 }
+
+void get_file(struct fio_file *f)
+{
+	f->references++;
+}
+
+void put_file(struct thread_data *td, struct fio_file *f)
+{
+	if (!(f->flags & FIO_FILE_OPEN))
+		return;
+
+	assert(f->references);
+	if (--f->references)
+		return;
+
+	if (td->io_ops->close_file)
+		td->io_ops->close_file(td, f);
+	td->nr_open_files--;
+	f->flags &= ~FIO_FILE_OPEN;
+}
diff --git a/fio.h b/fio.h
index e451f95..5bf8c43 100644
--- a/fio.h
+++ b/fio.h
@@ -219,8 +219,9 @@
 };
 
 enum fio_file_flags {
-	FIO_FILE_OPEN		= 1 << 0,
-	FIO_FILE_UNLINK		= 1 << 1,
+	FIO_FILE_OPEN		= 1 << 0,	/* file is open */
+	FIO_FILE_UNLINK		= 1 << 1,	/* unlink on close */
+	FIO_FILE_CLOSING	= 1 << 2,	/* file being closed */
 };
 
 /*
@@ -252,6 +253,7 @@
 	unsigned int num_maps;
 	unsigned int last_free_lookup;
 
+	int references;
 	enum fio_file_flags flags;
 };
 
@@ -654,6 +656,8 @@
 extern int __must_check generic_open_file(struct thread_data *, struct fio_file *);
 extern void generic_close_file(struct thread_data *, struct fio_file *);
 extern void add_file(struct thread_data *, const char *);
+extern void get_file(struct fio_file *);
+extern void put_file(struct thread_data *, struct fio_file *);
 
 /*
  * ETA/status stuff
diff --git a/init.c b/init.c
index f3c756e..66a50b0 100644
--- a/init.c
+++ b/init.c
@@ -752,7 +752,9 @@
 	if (td->iodepth_batch > td->iodepth || !td->iodepth_batch)
 		td->iodepth_batch = td->iodepth;
 
-	if (td->open_files > td->nr_files || !td->open_files)
+	if (!td->nr_files)
+		td->nr_files = td->open_files;
+	else if (td->open_files > td->nr_files || !td->open_files)
 		td->open_files = td->nr_files;
 }
 
@@ -1103,9 +1105,12 @@
 	struct thread_data *td = data;
 	char *fname, *str;
 
+	td->nr_files = 0;
 	str = strdup(input);
-	while ((fname = strsep(&str, ":")) != NULL)
+	while ((fname = strsep(&str, ":")) != NULL) {
 		add_file(td, fname);
+		td->nr_files++;
+	}
 
 	return 0;
 }
diff --git a/io_u.c b/io_u.c
index c16128e..477f387 100644
--- a/io_u.c
+++ b/io_u.c
@@ -340,7 +340,8 @@
 
 		fileno = (unsigned int) ((double) (td->open_files * r) / (RAND_MAX + 1.0));
 		f = &td->files[fileno];
-		if (f->flags & FIO_FILE_OPEN)
+		if ((f->flags & FIO_FILE_OPEN) &&
+		    !(f->flags & FIO_FILE_CLOSING))
 			return f;
 	} while (1);
 }
@@ -360,7 +361,8 @@
 		if (td->next_file >= td->open_files)
 			td->next_file = 0;
 
-		if (f->flags & FIO_FILE_OPEN)
+		if ((f->flags & FIO_FILE_OPEN) &&
+		    !(f->flags & FIO_FILE_CLOSING))
 			break;
 
 		f = NULL;
@@ -537,6 +539,8 @@
 	assert(io_u->flags & IO_U_F_FLIGHT);
 	io_u->flags &= ~IO_U_F_FLIGHT;
 
+	put_file(td, io_u->file);
+
 	if (io_u->ddir == DDIR_SYNC) {
 		td->last_was_sync = 1;
 		return;
diff --git a/ioengines.c b/ioengines.c
index 02455b8..185314f 100644
--- a/ioengines.c
+++ b/ioengines.c
@@ -206,6 +206,9 @@
 
 	ret = td->io_ops->queue(td, io_u);
 
+	if (ret == FIO_Q_QUEUED || ret == FIO_Q_COMPLETED)
+		get_file(io_u->file);
+
 	if (ret == FIO_Q_QUEUED) {
 		int r;
 
@@ -260,20 +263,23 @@
 	f->last_completed_pos = 0;
 	f->last_pos = 0;
 	f->flags |= FIO_FILE_OPEN;
+	f->flags &= ~FIO_FILE_CLOSING;
 
 	if (f->file_map)
 		memset(f->file_map, 0, f->num_maps * sizeof(long));
 
 	td->nr_open_files++;
+	get_file(f);
 	return 0;
 }
 
 void td_io_close_file(struct thread_data *td, struct fio_file *f)
 {
-	if (f->flags & FIO_FILE_OPEN) {
-		if (td->io_ops->close_file)
-			td->io_ops->close_file(td, f);
-		td->nr_open_files--;
-		f->flags &= ~FIO_FILE_OPEN;
-	}
+	/*
+	 * mark as closing, do real close when last io on it has completed
+	 */
+	f->flags |= FIO_FILE_CLOSING;
+
+	put_file(td, f);
 }
+